Skip to content

test(docx-comparison): field-bearing fast-check arbitrary for the Lean spec bridge#294

Merged
stevenobiajulu merged 4 commits into
mainfrom
add-field-bearing-bridge-arbitrary-20260602
Jun 2, 2026
Merged

test(docx-comparison): field-bearing fast-check arbitrary for the Lean spec bridge#294
stevenobiajulu merged 4 commits into
mainfrom
add-field-bearing-bridge-arbitrary-20260602

Conversation

@stevenobiajulu

Copy link
Copy Markdown
Member

Summary

Generalizes the three single NUMPAGES field fixtures in lean-spec-bridge.test.ts into a fast-check arbitrary that drives randomly-generated clean field-bearing document pairs through the live inplace comparison engine, falsifying both residual axioms over field type × operation × placement instead of three fixed XML strings.

Implements the approved OpenSpec change add-field-bearing-bridge-arbitrary — the follow-up deferred by name from Tier 2 (add-ooxml-doc-subset-and-inv-field-001-proof) and from add-inv-rt-001-proof.

Test-layer only: no Lean changes, no production-engine changes.

Why

The field-bearing surface — w:fldChar / w:instrText / w:delInstrText atoms exercising the field-walk and the delInstrText → instrText rename — is the riskiest part of the two axioms' domain (compareDocumentXml_output_preservation_friendly / compareDocumentXml_output_text_roundtrip), and previously had only single-fixture empirical grounding. The two field-free arbitraries (pairArb, trackedPairArb) never touch it.

Implementation

  • fieldBearingPairArb over {field-insert, field-delete, field-stable, text-only} × {NUMPAGES, PAGE, PAGEREF}, clean inputs (engine generates all tracking — the analogue of pairArb, not trackedPairArb).
  • Two property tests at numRuns: 100, plus all 12 operation×type combos seeded via fast-check examples so the coverage floor is deterministic.
  • Field XML sourced from the shared COMPLETE_* constants via new paragraphWithField / paragraphWithText helpers in ooxml-fixtures.ts (issue Extract shared XML test fixtures (NUMPAGES field, DOCX skeleton, fldChar primitives) into a single fixtures module #221 drift rule).
  • Header comment blocks updated (asymmetry-of-rot); ROADMAP follow-up flipped to delivered; traceability via .openspec([LEAN-FBA-NN]) tags.

Two load-bearing design decisions (design.md)

  1. Per-operation assertion strength. field-insert/stable/text-only runs assert the stronger assertRecursivelyWellformed and assertFieldInvariant; field-delete runs assert assertFieldInvariant only — post-inplace atomizer: emit fragmented fields per ECMA-376 Part 4 (split <w:ins>/<w:del> at field-character boundaries) #217 fragmentation makes deleted <w:del> subtrees non-context-neutral. The generator's operation tag is the discriminator (not output-XML inspection).
  2. Fallback = falsification + coverage floor, not fc.pre. The arbitrary is constrained to whole-field-at-run-boundary shapes the existing fixtures prove are inplace-safe; assertInplaceResult keeps throwing on fallback; the operation×type coverage assertion is the floor against a degenerate all-fallback run.

Verification (local)

  • npm run build -w @usejunior/docx-core — clean
  • npm run lint -w @usejunior/docx-core (tsc --noEmit, typechecks tests) — clean
  • Full docx-core suite — 1290 passed, 3 skipped, 87 files; both new properties green, full operation×type coverage, no inplace fallback
  • npm run check:spec-coverage — PASS (both workspaces)
  • npm run check:conformance-citations && check:conformance-doc — OK
  • Peer review (Gemini + Codex) — pending; appended below

OpenSpec

  • openspec/changes/add-field-bearing-bridge-arbitrary/ (proposal, design, tasks, docx-comparison delta with [LEAN-FBA-01..05])
  • openspec validate add-field-bearing-bridge-arbitrary --strict passes

…ange

Approved OpenSpec proposal for a field-bearing fast-check arbitrary in the
Lean spec bridge test. Generalizes the three single NUMPAGES field fixtures
into a property over field type (NUMPAGES/PAGE/PAGEREF) x operation
(insert/delete/stable/text-only) x placement, falsifying both residual
axioms (compareDocumentXml_output_preservation_friendly and
compareDocumentXml_output_text_roundtrip) over generated field documents.

Test-layer only: no Lean changes, no production-engine changes. Two
load-bearing design decisions recorded in design.md: (1) per-operation
assertion strength (field-delete drops to document-level assertFieldInvariant
because post-#217 fragmentation makes <w:del> subtrees non-context-neutral);
(2) fallback treated as falsification + coverage floor rather than silent
fc.pre filtering.

openspec validate add-field-bearing-bridge-arbitrary --strict passes.
…Lean spec bridge

Generalizes the three single NUMPAGES field fixtures in lean-spec-bridge.test.ts
into a property over field type (NUMPAGES/PAGE/PAGEREF) x operation
(field-insert / field-delete / field-stable / text-only) x placement, driving
randomly-generated clean field-bearing document pairs through the live inplace
comparison engine to falsify both residual axioms
(compareDocumentXml_output_preservation_friendly, INV-FIELD-001; and
compareDocumentXml_output_text_roundtrip, INV-RT-001) over a far wider surface
than three fixed XML strings.

Why this matters: the field-bearing surface (w:fldChar / w:instrText /
w:delInstrText atoms exercising the field-walk and the delInstrText->instrText
rename) is the riskiest part of the axioms' domain and previously had only
single-fixture empirical grounding. The two field-free arbitraries (pairArb,
trackedPairArb) never touch it.

Two load-bearing design decisions (see design.md):
- Per-operation assertion strength: field-insert/stable/text-only runs assert the
  stronger assertRecursivelyWellformed (per-subtree fieldContextNeutral forall ctx)
  in addition to the document-level assertFieldInvariant; field-delete runs assert
  only assertFieldInvariant, because post-#217 the inplace atomizer fragments
  deleted fields so the <w:del> subtrees are not context-neutral. The generator's
  operation tag is the honest discriminator (not output-XML inspection).
- Fallback = falsification + coverage floor, not silent fc.pre filtering: the
  arbitrary is constrained to whole-complete-field-at-run-boundary shapes the
  existing fixtures prove are inplace-safe; assertInplaceResult keeps throwing on
  fallback, and the operation x type coverage assertion is the floor against a
  degenerate all-fallback run. The 12 operation x type combos are also seeded via
  fast-check `examples` so the floor is deterministic on top of 100 random runs.

Field XML sourced from the shared COMPLETE_* constants via new
paragraphWithField / paragraphWithText helpers in ooxml-fixtures.ts (issue #221
drift rule). Header comment blocks updated so the file no longer oversells
field-free-ness now that a field-bearing generator exists (asymmetry-of-rot).
ROADMAP follow-up flipped to delivered. Traceability via .openspec([LEAN-FBA-NN])
tags (matrix auto-generates on archive).

Test-layer only: no Lean changes, no production-engine changes. Verified locally:
docx-core build + lint (tsc --noEmit) + full suite (1290 passed) +
check:spec-coverage + conformance checks all green.

Peer review (Gemini + Codex) pending before merge.

Implements openspec/changes/add-field-bearing-bridge-arbitrary.
@vercel

vercel Bot commented Jun 2, 2026

Copy link
Copy Markdown

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

Project Deployment Actions Updated (UTC)
site Ready Ready Preview, Comment Jun 2, 2026 8:07pm

Request Review

@github-actions github-actions Bot added the test label Jun 2, 2026
@github-actions

github-actions Bot commented Jun 2, 2026

Copy link
Copy Markdown
Contributor

LLM-Based Quality Gate

Overall: ✅ PASS (14 pass · 0 warn · 14 total)

Check Verdict
read_file response metadata parity The PR does not touch packages/docx-mcp/src/tools/read_file.ts or any read_file metadata, as it only modifies test code under packages/docx-core and openspec/verification documents.
Live DOM namespace-safe OOXML writes The PR does not touch packages/docx-core/src/primitives/comments.ts or write any prefixed OOXML attributes/elements, so the live DOM namespace-safe writes precondition is not met.
Deleted field markup keeps w:fldChar outside w:del The PR only modifies documentation and test suites, and does not touch the production implementation of field atomization, validateFieldStructure, hasFldCharInsideDel, or collapsed field comparison logic.
Field validation per story, not global The PR does not meet the precondition as it only modifies tests and documentation in packages/docx-core/src/integration/lean-spec-bridge.test.ts, packages/docx-core/src/testing/ooxml-fixtures.ts, and openspec/, without touching pipeline.ts or production field validation logic.
Revision IDs seeded from all revision-bearing side parts The PR only modifies test suites, testing helpers, and documentation, and does not touch packages/docx-mcp/src/session/manager.ts, revision-ID allocation, or any MCP tools.
Accept/reject sweep side parts and caches The PR does not touch DocxDocument.acceptChanges, DocxDocument.rejectChanges, REVISION_STORY_PART_PATHS, accept_changes, reject_changes, or side-part revision markup, as its changes are restricted to adding a field-bearing test arbitrary in packages/docx-core/src/integration/lean-spec-bridge.test.ts.
DocumentViewNode.heading stays canonical The PR does not touch packages/docx-core/src/primitives/document_view.ts, HeadingValue, heading heuristics, ListMetadata.header_style, or Google Docs document-view heading normalization, as it only modifies the Lean spec bridge test layer, OOXML test fixtures, and related documentation.
AI-author parity across entry points The PR does not touch packages/docx-mcp/src/server.ts, packages/docx-mcp/src/cli/tool_runner.ts, packages/docx-mcp/src/cli/commands/**, or add any SessionManager call sites.
Property-change wrapper discipline The PR does not touch packages/docx-core/src/primitives/layout.ts, packages/docx-core/src/primitives/text.ts, packages/docx-mcp/src/tools/clear_formatting.ts, or packages/docx-core/src/primitives/track-changes-emitter.ts, so the precondition is not met.
SUPPORT.md Table A drift vs. implementation The PR does not modify OOXML revision emission behavior in packages/docx-core/src/primitives/ or touch packages/docx-core/SUPPORT.md, so the precondition is not met.
Table A / Table B boundary on side-part revisions The PR only modifies test integration files, test fixtures, and documentation/specs, and does not touch comments.ts, footnotes.ts, or any side-part primitives.
Canonical-emission surface completeness The PR does not touch any tracked-edit surface in packages/docx-core/src/primitives/ or packages/docx-mcp/src/tools/, only modifying test suites, specifications, and the verification roadmap.
Lean predicate drift against engine semantics (asymmetric) The PR touches packages/docx-core/src/integration/lean-spec-bridge.test.ts:1053 to add a field-bearing fast-check arbitrary, but because TS engine semantics did not shift, no Lean predicate updates are required.
Unit-test quality (avoid tautological / change-detector tests) The added properties in packages/docx-core/src/integration/lean-spec-bridge.test.ts:1053-1175 are independent of the SUT, verifying first-principle invariants (round-trip text equality and ECMA field structure) on live output from generated inputs without any mocking.
Full checklist questions
  1. read_file response metadata parity: If this PR touches packages/docx-mcp/src/tools/read_file.ts, budgeted pagination returns, or additive response metadata like warnings / comment_load_error, do every successful return path (default budgeted early return, non-budget fallthrough, explicit limit/node_ids) preserve the same additive diagnostic fields? read_file has multiple success exits; diagnostics have already disappeared on one path before. Reference: fix(docx-core): declare xmlns:w14/w15 on comments root before writing prefixed attributes (#154) #180 surfaced comment_load_error, fix(docx-mcp): warn when read_file budget is exceeded by a single node (closes #184) #186 added an early budget return + warnings, fix(docx-mcp): surface comment_load_error on the default budgeted read path (closes #189) #191 fixed the missing comment_load_error on the default budgeted path.

  2. Live DOM namespace-safe OOXML writes: If this PR touches packages/docx-core/src/primitives/comments.ts or writes prefixed OOXML attributes/elements (w14:*, w15:*, xmlns:*, comments.xml, commentsExtended.xml, people.xml), are prefixed OOXML names written with namespace-aware APIs — root aliases bound with setAttributeNS(XMLNS_NS, ...), prefixed attributes with setAttributeNS(W14_NS/W15_NS, ...), and is there a test that proves the live DOM works before serialization/reparse? String-prefixed attributes can serialize plausibly while the live DOM still throws namespace errors. Reference: fix(docx-core): declare xmlns:w14/w15 on comments root before writing prefixed attributes (#154) #180 (xmlns:w14/w15 declared on comments root before writing prefixed attrs).

  3. Deleted field markup keeps w:fldChar outside w:del: If this PR touches field atomization, validateFieldStructure, hasFldCharInsideDel, w:fldChar, w:instrText, w:delInstrText, or collapsed field comparison logic, does deleted field output stay ECMA-376-conformant — w:fldChar sibling-level (never inside w:del), deleted instructions use w:delInstrText only inside valid delete wrappers, accept/reject safety checks still reject malformed combined output? Word treats deleted field-state markup in the wrong container as document-corrupting. References: fix(docx-core): validate w:delInstrText placement and reject w:fldChar inside <w:del> #211, fix(docx-core): partition field-closure validation by ECMA-376 story (#212) #225, fix(docx-core): fragment w:fldChar outside w:del per ECMA-376 Part 4 #228.

  4. Field validation per story, not global: If this PR touches packages/docx-core/src/baselines/atomizer/pipeline.ts, splitStories, validateFieldStructure, side-part merge logic, or footnote/endnote field handling, is field validation run independently per ECMA story (document.xml, each footnote, each endnote), with sidecars from both original and revised archives considered, and global counter balance not treated as sufficient? A document can be globally balanced but have an invalid field sequence inside one story. References: fix(docx-core): partition field-closure validation by ECMA-376 story (#212) #225, fix(docx-core): fragment w:fldChar outside w:del per ECMA-376 Part 4 #228, feat(docx-core): sweep side-part revisions on accept/reject #218.

  5. Revision IDs seeded from all revision-bearing side parts: If this PR touches packages/docx-mcp/src/session/manager.ts (especially getRevisionContextForSession or FIXED_REVISION_ID_SEED_PARTS), createRevisionContext, revision-ID allocation, or MCP tools that create tracked changes/comments/footnotes, does revision-ID allocation scan all relevant package parts before issuing new IDs — comments, footnotes, endnotes, glossary, headers, footers — ignore non-revision w:id values (comment IDs, bookmarks), and handle malformed optional parts gracefully? Revision IDs are package-wide; document-only seeding collides with existing side-part revisions. Reference: fix(docx-mcp): seed revision ids from side parts #216 (seed revision ids from side parts).

  6. Accept/reject sweep side parts and caches: If this PR touches DocxDocument.acceptChanges, DocxDocument.rejectChanges, REVISION_STORY_PART_PATHS, accept_changes, reject_changes, or side-part revision markup, does accept/reject process every revision-bearing story — updating document.xml + footnotes.xml + endnotes.xml + comments.xml, writing back only changed side parts while refreshing cached XML, and pruning orphan footnotes without deleting reserved separator entries? Accepting only in the main document leaves stale revisions and dangling references in the package. References: feat(docx-core): sweep side-part revisions on accept/reject #218, fix(docx-mcp): seed revision ids from side parts #216, fix(docx-core): partition field-closure validation by ECMA-376 story (#212) #225.

  7. DocumentViewNode.heading stays canonical: If this PR touches packages/docx-core/src/primitives/document_view.ts, HeadingValue, heading heuristics, ListMetadata.header_style, or Google Docs document-view heading normalization, does node.heading remain a structural heading signal — exact Word styles Heading1Heading6 win, heuristic sources suppressed inside table cells while real Word heading styles still pass, ordinary body paragraphs omit the heading key? Consumers use node.heading != null as a structural test; heuristic false positives break downstream navigation. References: fix(docx-core): harden heading detection (#157 Phase 1) #178, fix(docx-core): suppress non-sectional false-positive headings (closes #187) #188, feat(docx-core): add derived heading object to DocumentViewNode (closes #179) #190.

  8. AI-author parity across entry points: If this PR touches packages/docx-mcp/src/server.ts, packages/docx-mcp/src/cli/tool_runner.ts, packages/docx-mcp/src/cli/commands/**, or adds any new new SessionManager(...) call site in docx-mcp, does every entry point that constructs a SessionManager resolve SAFE_DOCX_AI_AUTHOR with the same three-way semantics (set → use it; empty string → opt out to untracked; unset → defaultAiAuthor), or has a new entry path silently bypassed tracked emission? Each entry path looks locally correct while diverging from another; tracked emission has gone dark in one path before anyone noticed. References: feat(docx-mcp): wire configurable AI author through MCP layer (#142) #172 (production MCP wiring would have kept tracked emission dark), fix(docx-mcp): honor SAFE_DOCX_AI_AUTHOR in CLI entry points (#181) #182 (CLI runners constructing bare SessionManager() silently produced untracked edits).

  9. Property-change wrapper discipline: If this PR touches packages/docx-core/src/primitives/layout.ts, packages/docx-core/src/primitives/text.ts, packages/docx-mcp/src/tools/clear_formatting.ts, or packages/docx-core/src/primitives/track-changes-emitter.ts, do tracked formatting/property edits emit exactly one correct *PrChange wrapper (pPrChange / rPrChange / trPrChange / tcPrChange) carrying a snapshot of the prior live properties — not stacking stale wrappers, not stripping valid historical children (cellIns/cellDel/cellMerge), and not omitting the snapshot when the operation is formatting-aware? Emitted OOXML is visually plausible but subtle snapshot mistakes only surface during later accept/reject or in Word's tracked-changes UI. References: feat(docx-core): emit pPrChange/trPrChange/tcPrChange from layout setters (#140) #167 (duplicate pPrChange/trPrChange/tcPrChange stacking + over-broad tcPr exclusion), feat(docx-mcp): emit rPrChange from clear_formatting MCP tool (#141) #170 (clear_formatting failing to strip stale rPrChange), feat(docx-core): emit rPrChange for formatted paragraph replacements #215 (rPrChange for formatted paragraph replacements + filtering nested stale records).

  10. SUPPORT.md Table A drift vs. implementation: If this PR modifies OOXML revision emission behavior (w:ins, w:del, w:rPrChange, etc.) in packages/docx-core/src/primitives/**, or touches packages/docx-core/SUPPORT.md, does the PR symmetrically update Table A in SUPPORT.md when the supported revision-emission surface in primitives changed — added, removed, or weakened — or is the documented contract now lying about what's supported? Reviewers focus on TS AST correctness and golden tests; Markdown contract tables get treated as an afterthought, so the documented surface drifts from the actual surface. Reference: [120.8] Regression suite for canonical revision emission across the surface #143 review caught replaceParagraphTextRange should emit w:rPrChange when run formatting changes #173 (formatting mismatch in Table A) and addCommentReply should emit body revision markup OR SUPPORT.md should be softened #174 (comment body revision omission forcing a Table A softening) late in peer review.

  11. Table A / Table B boundary on side-part revisions: If this PR touches packages/docx-core/src/primitives/comments.ts, packages/docx-core/src/primitives/footnotes.ts, or other side-part primitives, and adds/changes revision markup (w:ins, w:del), does tracked-change revision logic stay scoped to Table A (document-body content inside the side part) without leaking revision markup into Table B (the side-part package bootstrap — comments.xml/footnotes.xml element registration itself)? Body runs and side-part package elements share nearly identical XML namespace schemas; revisions emitted in the wrong table corrupt the package contract while looking plausible. References: [120.3] Emit w:ins/w:del for comment body anchors #138 (comment-body straddle constraints), [120.4] Emit w:ins/w:del for footnote reference and text #139 (footnote-reference straddle constraints).

  12. Canonical-emission surface completeness: If this PR adds or changes a tracked-edit surface in packages/docx-core/src/primitives/** or packages/docx-mcp/src/tools/**, are the paired artifacts updated together — packages/docx-core/src/integration/canonical-emission-regression.test.ts, packages/docx-mcp/src/integration/canonical-emission-mcp.test.ts, and the documented emitter surface (Table A) — or is the rollout only partially wired? The primitive change looks done before the MCP path, regression matrix, and documented contract are wired through; partial rollouts ship undocumented surface that drifts. References: feat(docx-mcp): wire configurable AI author through MCP layer (#142) #172 (RevisionContext threaded through every Table A MCP tool), test(docx-core,docx-mcp): final regression suite for canonical emission (#143) #175 (24-test regression suite + verified write-time emitter rows), feat(docx-core): emit rPrChange for formatted paragraph replacements #215 (re-enabled rPrChange regression + updated support surface for replaceParagraphTextRange).

  13. Lean predicate drift against engine semantics (asymmetric): If this PR changes field-wrapper semantics, the proof boundary, or atomizer behavior — packages/docx-core/src/baselines/atomizer/**, verification/lean/LeanSpike/Spec.lean, verification/lean/Tier2/**, or packages/docx-core/src/integration/lean-spec-bridge.test.ts — and if the TS engine semantics shifted, did the PR also update the Lean residual predicate and bridge tests, or is the proof now pinned to a stale stronger/weaker assumption? Asymmetric: a TS change without a corresponding Lean update is WARN; a Lean-only change without a TS update should not fire. The Lean side can still compile while the abstraction boundary is subtly wrong for the next engine refactor. References: feat(verification): close inv_field_001 with Tier 2 OoxmlDoc subset #208 (closed inv_field_001 using stronger recursivelyWellformed), refactor(verification): weaken inv_field_001 axiom to document-level preservationFriendly (rebased follow-up to #208) #220 (weakened the axiom to document-level preservationFriendly to avoid breakage when field fragmentation lands).

  14. Unit-test quality (avoid tautological / change-detector tests): If this PR adds or modifies any **/*.test.ts (or other test files), are the test assertions independent of the system under test — expected values constructed from first principles rather than re-derived from the function under test, mocks limited to external boundaries (filesystem, network, clocks) rather than mocking the SUT itself, assertions making concrete semantic claims rather than just snapshotting current behavior or asserting non-null, and any test added alongside a bug fix actually exercising the bug? Tests that re-implement the production code as the "expected" value, or mock out the system under test, pass green while providing no regression protection.

Estimated cost (this run): $0.0616 — 198,610 input + 756 output tokens (≈4 chars/token) on gemini-3.5-flash. Char-count estimate, not provider telemetry.

@codecov

codecov Bot commented Jun 2, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

…examples

Peer review (Codex, dynamic) found that fast-check consumes `examples` from
WITHIN the `numRuns` budget, not in addition to it — so `{ numRuns: 100,
examples: <12> }` ran 12 examples + 88 random = 100 total, not 112. The claim
of "100 random runs plus 12 deterministic examples" in the test/tasks was
therefore overstated (asymmetry-of-rot).

Fix: set the budget to `NUM_RUNS + fieldBearingExampleArgs.length` on both
field-bearing properties so all 12 deterministic operation×type combos run AND
a full NUM_RUNS (100) random cases run. Documents the fast-check budget
semantics at the examples definition and both call sites; aligns tasks.md.

Codex evidence: numRuns:10 + 3 examples yielded exactly 10 executions
(3 examples, 7 random). Re-verified: lint (tsc --noEmit) clean; both
properties green at 112 runs each.

No behavior change to the assertions; coverage floor unchanged.
@stevenobiajulu

Copy link
Copy Markdown
Member Author

Peer review (Gemini + Codex) — 2026-06-02

Codex (dynamic, executed): Ran the bridge tests package-scoped and stressed at numRuns: 500no field-bearing inplace flakiness. Probed the two load-bearing design decisions with a wrapper-neutrality dump on real combined output:

  • field-stable/* and text-only/*mode=inplace, no field atoms inside <w:ins>/<w:del>, all wrappers field-context-neutral → the strong assertRecursivelyWellformed is safe on these operations ✓
  • field-delete/*mode=inplace, the <w:delInstrText> wrapper is neutral=false while accept/reject validateFieldStructure stays true → confirms field-delete is uniquely the operation needing the weaker document-level check, so gating on exactly operation !== 'field-delete' is correct ✓

One Medium finding (resolved): fast-check consumes examples from within the numRuns budget, so { numRuns: 100, examples: <12> } ran 88 random + 12 examples = 100, not 112 — the "100 random plus 12 examples" wording oversold. Fixed in 9efa094 by making the budget additive (NUM_RUNS + fieldBearingExampleArgs.length) so a full 100 random cases run alongside the 12 deterministic combos; comments + tasks.md aligned. Re-verified green.

Gemini (static only): Execution was unavailable in its sandbox (run_shell_command blocked), so it self-flagged EXECUTION-UNAVAILABLE and could only review statically. It confirmed the structural claims (assertion-strength gate at the right site, #221 fixture hygiene, OpenSpec/ROADMAP alignment, header-comment accuracy) with no false positives, but ran zero tests — treated as NEEDS-EXECUTION, not a dispositive approval. The dynamic question was answered by Codex.

Resolution: the single finding is fixed and re-verified; both load-bearing design decisions are empirically validated by Codex's execution.

@stevenobiajulu

Copy link
Copy Markdown
Member Author

Re-review note: the focused re-review CLI crashed (tooling, not a finding), so I verified the fix directly with the same method Codex used — { numRuns: 100 + 12, examples: <12> } executes exactly 12 examples + 100 random = 112 runs; both call sites use the additive budget; bridge test green at 112/property. Finding resolved.

CI `allure-labels` failed: once this file carries `.openspec([LEAN-FBA-*])`
traceability tags, validate_allure_test_labels.mjs requires a named
`const TEST_FEATURE = ...` for deterministic Allure feature mapping (matching
openspec.traceability.test.ts / openspec.priority-scenarios.test.ts).

Hoist the existing friendly feature label 'Lean Spec Bridge (fast-check)' into
a TEST_FEATURE const and reference it via .withLabels({ feature: TEST_FEATURE }).
No label-value change (Allure grouping unchanged); using a const reference also
keeps it clear of the slug-literal Title-Case check.

Re-verified: check:allure-labels / -quality / -filenames pass; lint clean;
bridge properties green.
@stevenobiajulu stevenobiajulu enabled auto-merge (squash) June 2, 2026 20:07
@stevenobiajulu stevenobiajulu merged commit db82789 into main Jun 2, 2026
24 checks passed
@stevenobiajulu stevenobiajulu deleted the add-field-bearing-bridge-arbitrary-20260602 branch June 2, 2026 20:12
@stevenobiajulu

Copy link
Copy Markdown
Member Author

✅ Post-merge smoke passed

Merged: db82789 (squash)
Built from: main @ db82789
Smoke: build + lean-spec-bridge + full docx-core suite

Steps

  • ✅ clean build (@usejunior/docx-core, dist removed first)
  • ✅ field-bearing bridge properties green — INV-FIELD-001 and INV-RT-001 field-bearing (112 runs each = 100 random + 12 deterministic operation×type examples)
  • ✅ full docx-core suite — 1290 passed, 3 skipped, 87 files

Test-layer change (no production-engine code), so no real-document fixture run applies; the smoke exercises the new arbitrary against the live inplace engine plus the full regression suite.

LLM gate (Aggregate and post review) passed on the final head SHA after workflow_dispatch (it doesn't trigger on push). Branch cleaned up (remote auto-deleted, local deleted, refs pruned).

Log: /tmp/automerge-smoke-294-*.log (local).

@stevenobiajulu

Copy link
Copy Markdown
Member Author

✅ Post-merge smoke passed (real-document, document-shaped)

Merged: db82789 (squash)
Built from: main @ db82789
Smoke: real NVCA COI pair → live inplace compareDocuments → INV-FIELD-001 + INV-RT-001 on the combined output

This PR touches packages/docx-core/, so per the skill it is document-shaped — re-smoked against a real legal DOCX (my earlier pass used synthetic buildDocxFromBodyXml fixtures, which only tests the scaffolding). This run drives genuine document structure through the same invariants the new arbitrary covers.

Steps

  • ✅ clean build (@usejunior/docx-core, dist removed first)
  • ✅ full docx-core suite (run this session on merged main: 1290 passed, 3 skipped, 87 files)
  • ✅ real-document end-to-end (below)

Real-world fixture

  • tests/test_documents/nvca-coi-regression/ — real NVCA Certificate of Incorporation (source.docx 147 KB vs filled.docx 92 KB), read from disk, compared in reconstructionMode: 'inplace'
    • Stayed inplace (fallbackReason=(none)); combined output carries 99 <w:ins> / 462 <w:del> tracked changes
    • Genuinely field-bearing: combined has 408 <w:fldChar>, 148 <w:instrText>, 54 <w:delInstrText> — so the delInstrText → instrText rename path (INV-RT-001 reject side) is exercised on real input, not just synthetic
    • INV-RT-001: normalize(extractText(accept(combined))) == filled and == reject(combined) -> source
    • INV-FIELD-001: validateFieldStructure holds on both accept-all and reject-all projections ✓

Cleanup (already completed in the merge turn)

  • ✅ remote branch add-field-bearing-bridge-arbitrary-20260602 (auto-deleted by repo setting)
  • ✅ local branch deleted, origin pruned
  • ⚠️ no worktree (implementation ran in-place on the feature branch)

Log: /tmp/automerge-smoke-294-1780436976.log (local).

stevenobiajulu added a commit that referenced this pull request Jun 3, 2026
…d-bearing arbitrary (#295)

Both changes are merged on main but were left unarchived:
- add-inv-rt-001-proof (PR #293) — closes inv_rt_001, zero-sorry spike
- add-field-bearing-bridge-arbitrary (PR #294) — field-bearing fast-check arbitrary

`openspec archive` moves each change into changes/archive/ and applies its delta to
the canonical docx-comparison spec (two new requirements + their [LEAN-RT-*] /
[LEAN-FBA-*] scenarios). The traceability matrix is regenerated to reflect the now-
canonical scenarios; the Lean-build-verified (LEAN-RT-01/02/03), doc-verified
(LEAN-RT-04) and falsifiability-bridge (LEAN-RT-05) scenarios show as unmapped in the
vitest matrix by design, and check:spec-coverage stays green (PASS: canonical
scenarios covered).

Docs/spec only — no production-engine or test code changed.
stevenobiajulu added a commit that referenced this pull request Jun 3, 2026
…Lean spec bridge (#296)

* test(docx-comparison): fragmented-field fast-check arbitrary for the Lean spec bridge

Widens the field-bearing falsifiability layer for the two residual axioms
(INV-FIELD-001 / INV-RT-001) to the fragmented-field surface the predecessor
(add-field-bearing-bridge-arbitrary, #294) explicitly deferred: a field whose
result run changes under track changes, and/or a pre-tracked field whose
instruction code is already split into <w:ins>/<w:del>.

Empirically grounded: probing the live engine showed it CORRECTLY falls back
from inplace to rebuild on the clean->pretracked-fragmented + result-change
operation (its inplace candidate fails ONLY the fieldStructure safety check —
the #217 fldChar-in-del class — while acceptText/rejectText pass). So the
predecessor's "fallback = falsification" rule does not hold here. The new
`fragmentedFieldPairArb` property asserts the axioms MODE-INDEPENDENTLY (on the
resolved accept/reject projections, never the raw combined output) and a
mode-distribution coverage floor requires BOTH inplace and fallback outcomes to
be observed, so a silent all-inplace/all-fallback regression fails loudly.

Test-layer only: no Lean changes, no production-engine changes. The fallback is
the engine's correct defensive behavior, not a bug to fix.

- ooxml-fixtures.ts: add completeField / fragmentedFieldModification /
  FIELD_INSTRUCTIONS; refactor COMPLETE_* and FRAGMENTED_NUMPAGES_MODIFICATION
  onto them (byte-identical values).
- lean-spec-bridge.test.ts: fragmentedFieldPairArb + mode-independent property
  + mode-distribution coverage floor; header Coverage/Fallback blocks updated.
- OpenSpec change add-fragmented-field-bridge-arbitrary (ADD-only delta,
  [LEAN-FRAG-01..04]); nested + paragraph-spanning fields deferred to successor.

Peer review (Gemini + Codex) pending.

* test(docx-comparison): single-line LEAN-FRAG openspec tags for clean coverage mapping

Codex peer review: the multiline .openspec(...) calls with trailing commas
defeated the check:spec-coverage parser (regex expects .openspec('...') on one
line), so the FRAG block was reported as one Extra-scenario blob instead of
mapping [LEAN-FRAG-01..04] individually. Reformat to single-line, matching the
existing [LEAN-FBA-02..05] style. No behavior change.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant