Skip to content

extradoc: keep exact-text pre-pins immovable in table-flank pinning - #77

Open
cassus wants to merge 1 commit into
think41:mainfrom
cassus:fix/table-flank-pin
Open

extradoc: keep exact-text pre-pins immovable in table-flank pinning#77
cassus wants to merge 1 commit into
think41:mainfrom
cassus:fix/table-flank-pin

Conversation

@cassus

@cassus cassus commented Jul 22, 2026

Copy link
Copy Markdown

Closes #75.

Context & disclosure. I hit this bug personally while using reconcile_v3
in SUGGEST mode, and I've personally verified that this fix resolves it on a
real document. That said, the investigation, the fix, and most of this write-up
are largely AI-generated (Claude Opus) — I found the bug and confirmed the fix,
but the prose and code are largely AI-authored, so please weight the detail
accordingly.

This surfaced specifically through SUGGEST mode, which isn't generally
available yet and whose main PR hasn't landed — so it's completely fine if you'd
rather not merge this now. I'm opening it mainly to share the finding upstream
so anyone who hits the same thing can benefit, merged or not. I also haven't
tested whether the same bug (or fix) manifests in direct edits without
SUGGEST mode.

This project has been really useful to me and I'd like to give back: if you want
more human attention, deeper testing, or changes to make this mergeable, I'm
happy to engage personally and put in the work — just say the word.

What

_pin_table_flanks (diffmerge/content_align.py) rebuilds its anchor set from
each matched table's ±1 flanking positions with no awareness of the exact-text
pre-pins already established by _pre_pin_stable_anchors. A pre-pin is a
proven unique correspondence; a flank pin is only a heuristic. When a table's
neighbour shifts (e.g. a new heading inserted adjacent to the table), a flank
pin can land on a base index already held by a correct exact-text pre-pin and
silently overwrite it — so a low-confidence heuristic overrides a
high-confidence anchor, and the displaced paragraph gets duplicated/garbled.

See #75 for the full root-cause and the align_content() pipeline ordering.

Changes

  • diffmerge/content_align.py — thread pre_pins into _pin_table_flanks and
    seed them into the anchor set at top priority; the existing conflict-
    resolution loop now only ever drops a flank pin, never a pre-pin.
  • tests/diffmerge/test_table_flank_pinning.py — two prior tests that asserted
    the old flank-override behaviour updated to the correct exact-text match;
    adds test_flank_pin_never_overrides_exact_text_pre_pin (fails before the
    fix, passes after).

Verification

  • Full unit suite green.
  • A/B on a real document overlay: fix reverted → heading re-inserted as a
    duplicate, mangled into the new heading's text, with spurious deletes; fix
    restored → heading untouched, no spurious deletes.
  • Real-API SUGGEST push + verify_accepted.py: 227/227 paragraphs clean on two
    independent copies.

_pin_table_flanks rebuilt its anchor set from matched-table ±1 flanks with no
awareness of the exact-text / API-uncreatable pre-pins established by
_pre_pin_stable_anchors. When a table's neighbour shifts (e.g. a new heading
inserted adjacent to the table), a flank pin could land on a base index
already pinned by an unambiguous exact-text match elsewhere and clobber it:
the original heading got word-diffed into the new adjacent one and the real
match re-inserted as a duplicate downstream.

Thread pre_pins into _pin_table_flanks and seed them into the anchor set at
top priority; the existing conflict-resolution loop only ever drops a
contributing table pair, never a pre-pin. Two prior tests asserted the old
flank-override behaviour and are updated to the correct exact-text match;
adds a regression test for the clobber case.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

table-flank pinning clobbers exact-text pre-pins, duplicating adjacent headings

1 participant