fix(word-addin): author and resolve tracked edits so cards work on Word for Mac desktop

↗ view on GitHub · Amal · 2026-08-17 · 438d6e1e

WHY THIS MATTERS
On Word for Mac (16.112), Accept/Reject on a Mike edit card silently
failed: the card errored with the generic "Word couldn't update this
change" while the redline stayed pending, forcing users to double-accept
from Word's Review tab. Replicated live in the real host before fixing.

WHAT THE MAC HOST ACTUALLY DOES (live-probed, OOXML-verified)
1. insertText(Replace) under TrackAll applies the DELETION immediately and
   untracked - the .docx holds only w:ins, no w:del - so the deletion half
   of a replacement never exists as a revision. The "exactly one Added +
   one Deleted" ownership proof can then never succeed, every resolution
   layer refuses honestly, and the card degrades. (Superset of the
   Microsoft-acknowledged, still-open office-js#5188; Windows and web
   track the pair correctly.)
2. Split operations - insertText(after) + delete() on the match - DO
   produce a real tracked deletion (w:del with w:delText in the file), but
   its text reads EMPTY through every JS surface (#5188 proper).
3. The desktop-only Word.Revision API (WordApiDesktop 1.4,
   document.revisions) reliably lists all revisions and accept()/reject()
   work; compareLocationWith() reports each replacement's Delete revision
   as Adjacent to exactly its own Insert.
4. Retained cross-batch range proxies intermittently die with
   GeneralException at Document._GetObjectByReferenceId.

HOW THE FIX WORKS
1. Authoring: replacements queue insertText(..., After) + match.delete()
   instead of insertText(Replace) - the only form every host records as a
   full Added+Deleted pair (also the pattern production Word add-ins use;
   both public Mac bug reports were filed against the Replace form). Pure
   deletions just delete().
2. Resolution: where WordApiDesktop 1.4 is supported, the document-level
   fallback reads Word.Revision objects instead of getTrackedChanges. The
   ownership proof adapts to the empty-text bug in two provable steps:
   exact text match first; an empty-text Delete may stand in for the
   edit's deleted side only when it is the SOLE pending Delete in the
   document, or when adjacency to the edit's uniquely matched Insert
   singles it out (AdjacentBefore/After - split authoring writes the
   insertion immediately beside the deletion). Anything ambiguous still
   aborts untouched.
3. Restore: the same Revision-surface match re-links cards after a task
   pane reload (batched and single-edit paths), holding no revision
   proxies - resolution re-verifies at decision time.
4. Error honesty: when the document-level fallback reaches a semantic
   "changed" verdict, that now outranks a dead-proxy GeneralException from
   the anchor pass, so the card reports why instead of the generic
   failure. Silent catch sites also log their errors.

The e2e mock mirrors the new authoring: insertText still materializes the
revision pair (the mock is a web-host model), range.delete() is tracked
(no-op after an insert; a Deleted-only group for pure deletions), and a
pure deletion no longer fabricates an empty Added revision. Spec write
expectations move from location "Replace" to "After".

Verified live on Word for Mac 16.112: two same-paragraph spelling fixes
resolve from their cards (adjacency disambiguation picking the right
empty-text Delete), accept-all drains the Review tab to zero, Direct-mode
auto-accept and a Formatted heading revision resolve, and the document's
final text was confirmed via body + OOXML reads. The web path is
untouched (body-level getTrackedChanges fallback unchanged; the desktop
path is capability-gated), and the full hermetic suite passes.

Trade-off: split authoring renders a replacement as adjacent Deleted +
Added runs rather than one combined replace mark - visually
near-identical, and the standard shape other legal redlining add-ins
produce.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JN4mcnhSK7xpxzF2b3WFkE
Repository open-legal-products/mike
Author Amal <mamalanand3@gmail.com>
Authored
Committed
Parents 673ab898
Stats 6 files changed , +324 , -44
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-438d6e1e.md from inside the repo you want the change in.

⬇ Download capture-commit-438d6e1e.md