Skip to content

docx_io split - Slice 5: hardening.py + revisions + document.py coordinator + shim #76

Description

@LegalMarc

Design doc (read in full first): docs/design/docx_io_package_split.md — §2, §4 Slice 5, and §3.2 item 5 (order-dependency test).

Slice 5 of 5 — the final and largest behavioral surface of the docx_io.py package split. Land this last. At completion, docx_io.py becomes a thin compatibility shim and the package is complete.

Scope

Split the remaining in-memory hardening and revision-authoring code into two modules, with DocxMap becoming a thin coordinator in marcut/docx/document.py:

  • hardening.pyscrub_metadata() (docx_io.py:1801), harden_document() (docx_io.py:1711) and their private helpers (_unlink_hyperlinks, _strip_comment_markers*, _comment_visibility_map :1049, _remove_comment_entries, mail-merge/data-binding/hidden-text/invisible-object/header-footer/watermark/ink-annotation removal).
  • revisions.py (name it e.g. revision_writer.py to avoid confusion with the existing top-level marcut/docx_revisions.py, which handles accepting pre-existing revisions on load — a different concern): track-changes authoring — _make_text_run, _insert_deletion_after, _insert_insertion_after, apply_replacements() (docx_io.py:2292), _ensure_track_revisions_enabled.
  • document.pyDocxMap as a thin façade composing scan + hardening + revisions + zip_postprocess; owns load()/load_accepting_revisions()/save()/_postprocess_zip() and delegates the rest, preserving every existing public method signature exactly.

The critical order-dependency contract

save() runs ZIP-level hardening (_rewrite_docx_zip) only if scrub_metadata() ran first (i.e. self._metadata_settings was set). This is enforced by convention, not the type system, and this slice is the one most likely to disturb it. The order-dependency regression test from #71 must be green before and after this slice. If the split changes when _metadata_settings gets attached to the coordinator, hardening can silently stop running on save.

Compatibility shim

At the end of this slice, replace marcut/docx_io.py with a shim re-exporting the public surface (from .docx.document import DocxMap; from .docx.settings import MetadataCleaningSettings, CLI_ARG_PAIRS, ...) so any lingering from .docx_io import ... reference (incl. the 8 test files) keeps working during a deprecation window. Then update pipeline.py:13 and cli.py:9 to import from marcut.docx directly (this call-site update is a distinct commit from the extraction, per the "don't combine refactor + behavior change" rule — keep it mechanical and reviewable).

Non-negotiable requirements

  • Blocked by BOTH #70 and #71; land after #75 (scan.py) since document.py composes the scan type.
  • Order-dependency test green before and after.
  • Public DocxMap surface byte-for-byte compatible; the shim keeps old import paths working.
  • Golden-file harness diffs to zero.

Acceptance criteria

  • hardening.py + revision_writer.py (or agreed name) + document.py created; DocxMap is a thin delegating coordinator with unchanged public signatures.
  • _metadata_settingssave() order-dependency test green before and after.
  • marcut/docx_io.py shim re-exports the full public surface; all 8 test-file imports still resolve.
  • pipeline.py/cli.py updated to import from marcut.docx directly (separate mechanical commit).
  • Golden-file harness (#70) diffs to zero; full §3.3 loop green.
  • One real marcut redact run diffed against pre-slice baseline → no difference.

Verification loop (design doc §3.3)

  1. PYTHONPATH=src/python python3 -m pytest tests/test_docx_io.py tests/test_metadata_scrubbing.py tests/test_pipeline.py tests/test_url_redaction.py tests/test_failure_scenarios.py tests/test_security.py -q.
  2. Golden-file harness — empty diff.
  3. marcut redact against a sample-files/ fixture, diffed vs baseline.

Plus the standard gate: full pytest -q, swift build, swift test, bash scripts/release_preflight.sh.

Dependencies

Blocked by #70, #71, and #75. This is the final slice — land last.


Land as its own PR; do not combine with other issues in this set. Do not reopen design-spike #25.

Metadata

Metadata

Assignees

No one assigned

    Labels

    afkAutonomous workflow-loop eligible

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions