Summary
Add ODF (.odt) support for compare_documents: redline two .odt files (or a session vs. its original), emitting ODF tracked-changes markup (text:tracked-changes / text:changed-region / text:insertion / text:deletion / text:change-start / text:change-end / text:change). This is the heavy second half of Phase-2 ODF support — the analogue of docx-core's w:ins/w:del atomizer, but ODF's emission model is structurally different (deletions are stored out of line in a changed-region, not inline), so it needs a fresh ODF tracked-changes atomizer in odf-core (~2,000–2,500 LOC).
Builds on the merged ODF lane: provider-aware dispatch (#335), grep/insert (#336), and comments (#341). compare_documents is the last Phase-2 tool still returning UNSUPPORTED_FOR_ODF for .odt.
Scope & sequencing (one PR per slice, gate each on green CI)
- Paragraph-granularity, two-file mode — whole-paragraph insert/delete via an LCS over paragraph text; emit insertions (
text:change-start/-end → text:insertion region) and deletions (out-of-line text:deletion region + in-body text:change point marker). Return stats + granularity: 'paragraph'.
- Intra-paragraph (run-level) diffs — segment-level LCS within modified paragraph pairs (reuse
text_segments.buildSegments); inline insertion/deletion markers with deleted runs stored in the region. The structural ODF/DOCX difference bites hardest here.
- Session mode —
file_path .odt session: session.originalBuffer vs current doc.
Key constraints
- Optional-provider architecture is non-negotiable: odf-core stays
private: true, referenced only via loadOdfCore(); never a package.json dep of docx-mcp (a standalone install would 404). Keep the diff algorithm separable from ODF emission (own module + unit tests), mirroring docx-core's atomLcs.ts vs documentReconstructor.ts.
- Traversal safety:
OdfDocument.collectBlocks/segment walk must skip the text:tracked-changes container (like it already skips office:annotation), with a no-leak regression test.
- LibreOffice is the authoritative compatibility check: a produced redline
.odt must open with the changes visible/acceptable in Edit ▸ Track Changes ▸ Manage.
- Land via PR +
/automerge-smoke with a real-document smoke (NVCA .docx→.odt, redline through dispatchToolCall, reopen in LibreOffice). No Claude attribution; emoji-free, provider-agnostic naming.
Acceptance criteria (Slice 1)
Implementation prompt
A full self-contained implementation prompt (architecture, CI gates, ODF tracked-changes markup reference, file map, workflow, gotchas) lives at /tmp/odf-compare-implementation-prompt.md locally — paste it into a fresh thread to start.
Out of scope: .ods/.odp, durable injected xml:id anchors, docx→odf conversion, accept/reject of ODF tracked changes.
Summary
Add ODF (
.odt) support forcompare_documents: redline two.odtfiles (or a session vs. its original), emitting ODF tracked-changes markup (text:tracked-changes/text:changed-region/text:insertion/text:deletion/text:change-start/text:change-end/text:change). This is the heavy second half of Phase-2 ODF support — the analogue of docx-core'sw:ins/w:delatomizer, but ODF's emission model is structurally different (deletions are stored out of line in a changed-region, not inline), so it needs a fresh ODF tracked-changes atomizer inodf-core(~2,000–2,500 LOC).Builds on the merged ODF lane: provider-aware dispatch (#335), grep/insert (#336), and comments (#341).
compare_documentsis the last Phase-2 tool still returningUNSUPPORTED_FOR_ODFfor.odt.Scope & sequencing (one PR per slice, gate each on green CI)
text:change-start/-end→text:insertionregion) and deletions (out-of-linetext:deletionregion + in-bodytext:changepoint marker). Returnstats+granularity: 'paragraph'.text_segments.buildSegments); inline insertion/deletion markers with deleted runs stored in the region. The structural ODF/DOCX difference bites hardest here.file_path.odtsession:session.originalBuffervs currentdoc.Key constraints
private: true, referenced only vialoadOdfCore(); never a package.json dep of docx-mcp (a standalone install would 404). Keep the diff algorithm separable from ODF emission (own module + unit tests), mirroring docx-core'satomLcs.tsvsdocumentReconstructor.ts.OdfDocument.collectBlocks/segment walk must skip thetext:tracked-changescontainer (like it already skipsoffice:annotation), with a no-leak regression test..odtmust open with the changes visible/acceptable in Edit ▸ Track Changes ▸ Manage./automerge-smokewith a real-document smoke (NVCA.docx→.odt, redline throughdispatchToolCall, reopen in LibreOffice). No Claude attribution; emoji-free, provider-agnostic naming.Acceptance criteria (Slice 1)
compare_documentsroutes.odtinputs to an ODF handler (two-file mode); DOCX/gdocs paths unchanged.odf-corecomparer: pure paragraph-level diff + ODF tracked-changes emitter, exported viaindex.ts, unit-tested in isolation..odtcarries validtext:tracked-changesregions + in-body markers; untouched paragraphs preserved; mimetype-first STORED; opens in LibreOffice.add-odf-compare, ADDED-only) with mappedtest.openspecscenarios +TEST_FEATURE; all CI gates green (allure, spec-coverage--strict, coverage ratchet, runtime smoke, cycles, release-isolation, tool-docs)..odtdocument smoke passes.Implementation prompt
A full self-contained implementation prompt (architecture, CI gates, ODF tracked-changes markup reference, file map, workflow, gotchas) lives at
/tmp/odf-compare-implementation-prompt.mdlocally — paste it into a fresh thread to start.Out of scope:
.ods/.odp, durable injectedxml:idanchors, docx→odf conversion, accept/reject of ODF tracked changes.