Design doc (read in full first): docs/design/docx_io_package_split.md — §2 (target structure), §4 Slice 1.
Slice 1 of 5 of the docx_io.py package split — the lowest-risk slice (pure config, zero python-docx/lxml dependency), already near-fully covered by tests/test_docx_io.py.
Scope
Move the CLI/settings configuration surface verbatim into a new marcut/docx/settings.py:
CLI_ARG_PAIRS, CLI_ARG_MAP, CLI_CLEAN_ARG_PAIRS, CLI_CLEAN_ARG_MAP, FIELD_TO_CLI (declared from docx_io.py:25);
_normalize_metadata_field_key();
MetadataCleaningSettings (docx_io.py:118) with all its methods (from_preset, from_cli_args, from_environment, to_cli_args, etc.).
This class has no python-docx/lxml/zipfile dependency — it is pure dataclass + dict logic and must be importable without touching a real document.
Package naming decision (needs a call in the PR)
The design doc proposes the package at marcut/docx/. Note that docx_io.py imports the third-party python-docx library as docx (from docx import Document, docx_io.py:9). Under Python-3 absolute imports a sibling marcut/docx/ package does not shadow the third-party docx, so it is technically safe — but it is a readability footgun. Decide in this PR whether to follow the doc (marcut/docx/) or use a less collision-prone name (e.g. marcut/docx_pkg/), and apply that choice consistently across all five slices. Document the decision in the PR description.
Import-path preservation
Consumers today: cli.py:9 (from .docx_io import CLI_ARG_PAIRS), pipeline.py:13 (from .docx_io import DocxMap, MetadataCleaningSettings), plus 8 test files. To avoid touching call sites in this slice, add a re-export shim in docx_io.py (from .docx.settings import MetadataCleaningSettings, CLI_ARG_PAIRS, ...) so existing from .docx_io import ... references keep working. (The full shim is finalized in Slice 5; this slice only needs the settings symbols re-exported.)
Acceptance criteria
Verification loop (design doc §3.3, run every slice)
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 — green with only import-path edits.
- Golden-file harness from
#70 — empty diff.
marcut redact --in <sample-files fixture> --out ... --report ...; diff .docx + report JSON vs pre-slice baseline.
Plus the standard gate: full pytest -q, swift build, swift test, bash scripts/release_preflight.sh.
Dependencies
Blocked by #70 (golden harness must exist and be green first). Independent of the other slices — may land before or after B2/B3.
Land as its own PR; do not combine with other issues in this set. Do not reopen design-spike #25.
Design doc (read in full first):
docs/design/docx_io_package_split.md— §2 (target structure), §4 Slice 1.Slice 1 of 5 of the
docx_io.pypackage split — the lowest-risk slice (pure config, zeropython-docx/lxmldependency), already near-fully covered bytests/test_docx_io.py.Scope
Move the CLI/settings configuration surface verbatim into a new
marcut/docx/settings.py:CLI_ARG_PAIRS,CLI_ARG_MAP,CLI_CLEAN_ARG_PAIRS,CLI_CLEAN_ARG_MAP,FIELD_TO_CLI(declared fromdocx_io.py:25);_normalize_metadata_field_key();MetadataCleaningSettings(docx_io.py:118) with all its methods (from_preset,from_cli_args,from_environment,to_cli_args, etc.).This class has no
python-docx/lxml/zipfiledependency — it is pure dataclass + dict logic and must be importable without touching a real document.Package naming decision (needs a call in the PR)
The design doc proposes the package at
marcut/docx/. Note thatdocx_io.pyimports the third-partypython-docxlibrary asdocx(from docx import Document,docx_io.py:9). Under Python-3 absolute imports a siblingmarcut/docx/package does not shadow the third-partydocx, so it is technically safe — but it is a readability footgun. Decide in this PR whether to follow the doc (marcut/docx/) or use a less collision-prone name (e.g.marcut/docx_pkg/), and apply that choice consistently across all five slices. Document the decision in the PR description.Import-path preservation
Consumers today:
cli.py:9(from .docx_io import CLI_ARG_PAIRS),pipeline.py:13(from .docx_io import DocxMap, MetadataCleaningSettings), plus 8 test files. To avoid touching call sites in this slice, add a re-export shim indocx_io.py(from .docx.settings import MetadataCleaningSettings, CLI_ARG_PAIRS, ...) so existingfrom .docx_io import ...references keep working. (The full shim is finalized in Slice 5; this slice only needs the settings symbols re-exported.)Acceptance criteria
marcut/docx/settings.py(or agreed package name).docx_io.pyre-exports the moved symbols so no consumer import breaks.tests/test_docx_io.pypasses with only import-path updates (if any) — no behavior edits.#70) diffs to zero.marcut redactrun against asample-files/fixture,.docx+ report JSON diffed against a pre-slice baseline → no difference.Verification loop (design doc §3.3, run every slice)
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— green with only import-path edits.#70— empty diff.marcut redact --in <sample-files fixture> --out ... --report ...; diff.docx+ report JSON vs pre-slice baseline.Plus the standard gate: full
pytest -q,swift build,swift test,bash scripts/release_preflight.sh.Dependencies
Blocked by
#70(golden harness must exist and be green first). Independent of the other slices — may land before or after B2/B3.Land as its own PR; do not combine with other issues in this set. Do not reopen design-spike #25.