@ansonlai/docx-redline-js 0.4.0 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (100) hide show
  1. package/AGENTS.md +589 -287
  2. package/ARCHITECTURE.md +215 -9
  3. package/CHANGELOG.md +319 -0
  4. package/README.md +604 -360
  5. package/adapters/config.js +45 -43
  6. package/bin/docx-redline.js +3 -0
  7. package/core/list-targeting.js +101 -110
  8. package/core/paragraph-targeting.js +501 -61
  9. package/core/paragraph-text.js +209 -0
  10. package/core/revision-cloning.js +38 -0
  11. package/core/types.js +64 -10
  12. package/core/word-xml.js +43 -15
  13. package/dist/docx-redline-js.esm.js +2849 -466
  14. package/dist/docx-redline-js.esm.js.map +4 -4
  15. package/dist/docx-redline-js.esm.min.js +87 -76
  16. package/dist/docx-redline-js.esm.min.js.map +4 -4
  17. package/docs/TESTING.md +342 -23
  18. package/docs/plans/2026-09-05-structural-revisions-and-fidelity-oracles.md +1669 -0
  19. package/docs/plans/completed/2026-09-01-performance-and-complexity-reduction.md +669 -0
  20. package/docs/plans/completed/2026-09-03-agent-friendly-document-workflows.md +427 -0
  21. package/docs/plans/completed/2026-09-04-comment-anchor-and-cli-reliability.md +519 -0
  22. package/docs/plans/completed/PERFORMANCE-CONSOLIDATION.md +69 -0
  23. package/docs/plans/completed/structural-revision-capability-matrix.md +115 -0
  24. package/docs/schemas/document-operations.schema.json +109 -0
  25. package/docs/test-comparison-dashboard.html +4250 -7
  26. package/engine/formatting-removal.js +11 -2
  27. package/engine/oxml-engine.js +491 -336
  28. package/engine/reconstruction-mode.js +15 -14
  29. package/engine/reconstruction-writer.js +247 -142
  30. package/engine/route-selection.js +35 -0
  31. package/engine/rpr-helpers.js +334 -35
  32. package/engine/run-builders.js +239 -196
  33. package/engine/surgical-diff-application.js +222 -37
  34. package/engine/surgical-mode.js +134 -6
  35. package/engine/surgical-spans.js +52 -1
  36. package/engine/table-cell-context.js +3 -6
  37. package/engine/table-mode.js +1 -1
  38. package/index.d.ts +234 -6
  39. package/index.js +24 -1
  40. package/node/cli.js +317 -0
  41. package/node/docx-document.js +302 -0
  42. package/node/index.d.ts +31 -0
  43. package/node/index.js +2 -0
  44. package/node/zip-archive.js +52 -0
  45. package/orchestration/list-markdown.js +10 -16
  46. package/orchestration/list-parsing.js +7 -12
  47. package/orchestration/list-structural-fallback.js +21 -10
  48. package/package.json +24 -3
  49. package/pipeline/content-analysis.js +12 -17
  50. package/pipeline/ingestion-export.js +3 -31
  51. package/pipeline/ingestion-paragraph.js +10 -5
  52. package/pipeline/list-generation.js +150 -55
  53. package/pipeline/list-markers.js +70 -3
  54. package/pipeline/serialization.js +4 -2
  55. package/pipeline/structured-content.js +160 -0
  56. package/scripts/apply_changes.mjs +27 -0
  57. package/scripts/benchmark-operation-session.mjs +137 -0
  58. package/scripts/benchmark-targeting-browser.html +74 -0
  59. package/scripts/benchmark-targeting-hot-paths.mjs +67 -0
  60. package/scripts/benchmark-test-runner.mjs +59 -0
  61. package/scripts/build-test-dashboard.mjs +23 -0
  62. package/scripts/export-lane1-fixtures.mjs +380 -0
  63. package/scripts/export-reredline-stress-fixtures.mjs +317 -0
  64. package/scripts/export-validation-fixtures.mjs +1 -1
  65. package/scripts/extract_text.mjs +7 -0
  66. package/scripts/generate-paragraph-boundary-fixtures.ps1 +215 -0
  67. package/scripts/generate-test-dashboard.mjs +362 -11
  68. package/scripts/lib/word-coverage-catalogue.mjs +6 -2
  69. package/scripts/profile-route-selection.mjs +19 -0
  70. package/scripts/render-agenda-multilevel.mjs +0 -5
  71. package/scripts/render-multilevel-cases.mjs +0 -1
  72. package/scripts/run-tests.mjs +107 -35
  73. package/scripts/word-com-corpus-suite.ps1 +3 -0
  74. package/scripts/word-com-differential.ps1 +64 -4
  75. package/scripts/word-com-suite.ps1 +3 -0
  76. package/services/batch-operation-orchestrator.js +494 -0
  77. package/services/capture-engine.js +226 -0
  78. package/services/comment-builders.js +23 -6
  79. package/services/comment-engine.js +108 -47
  80. package/services/comment-locator.js +187 -82
  81. package/services/comment-replies.js +95 -0
  82. package/services/document-inspection.js +258 -0
  83. package/services/document-operation-applier.js +372 -0
  84. package/services/document-operation-contract.js +323 -0
  85. package/services/document-operation-mutations.js +1733 -0
  86. package/services/document-operation-session.js +258 -0
  87. package/services/numbering-service.js +14 -5
  88. package/services/operation-heuristics.js +173 -0
  89. package/services/operation-preflight.js +366 -0
  90. package/services/receipt-collector.js +288 -0
  91. package/services/revision-comment-management.js +37 -5
  92. package/services/revision-token.js +290 -0
  93. package/services/standalone-docx-plumbing.js +123 -8
  94. package/services/standalone-operation-runner.d.ts +296 -0
  95. package/services/standalone-operation-runner.js +10 -1455
  96. package/services/table-reconciliation.js +15 -6
  97. package/docs/VALIDATION.md +0 -183
  98. package/docs/WORD-MANUAL-REVIEW.md +0 -138
  99. package/docs/plans/2026-09-01-performance-and-complexity-reduction.md +0 -210
  100. /package/docs/plans/{2026-08-30-reliability-testing-improvements.md → completed/2026-08-30-reliability-testing-improvements.md} +0 -0
package/docs/TESTING.md CHANGED
@@ -20,7 +20,63 @@ fixtures rarely contain.
20
20
  | Visual evidence inspection | `npm run test:visual:inspect` | Automated inspection of rendered Word PDFs across synthetic and SuperDoc suites (page counts, PDF integrity, anomaly detection) | Human visual sign-off |
21
21
  | Visual failure regressions | `node tests/visual_failure_regression_tests.mjs` | Semantic OOXML guards against visual failures (formatting leaks, font resets, ghost bullets, table cell destruction) | Visual rendering proof in Word |
22
22
  | Multimodal LLM visual spot check | On-demand / sampled | Evaluates rendered real-document pages with vision models for layout, table alignment, and typography regressions | Full-corpus automated coverage (intentionally decoupled and sampled due to cost/time) |
23
- | XSD and LibreOffice | See `docs/VALIDATION.md` | Schema conformance and acceptance by a second consumer | Word-specific revision semantics |
23
+ | XSD and LibreOffice | See [Release validation and independent oracles](#release-validation-and-independent-oracles) | Schema conformance and acceptance by a second consumer | Word-specific revision semantics |
24
+ | Agent inspection and package facade | `node tests/document_inspection_tests.mjs`, `node tests/docx_package_facade_tests.mjs` | Canonical text, comment/list resolution, package-scoped IDs, untouched-part preservation, and atomic rollback | Desktop Word rendering |
25
+ | Agent CLI | `node tests/agent_cli_tests.mjs` | JSON contracts, exact-text extraction, author requirements, safe output behavior, all command families, and operation-schema readability | Cross-platform CI beyond the current runner |
26
+ | Agent edge cases | `node tests/canonical_paragraph_text_tests.mjs`, `node tests/document_inspection_edge_tests.mjs`, `node tests/docx_package_transaction_edge_tests.mjs`, `node tests/node_zip_archive_tests.mjs`, `node tests/agent_cli_edge_tests.mjs` | Revision-view semantics, cross-paragraph anchors, nested numbering, transaction reuse, multi-author cleanup, malformed ZIP handling, and destructive CLI safeguards | Desktop Word rendering and non-Windows CI |
27
+ | Performance boundary regression | `node tests/performance_phase2_boundary_tests.mjs` | Stable facade re-exports, leaf imports, session rollback, isolated context commit, and comment-first scheduling | The Phase 1 one-parse/one-serialize performance target |
28
+ | Live-session accuracy and instrumentation | `node tests/performance_phase1_session_tests.mjs` | One full parse/serialization, sequential semantic equivalence, exact accepted/rejected text, valid revisions, list/table/comment/highlight preservation, savepoint no-ops, and zero-serialization rollback | Desktop Word rendering |
29
+ | Target hot-path parity | `node tests/performance_phase3_target_hot_paths_tests.mjs` | Cached and uncached resolution metadata parity, duplicate safety, snapshots, session invalidation, and pointer-based revision-ID seeding | Machine-independent latency thresholds |
30
+ | List and text-walker parity | `node tests/performance_phase4_list_and_text_parity_tests.mjs` | One shared marker vocabulary and canonical/specialized visible-text agreement across structural characters and revision views | That all specialized mappings are interchangeable |
31
+ | Route compatibility | `node tests/performance_phase5_route_consolidation_tests.mjs` | Direct/legacy list accepted-rejected parity, numbering artifacts, route selection, capability records, and stable public exports | Permission to migrate the retained multi-paragraph compatibility route |
32
+
33
+ The package-facade regression opens a real ZIP buffer, adds a comment beside an
34
+ existing high ID, validates OPC wiring, and checks an unrelated binary part is
35
+ unchanged after extraction. Its failure case requires byte-exact rollback. The
36
+ inspection regression covers revisions, structural characters, heading
37
+ context, comment joining, and a numbering start override.
38
+
39
+ The CLI regression executes the recommended extract → preflight → apply →
40
+ validate flow and also covers accept, reject, comment deletion, output collision
41
+ refusal, and source-byte preservation. CLI stdout is captured and parsed as one
42
+ JSON value so diagnostic logging cannot corrupt agent output.
43
+
44
+ The edge suites deliberately use structures omitted from the basic happy-path
45
+ fixtures: `w:moveFrom`/`w:moveTo`, deleted text, structural breaks and hyphens,
46
+ comments crossing paragraph boundaries, orphan definitions, outline headings,
47
+ footnote/endnote references, nested Roman/alphabetic numbering, sequential
48
+ commit-then-rollback calls, selective comment authors, existing high anchor
49
+ IDs, encrypted/unsupported ZIP entries, `--force`, and `--in-place`.
50
+
51
+ The Phase 2 boundary regression is intentionally structural as well as
52
+ behavioral. It prevents the public runner from accumulating implementation
53
+ logic again, verifies that internal operation code does not import the root
54
+ entry point, and checks that failed atomic work can retain the exact source
55
+ string while working maps and sets remain isolated until commit.
56
+
57
+ The checked post-Phase-5 2026-09-04 coverage run reports 89.81%
58
+ statements/lines, 77.34% branches, and 93.04% functions repository-wide; this
59
+ is above every recorded pre-refactor dimension. Targeting performance can be
60
+ measured in Node with `npm run benchmark:targeting` and against the native DOM
61
+ by serving the repository locally and opening
62
+ `scripts/benchmark-targeting-browser.html`; its `paragraphs`, `operations`, and
63
+ `iterations` query parameters reproduce the checked fixture sizes.
64
+ The Node surface reports 99.61% statements/lines; `document-inspection.js`
65
+ reports 97.64% statements/lines and 100% functions; `paragraph-text.js` reports
66
+ 100% statements/lines/functions.
67
+
68
+ Run the observational live-session benchmark with:
69
+
70
+ ```powershell
71
+ npm run benchmark:session
72
+ ```
73
+
74
+ It writes `tmp/benchmarks/operation-session-latest.json`. The checked
75
+ 1,000-paragraph/10-operation run reduced full-document parse/serialization from
76
+ 10/10 to 1/1 and measured a 1.58x median speedup. Timing and heap figures are
77
+ not correctness gates. Per-operation DOM savepoints are retained because
78
+ redline accuracy, no-op isolation, and rollback fidelity take precedence over
79
+ the aspirational speed target.
24
80
 
25
81
  ## Coverage matrix and test selection
26
82
 
@@ -125,9 +181,9 @@ package.
125
181
  ### Expand weak list and table cells with structural operations
126
182
 
127
183
  The list/table expansion was driven directly by the task-by-structure matrix;
128
- it did not add more plain replacement cases. The current catalogue contains 107
129
- Word cases: 47 synthetic fixtures and 60 reviewed real-document scenarios. The
130
- `List Change × List` cell now contains 26 cases; the
184
+ it did not add more plain replacement cases. The current catalogue contains 109
185
+ Word cases: 49 synthetic fixtures and 60 reviewed real-document scenarios. The
186
+ `List Change × List` cell now contains 27 cases; the
131
187
  `Table Reconciliation × Table` cell contains 11 cases.
132
188
 
133
189
  The added synthetic cases isolate one structural decision at a time:
@@ -138,11 +194,18 @@ The added synthetic cases isolate one structural decision at a time:
138
194
  - insert into the middle of an explicit list range;
139
195
  - preserve upper- and lower-Roman numbering;
140
196
  - preserve upper-letter, parenthesized lower-letter, dash, and symbol bullets;
197
+ - expand a manually numbered heading whose `w:numId="0"` suppresses numbering
198
+ into separate, positively numbered list paragraphs;
141
199
  - update one table cell;
142
200
  - insert a table row;
143
201
  - delete a table row; and
144
202
  - update several cells in one reconciliation.
145
203
 
204
+ The mixed-attachment fixture replaces one anchor with explicit headings,
205
+ ordinary paragraphs, a Markdown table, and a numbered list. It verifies real
206
+ `w:pStyle`, `w:tbl`, and positive-numbering structures plus independent Word
207
+ Accept All and Reject All results; literal table pipes are forbidden.
208
+
146
209
  The real-document cases repeat those claims against native numbering and table
147
210
  markup from municipal notices, board agendas, a long zoning ordinance, PPG
148
211
  minutes, action tables, and invoice tables. They include consecutive and nested
@@ -311,10 +374,25 @@ unchanged sample.
311
374
  ## Automated JavaScript tests
312
375
 
313
376
  Files matching `tests/*.mjs` are discovered by `scripts/run-tests.mjs`. Tests
314
- use `assert/strict` and run as separate Node processes. Shared OOXML assertions
315
- belong in `tests/helpers/ooxml-assertions.mjs`; XML-provider setup belongs in
377
+ use `assert/strict` and each runs in a separate Node process. The runner uses up
378
+ to four workers by default while preserving filename-sorted reporting. Set
379
+ `DOCX_TEST_CONCURRENCY=1` for deterministic serial reproduction, or another
380
+ positive integer for an explicit worker bound. Word, visual, and corpus lanes
381
+ remain separate serial commands. Shared OOXML assertions belong in
382
+ `tests/helpers/ooxml-assertions.mjs`; XML-provider setup belongs in
316
383
  `tests/setup-xml-provider.mjs`.
317
384
 
385
+ Measure serial and parallel execution on the same machine with:
386
+
387
+ ```powershell
388
+ npm run benchmark:tests
389
+ ```
390
+
391
+ The benchmark performs paired warmups/runs and writes its JSON result to
392
+ `tmp/benchmarks/test-runner-latest.json`. The checked Node 24/Windows x64 run
393
+ reduced median time from 18.02 seconds to 7.96 seconds (55.80%). Twenty
394
+ consecutive four-worker runs completed without a failure.
395
+
318
396
  For function-level gap work, run:
319
397
 
320
398
  ```powershell
@@ -345,6 +423,13 @@ For an engine regression:
345
423
  corpus so nearby inputs are exercised too.
346
424
  5. Run `npm test`, isolation, types, and lint.
347
425
 
426
+ For a mixed attachment regression, also run the content through
427
+ `planStructuredReplacement(...)`. Assert the declared block sequence, reject
428
+ malformed table grammar, and verify that the result contains real `w:tbl`,
429
+ heading styles, positive list numbering IDs, valid non-nested revisions, and
430
+ independent Accept/Reject outcomes. Do not treat absence of literal pipe text as
431
+ sufficient proof of a table.
432
+
348
433
  ## Synthetic Microsoft Word tests
349
434
 
350
435
  The runtime library contains no COM or Word dependency. Word automation exists
@@ -463,11 +548,12 @@ alignment, font substitution, changed list indentation, clipped headers,
463
548
  visually stale fields, or a comment/footnote marker that is technically present
464
549
  but poorly placed.
465
550
 
466
- ### Human Word visual review
551
+ ### Microsoft Word visual review guide
467
552
 
468
- Human visual review complements—not replaces—the automated differential. Use
469
- the checklist and report template in
470
- [`WORD-MANUAL-REVIEW.md`](./WORD-MANUAL-REVIEW.md).
553
+ Human visual review complements—not replaces—the automated differential. The
554
+ automated differential proves Word revision semantics by comparing text after
555
+ Accept All and Reject All, but this review checks the layout, typography, and
556
+ interaction details that `Document.Content.Text` cannot see.
471
557
 
472
558
  Review is required for:
473
559
 
@@ -478,19 +564,131 @@ Review is required for:
478
564
  - any case that needs normalized rather than exact text comparison; and
479
565
  - any automated Word failure whose cause is not immediately textual.
480
566
 
481
- Before a release, review all new/changed cases plus a rotating sample of at
482
- least 20% of the unchanged synthetic catalogue. The sample must include legal
483
- and administrative content and at least one list, table, formatted-run, and
484
- structural-anchor case. Also review at least one legal and one administrative
485
- SuperDoc result. Rotate the sample so every retained synthetic case receives a
486
- human review over five release cycles. A major release or a change to package
487
- assembly requires a full visual sweep of affected structure families.
567
+ #### Prepare the review set
568
+
569
+ Run the automated lanes first:
570
+
571
+ ```powershell
572
+ npm run test:word
573
+ npm run test:corpus:word
574
+ npm run report:word:coverage
575
+ npm run review:word:prepare -- --cycle=0
576
+ ```
577
+
578
+ The last command writes a pending review manifest under ignored
579
+ `tmp/word-manual-review/` containing changed catalogue families, the rotating
580
+ 20% synthetic release sample, and legal/administrative corpus representatives.
581
+ It only prepares the selection: it never records a visual pass or human
582
+ sign-off. Use a new cycle number for each release rotation.
583
+
584
+ Synthetic documents are generated under `tmp/word-validation/`; reviewed
585
+ SuperDoc results are under `tmp/superdoc-word-fixtures/`. Do not commit generated
586
+ or downloaded `.docx` files.
587
+
588
+ Selection rules:
589
+ - every new or changed case;
590
+ - every case required by the triggers above;
591
+ - at least 20% of unchanged synthetic cases, rotating from the prior release (so
592
+ every retained synthetic case receives a human review over five release cycles);
593
+ - at least one legal and one administrative SuperDoc result; and
594
+ - representative list, table, formatted-run, and anchor/field/content-control
595
+ structures.
596
+
597
+ A major release or a change to package assembly requires a full visual sweep of
598
+ affected structure families.
599
+
600
+ Record the installed Word version and build from **File → Account → About Word**.
601
+ Differences in rendering can be version-specific.
602
+
603
+ #### Configure Word
604
+
605
+ For the tracked-change inspection:
606
+
607
+ 1. Open the generated fixture directly in desktop Word.
608
+ 2. On **Review**, select **All Markup**.
609
+ 3. Under **Show Markup**, enable insertions/deletions, formatting, comments, and
610
+ all reviewers relevant to the case.
611
+ 4. Use the expected balloons/inline display for the document and enable
612
+ paragraph marks when checking whitespace, tabs, breaks, lists, and empty
613
+ paragraphs.
614
+ 5. Do not overwrite the generated fixture. Work on disposable copies if a
615
+ saved accepted or rejected view is useful.
616
+
617
+ #### Inspect each case (3-view checklist)
618
+
619
+ ##### 1. Tracked-change view
620
+
621
+ - The document opens without a repair, conversion, or unreadable-content prompt.
622
+ - Word shows the expected revision count and author attribution.
623
+ - Insertions and deletions are anchored at the intended words or paragraph
624
+ marks; a small edit has not become an unexplained whole-paragraph rewrite.
625
+ - Untargeted text and surrounding revisions remain unchanged.
626
+ - Existing bold, italic, underline, highlighting, fonts, styles, and language
627
+ settings remain visually consistent.
628
+ - Spaces, tabs, manual breaks, paragraph spacing, indentation, and alignment
629
+ look intentional with formatting marks visible.
630
+ - Lists retain numbering, levels, continuation, indentation, and marker style.
631
+ - Tables retain widths, borders, merged cells, row heights, and alignment.
632
+ - Bookmarks, hyperlinks, fields, content controls, comments, and note references
633
+ remain in the correct visible location and still behave when activated.
634
+ - Headers, footers, section boundaries, page breaks, and pagination remain
635
+ stable around the edit.
636
+ - Revision balloons and comment balloons point to the correct content and do
637
+ not obscure or displace unrelated layout unexpectedly.
638
+
639
+ ##### 2. Accept All view
640
+
641
+ On a disposable copy, choose **Accept All Changes** and verify:
642
+
643
+ - The resulting visible text expresses the intended edit.
644
+ - No deletion residue, empty revision wrapper, unexpected blank line, or stale
645
+ formatting remains.
646
+ - Lists, tables, fields, links, comments, notes, and page layout still work.
647
+ - Untargeted content is visually unchanged.
648
+
649
+ Close the copy without replacing the generated fixture.
650
+
651
+ ##### 3. Reject All view
652
+
653
+ Reopen a fresh copy, choose **Reject All Changes**, and verify:
654
+
655
+ - The original visible text and formatting are restored.
656
+ - Original list numbering, table layout, fields, anchors, and pagination return.
657
+ - No content introduced by the edit remains.
658
+
659
+ #### Record the visual review result
660
+
661
+ Keep the review report with release-validation artifacts. Screenshots may be
662
+ stored under ignored `tmp/word-manual-review/<date>/` when useful, but do not
663
+ commit corpus document images or document contents without checking rights and
664
+ sensitivity.
665
+
666
+ Suggested report template:
667
+
668
+ ```markdown
669
+ # Word visual review — <release/date>
670
+
671
+ - Reviewer:
672
+ - Review date:
673
+ - Word version/build:
674
+ - Automated synthetic result:
675
+ - Automated corpus result:
676
+ - Review type: Human sign-off | AI visual preflight
677
+
678
+ | Case | Why selected | All Markup | Accept All | Reject All | Result | Notes |
679
+ |---|---|---|---|---|---|---|
680
+ | example-case | New table structure | Pass | Pass | Pass | Pass | No layout shift |
681
+
682
+ ## Failures or follow-ups
683
+
684
+ - None.
685
+ ```
686
+
687
+ A **Pass** requires all three views to pass. Record **Fail** if rendering or
688
+ interaction is wrong even when automated text comparison passes. Turn a failure
689
+ into a fixed regression case when possible; otherwise record the exact harness
690
+ or Word-version limitation in the active reliability plan.
488
691
 
489
- The reviewer inspects three states in Word: tracked changes with **All Markup**,
490
- the result after **Accept All**, and a fresh copy after **Reject All**. Record
491
- the reviewer, date, Word version/build, cases selected, pass/fail result, and
492
- notes. A visual failure becomes a regression case or a documented harness gap;
493
- do not waive it merely because the COM text differential passed.
494
692
 
495
693
  ### AI-assisted Word visual preflight
496
694
 
@@ -670,7 +868,128 @@ revision part (`word/document.xml` by default, or a named header part), and
670
868
  verifies every untouched ZIP part byte-for-byte before Word opens the result.
671
869
  The Word oracle derives multi-change expectations by applying each independently
672
870
  declared replacement to Word's source text and reads header-story text separately
673
- for header cases.
871
+ ## Release validation and independent oracles
872
+
873
+ This package works on OOXML strings and intentionally leaves `.docx` zip
874
+ packaging to consumers (release *tooling* assembles minimal `.docx` fixtures
875
+ with a script-local zip writer; the published library has no zip dependency).
876
+
877
+ The test suite verifies the accept/reject round-trip invariant using the
878
+ library's own transforms. Because a shared misconception between the
879
+ generator and the resolver would pass those tests silently, release
880
+ validation adds **independent oracles**: Microsoft Word, LibreOffice, and
881
+ the ECMA-376 schemas.
882
+
883
+ ### Pre-release automated check sequence
884
+
885
+ ```bash
886
+ npm test # includes tests/roundtrip_fuzz_tests.mjs (seeded, deterministic)
887
+ npm run test:isolation
888
+ npm run check:types
889
+ npm run lint
890
+ ```
891
+
892
+ The fuzz harness generates random paragraph structures and edits, asserting the
893
+ round-trip invariant plus `validateRedlineOoxml` on each case. Tune or reproduce with:
894
+
895
+ ```bash
896
+ FUZZ_SEED=<seed> FUZZ_ITERATIONS=<n> node tests/roundtrip_fuzz_tests.mjs
897
+ ```
898
+
899
+ A failing case prints its exact reproduction command.
900
+
901
+ ### Runtime guardrail
902
+
903
+ `validateRedlineOoxml(oxml)` (exported from `index.js`) runs structural
904
+ invariants at runtime and returns `{ valid, issues }`. Downstream packagers
905
+ should call it before writing engine output into `word/document.xml`.
906
+
907
+ ### Export fixtures
908
+
909
+ ```bash
910
+ node scripts/export-validation-fixtures.mjs
911
+ ```
912
+
913
+ Writes to `tmp/validation-docx/`, per case:
914
+
915
+ - `<name>.document.xml` — generated `word/document.xml` payload
916
+ - `<name>.docx` — minimal assembled package
917
+ - `<name>.expected.json` — expected accept-all / reject-all plain text,
918
+ derived from edit *intent* (not from this library's transforms), so
919
+ external consumers act as independent oracles.
920
+
921
+ ### Schema validation (ECMA-376 transitional XSD)
922
+
923
+ ```bash
924
+ node scripts/export-validation-fixtures.mjs
925
+ bash scripts/validate-fixtures-xsd.sh
926
+ ```
927
+
928
+ Downloads (and caches in `.cache/ooxml-schemas/`) the transitional
929
+ wordprocessingml schemas from ECMA-376 Part 4, patches the `xml:` namespace
930
+ import to resolve offline, and validates every `*.document.xml` fixture with
931
+ `xmllint`. Requires `curl`, `unzip`, and `xmllint` (`libxml2-utils` on
932
+ Debian/Ubuntu; available on Windows via conda/msys).
933
+
934
+ ### LibreOffice consumer check
935
+
936
+ ```bash
937
+ cd tmp/validation-docx
938
+ soffice --headless --convert-to pdf --outdir converted *.docx
939
+ ```
940
+
941
+ Verifies that a second independent OOXML consumer parses and converts the
942
+ fixtures without error.
943
+
944
+ ### Continuous validation
945
+
946
+ `.github/workflows/validation.yml` runs nightly (and on demand via
947
+ `workflow_dispatch`):
948
+
949
+ 1. **xsd-schema** — exports fixtures and validates them against the
950
+ ECMA-376 transitional `wml.xsd`.
951
+ 2. **libreoffice** — exports fixtures and converts them with headless
952
+ LibreOffice.
953
+ 3. **fuzz-extended** — 20,000 fuzz round-trip cases with a date-derived
954
+ seed, so every night explores new inputs. A failure log includes the
955
+ exact `FUZZ_SEED` reproduction command.
956
+
957
+ The Word differential check stays manual because it requires desktop Word;
958
+ run it before tagging a release.
959
+
960
+ ### JavaScript coverage baselines
961
+
962
+ ```bash
963
+ npm run test:coverage
964
+ ```
965
+
966
+ The command runs the complete JavaScript test suite under c8 and prints a
967
+ per-file report. Coverage answers "which implementation paths did the automated
968
+ JavaScript tests execute?" It does not prove that executed paths are correct,
969
+ that generated OOXML opens in Word, or that the real-document corpus is broad
970
+ enough. Treat it as a map for finding thinly tested code; Word, schema,
971
+ LibreOffice, fuzz, and corpus checks provide different evidence.
972
+
973
+ Historical reference snapshots:
974
+
975
+ - **Initial Phase 7 baseline (2026-08-29)**:
976
+ | Metric | Coverage |
977
+ |---|---:|
978
+ | Lines / statements | 79.57% |
979
+ | Functions | 80.07% |
980
+ | Branches | 69.22% |
981
+
982
+ - **Post-Phase-5 snapshot (2026-08-30)**:
983
+ 79.96% lines/statements, 80.95% functions, and 69.52% branches.
984
+
985
+ - **Post-Phase-5 snapshot (2026-09-04)**:
986
+ 89.81% statements/lines, 93.04% functions, and 77.34% branches repo-wide.
987
+ The Node surface reports 99.61% statements/lines; `document-inspection.js`
988
+ reports 97.64% statements/lines and 100% functions; `paragraph-text.js` reports
989
+ 100% statements/lines/functions.
990
+
991
+ Preserve snapshots so progress is visible over time rather than presenting
992
+ coverage as a single pass/fail quality score.
674
993
 
675
994
  ## Choosing where a new test belongs
676
995