Skip to content

Decide the token-stream arrangement mode for regions without block correspondence - #698

Merged
JSv4 merged 2 commits into
mainfrom
docs/token-stream-arrangement-694
Sep 4, 2026
Merged

Decide the token-stream arrangement mode for regions without block correspondence#698
JSv4 merged 2 commits into
mainfrom
docs/token-stream-arrangement-694

Conversation

@JSv4

@JSv4 JSv4 commented Sep 4, 2026

Copy link
Copy Markdown
Owner

Settles #694: the architecture of DocxDiff's second arrangement mode — the one that renders a
region as a single word-level token stream instead of block-wise — as a design note under
docs/architecture/.

Reading the engine first changed what the decision is

#694 describes the token-stream mode as a set of "narrowly gated constructs" and asks for an
architecture to be decided before anyone implements one. Most of it is already decided and shipping:

  • IrCrossParagraphSegmenter.SegmentRegion is already the general region form. Members are a
    region's paragraphs in document order per side, pairs lists the aligner's word-matched pairs, and
    any member not covered by a pair is one-sided at any position — leading, interior or trailing.
    Zero pairs is a legal input.
  • IrEditScriptBuilder.TryBuildStoryFinalMixedRegionOp already admits interior zero-pair regions.

What is narrow is the gate, not the mechanism. So the note records the decisions around the
algorithm and cites the segmenter's class remarks for the algorithm itself, rather than restating
them in worse prose where they would go stale.

What it settles

  • Placement — a per-region second mode over a region the block aligner delimits, not a
    replacement for block alignment. A wholesale replacement would have to re-derive everything the
    block path gets right, and the evidence base for the block path is not transferable to it.
  • Reversibility — written out rather than asserted, from the three construction invariants the
    segmenter's walk already asserts (single-member cells, contiguous slices, a total disjoint cover in
    stream order). The argument is structural, which is the load-bearing part: widening the gate
    cannot break accept ≡ right / reject ≡ left, including paragraph order
    , because the property
    does not depend on which regions are selected or on how good the matching is.
  • Formatting provenance, structure boundaries, cost, moves — each records the contract the code
    already holds (per-cell re-diff through IrTokenDiffer; IsStreamable + HasStructuralCarrier
    exclusions with whole-region maximality; LcsCellCap; the move-source decline) and says which a
    wider gate stresses first. Cost is the obvious one.

What it deliberately leaves open

The selection criterion, and the note says why that is the honest answer rather than a gap.

Two literals stand in for it today, and they are exactly the tuned thresholds #694's point 2 rules
out: the > 8 member cap in the builder, and crossUnitMatches >= 2 || constructPairs > 0 in the
segmenter. Every replacement that can be written from here fails one of the issue's own constraints:

  • a coverage fraction is the forbidden threshold — and the segmenter has already been burned
    twice this way; its own pass-1 remark records that "the retain-in-place and yield-to-cross classes
    overlap on coverage, so no threshold separates them";
  • "stream every zero-pair region" is the cleanest rule available and may well be right, but it
    contradicts the only evidence in hand — the code comment carrying the size cap says the stream
    constructs were all decoded from small regions while the replace-gap grammar was validated on the
    large ones;
  • "emit the whole-story LCS and accept the interleave" assumes Word is token-first at every
    scale, which is DocxDiff: decide the token-stream arrangement mode for regions with no block correspondence #694's premise and is unverified exactly where it matters.

So the criterion has to be decoded, not designed. The note names the measurement that would
settle it — reference compare output for large zero-pair regions (template vs filled-in copy, memo vs
the contract that replaced it, two drafts rewritten rather than edited), scored on whether surviving
fragments stay in place or the region replaces whole — and files it as the first child issue. A
decision about how to decide is what an architecture note owes when the evidence is not in yet.

Test plan

Named in the note, with what a moved pin in each would mean: DocxDiffFuzzRoundTripTests on the wide
sweep (DOCXODUS_FUZZ_SEEDS=2000, since the order-sensitive seeds from #288 are rare),
DocxDiffCorpusBaselineTests, and DocxDiffGapArrangementTests.

Scope

Documentation only — no code, no behaviour change, so no CHANGELOG entry. Cross-referenced from
CLAUDE.md's diff/comparison list and from ir_diff_engine.md, whose stage list did not mention
IrCrossParagraphSegmenter at all.

Child issues are filed and linked below; #694 closes on the decision, not on the children.

Closes #694

🤖 Generated with Claude Code

https://claude.ai/code/session_01SxehJkp2547AyP8LSd6uFS

JSv4 and others added 2 commits September 4, 2026 11:57
…ions

Issue #694 asks for the architecture of DocxDiff's second arrangement mode — the
one that renders a region as a single word-level token stream instead of
block-wise — to be settled before anyone implements it.

Reading the engine first changes what the decision is. IrCrossParagraphSegmenter
.SegmentRegion is already the GENERAL region form: members are a region's
paragraphs per side, pairs lists the aligner's word-matched pairs, and any member
not covered by a pair is one-sided at any position — zero pairs included.
IrEditScriptBuilder.TryBuildStoryFinalMixedRegionOp already admits interior
zero-pair regions. The mode is built and shipping; what is narrow is the gate, not
the mechanism.

So the note records the decisions around the algorithm and cites the segmenter's
class remarks for the algorithm itself, rather than restating them where they
would go stale. Placement is settled as a per-region second mode over a region the
block aligner delimits, keeping every guarantee the block path already carries.
Reversibility is written out rather than asserted, from the three construction
invariants the walk asserts (single-member cells, contiguous slices, a total
disjoint cover in stream order) — and the argument is structural, so widening the
gate cannot break accept ≡ right or reject ≡ left, including paragraph order.
Formatting provenance, structure boundaries, cost and moves each record the
contract the code already holds, and say which a wider gate stresses first.

One point is left open, deliberately: the selection criterion. The two literals
that stand in for it today — the eight-member region cap and the
crossUnitMatches >= 2 || constructPairs > 0 ship gate — are exactly the tuned
thresholds the issue rules out, and every replacement that can be written from
here fails one of its own constraints: a coverage fraction is the forbidden
threshold (and the segmenter has already been burned twice by density and
matched-char floors, whose classes overlap on coverage), "stream every zero-pair
region" contradicts the only evidence in hand, and "emit the whole-story LCS"
assumes a premise the decode never covered. The criterion has to be decoded from
reference output for LARGE zero-pair regions, not designed, so the note names that
measurement as the prerequisite and files it as the first child issue.

The test plan names the three guards a widening must pass — DocxDiffFuzzRoundTrip
Tests on the wide sweep, DocxDiffCorpusBaselineTests, and DocxDiffGapArrangement
Tests — and says what a moved pin in each would mean.

Closes #694

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SxehJkp2547AyP8LSd6uFS
@JSv4
JSv4 merged commit b696cc0 into main Sep 4, 2026
14 checks passed
@JSv4
JSv4 deleted the docs/token-stream-arrangement-694 branch September 4, 2026 17:19
JSv4 pushed a commit that referenced this pull request Sep 4, 2026
The release cut is the reason this merge needed hands. main emptied
[Unreleased] into a 12.0.0 section, and git's auto-merge put this demo's entry
back at the position that text now occupies -- inside the released section,
where it would have claimed the demo shipped in a version it is not in. Moved
it back under [Unreleased], and dropped the Office Math entry that came with it,
because that one did ship in 12.0.0 and would otherwise appear twice.

Re-pinned docs/demo/redline.html from 11.0.0 to 12.0.0. The release re-pins
every sibling demo page but not this one, since this one is not on main yet;
what is new is that #654's engine-pin guard globs docs/demo/*.html and requires
them all to name one version, so a stale pin here is now a failing check rather
than a quiet inconsistency. Confirmed jsDelivr actually serves 12.0.0 before
moving it. The README conflict was the same event seen from the other side:
seven pages (this branch adds one) at the new pin.

#697 fixes the comparison timeout that made the last CI run red. That failure
was #693's, bisected and filed as #695; the fix warms the WASM engine before
the first real comparison rather than touching the aligner, so my guess at the
cause in that issue was wrong even though the bisect was right. Verified here:
the test that timed out at 60 seconds now passes.

Build and pretest clean, 61 node checks, 14 browser assertions, and the
engine-pin guard passing on all seven pages. Not re-measured: #698 changes
token-stream arrangement for regions with no block correspondence, which the
demo's 3 KB document does not exercise.
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.

DocxDiff: decide the token-stream arrangement mode for regions with no block correspondence

1 participant