The edit that rewrote the page you did not touch
TLDR
An editing canvas copied every detected region into a mutable array so it could hit-test clicks. That array was then shipped verbatim as the "user overrides" payload. Clicking on the canvas and changing nothing replaced the page's real classification with a crude rebuild: duplicated body text, tables flattened to a single cell, and on some pages a crash. The assumption that failed was that a UI working set and an engine payload are the same thing.
The assumption that seemed reasonable
The correction UI lets you select, move, resize, retype and delete detected regions on a canvas. Selection is by index into an array, so the array has to be stable and mutable for the whole editing session.
Building it lazily on the first pointer event was the obvious design. Nothing is copied until you interact, and once you do, everything is editable.
The engine's re-extract call takes a customRegions list. The editable array is the list of regions as the user currently sees them. Passing it straight through looked like the definition of correct.
When it failed
It failed on a two column research paper with a large architecture table.
The user drew one region, re-ran, and the page came back mangled. Body lines appeared twice. The table lost every cell. On the neighbouring page the whole re-extract failed with Cannot read properties of undefined (reading 'length') and no page-level attribution.
The critical detail took a while to surface: it did not need a drawn region. It needed a click. Any pointer-down on the canvas populated the array, and the next re-extract shipped all 75 regions as overrides.
What was actually wrong
Three defects, stacked, and only the first one was the real bug.
The blast radius. Downstream, an override suppresses any naturally detected region overlapping it by more than 40%. That rule exists so a user-drawn box wins over the detector's guess. Shipping every region as an override meant every natural region was suppressed by its own copy, so the page's actual classification was deleted and replaced by the override rebuild for regions nobody had edited.
Duplication. The rebuild claimed text by padded containment on each region independently. Nothing stopped two regions claiming the same line, and a one-line region padded by the table cell padding reached into the line below. Every body line was emitted twice, taking the page from 30,480 bytes to 34,512.
The crash and the flattening. The rebuild's grid builder returned an object without the hLines and vLines the renderer reads, which is where the length error came from. When it did not crash, it returned a single cell, so a nine row, thirty cell table rendered as one empty row.
Defects two and three had presumably always been there. They were invisible while the override path only ever handled regions a user had actually drawn.
What got deleted
The pass-through of the editable array is gone. So is the hand-rolled ruled-grid builder that reconstructed from a filtered subset of line segments.
The standalone text bander survived, but only as a fallback for the case where the real detector declines to find a table where the user drew one.
What replaced it
A diff before send. The panel now compares each editable region against the baseline it was copied from and ships only the ones that differ by type, position, size, or existence. A drag that returns to its starting point is not an edit, so the comparison uses a half-pixel epsilon to absorb the scale round trip between canvas and engine.
Everything unedited stays locked to the normal classifier path. Fewer regions to rebuild is also less work, so the correct behaviour is the faster one.
Exclusive, two-pass text claiming. Items are assigned to exactly one region. Prose matches on the glyph run's centre with no padding, table cells keep their padding because cell text genuinely hugs the rules. A second relaxed pass picks up anything the strict pass left homeless, which stopped a figure label from vanishing entirely.
Real detectors instead of reimplementations. Ruled grids are now reconstructed over the whole page, exactly as the automatic detector does, and the region picks the candidate it overlaps most. Borderless grids call the real stream detector on the claimed items.
The generalizable lesson
A UI working set and an engine payload have different lifetimes and different meanings. Do not let them be the same object.
The editable array answers "what can the user manipulate right now". The override payload answers "what did the user decide differently from you". Those are not the same question, and the moment one array answers both, a mouse click becomes a destructive operation.
There is a second lesson underneath it. The override path had been shipping duplication and table-flattening bugs for as long as it existed, and nobody saw them because it was only ever exercised on one or two hand-drawn regions at a time. The blast-radius bug did not create those defects. It just turned the volume up until they were audible.
If you have a code path that is normally exercised with one item, write the test that exercises it with all of them. It is the cheapest way to find out whether the path is a real implementation or a demo.