The DOCX tracked-change pipeline
How a plain-text edit from the model becomes surgical Word tracked-change markup that preserves formatting — from the skill the model reads to the OOXML engine and LibreOffice finalization.
Tool Selection
Author name for tracked changes: Use the user's name from anylegal.md (the "About Me" section). If unknown, use "Anylegal.ai".
edit_document is the default tool for editing existing DOCX files. Pass plain-text old_text / new_text; the server generates <w:ins> / <w:del> markup and preserves run properties.
| Task | Tool | Notes |
|---|---|---|
| Change clause text (tracked) | edit_document |
One change per call. |
| Fill a placeholder | edit_document |
Same tool — tracked change; finalize later if user wants a clean output. |
| Delete a section | edit_document with start_text / end_text |
Range deletion — everything between two anchors, inclusive. |
| Disambiguate duplicate text | edit_document + near_text |
Picks the occurrence closest to the anchor. |
| Create a doc from a template | instantiate_template |
Fill placeholders, save as a new file. NO tracked changes in output — produces a clean final document. The template is untouched. Name output by content (e.g. "Acme Board Resolution 2026-04-25.docx"), not "_v2". |
| Add a margin comment | add_comment |
Handles 4-file OOXML coordination. |
| Accept all tracked changes | accept_all_changes |
LibreOffice-backed finalization. Pass output_path to save as a new file. |
| Reject all tracked changes | reject_all_changes |
LibreOffice-backed restoration. |
| Accept SPECIFIC changes by ID | accept_changes |
Per-revision accept (lawyer-style: "accept the indemnity edits, leave the IP open"). Pair with get_revision_stats(with_snippets=True) to pick IDs. |
| Reject SPECIFIC changes by ID | reject_changes |
Per-revision reject. Same workflow — get stats with snippets, pass the IDs you want to reject. |
| Revert specific edits by ID | revert_edit |
Surgical revert of edits we made (from the edit_document response). Functionally similar to reject_changes; use whichever framing fits the user's ask ("undo my edit" vs. "reject the counterparty's change"). |
| Read tracked-change stats | get_revision_stats |
Counts, authors, IDs. Pass with_snippets=True to also get per-revision text + context — required input for accept_changes / reject_changes. |
| Compare two documents (agent-internal diff) | compare |
Returns structured text diff with addition/deletion counts and similarity %. Use for "what changed between v1 and v2" reasoning. For a Word-openable redlined DOCX deliverable for the user, use produce_redline instead. |
| Produce a redlined comparison DOCX (user-facing) | produce_redline |
Word-openable DOCX with file2's changes shown as tracked changes against file1. LibreOffice-backed. For "show me what changed" deliverables. |
| Clone before a big edit session | clone_document |
Optional — edit_document auto-clones to _v2.docx on first edit. Use only to pick a non-default name. |
Rules:
- ONE CHANGE = ONE
edit_documentCALL. Multiple changes → multiple calls. - Never use
run_codefor ordinary text edits — even placeholder fills like[●]areedit_documentcases. - For STRUCTURAL edits the plain-text tools can't express (delete clause + paragraph mark, insert multi-paragraph content, edit styles.xml / headers / footers), invoke
Skill('docx-xml')for the run_code + lxml + zipfile reference. - For creating a NEW DOCX from scratch, invoke
Skill('draft')instead — that skill uses docx-js for new-doc creation.