Skip to content

feat(docx-markdoc): refuse to rebuild over an edited create DOCX - #1190

Merged
stevenobiajulu merged 1 commit into
mainfrom
1186-protect-edited-outputs-20261008
Oct 9, 2026
Merged

stevenobiajulu merged 1 commit into
mainfrom
1186-protect-edited-outputs-20261008

Conversation

@stevenobiajulu

Copy link
Copy Markdown
Member

Problem (#1186)

create --replace rebuilt <stem>.docx unconditionally. Someone who edited the DOCX in Word after a build lost those edits on the next rebuild, with no warning.

Change (choices held for Steven's explicit approval; do not merge before it)

  • --replace is the safe rebuild.
    • It rebuilds over an existing DOCX only when that DOCX's SHA-256 equals the docxSha256 in a passing <stem>.verification.json (the last build's fingerprint).
    • Otherwise it fails with CREATION_OUTPUT_EDITED and changes nothing.
  • A missing or stale fingerprint is refused. A missing, unreadable or failed certificate, or one with no fingerprint, can't show the DOCX is unedited, so the run is refused and the message says why.
  • Checked twice.
    • The check runs before the build.
    • It runs again at publication, on the bytes actually moved aside, so an edit saved during the build is caught.
    • A refusal at that point rolls the publication back.
    • A DOCX that appears during a --replace build, when there was none before, is refused the same way.
  • --dangerously-overwrite-edited-docx implies --replace and accepts such a DOCX.
    • It keeps the displaced bytes, exactly as they were moved aside, in .<stem>.create-overwritten-<uuid>/<stem>.docx, and the result and summary name that path and its SHA-256.
    • If that move fails, the staging directory holding the bytes is kept and named (CREATION_OVERWRITE_BACKUP_FAILED).
    • Overwriting the certified DOCX keeps nothing, since there's nothing unique to keep.
  • Unchanged protections: the symlink and hard-link refusals, the stem lock, transactional rollback and recovery directories, and stale-PDF retirement.
  • Docs make no absolute claims. These checks see edits saved before publication moves the DOCX aside. A program still writing to the file after that point writes into the kept copy.
  • The README (including the migration note), design, proposal and tasks are updated, and there is a new scenario, SDX-MDOC-CREATE-14.

Verification

  • docx-markdoc tests pass (268/268), as does strict OpenSpec.
  • Tests:
    • an unchanged rebuild;
    • a real external edit, which is refused with every file byte-identical;
    • certificates that are missing, unreadable, failed, foreign or lack a fingerprint;
    • a forced overwrite that keeps the exact edited bytes;
    • a rollback of a forced publication, after which the edited DOCX is restored in place;
    • an edit during the build, refused and also exercised under force;
    • a DOCX that appears during the build;
    • a failure to move the kept copy.
  • Red checks: removing the publication-time recheck, the preservation step or the missing-record refusal each makes its probe fail.

Refs #1186
Refs #1162

@vercel

vercel Bot commented Oct 8, 2026 •

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated
site Ready Ready Preview Oct 9, 2026 2:34am UTC

Request Review

@usejunior-llm-gate

usejunior-llm-gate Bot commented Oct 8, 2026 •

Copy link
Copy Markdown

LLM gate (advisory)

1 finding to review (advisory, does not block) - 4 pass, 1 warn, 0 error, 11 skipped, 16 total

Findings (1)

Rule Verdict Detail
Re-derived facts vs canonical sources WARN The PR re-implements SHA-256 hashing in packages/docx-markdoc/src/create/cli-create.ts (line ~100) and packages/docx-markdoc/src/create/edited-output-guard.test.ts (line ~18) instead of importing the existing canonical helper from packages/docx-markdoc/src/hash.ts.
All 16 rules (5 evaluated, 11 skipped)
Rule Verdict Detail
read_file response metadata parity SKIPPED paths not touched by this PR
Live DOM namespace-safe OOXML writes SKIPPED paths not touched by this PR
Complex-field revisions preserve complete accept/reject state machines PASS The PR does not touch field atomization, validateFieldStructure, w:fldChar, w:instrText, w:delInstrText, or collapsed-field comparison logic, as it only implements an edited-DOCX guard and overwrite options for the docx-markdoc package.
Field validation per story, not global SKIPPED paths not touched by this PR
Revision IDs seeded from all revision-bearing side parts SKIPPED paths not touched by this PR
Accept/reject sweep side parts and caches PASS The PR does not touch DocxDocument.acceptChanges, DocxDocument.rejectChanges, REVISION_STORY_PART_PATHS, accept_changes, reject_changes, or side-part revision markup, as it only introduces edited-DOCX protection guards for document creation.
DocumentViewNode.heading stays canonical SKIPPED paths not touched by this PR
AI-author parity across entry points SKIPPED paths not touched by this PR
Property-change wrapper discipline SKIPPED paths not touched by this PR
SUPPORT.md Table A drift vs. implementation SKIPPED paths not touched by this PR
Table A / Table B boundary on side-part revisions SKIPPED paths not touched by this PR
Canonical-emission surface completeness SKIPPED paths not touched by this PR
Unit-test quality (avoid tautological / change-detector tests) PASS The added test file packages/docx-markdoc/src/create/edited-output-guard.test.ts verifies directory rollbacks and file content integrity via exact SHA-256/Buffer comparisons and directory snapshots. Filesystem and PDF-tool boundaries are mocked to simulate complex failure and race scenarios, with assertions derived from first principles rather than mocking or re-implementing the system under test.
Re-derived facts vs canonical sources WARN The PR re-implements SHA-256 hashing in packages/docx-markdoc/src/create/cli-create.ts (line ~100) and packages/docx-markdoc/src/create/edited-output-guard.test.ts (line ~18) instead of importing the existing canonical helper from packages/docx-markdoc/src/hash.ts.
.openspec tag ↔ test-assertion drift PASS The added test.openspec tags in packages/docx-markdoc/src/create/edited-output-guard.test.ts:37,89,141 fully exercise all GIVEN/WHEN/THEN requirements for [SDX-MDOC-CREATE-14], asserting certificate checks, file preservation, and publication-race rollback without tag-stuffing.
Library stays general (no downstream-domain leakage) SKIPPED paths not touched by this PR

@stevenobiajulu
stevenobiajulu force-pushed the 1186-protect-edited-outputs-20261008 branch from 8971b98 to 77047df Compare October 8, 2026 23:10
@stevenobiajulu
stevenobiajulu force-pushed the 1186-protect-edited-outputs-20261008 branch from 77047df to f41f9f4 Compare October 9, 2026 00:13
@stevenobiajulu
stevenobiajulu force-pushed the 1186-protect-edited-outputs-20261008 branch from f41f9f4 to 6efe69a Compare October 9, 2026 00:37
@stevenobiajulu
stevenobiajulu force-pushed the 1185-independent-roundtrip-oracle-20261008 branch from e0d2cae to 4063e0a Compare October 9, 2026 00:55
@stevenobiajulu
stevenobiajulu force-pushed the 1186-protect-edited-outputs-20261008 branch from 6efe69a to 7c20524 Compare October 9, 2026 00:56
@stevenobiajulu
stevenobiajulu force-pushed the 1186-protect-edited-outputs-20261008 branch from 7c20524 to af3d743 Compare October 9, 2026 01:14
@stevenobiajulu
stevenobiajulu force-pushed the 1186-protect-edited-outputs-20261008 branch from af3d743 to a95c6dd Compare October 9, 2026 01:43
@stevenobiajulu
stevenobiajulu force-pushed the 1186-protect-edited-outputs-20261008 branch from a95c6dd to 586d6c5 Compare October 9, 2026 02:05
@stevenobiajulu
stevenobiajulu force-pushed the 1186-protect-edited-outputs-20261008 branch from 586d6c5 to ca84498 Compare October 9, 2026 02:17
Base automatically changed from 1185-independent-roundtrip-oracle-20261008 to main October 9, 2026 02:34
@github-actions github-actions Bot added the feat PR type: feature label Oct 9, 2026
--replace now rebuilds over <stem>.docx only when its SHA-256 equals the
docxSha256 of a passing <stem>.verification.json. An edited DOCX, or one
with a missing, unreadable, failed or fingerprint-less certificate, fails
with CREATION_OUTPUT_EDITED and changes nothing. Publication re-hashes
the bytes it actually moves aside, so an edit saved during the build is
caught and rolled back.

--dangerously-overwrite-edited-docx implies --replace, and keeps the
displaced bytes in .<stem>.create-overwritten-<uuid>/<stem>.docx, named
in the result and summary.

Refs #1186
@codecov

codecov Bot commented Oct 9, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 91.89189% with 3 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
packages/docx-markdoc/src/create/cli-create.ts 91.89% 0 Missing and 3 partials ⚠️

📢 Thoughts on this report? Let us know!

@stevenobiajulu
stevenobiajulu merged commit 52ae90b into main Oct 9, 2026
27 checks passed
@stevenobiajulu
stevenobiajulu deleted the 1186-protect-edited-outputs-20261008 branch October 9, 2026 02:52
stevenobiajulu added a commit that referenced this pull request Oct 9, 2026
Releases the docx-markdoc create follow-ups: explicit fill-in markup with
literal bare brackets (#1187, #1184), the independent round-trip oracle,
word-level PDF check and failure report (#1189, #1185), and the
edited-DOCX guard for --replace (#1190, #1186), plus the header/footer
carry-forward fix (#1188). Versions bumped with scripts/bump_version.mjs;
the CHANGELOG entry is hand-written.

Refs #1162

This branch was successfully deployed

1 active deployment
Preview — 9f920960 Deployed Oct 9, 2026 by vercel[bot]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

feat PR type: feature

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant