feat(word-addin): formatting tracked changes and click-to-locate document citations

↗ view on GitHub · Amal · 2026-08-16 · 1ff94349

WHY THIS MATTERS
Legal review is not only about wording. Reviewers need Mike to (a) propose
styling changes - bold a heading, italicize a defined term, promote a
paragraph to a real Word heading - through the same accept/reject redline
flow as text edits, and (b) back its answers with citations that jump to
the exact contract language in the open document instead of leaving the
reader to hunt for it.

FORMATTING TRACKED CHANGES - HOW IT WORKS
The edit protocol gains a format-only block:
  <original>exact text from the document</original>
  <format>bold</format>
  <reason>...</reason>
Recognized formats: bold, italic, underline (character formatting) and
heading1/heading2/heading3 (Word paragraph styles; "heading 1" spellings
normalize). A block carries either <replacement> or <format>, never both -
a malformed block with both (or a <format> naming nothing recognized)
fails the complete-block match and settles as an "incomplete" card that
never touches Word. Combined rewrite+restyle was deliberately rejected so
the revision set stays provable.

Applying a format block reuses the entire text-edit pipeline (unique
search match, pre-existing-revision skip, TrackAll authoring) but writes
font properties - or, for heading styles, the containing paragraph's
styleBuiltIn - instead of replacing text, which makes Word generate
"Formatted" revisions. The revision-matching layers gain a "formatted"
side with host-informed matching rules, each verified against real Word
for the web:
- Character formats: revision text equals the original passage; N >= 1
  matches accepted because Word may split one restyle per font property
  or run (every text-matching formatted revision is provably this edit's,
  since apply required a unique match of a revision-free passage).
- Heading styles are paragraph-scoped by Word's own model, so their
  revision reports the whole paragraph's text - or, on Word for the web,
  an EMPTY string. Anchor-scoped matching therefore accepts contains-or-
  empty for style edits (ranges only report revisions they intersect, so
  the empty-text rule stays confined to the edited passage), while the
  document-wide fallback demands a unique match, because empty text
  cannot disambiguate two pending paragraph-format revisions.
Cards preview the styling on the original text (no red/green diff) plus a
small label of the formats applied.

Malformed blocks of ANY kind now settle as "incomplete" when the stream
finishes (previously they could sit on a "Receiving change..." spinner
forever; markIncompleteRedlines only ran on abort/error paths).

DOCUMENT CITATIONS - HOW IT WORKS
The shared chat pipeline already emits [n] markers in prose plus a
citations frame carrying each marker's verbatim quote; the pane simply
ignored that frame. Now: the stream boundary surfaces it, the chat hook
attaches citations to the assistant message (and persists them through
local saves; the cloud path already stored the column), and prose
rendering joins each [n] marker to its quote - rendering a chip link with
a reserved "#mike-cite:<encoded quote>" fragment. Literal <cite>verbatim
quote</cite> tags render the quote itself as the chip, as an inline
alternative (partial tags are hidden mid-stream so raw markup never
flashes). Clicking a chip runs selectDocumentText(): an exact-match body
search (case-insensitive fallback), then Range.select(), which is Word's
scroll-and-highlight. A stale citation - text since edited away - is a
silent no-op logged at debug level; citations are navigation, not state,
so nothing is anchored or persisted for them.

Verified live against real Word on the web: a bold ask produced a genuine
Formatted revision with an actionable card; a Heading 1 ask restyled the
paragraph (styleBuiltIn read back "Heading1" after accept, text
byte-identical, revision resolved); and a notice-period question produced
a [1] chip whose click selected the exact termination clause.

The e2e Office mock gains range.font setters and a paragraph styleBuiltIn
setter that materialize Formatted revisions under TrackAll, plus a
citations frame in the chat-stream fixture; new specs cover the
formatting lifecycle (accept, reject, direct mode, heading styles, the
unrecognized-format guard) and citations (chip rendering, mid-tag
streaming, click-to-select, native [n] markers, stale-citation and
case-insensitive fallback).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Repository open-legal-products/mike
Author Amal <mamalanand3@gmail.com>
Authored
Committed
Parents f84ffd22
Stats 18 files changed , +1048 , -93
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-1ff94349.md from inside the repo you want the change in.

⬇ Download capture-commit-1ff94349.md