Skip to content

ODF redline engine — compare_documents tracked changes for .odt #343

@stevenobiajulu

Description

@stevenobiajulu

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)

  1. Paragraph-granularity, two-file mode — whole-paragraph insert/delete via an LCS over paragraph text; emit insertions (text:change-start/-endtext:insertion region) and deletions (out-of-line text:deletion region + in-body text:change point marker). Return stats + granularity: 'paragraph'.
  2. 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.
  3. Session modefile_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)

  • compare_documents routes .odt inputs to an ODF handler (two-file mode); DOCX/gdocs paths unchanged.
  • New odf-core comparer: pure paragraph-level diff + ODF tracked-changes emitter, exported via index.ts, unit-tested in isolation.
  • Redline .odt carries valid text:tracked-changes regions + in-body markers; untouched paragraphs preserved; mimetype-first STORED; opens in LibreOffice.
  • New OpenSpec change (add-odf-compare, ADDED-only) with mapped test.openspec scenarios + TEST_FEATURE; all CI gates green (allure, spec-coverage --strict, coverage ratchet, runtime smoke, cycles, release-isolation, tool-docs).
  • Real-NVCA-.odt document 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.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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions