feat(word-addin): send the document to Mike as structure-annotated markdown

↗ view on GitHub · Amal · 2026-08-19 · 7fbd8132

WHY THIS MATTERS
The pane used to send Word's flat body.text as document_context. Flat
text erases everything Word knows about structure: a contract's clause
hierarchy becomes an undifferentiated wall of prose, headings are
indistinguishable from body lines, and a table arrives as run-together
cell text. The model reasons worse about a document it cannot see the
shape of.

THE CONSTRAINT THAT SHAPES THE DESIGN
Two features depend on the model quoting document text VERBATIM, because
the add-in locates quotes with Word's search API: edit blocks
(<original> becomes a tracked-change target) and citations (a clicked
quote is found and selected). Naive markdown breaks both - a model
reading "**Term**" or "# Definitions" quotes characters that exist
nowhere in the document, and every such search misses.

HOW IT WORKS
lib/documentMarkdown.ts renders structure ADDITIVELY, leaving each
passage's own text byte-identical:
- heading paragraphs (Title, Heading1-6 styles) gain leading # marks;
- list items gain their real Word labels ("a.", "1.", bullets → "-")
  plus nesting indentation;
- tables render as pipe tables from Table.values;
- inline bold/italic is deliberately NOT represented - emphasis markers
  sit inside passages and would poison verbatim quoting.
useWordDoc's readDocumentMarkdown() walks body.paragraphs and
body.tables in document order; if any structure API misbehaves on a host
it falls back to flat body.text, because a degraded context beats a
failed send.

DEFENSE IN DEPTH FOR MARKER-QUOTING MODELS
The prompt (wordPrompt.ts) now explains the rendering and forbids the
markers in <original> and citation quotes. If a model copies one anyway,
stripStructuralMarkers() undoes exactly what the renderer adds - at most
ONE marker layer, so "# 1. Introduction" where "1." is real document
text survives - and both the edit-apply loop and the citation locator
retry a missed verbatim search with the stripped text, adopting it as
the edit's identity so revision scanning, anchors, and persistence all
agree with what was actually located.

Backend: document_context stays an opaque string (same parsing, same
200k-char cap); the inline doc's file_type becomes text/markdown.

Verified: add-in typecheck + production build green; backend word-chat
suites green (75 tests); full add-in e2e suite green on chromium and
webkit. E2E coverage that fails on flat text and passes here lands in
the next commit.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Repository open-legal-products/mike
Author Amal <mamalanand3@gmail.com>
Authored
Committed
Parents 435aa48d
Stats 6 files changed , +279 , -27
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-7fbd8132.md from inside the repo you want the change in.

⬇ Download capture-commit-7fbd8132.md