The arc after #434. The unified reader retains classified, provenance-carrying projections on every mutation-safe read, and plan_whole_run_replacement (N0c1) consumes them — but nothing shipped calls it. This campaign makes ONE carefully bounded operation trust the new truth source.
Shape (design of record: the post-#434 direction consultation; matches docs/office-major-parity.md N1a→N1b): an ADDRESSED operation, semantically distinct from replace_text's literal matcher — neither a second literal-replace nor a cutover of run_rewrite.mbt (the old emitter canonicalizes w:t and deletes empty runs; N0c1 preserves properties, spellings, and suppressed content — reconciliation is a measured follow-up, not first activation).
Stages
- A1 — package boundary:
plan_run_text_replacement_archive(annotated, archive, story, at_relative~, expect~, text~) → (SplicePlan, RunTextReceipt) in docx2html/docx. Projection types stay private. The resolver uses the retained projection's OWN scan for path resolution (never story_run_span — the annotation index must not become mutation authority), maps to exactly one paragraph/run (refusing zero/duplicate/ambiguous), and pins the plan to its source.
- A2 — transaction SDK:
DocxEditSession::queue_run_text_replacement + office/docx/edit_set_text.mbt. One bounded target; REQUIRED expect (snapshot-relative addresses need a precondition); no mixing with literal replace/revision resolution in one program; no output on refusal; byte-equal no-op when text == expect; full affected-paragraph readback as the semantic postcondition; an explicit text-character policy decision (the edit parser rejects all C0, N0c1 permits XML-valid tab/LF/CR).
- A3 — shipped surface:
docx.edit/2 with {"op":"set_run_text","params":{"at","expect","text"}}; /1 untouched; capabilities/input-contract discovery; cram + SDK + wasm + fresh-agent acceptance; both .mbti diffs intentional.
- A4 — optional consolidation (separate): differential evidence before any emitter replacement; never both emitters in production.
Release gate additionally includes the #438 XML-token-budget calibration (39/591 corpus documents currently refuse on budgets — availability work that belongs to this surface going live).
After this
- C: migrate
annotation_index/annotation_spans onto the retained trace behind this contract, proving public path/warning/ordinal parity (they still run their own physical scans).
- B: N0c2 partial-boundary surgery (sub-run ranges, run splitting).
Non-goals
Public projection API; cross-run or range editing; replace_text fidelity changes; template row-clone emission changes; header/footer mutation policy widening (the package wrapper is story-aware; the office surface starts body-only).
The arc after #434. The unified reader retains classified, provenance-carrying projections on every mutation-safe read, and
plan_whole_run_replacement(N0c1) consumes them — but nothing shipped calls it. This campaign makes ONE carefully bounded operation trust the new truth source.Shape (design of record: the post-#434 direction consultation; matches docs/office-major-parity.md N1a→N1b): an ADDRESSED operation, semantically distinct from
replace_text's literal matcher — neither a second literal-replace nor a cutover ofrun_rewrite.mbt(the old emitter canonicalizesw:tand deletes empty runs; N0c1 preserves properties, spellings, and suppressed content — reconciliation is a measured follow-up, not first activation).Stages
plan_run_text_replacement_archive(annotated, archive, story, at_relative~, expect~, text~) → (SplicePlan, RunTextReceipt)indocx2html/docx. Projection types stay private. The resolver uses the retained projection's OWN scan for path resolution (neverstory_run_span— the annotation index must not become mutation authority), maps to exactly one paragraph/run (refusing zero/duplicate/ambiguous), and pins the plan to its source.DocxEditSession::queue_run_text_replacement+office/docx/edit_set_text.mbt. One bounded target; REQUIREDexpect(snapshot-relative addresses need a precondition); no mixing with literal replace/revision resolution in one program; no output on refusal; byte-equal no-op whentext == expect; full affected-paragraph readback as the semantic postcondition; an explicit text-character policy decision (the edit parser rejects all C0, N0c1 permits XML-valid tab/LF/CR).docx.edit/2with{"op":"set_run_text","params":{"at","expect","text"}};/1untouched; capabilities/input-contract discovery; cram + SDK + wasm + fresh-agent acceptance; both.mbtidiffs intentional.Release gate additionally includes the #438 XML-token-budget calibration (39/591 corpus documents currently refuse on budgets — availability work that belongs to this surface going live).
After this
annotation_index/annotation_spansonto the retained trace behind this contract, proving public path/warning/ordinal parity (they still run their own physical scans).Non-goals
Public projection API; cross-run or range editing;
replace_textfidelity changes; template row-clone emission changes; header/footer mutation policy widening (the package wrapper is story-aware; the office surface starts body-only).