@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.
- package/AGENTS.md +589 -287
- package/ARCHITECTURE.md +215 -9
- package/CHANGELOG.md +319 -0
- package/README.md +604 -360
- package/adapters/config.js +45 -43
- package/bin/docx-redline.js +3 -0
- package/core/list-targeting.js +101 -110
- package/core/paragraph-targeting.js +501 -61
- package/core/paragraph-text.js +209 -0
- package/core/revision-cloning.js +38 -0
- package/core/types.js +64 -10
- package/core/word-xml.js +43 -15
- package/dist/docx-redline-js.esm.js +2849 -466
- package/dist/docx-redline-js.esm.js.map +4 -4
- package/dist/docx-redline-js.esm.min.js +87 -76
- package/dist/docx-redline-js.esm.min.js.map +4 -4
- package/docs/TESTING.md +342 -23
- package/docs/plans/2026-09-05-structural-revisions-and-fidelity-oracles.md +1669 -0
- package/docs/plans/completed/2026-09-01-performance-and-complexity-reduction.md +669 -0
- package/docs/plans/completed/2026-09-03-agent-friendly-document-workflows.md +427 -0
- package/docs/plans/completed/2026-09-04-comment-anchor-and-cli-reliability.md +519 -0
- package/docs/plans/completed/PERFORMANCE-CONSOLIDATION.md +69 -0
- package/docs/plans/completed/structural-revision-capability-matrix.md +115 -0
- package/docs/schemas/document-operations.schema.json +109 -0
- package/docs/test-comparison-dashboard.html +4250 -7
- package/engine/formatting-removal.js +11 -2
- package/engine/oxml-engine.js +491 -336
- package/engine/reconstruction-mode.js +15 -14
- package/engine/reconstruction-writer.js +247 -142
- package/engine/route-selection.js +35 -0
- package/engine/rpr-helpers.js +334 -35
- package/engine/run-builders.js +239 -196
- package/engine/surgical-diff-application.js +222 -37
- package/engine/surgical-mode.js +134 -6
- package/engine/surgical-spans.js +52 -1
- package/engine/table-cell-context.js +3 -6
- package/engine/table-mode.js +1 -1
- package/index.d.ts +234 -6
- package/index.js +24 -1
- package/node/cli.js +317 -0
- package/node/docx-document.js +302 -0
- package/node/index.d.ts +31 -0
- package/node/index.js +2 -0
- package/node/zip-archive.js +52 -0
- package/orchestration/list-markdown.js +10 -16
- package/orchestration/list-parsing.js +7 -12
- package/orchestration/list-structural-fallback.js +21 -10
- package/package.json +24 -3
- package/pipeline/content-analysis.js +12 -17
- package/pipeline/ingestion-export.js +3 -31
- package/pipeline/ingestion-paragraph.js +10 -5
- package/pipeline/list-generation.js +150 -55
- package/pipeline/list-markers.js +70 -3
- package/pipeline/serialization.js +4 -2
- package/pipeline/structured-content.js +160 -0
- package/scripts/apply_changes.mjs +27 -0
- package/scripts/benchmark-operation-session.mjs +137 -0
- package/scripts/benchmark-targeting-browser.html +74 -0
- package/scripts/benchmark-targeting-hot-paths.mjs +67 -0
- package/scripts/benchmark-test-runner.mjs +59 -0
- package/scripts/build-test-dashboard.mjs +23 -0
- package/scripts/export-lane1-fixtures.mjs +380 -0
- package/scripts/export-reredline-stress-fixtures.mjs +317 -0
- package/scripts/export-validation-fixtures.mjs +1 -1
- package/scripts/extract_text.mjs +7 -0
- package/scripts/generate-paragraph-boundary-fixtures.ps1 +215 -0
- package/scripts/generate-test-dashboard.mjs +362 -11
- package/scripts/lib/word-coverage-catalogue.mjs +6 -2
- package/scripts/profile-route-selection.mjs +19 -0
- package/scripts/render-agenda-multilevel.mjs +0 -5
- package/scripts/render-multilevel-cases.mjs +0 -1
- package/scripts/run-tests.mjs +107 -35
- package/scripts/word-com-corpus-suite.ps1 +3 -0
- package/scripts/word-com-differential.ps1 +64 -4
- package/scripts/word-com-suite.ps1 +3 -0
- package/services/batch-operation-orchestrator.js +494 -0
- package/services/capture-engine.js +226 -0
- package/services/comment-builders.js +23 -6
- package/services/comment-engine.js +108 -47
- package/services/comment-locator.js +187 -82
- package/services/comment-replies.js +95 -0
- package/services/document-inspection.js +258 -0
- package/services/document-operation-applier.js +372 -0
- package/services/document-operation-contract.js +323 -0
- package/services/document-operation-mutations.js +1733 -0
- package/services/document-operation-session.js +258 -0
- package/services/numbering-service.js +14 -5
- package/services/operation-heuristics.js +173 -0
- package/services/operation-preflight.js +366 -0
- package/services/receipt-collector.js +288 -0
- package/services/revision-comment-management.js +37 -5
- package/services/revision-token.js +290 -0
- package/services/standalone-docx-plumbing.js +123 -8
- package/services/standalone-operation-runner.d.ts +296 -0
- package/services/standalone-operation-runner.js +10 -1455
- package/services/table-reconciliation.js +15 -6
- package/docs/VALIDATION.md +0 -183
- package/docs/WORD-MANUAL-REVIEW.md +0 -138
- package/docs/plans/2026-09-01-performance-and-complexity-reduction.md +0 -210
- /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
|
|
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
|
|
129
|
-
Word cases:
|
|
130
|
-
`List Change × List` cell now contains
|
|
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
|
|
315
|
-
|
|
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
|
-
###
|
|
551
|
+
### Microsoft Word visual review guide
|
|
467
552
|
|
|
468
|
-
Human visual review complements—not replaces—the automated differential.
|
|
469
|
-
|
|
470
|
-
|
|
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
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
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
|
-
|
|
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
|
|