Postmortem: I Shipped Five Algorithm Ports and the User Saw Nothing
TLDR
After porting five algorithm families from a PDFium-based extractor, the user ran their test PDF and reported no visible difference: the formula still didn't reconstruct, and — the thing they cared about most — centered and justified text still extracted with no alignment. Both complaints were accurate. The formula needed the one piece I had explicitly deferred; the alignment failure was a native bug I hadn't looked at because no upstream reference implemented it. The session's lesson: ports get prioritized by what the reference library offers, but users evaluate by what the page looks like.
Repo: tools/pdf-processor
What Was True When the Complaint Arrived
The five ported families were real and tested: nearest-baseline line clustering, bimodal word-gap thresholds, sub/superscript detection, cross-page running-header signatures, OCR-layer gates. Twenty-seven passing node tests, clean build.
But the test document — Prince's "magic" PDF — exercises exactly two things a reader notices at a glance: a stacked math formula and heavy typographic alignment (centered headers and footers, justified body text). The formula was the one pdf_md piece I had deferred as "needs its own pass." The alignment was worse: an _inferAlignment function already existed in the codebase, silently returning left for every block that mattered, and I had never audited it because the porting agenda was set by what pdf_md had to offer, not by what our output got wrong.
So the user's "the current code fails miserably" was not a perception gap. It was an accurate audit from the only vantage point that counts.
Why the Alignment Bug Survived So Long
Three independent defects, each individually invisible:
- Blocks with fewer than two lines returned
leftunconditionally — and headers, footers, headings, and page numbers are almost always single lines. - The HEADER/FOOTER render branch never called the classifier at all, so even a fixed classifier wouldn't have helped the user's actual example.
- The classifier compared a block only against its own bounding box — which is fitted tight to the text, so a single centered line is geometrically identical to a left-aligned one. The discriminating information (where the block sits inside its column) was never in the input.
Why the Formula Was Deferred — and Why That Was Still a Mistake
Deferring stacked-math reconstruction had a solid engineering rationale: it needs multi-baseline atom groups, it interacts with the drawn-geometry layer (the fraction bar isn't text), and shipping the simpler script detection first was honest incrementalism.
The mistake wasn't the sequencing — it was reporting the port as done while its most demonstrable artifact was the deferred part. "Formulas" to a user means the fraction they can see, not the subscript classifier that feeds it. The status report said "sub/superscripts ported"; the user heard "math works now." When the visible thing didn't change, the invisible things lost credibility with it — including the ones that worked.
The Repair
Both gaps closed in one session, in priority order set by the user, not the reference library: alignment first (container frames, single-line classification, justified detection, footer/header alignment classes, first-line indents), then the math port (fraction stacks, radicals, stretch delimiters, oversets — first-shot correct on the real document once the geometry dump was in hand).
The dump was the other quiet lesson. Ten minutes spent printing the formula's raw glyph runs — before writing any code — turned the port from speculative to mechanical: every threshold could be checked against real coordinates on paper first. The two bugs that did occur were both in code paths the dump didn't cover.
What Changes Going Forward
- Prioritize by visible delta, not by available reference. Before porting from a library, run the target document and list what a reader would notice is wrong. Port to that list.
- Audit native code adjacent to the port. The alignment classifier sat one function away from files I edited for days. "Not part of the port" is not the same as "working."
- Report deferred pieces in the user's vocabulary. "Formulas will still look flat — that's the deferred piece" would have cost one sentence and preserved the signal of everything that did land.
- Dump real geometry before writing geometric code. The cheapest verification loop in this domain is
console.logon coordinates, run once, read carefully.