How it works What it refuses Security

The Kollen correction was six characters. The file grew by 127, and the filing had already contradicted itself 223 pages later.

Kentucky PSC Case No. 2025-00113 · 4 August 2026

On 9 September 2025 the Kentucky Attorney General and the Kentucky Industrial Utility Customers filed Lane Kollen's joint direct testimony in the KU rate case: 378 pages, 1,024,409 characters. On 30 September they filed a corrected version: 1,024,536 characters. A difference of 127 in a million.

What moved, what it would cost to miss, and why a diff of the two reports 144 changes rather than two. Every number below came out of those files; the recipe is at the foot.

What moved

Two substitutions, both on PDF page 98 — the page the testimony prints as 95.

Original, filed 9 September 2025

7 their request for CPCN for Brown BESS. The Companies agree to take necessary

ky-2025-00113-kollen-direct-testimony-original.txt, PDF page 98 · commission copy
Corrected, filed 30 September 2025

7 their request for CPCN for Cane Run BESS. The Companies agree to take

ky-2025-00113-kollen-direct-testimony-corrected-clean.txt, PDF page 98 · commission copy

The same swap happens four lines earlier, on line 3. That is the whole correction. The Companies were withdrawing a certificate application for a battery plant and the testimony named the wrong plant. That is not a typo. It is testimony about which asset they have stopped building.

The 127 characters are not the correction

"Brown" to "Cane Run" adds three characters, twice. Six. The file grew by 127.

Count both files again with every space, tab and newline discarded and the corrected copy is shorter: 514,194 non-space characters against 514,259. Its whitespace grew by 192. The OCR jitter below says it was re-scanned rather than re-exported, and the extractor broke the lines differently, so the delta measures the scanner. Watch file size and you would have seen this document grow on the day its text shrank.

The diff reports 144 changes

I ran this product's own diff engine over the pair — app/diff/engine.py, 126 lines around difflib.SequenceMatcher, no model and no network. The splitting happens before it, in app/ingestion/ingest.py, whose _segment cuts each file on blank lines: 4,285 passages against 4,287. The engine takes those two lists and returns 144 changes: 134 modified, 6 added, 4 removed.

It takes about 0.4 seconds on an Apple M5 Pro. Measure your own machine rather than quoting mine, and treat the figure as a scale rather than a benchmark: it is one pair of files on one laptop.

Two of the 144 are the correction.

Where a diff of the two filings puts its 144 changes changes reported, by PDF page · 61 of 378 pages touched pages 244–378 · 135 of the 144 changes 2 4 6 8 10 page 98 2 of its 6 are the correction 1 100 200 300 378 PDF page
reported change the correction, and the line pointing at it
The amendment is not the loudest thing in the diff. Page 98 carries six reported changes and two of them are the correction. Page 314 carries eight and none of them is. The testimony and its affidavit end at page 101 and the exhibits run from 102 to 378, but the churn is not spread across them: 135 of the 144 fall after page 243 — data-request attachments, depreciation schedules, the stipulation with its signature pages, and tariff sheets. Counts produced by the run described at the foot of this page.

The other 142

They are the scanner disagreeing with itself. Discard every space, tab and newline and 63 of the 144 disappear; 81 survive. Fifty-three of the 81 change at least one digit. Merely collapsing runs of whitespace removes none of the 144: the engine already does that before it compares.

Original, PDF page 345 — a tariff sheet

ISSUED BY: /s/ Robert M. Conroy, Vice President

ky-2025-00113-kollen-direct-testimony-original.txt · commission copy
Corrected, PDF page 345 — the same sheet

ISSUED BY: Isl Robert M. Conroy, Vice President

ky-2025-00113-kollen-direct-testimony-corrected-clean.txt · commission copy

That one is harmless once seen. This one is not, and it runs the other way — the original is corrupt and the correction repairs it, on a page nobody amended.

Original, PDF page 287 — a depreciation schedule

8,000,000.00 B, 960,000 248,131

ky-2025-00113-kollen-direct-testimony-original.txt · commission copy
Corrected, PDF page 287

8,000,000.00 8,960,000 248,131

ky-2025-00113-kollen-direct-testimony-corrected-clean.txt · commission copy

So "the later filing is the better text" is not a rule you can use. In Exhibit LK-19 the original misreads Parties as Patties six times against the corrected copy's two — but the corrected copy invents Pa11ies twice and repo11s once where the original had neither.

Ranking will not rescue you. Order all 144 by alignment confidence, least first, and the real changes arrive 38th and 39th. Nothing about a genuine amendment makes it look more changed than a re-scanned signature block.

The cover letter is not a manifest

The cover letter filed with the correction, 30 September 2025

The corrections are found on page 95 of Mr. Kollen’s testimony, and changes original references to “Brown BESS” to now read “Cane Run BESS.”

ky-2025-00113-kollen-correction-cover-letter.txt · commission copy

References, plural. One page named — the page printed on the document, which is PDF page 98. No count. The original holds six occurrences of Brown followed by BESS; two were changed. The four that survive, on PDF pages 277 and 279, are right as they stand. They sit inside a data request the Attorney General and KIUC sent themselves, Joint Supplemental Question No. 33, quoted back word for word in KU's response on page 277 and LG&E's on page 279.

Corrected copy, PDF page 277 — inside KU's response to Question No. 33, left alone

depreciation rates for Brown BESS, AMI, Paddy's Run CT pipeline,

ky-2025-00113-kollen-direct-testimony-corrected-clean.txt · commission copy

Note the double spaces — and three lines up, the extraction breaks Brown and BESS across a line. So a plain search for Brown BESS in the corrected copy returns zero, telling you the term is gone while four remain. Replace what such a search does find and you misquote the Attorney General's own data request back at the commission.

Widening the search is worse. Brown appears 41 times in the corrected copy and 37 have nothing to do with a battery: the E.W. Brown Generating Station, ten Brown Units in one depreciation table — there is no Unit 4 — Brown Solar and Wind, Brown 12 twice on the corrected page 95 and never wrong, and a man named Elijah Brown. The correction is scoped to a page, not a term, and the letter does not say so.

The filing already knew

The sentence that was wrong carries a footnote.

Original, PDF page 98 — footnote 84

84 I have attached a copy of the Stipulation and Recommendation in Case 2025-00045 as Exhibit LK-19.

ky-2025-00113-kollen-direct-testimony-original.txt · commission copy

Exhibit LK-19 begins on PDF page 318 of the same file. Paragraph 1.2 of the stipulation, on PDF page 321, reads:

Original, PDF page 321 — inside Exhibit LK-19

1.2. Withdrawal of Cane Run Battery Energy Storage System ("BESS") CPCN Request. The Utilities will withdraw their request for the Cane Run BESS without prejudice in this case, but they may re-file a CPCN Application for Cane Run BESS, or a substitute for it, at any time, which would be supported by a competitive procurement process.

ky-2025-00113-kollen-direct-testimony-original.txt · commission copy
One filing · 9 September 2025 · 378 pages Testimony, PDF page 98 the page it prints as 95, line 7 …request for CPCN for Brown BESS. superseded 30 September Exhibit LK-19, PDF page 321 the stipulation itself, ¶ 1.2 …their request for the Cane Run BESS never amended footnote 84 attaches it One filing, two names, one withdrawal checkable on the day it landed, with no second version The correction was filed 21 days later, on 30 September 2025
a passage in the record a check that can refuse the path taken when something cannot be proved
The document disagreed with the exhibit it cited. No comparison across versions was needed and no outside knowledge either: the question is only whether the name the testimony uses matches the name in the attachment that same sentence points at. Twenty-one days passed.

One more trap: the mark-up copy

The correction arrived as two documents: a clean copy and a mark-up copy. Extract the text of the mark-up and PDF page 98 reads:

Corrected Mark-up, PDF page 98

7 their request for CPCN for Brown BESS. The Companies agree to take necessary

ky-2025-00113-kollen-direct-testimony-corrected-markup.txt · commission copy

Word for word the superseded original, at the one spot the correction was about. Everywhere else it matches the clean copy: six changed lines out of 15,853, all six on page 98. Whatever a person sees on that page, a machine gets the withdrawn name out of a file whose own name says corrected. I have not established how the mark-up is rendered — the page carries no PDF annotation objects — and would rather say so than guess.

What to do on Monday

None of this needs a purchase. It needs half a day and difflib.

  1. Compare twice, and report both counts. Once as the text stands, once with every space, tab and newline discarded. Here that is 144 against 81, and the 63 that vanish are pure re-layout.
  2. Never replace on the strength of a correction notice. It names a page, not a scope. Diff, and act on what moved.
  3. Check a filing against its own attachments. Take the proper nouns in the testimony — plant names, docket and exhibit numbers — and check them against the exhibits it cites. That one pass would have caught this on 9 September.
  4. Quote from the clean copy. This mark-up extracts to the superseded text, so the helpful-looking copy is the one likeliest to poison your workflow. Whether every mark-up does that, I do not know.
  5. Do not assume the later filing is the better text. Where a re-scan is involved, corruption runs both ways. Say which way each difference runs, or do not report it.

How these numbers were made

Two PDFs from the Kentucky PSC's public filing path, extracted with PyMuPDF using page.get_text("text", sort=True) so the margin line numbers stay on the lines they number, pages joined with a form feed, nothing cleaned. Character counts are len() over the decoded text; the non-space counts are the same strings under re.sub(r"\s", "", …). Passages come from app/ingestion/ingest.py::_segment, which cuts on blank lines; the 144 changes come from app/diff/engine.py::diff over those two lists, timed with time.perf_counter() around the diff call alone. The 81 are the changes whose two sides still differ under re.sub(r"\s", "", …), the 53 those whose two sides differ under re.sub(r"\D", "", …). A change is charged to the page its passage starts on, counting form feeds — the corrected file where there is one, the original otherwise. Brown BESS is counted with re.findall(r"Brown\s+BESS", …). The files hash to 51232537…c33b66 and f39fcd14…90ac84, both recomputing against the corpus provenance records.

Disclosure, and a limit. Verbatim is my product and the diff engine above is its code. It is not what found this: it reported 144 changes and ranked the two that mattered 38th and 39th. I read all 144.

The machine is fast and honest about what it saw. It cannot yet tell you that a testimony contradicts the exhibit it cites, which is the check that would have mattered most here. That is the next thing to build, and it is a rule you can check rather than a judgement.