feat(backend): Word edit-protocol contract - unique originals, occurrence-all, whole-item deletion rules

↗ view on GitHub · Amal · 2026-08-19 · 0f54e7fb

WHY THIS MATTERS
The client refuses any <original> that matches more than one place in the
document (safety: applying one block to every identical clause would
silently broaden the edit). But the prompt never told the model that -
worse, it demanded the SHORTEST possible passage, actively steering models
into ambiguous quotes that the client then skipped while the streamed
prose claimed success. The other half of the bug: "replace all X with Y"
was inexpressible, so it always dead-ended.

WHAT THE CONTRACT NOW SAYS
- <original> must identify exactly ONE place: when the target text also
  appears elsewhere, extend the quote with surrounding words from its own
  paragraph until it is document-unique. The old "shortest passage" rule
  becomes "shortest passage that is still unique" - the two rules used to
  conflict.
- Escape hatch: if no unique passage fits the 200-character budget (the
  budget exists because Word's search API rejects long strings - the
  documented cap is 255, enforced inconsistently per host), ASK the user
  which occurrence they mean instead of guessing.
- Occurrence-targeted requests ("the second one", "the one in the closing
  paragraph") must quote context FROM that occurrence and name the
  location in <reason> - the reason line renders on the card, giving the
  user free visible disambiguation before they accept.
- Replace-all: ONE block with the exact repeated text plus
  <occurrence>all</occurrence>, never context-extended, never any other
  value. The client applies it to every occurrence.
- Citations get the same uniqueness rule: the click-to-locate control
  silently jumped to the FIRST occurrence of a repeated quote, which may
  not be the cited one.
- Whole-item deletions must quote the paragraph's ENTIRE text (the client
  escalates to removing the paragraph mark so Word renumbers the list) and
  never "renumber" by editing list numbers, which are renderer annotations
  rather than document text.

DEPLOY-ORDER NOTE (why this commit is last)
The prompt starts EMITTING <occurrence> only here, after the parser
commits: an older cached add-in that receives an unknown tag never seals
the block, leaving a permanently "Incomplete" card. In production the
add-in must ship before the backend prompt does. The commit order in this
branch mirrors that constraint on purpose.

The documentContext test pins the prompt's marker-annotation guidance so
the renderer contract (leading # marks, list markers, table pipes are
annotations, NOT document characters) cannot drift silently.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01A5yxFV2YCFmR61KLyB33yi
Repository open-legal-products/mike
Author Amal <mamalanand3@gmail.com>
Authored
Committed
Parents 3c651a6b
Stats 2 files changed , +12 , -3
Part of Make Word edit approval reliable across hosts

Capture this commit into my fork

Download a Markdown prompt that tells Claude how to port this exact commit into your working tree. Run it via claude -p < capture-commit-0f54e7fb.md from inside the repo you want the change in.

⬇ Download capture-commit-0f54e7fb.md