@ansonlai/docx-redline-js 0.8.4 → 0.10.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 (72) hide show
  1. package/ARCHITECTURE.md +679 -460
  2. package/CHANGELOG.md +208 -1
  3. package/README.md +152 -16
  4. package/core/current-properties.js +136 -0
  5. package/core/paragraph-text.js +211 -209
  6. package/core/types.js +9 -3
  7. package/core/word-revision-semantics.js +256 -0
  8. package/core/word-xml.js +160 -118
  9. package/dist/docx-redline-js.esm.js +4338 -796
  10. package/dist/docx-redline-js.esm.js.map +4 -4
  11. package/dist/docx-redline-js.esm.min.js +111 -108
  12. package/dist/docx-redline-js.esm.min.js.map +4 -4
  13. package/dist/docx-redline.bundle.cjs +4463 -921
  14. package/dist/docx-redline.bundle.cjs.map +4 -4
  15. package/dist/docx-redline.bundle.js +4463 -921
  16. package/dist/docx-redline.bundle.js.map +4 -4
  17. package/docs/AGENT_FAST_START.md +2 -0
  18. package/docs/AGENT_KNOWLEDGE_BASE.md +49 -7
  19. package/docs/SKILL_AUTHORING.md +169 -150
  20. package/docs/TESTING.md +1146 -1059
  21. package/docs/schemas/document-operations.schema.json +55 -2
  22. package/document/docx-document.js +1022 -1010
  23. package/engine/oxml-engine.js +21 -22
  24. package/engine/paragraph-mark-revisions.js +292 -0
  25. package/engine/reconstruction-mapper.js +312 -311
  26. package/engine/reconstruction-writer.js +67 -10
  27. package/engine/rpr-helpers.js +42 -0
  28. package/engine/run-builders.js +0 -73
  29. package/engine/surgical-diff-application.js +604 -605
  30. package/engine/table-mode.js +63 -63
  31. package/index.d.ts +53 -2
  32. package/index.js +13 -2
  33. package/node/cli-help.js +1 -0
  34. package/node/cli.js +21 -3
  35. package/orchestration/list-structural-fallback.js +67 -9
  36. package/package.json +1 -1
  37. package/pipeline/diff-engine.js +130 -1
  38. package/pipeline/ingestion-export.js +227 -243
  39. package/pipeline/ingestion-paragraph.js +93 -98
  40. package/pipeline/list-generation.js +415 -419
  41. package/pipeline/markdown-processor.js +5 -2
  42. package/pipeline/pipeline.js +206 -204
  43. package/scripts/export-validation-fixtures.mjs +19 -6
  44. package/scripts/generate-word-revision-semantics-matrix.mjs +134 -0
  45. package/scripts/lib/word-coverage-catalogue.mjs +7 -2
  46. package/scripts/render-multilevel-bullet-visual.ps1 +2 -2
  47. package/scripts/word-com-corpus-visual-suite.ps1 +2 -2
  48. package/scripts/word-com-differential.ps1 +443 -335
  49. package/scripts/word-com-visual-suite.ps1 +2 -2
  50. package/scripts/word-revision-semantics-matrix.ps1 +108 -0
  51. package/services/batch-operation-orchestrator.js +23 -3
  52. package/services/capture-engine.js +228 -226
  53. package/services/comment-replies.js +1 -1
  54. package/services/document-operation-applier.js +214 -0
  55. package/services/document-operation-contract.js +76 -0
  56. package/services/document-operation-mutations.js +688 -51
  57. package/services/error-recovery.js +17 -0
  58. package/services/inspection-comparison.js +82 -0
  59. package/services/list-conversion-diagnostics.js +296 -0
  60. package/services/list-format-change.js +586 -0
  61. package/services/list-format-mutation.js +416 -0
  62. package/services/list-format.js +305 -0
  63. package/services/numbering-allocation.js +642 -0
  64. package/services/numbering-helpers.js +354 -524
  65. package/services/operation-batch-compiler.js +183 -11
  66. package/services/operation-preflight.js +43 -33
  67. package/services/receipt-collector.js +288 -288
  68. package/services/revision-comment-management.js +38 -5
  69. package/services/revision-fidelity-verifier.js +484 -0
  70. package/services/standalone-docx-plumbing.js +627 -631
  71. package/services/standalone-operation-runner.d.ts +182 -5
  72. package/services/table-reconciliation.js +59 -7
package/docs/TESTING.md CHANGED
@@ -1,1059 +1,1146 @@
1
- # Testing Guide
2
-
3
- This project uses several test lanes because no single oracle can prove that a
4
- tracked-change document is correct. JavaScript assertions can verify exact XML
5
- and return contracts; Microsoft Word can verify what the primary consumer
6
- actually accepts and rejects; real documents expose structures that small
7
- fixtures rarely contain.
8
-
9
- ## Test lanes at a glance
10
-
11
- | Lane | Command | What it proves | What it does not prove |
12
- |---|---|---|---|
13
- | Automated regression suite | `npm test` | API behavior, exact text invariants, OOXML structure, deterministic fuzz cases, and catalogue integrity | That desktop Word accepts the generated package |
14
- | Isolation, types, and lint | `npm run test:isolation`, `npm run check:types`, `npm run lint` | Runtime boundaries, declaration alignment, and static repository rules | Document correctness |
15
- | JavaScript coverage | `npm run test:coverage` | Which source lines, functions, and branches the automated suite executes | That an executed path is correct |
16
- | Synthetic Word differential | `npm run test:word` | Word opens generated packages, sees revisions, and produces the intended text after Accept All and Reject All | The diversity of real-world packages |
17
- | Word visual evidence | `npm run test:word:visual` | Word renders layout-sensitive fixtures in All Markup, Accept All, and Reject All views and writes a pending review manifest | That the rendered pages are visually correct until a reviewer inspects them |
18
- | SuperDoc Word corpus | `npm run test:corpus:word` | The same Word differential on 60 scenarios drawn from 23 reviewed, pinned real English legal/administrative documents while untouched package parts remain byte-identical | Every possible DOCX producer or document type |
19
- | SuperDoc visual evidence | `npm run test:corpus:word:visual` | Word renders 40 focused list, table, long-document, and page-header scenarios in All Markup, Accept All, and Reject All views | Human visual sign-off |
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
- | 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
- | 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 [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`, `node tests/agent_cli_protocol_tests.mjs`, `node tests/agent_cli_help_tests.mjs`, `node tests/agent_cli_inspection_context_tests.mjs`, `node tests/agent_cli_output_budget_tests.mjs` | JSON contracts, command-specific help, contextual pagination, composable progressive/atomic profiles, deduplicated receipts, compact stdout, file/stdin Unicode fidelity, complete-success exits, source safety, command families, and schema readability | Cross-platform CI beyond the current runner |
26
- | Example agent session | `node tests/agent_session_example_tests.mjs`, `node tests/localized_edit_example_tests.mjs` | A non-package sample wrapper delegates to the Node facade, binds and refreshes opaque handles across package revisions, returns context windows, compiles exact localized edits, preserves accepted/rejected lifecycle and atomic rollback, and supports review resolution | LLM/provider latency or a production tool-server transport |
27
- | Batch compilation and recovery | `node tests/batch_source_binding_tests.mjs`, `node tests/error_recovery_contract_tests.mjs`, `node tests/capture_dependency_graph_tests.mjs` | Batch-start source identity survives structural index drift and savepoint restoration; true overlap and capture fan-out fail before mutation; recovery envelopes, retry bases, bounded CLI diagnostics, and complete-success exits remain stable | Whether a model follows the returned recovery action correctly |
28
- | Agent documentation contract | `node tests/agent_documentation_contract_tests.mjs` | The ordinary fast start stays within its 40–60 line/600-word budget, examples lead with scoped contextual extraction, and packaged skill-authoring guidance separates invariants, policies, transports, recovery, and presentation while the development wrapper stays outside package files | Whether a particular model reads or follows the instructions |
29
- | 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 |
30
- | 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 |
31
- | 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 |
32
- | 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 |
33
- | 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 |
34
- | Universal document facade & ZIP | `node tests/universal_docx_document_tests.mjs`, `node tests/universal_zip_archive_tests.mjs`, `node tests/universal_facade_edge_cases_tests.mjs` | Universal `Uint8Array` binary handling, `fflate` compression/decompression, optimistic concurrency, and complete-DOCX operations without Node built-ins | Desktop Word rendering |
35
- | Sandbox bundle isolation | `node tests/sandbox_bundle_isolation_tests.mjs` | Zero-dependency standalone ESM/CJS bundles execute in restricted VM sandboxes without `process`, `Buffer`, or `require`, with zero Node built-in imports | Web Worker latency |
36
- | Cross-author revision slicing | `node tests/cross_author_slicing_fixtures_tests.mjs`, `node tests/cross_author_carrier_splitting_tests.mjs`, `node tests/cross_author_slicing_synthetic_tests.mjs`, `node tests/cross_author_slicing_real_tests.mjs`, `node tests/cross_author_slicing_hyperlink_roundtrip_tests.mjs`, `node tests/cross_author_slicing_insertion_stress_tests.mjs` | Word-native `<w:ins>` splitting, `<w:del>` nesting, multi-author stacking, straddle boundaries, synthetic matrix (SYN-01..12d), strict package differential replay (PKG-01..06), exact hyperlink/NBSP round trips, and 76 insertion stress scenarios | Non-Word consumers beyond OpenXML XSD validation |
37
-
38
- The package-facade regression opens a real ZIP buffer, adds a comment beside an
39
- existing high ID, validates OPC wiring, and checks an unrelated binary part is
40
- unchanged after extraction. Its failure case requires byte-exact rollback. The
41
- inspection regression covers revisions, structural characters, heading
42
- context, comment joining, and a numbering start override.
43
-
44
- The CLI regression executes the recommended extract → preflight → apply →
45
- validate flow and also covers accept, reject, comment deletion, output collision
46
- refusal, and source-byte preservation. CLI stdout is captured and parsed as one
47
- JSON value so diagnostic logging cannot corrupt agent output.
48
-
49
- The edge suites deliberately use structures omitted from the basic happy-path
50
- fixtures: `w:moveFrom`/`w:moveTo`, deleted text, structural breaks and hyphens,
51
- comments crossing paragraph boundaries, orphan definitions, outline headings,
52
- footnote/endnote references, nested Roman/alphabetic numbering, sequential
53
- commit-then-rollback calls, selective comment authors, existing high anchor
54
- IDs, encrypted/unsupported ZIP entries, `--force`, and `--in-place`.
55
-
56
- The Phase 2 boundary regression is intentionally structural as well as
57
- behavioral. It prevents the public runner from accumulating implementation
58
- logic again, verifies that internal operation code does not import the root
59
- entry point, and checks that failed atomic work can retain the exact source
60
- string while working maps and sets remain isolated until commit.
61
-
62
- The checked post-Phase-5 2026-09-04 coverage run reports 89.81%
63
- statements/lines, 77.34% branches, and 93.04% functions repository-wide; this
64
- is above every recorded pre-refactor dimension. Targeting performance can be
65
- measured in Node with `npm run benchmark:targeting` and against the native DOM
66
- by serving the repository locally and opening
67
- `scripts/benchmark-targeting-browser.html`; its `paragraphs`, `operations`, and
68
- `iterations` query parameters reproduce the checked fixture sizes.
69
- The Node surface reports 99.61% statements/lines; `document-inspection.js`
70
- reports 97.64% statements/lines and 100% functions; `paragraph-text.js` reports
71
- 100% statements/lines/functions.
72
-
73
- Run the observational live-session benchmark with:
74
-
75
- ```powershell
76
- npm run benchmark:session
77
- ```
78
-
79
- It writes `tmp/benchmarks/operation-session-latest.json`. The checked
80
- 1,000-paragraph/10-operation run reduced full-document parse/serialization from
81
- 10/10 to 1/1 and measured a 1.58x median speedup. Timing and heap figures are
82
- not correctness gates. Per-operation DOM savepoints are retained because
83
- redline accuracy, no-op isolation, and rollback fidelity take precedence over
84
- the aspirational speed target.
85
-
86
- Run the observational agent-protocol benchmark with:
87
-
88
- ```powershell
89
- npm run benchmark:agent
90
- ```
91
-
92
- It writes `tmp/benchmarks/agent-workflow-latest.json` and compares canonical
93
- stateless Node, legacy file-based CLI, compact stdin/profile CLI, and the
94
- development-only session example. It records native/in-process CLI time, heap,
95
- serialized request bytes, protocol calls, instruction words/bytes, exact
96
- accepted/rejected text, comment preservation, ordering, recovery, and
97
- cross-author attribution. It does not measure or estimate LLM reasoning, exact
98
- tokens, tool transport, Claude, or OpenCode wall time; collect those separately
99
- in the calling harness. The checked WP-07 results are summarized in the
100
- [agent protocol rollout audit](validation-reports/2026-09-12-agent-protocol-rollout.md).
101
-
102
- Run the CLI discovery/output benchmark with:
103
-
104
- ```powershell
105
- npm run benchmark:agent-cli
106
- ```
107
-
108
- It records command-specific help size, broad inspect/search response size,
109
- context-window call count, pagination metadata, case-insensitive match parity,
110
- and native elapsed time in `tmp/benchmarks/agent-cli-discovery-latest.json`.
111
- The checked WP-00 through WP-02 before/after measurements are in the
112
- [agent CLI discovery baseline](validation-reports/2026-09-12-agent-cli-discovery-baseline.md).
113
- WP-03 through WP-05 profile, output-size, workflow, documentation, and package
114
- results are in the
115
- [agent CLI efficiency rollout audit](validation-reports/2026-09-13-agent-cli-efficiency-rollout.md).
116
-
117
- ## Cross-author revision slicing test suite
118
-
119
- The cross-author revision slicing subsystem introduces six complementary test lanes:
120
-
121
- 1. **Native Word Desktop Fixtures (`tests/cross_author_slicing_fixtures_tests.mjs`)**:
122
- Verifies 36 reference `.docx` and `.xml` files generated via Microsoft Word Desktop 365 COM automation across 6 golden scenarios (`insert-interior`, `delete-interior`, `delete-boundary-start`, `delete-boundary-end`, `delete-straddle-baseline-insertion`, and `multi-author-stacked`) in pending, accepted, and rejected states. Proves Word-native OOXML patterns (carrier `<w:ins>` splitting for insertions, direct `<w:del>` nesting inside `<w:ins>` for deletions) and lifecycle parity under `acceptTrackedChangesInOoxml` and `rejectTrackedChangesInOoxml`.
123
- 2. **Carrier Splitting Unit Suite (`tests/cross_author_carrier_splitting_tests.mjs`)**:
124
- Tests `splitTrackChangeCarrier` across interior and boundary cuts, multi-run formatting preservation, stable metadata cloning, independent revision ID allocation via `RevisionIdAllocator`, and immutable input handling.
125
- 3. **Synthetic Boundary Matrix (`tests/cross_author_slicing_synthetic_tests.mjs`)**:
126
- Executes the full SYN-01 through SYN-12d test matrix covering pure interior inserts, pure interior deletes, boundary start/end cuts, full-content annihilation, baseline/insertion straddles, multi-carrier straddles, formatting preservation, paired replacements, 3-author stacked revisions, and selective author accept/reject lifecycle oracles.
127
- 4. **Checked-In Word Package Differential Suite (`tests/cross_author_slicing_real_tests.mjs`)**:
128
- Reconstructs the pre-mutation document packages, applies `slice-cross-author` via the strict `openDocx` package facade, enforces atomic package validation, and compares engine `AcceptAll` and `RejectAll` output against Word Desktop-generated golden packages (PKG-01 through PKG-06).
129
- 5. **Hyperlink and Exact-Text Regression (`tests/cross_author_slicing_hyperlink_roundtrip_tests.mjs`)**:
130
- Reproduces the repeated-phrase, hyperlink-boundary, and NBSP-to-space failure through both the low-level engine and document operation runner. It requires exact accepted-view equality, preserves both hyperlink relationship containers, and proves that an unreconstructable slicing mutation returns `PATCH_ROUNDTRIP_MISMATCH` with the original OOXML unchanged.
131
- 6. **Insertion Stress Matrix (`tests/cross_author_slicing_insertion_stress_tests.mjs`)**:
132
- Runs 76 deterministic scenarios across carrier start/end/interior positions, repeated phrases, multi-run and formatted carriers, spaces/tabs/NBSP, XML-sensitive characters, emoji/combining Unicode, hyperlink interiors and boundaries, bookmarks, comments, nested deletions, adjacent authors, mixed same/foreign-author carriers, multiple structural containers, consecutive reviewer rounds, and the atomic document runner. Every supported case requires exact current-view, Accept-All, Reject-Current, schema, metadata, and non-empty-wrapper invariants; unsupported nested structures must fail without throwing or returning changed output.
133
-
134
- ## Coverage matrix and test selection
135
-
136
- Run the deterministic task-by-structure report before choosing the next Word
137
- case:
138
-
139
- ```powershell
140
- npm run report:word:coverage
141
- npm run report:word:coverage -- --json
142
- npm run report:test:dashboard
143
- ```
144
-
145
- The dashboard command writes a self-contained interactive report to
146
- `docs/test-comparison-dashboard.html`. It compares task/structure cells,
147
- synthetic and real-document coverage, independent oracles, visual-render
148
- eligibility, and planned high-priority gaps from the live catalogues. It also
149
- embeds source, tracked, accepted, and rejected packages for every synthetic
150
- fixture and, when the pinned corpus is downloaded, all 60 reviewed real legal
151
- and administrative scenarios. Its `docx-preview` workbench supports arbitrary left/right states,
152
- comparison presets, synchronized scrolling, revision metadata, expected text,
153
- and local downloads; no fixture upload or file picker is required. Real cases
154
- are labeled in the document selector and selected by default when available.
155
- Use **Hide sidebar** beside the dashboard filters to remove the coverage-detail
156
- column and expand the comparison workbench to the full browser width. The
157
- button becomes **Show sidebar**, and the preference is stored locally so the
158
- wide document view survives a reload.
159
- Without the local corpus, generation remains offline-safe and embeds only the
160
- synthetic previews.
161
-
162
- The report combines all synthetic and reviewed SuperDoc scenarios. Synthetic
163
- metadata is declared in `tests/fixtures/word-task-coverage.mjs`; SuperDoc
164
- metadata is declared in `tests/corpus/superdoc-word-coverage.json`. Descriptive
165
- `task`, `shape`, and `coverage` labels remain on the original cases as review
166
- notes, while the matrix uses shared task, structure, and oracle vocabularies.
167
-
168
- Catalogue tests reject unknown vocabulary labels, missing oracle or manual-review
169
- metadata, duplicate identities, structural claims unsupported by fixture XML or
170
- reviewed corpus labels, and uncovered high-priority cells without a recorded
171
- plan or exclusion. The dispositions live in
172
- `tests/fixtures/coverage-matrix-priorities.json` and require both a reason and a
173
- dependency.
174
-
175
- Choose new cases from uncovered high-priority cells first, then prefer a new
176
- task/structure combination over another case in a dense cell. A higher count is
177
- not itself a reason to add a fixture. Adding a catalogue case automatically
178
- changes the report because it reads the live metadata.
179
-
180
- ## Reliability improvement process used for this test expansion
181
-
182
- The current corpus, visual runners, and comparison dashboard were built as one
183
- feedback loop rather than as separate test features. The working sequence is:
184
-
185
- 1. inventory the claimed behavior and existing test lanes;
186
- 2. add realistic cases that expose weak structures;
187
- 3. assert the smallest machine-checkable invariant at the engine level;
188
- 4. package the result into the original real document without changing
189
- unrelated parts;
190
- 5. ask Word to accept and reject the revisions;
191
- 6. render All Markup, Accept All, and Reject All views;
192
- 7. inspect the actual pages at both contact-sheet and full-page scale; and
193
- 8. turn every visual defect into a focused regression plus a corpus assertion.
194
-
195
- This ordering matters. A valid `<w:ins>` or `<w:del>` tree does not establish
196
- that the inserted run inherited the correct font, list indentation, hyperlink
197
- wrapper, table-cell formatting, or header styling. Conversely, a good-looking
198
- PDF does not prove that Reject All restores the exact source. Each stage answers
199
- a different question, and a case is useful only when its intended claim is
200
- explicit.
201
-
202
- ### Start from coverage claims, not fixture count
203
-
204
- Read the task-by-structure matrix and select a real document that adds a missing
205
- or weak combination. The expansion deliberately favored bullets, nested lists,
206
- table cells, headers, long documents, and atomic multi-change batches because
207
- these structures cross more OOXML boundaries than a single plain paragraph.
208
- Several scenarios reuse a pinned source when a new batch exercises a genuinely
209
- different structure; this increases behavioral coverage without pretending
210
- that another copy of the same document is a new source.
211
-
212
- For long real documents, record structural minimums such as word count, table
213
- count, list-paragraph count, section count, and related header/footer parts.
214
- `scripts/prepare-superdoc-word-corpus.mjs` checks these declarations against the
215
- source package. This prevents a scenario description such as “table-heavy” or
216
- “multi-section” from silently drifting away from the actual fixture.
217
-
218
- ### Use multi-change batches to exercise interaction effects
219
-
220
- Single replacements remain valuable for isolating engine behavior, but real
221
- editing sessions usually contain several changes. The larger corpus scenarios
222
- therefore use `operations` to apply independent edits atomically across one
223
- document. Targets are chosen from different list items or table cells when
224
- possible. This catches failures caused by earlier replacements shifting later
225
- anchors, revision-ID allocation across a batch, and formatting state leaking
226
- between operations.
227
-
228
- The batch runner must either produce all requested revisions or roll back the
229
- document. Word then checks the complete accepted and rejected story, not merely
230
- the first target. Untouched ZIP parts are hashed and compared with the pinned
231
- source, so success cannot come from rebuilding or normalizing the rest of the
232
- package.
233
-
234
- ### Expand weak list and table cells with structural operations
235
-
236
- The list/table expansion was driven directly by the task-by-structure matrix;
237
- it did not add more plain replacement cases. The current catalogue contains 109
238
- Word cases: 49 synthetic fixtures and 60 reviewed real-document scenarios. The
239
- `List Change × List` cell now contains 27 cases; the
240
- `Table Reconciliation × Table` cell contains 11 cases.
241
-
242
- The added synthetic cases isolate one structural decision at a time:
243
-
244
- - append one list item;
245
- - append several adjacent items;
246
- - add a nested child;
247
- - insert into the middle of an explicit list range;
248
- - preserve upper- and lower-Roman numbering;
249
- - preserve upper-letter, parenthesized lower-letter, dash, and symbol bullets;
250
- - expand a manually numbered heading whose `w:numId="0"` suppresses numbering
251
- into separate, positively numbered list paragraphs;
252
- - update one table cell;
253
- - insert a table row;
254
- - delete a table row; and
255
- - update several cells in one reconciliation.
256
-
257
- The mixed-attachment fixture replaces one anchor with explicit headings,
258
- ordinary paragraphs, a Markdown table, and a numbered list. It verifies real
259
- `w:pStyle`, `w:tbl`, and positive-numbering structures plus independent Word
260
- Accept All and Reject All results; literal table pipes are forbidden.
261
-
262
- The real-document cases repeat those claims against native numbering and table
263
- markup from municipal notices, board agendas, a long zoning ordinance, PPG
264
- minutes, action tables, and invoice tables. They include consecutive and nested
265
- list insertions, a long-document definition insertion, single- and multi-cell
266
- updates, row insertion/deletion, and invoice row changes. Reusing a reviewed,
267
- hash-pinned source is intentional when the scenario exercises a different
268
- operation; the unit of coverage is the behavioral claim, not the download.
269
-
270
- These cases declare `operation.type` as `list-change` or
271
- `table-reconciliation`. Their accepted/rejected expectations use explicit
272
- contains/absent assertions when a Markdown table payload or a structural list
273
- edit cannot be compared meaningfully to the raw Word source as one exact text
274
- string. Structural expectations still verify real properties such as native
275
- list paragraphs, tables, headers/footers, sections, and minimum document size.
276
- The catalogue tests require these expectations and reject unknown operation or
277
- coverage labels.
278
-
279
- List-style cases do not infer their claim from visible text alone. Synthetic
280
- fixtures declare the required `w:numFmt` values and use complete, schema-ordered
281
- numbering parts: every `w:abstractNum` must precede the first concrete `w:num`
282
- mapping. That ordering is regression-tested because Word otherwise opens the
283
- package but silently substitutes its fallback bullet. Real scenarios declare a
284
- `listStyleExpectation` with the source level, number format, and, where useful,
285
- the exact `w:lvlText`. Corpus preparation resolves the target paragraph's
286
- `w:numId` through `word/numbering.xml` and fails before editing if the pinned
287
- source no longer has that native style. The current real cases exercise upper
288
- and lower Roman, upper and lower letter, parenthesized lower letter, dash, and
289
- symbol bullets across bylaws, agendas, healthcare minutes, and long council
290
- minutes.
291
-
292
- ### Diagnose visual failures in the generated OOXML first
293
-
294
- When a page looks wrong, compare the relevant source and tracked
295
- `word/document.xml` or related part before changing the renderer. Locate the
296
- visible text, inspect its nearest `w:r`, `w:rPr`, wrapper, paragraph properties,
297
- and neighboring runs, then compare those properties with the source. This
298
- separates an engine defect from a Word, PDF, or `docx-preview` display issue.
299
-
300
- Three defects illustrate the approach:
301
-
302
- - The council header date replacement inherited `w:vertAlign="superscript"`
303
- from the trailing ordinal suffix. The fix anchors replacement formatting at
304
- the beginning of the deleted range, and the regression explicitly forbids
305
- `vertAlign` on the inserted runs.
306
- - The prospectus filing-date replacement crossed normal runs and a hyperlink.
307
- A forward-only property lookup had already moved past the beginning of the
308
- deletion when the insertion asked for its formatting, so the new run had no
309
- `w:rPr`. Word and SuperDoc correctly fell back from the surrounding 10 pt to
310
- the document default of 12 pt. The lookup now supports that deliberate
311
- backward query. The focused regression requires `w:sz="20"` and
312
- `w:szCs="20"`, and the real prospectus scenario requires those values on
313
- every inserted text run.
314
- - A wholly inserted list item originally tracked only its text run. Accept All
315
- looked correct and text-only Reject All assertions passed, but Word retained
316
- the untracked paragraph mark as an empty bullet or number. Inserted list
317
- paragraphs now track both their text and paragraph mark. The regression
318
- rejects the change and asserts the exact original paragraph count, text, and
319
- numbering, while the real Word PDFs confirm that no ghost marker remains.
320
-
321
- These assertions test the semantic cause rather than a screenshot pixel. The
322
- visual render remains necessary to confirm that the corrected properties
323
- produce the intended page. The automated test suite in
324
- `tests/visual_failure_regression_tests.mjs` codifies these guards directly in
325
- the regression test pipeline (`npm test`) to prevent regressions before DOCX
326
- packages reach Word.
327
-
328
- ### Regenerate packages; do not reason from stale outputs
329
-
330
- Source downloads are content-addressed and may be reused. Generated tracked,
331
- accepted, rejected, PDF, and dashboard artifacts are outputs and must be
332
- rebuilt after an engine or scenario change. A `Verified cached ...` line from
333
- the corpus fetcher means only that the pinned source `.docx` already matches its
334
- recorded hash; it does not mean the edited package was reused.
335
-
336
- Use this sequence for a corpus-affecting engine change:
337
-
338
- ```powershell
339
- npm test
340
- npm run check:types
341
- npm run lint
342
- npm run test:isolation
343
- npm run test:corpus:word
344
- npm run test:corpus:word:visual
345
- npm run report:test:dashboard
346
- ```
347
-
348
- For faster iteration, `prepare-superdoc-word-corpus.mjs` accepts
349
- `--input-dir` and `--output-dir`, and the visual PowerShell runner accepts
350
- `-Case <scenario-key>` plus custom fixture/output directories. Run the complete
351
- lane before handoff even when a focused render passed.
352
-
353
- ### Treat Word COM cleanup separately from render success
354
-
355
- Word sometimes disconnects a COM document proxy after successfully exporting
356
- a PDF and raises `RPC_E_DISCONNECTED` during `Document.Close()`. The visual
357
- runner uses safe close/quit helpers: a disconnect during post-export cleanup is
358
- reported but does not discard a valid, non-empty PDF; a disconnect during the
359
- actual render causes Word to restart and the case to retry once. Other COM
360
- exceptions still fail the run. This distinction avoids both false failures and
361
- false passes.
362
-
363
- Each visual invocation uses a process-specific fixture directory. That keeps a
364
- Word process left behind by an earlier disconnect from locking or contaminating
365
- the next set of generated DOCX files.
366
-
367
- ### Review both the whole document and the changed page
368
-
369
- For a long document, create low-resolution contact sheets for every page in all
370
- three revision states and inspect them for pagination changes, blank pages,
371
- clipping, table displacement, or large typography shifts. Then inspect every
372
- changed page at full resolution. Contact sheets are good at document-wide
373
- layout; they are not reliable for a 10 pt versus 12 pt difference or a leaked
374
- superscript flag.
375
-
376
- Record the exact scenario, Word version/build, revision view, page, observed
377
- fact, and whether the judgment is certain. The visual manifest remains pending
378
- until a reviewer makes that judgment; successful PDF export alone is not a
379
- visual pass.
380
-
381
- ### Keep the dashboard generated and test its controls
382
-
383
- `scripts/generate-test-dashboard.mjs` owns the dashboard markup, styles, and
384
- behavior. Do not hand-edit `docs/test-comparison-dashboard.html`; rebuild it
385
- with `npm run report:test:dashboard`. The report embeds the four DOCX states so
386
- it remains self-contained and can show the exact generated packages without a
387
- file picker or server.
388
-
389
- Dashboard changes require generator-level assertions in
390
- `tests/test_dashboard_report_tests.mjs`. The sidebar-width control, for
391
- example, is checked for its accessible relationship, full-width CSS state, and
392
- persisted preference. DOCX package parsing remains covered separately by
393
- `tests/docxjs_dashboard_rendering_tests.mjs`. When browser control is available,
394
- also click the control in the generated page and verify that the sidebar is
395
- hidden, both document panes widen, the label changes, and reloading preserves
396
- the selected state.
397
-
398
- ### Definition of done for a discovered real-document defect
399
-
400
- A visual defect is complete only when all of the following are true:
401
-
402
- - the source and faulty generated OOXML have been compared;
403
- - the engine cause is understood rather than masked in CSS or the renderer;
404
- - a minimal automated regression reproduces the structural boundary;
405
- - Accept All yields the requested text and Reject All restores the source;
406
- - the affected real corpus scenario has an appropriate structural or
407
- formatting assertion;
408
- - its source, tracked, accepted, and rejected packages have been regenerated;
409
- - desktop Word differential passes;
410
- - the changed page and the document-wide three-view render have been inspected;
411
- - the self-contained comparison dashboard has been rebuilt; and
412
- - the full automated, isolation, type, lint, and whitespace checks pass.
413
-
414
- Prepare, but do not approve, a human review sample with:
415
-
416
- ```powershell
417
- npm run review:word:prepare -- --cycle=0
418
- ```
419
-
420
- The ignored `tmp/word-manual-review/review-manifest-cycle-0.json` selects all
421
- catalogue families changed in the worktree, a rotating 20% synthetic sample,
422
- and one legal plus one administrative SuperDoc case. Every All Markup, Accept
423
- All, Reject All, and human sign-off field starts as `pending`; the helper cannot
424
- turn them into passes. Increment `--cycle` between releases to rotate the
425
- unchanged sample.
426
-
427
- ## Automated JavaScript tests
428
-
429
- Files matching `tests/*.mjs` are discovered by `scripts/run-tests.mjs`. Tests
430
- use `assert/strict` and each runs in a separate Node process. The runner uses up
431
- to four workers by default while preserving filename-sorted reporting. Set
432
- `DOCX_TEST_CONCURRENCY=1` for deterministic serial reproduction, or another
433
- positive integer for an explicit worker bound. Word, visual, and corpus lanes
434
- remain separate serial commands. Shared OOXML assertions belong in
435
- `tests/helpers/ooxml-assertions.mjs`; XML-provider setup belongs in
436
- `tests/setup-xml-provider.mjs`.
437
-
438
- Measure serial and parallel execution on the same machine with:
439
-
440
- ```powershell
441
- npm run benchmark:tests
442
- ```
443
-
444
- The benchmark performs paired warmups/runs and writes its JSON result to
445
- `tmp/benchmarks/test-runner-latest.json`. The checked Node 24/Windows x64 run
446
- reduced median time from 18.02 seconds to 7.96 seconds (55.80%). Twenty
447
- consecutive four-worker runs completed without a failure.
448
-
449
- For function-level gap work, run:
450
-
451
- ```powershell
452
- npm run test:coverage
453
- npm run coverage:gaps
454
- npm run coverage:gaps -- --json
455
- ```
456
-
457
- The coverage command emits both the text summary and detailed Istanbul JSON.
458
- The gap report includes only runtime production roots, lists each uncovered
459
- function with its file and declaration line, assigns the Phase 3 P0/P1/P2
460
- priority, and fails if a targeted file drops below the checked covered-function
461
- or covered-branch baseline in `tests/coverage-data/phase3-baseline.json`. Because V8
462
- discovers new branch sites when a formerly cold function first executes, review
463
- covered counts and behavior assertions alongside percentages. Do not add ignore
464
- annotations or call private code merely to improve a score; classify a retained
465
- gap with a reachability or environment reason.
466
-
467
- For an engine regression:
468
-
469
- 1. Add the smallest fixed case that reproduces the bug and asserts the exact
470
- result or structured error.
471
- 2. Assert both sides of tracked changes: accepting must produce the requested
472
- text and rejecting must restore the original text exactly.
473
- 3. Assert structural invariants with `validateRedlineOoxml` and the shared OOXML
474
- helpers. Do not use a whitespace-collapsing reader as the oracle.
475
- 4. If engine behavior changed, add the same shape to the deterministic fuzz
476
- corpus so nearby inputs are exercised too.
477
- 5. Run `npm test`, isolation, types, and lint.
478
-
479
- For a mixed attachment regression, also run the content through
480
- `planStructuredReplacement(...)`. Assert the declared block sequence, reject
481
- malformed table grammar, and verify that the result contains real `w:tbl`,
482
- heading styles, positive list numbering IDs, valid non-nested revisions, and
483
- independent Accept/Reject outcomes. Do not treat absence of literal pipe text as
484
- sufficient proof of a table.
485
-
486
- ## Synthetic Microsoft Word tests
487
-
488
- The runtime library contains no COM or Word dependency. Word automation exists
489
- only in Windows development scripts and is intentionally excluded from package
490
- runtime paths.
491
-
492
- The synthetic catalogue is `tests/fixtures/word-task-cases.mjs`. During
493
- `npm run test:word`:
494
-
495
- 1. `scripts/export-validation-fixtures.mjs` applies each operation, runs the
496
- library's structural validator, checks any case-specific `requiredElements`,
497
- and builds a minimal `.docx` under ignored `tmp/word-validation/` storage.
498
- 2. Expected Accept All and Reject All text is derived from the test's edit
499
- intent, not from the library's own revision-management functions.
500
- 3. `scripts/word-com-differential.ps1` opens each fixture through desktop Word
501
- without a repair dialog and confirms Word sees at least one revision.
502
- 4. Word accepts every revision in one fresh document and rejects every revision
503
- in another. Both resulting texts are compared with the intent-derived
504
- expectations.
505
-
506
- To add a synthetic Word case:
507
-
508
- 1. Add a unique lowercase-hyphenated entry to `WORD_TASK_CASES` with
509
- `category: 'legal'` or `'administrative'`, a distinct `task`, and `original`
510
- and `modified` text.
511
- 2. Use `sourceDocumentXml` when the case needs bookmarks, hyperlinks, tables,
512
- content controls, prior revisions, or another deliberate structure.
513
- 3. Add `requiredElements`, such as `{ bookmarkStart: 1, bookmarkEnd: 1 }`, when
514
- retaining a structure is part of the claim. These are minimum namespace-aware
515
- element counts checked before Word runs.
516
- 4. Supply `expectedAcceptedText` and `expectedRejectedText` when the source is a
517
- multi-paragraph document or Word exposes structural separators such as table
518
- row boundaries. Exact comparison is the default.
519
- 5. Run `node tests/word_task_catalog_tests.mjs`, then `npm run test:word` on a
520
- Windows machine with desktop Word installed.
521
-
522
- ### Related-part fixture schema
523
-
524
- The script-only packager supports optional numbering plus comments, footnotes,
525
- endnotes, headers, footers, and external hyperlinks. Add them to a catalogue
526
- case through `packageParts`:
527
-
528
- ```js
529
- {
530
- sourceDocumentXml,
531
- packageParts: {
532
- commentsXml,
533
- footnotesXml,
534
- endnotesXml,
535
- headers: [{
536
- partName: 'header1.xml',
537
- relationshipId: 'rIdHeader1',
538
- xml: headerXml
539
- }],
540
- footers: [{
541
- partName: 'footer1.xml',
542
- relationshipId: 'rIdFooter1',
543
- xml: footerXml
544
- }],
545
- externalHyperlinks: [{
546
- relationshipId: 'rIdPolicy',
547
- target: 'https://example.com/policy'
548
- }]
549
- }
550
- }
551
- ```
552
-
553
- `createCommentsPart`, `createNotesPart`, and `createHeaderFooterPart` in
554
- `tests/fixtures/word-package-parts.mjs` provide small escaped constructors for
555
- the common XML parts. Header/footer `partName` and `relationshipId` values have
556
- deterministic defaults, but explicit values make the corresponding
557
- `w:headerReference`, `w:footerReference`, or `w:hyperlink` easier to audit.
558
-
559
- Before ZIP emission, the packager rejects malformed XML, duplicate relationship
560
- IDs or part names, missing relationship targets, undefined comment/note IDs,
561
- and note parts without separator IDs `-1` and `0`. It generates the required
562
- content-type overrides and document relationships. Every supplied related part
563
- is compared byte-for-byte with the packaged entry, recorded as SHA-256 in the
564
- case sidecar, and rechecked by the Word differential before opening the DOCX.
565
- These helpers remain under `scripts/` and `tests/`; none are shipped through the
566
- runtime entry point.
567
-
568
- ### What the automated Word differential proves
569
-
570
- The Word process is real desktop Microsoft Word, but it is driven invisibly
571
- through COM. For each fixture, the script:
572
-
573
- 1. starts `Word.Application` with alerts and the window disabled;
574
- 2. opens the generated package with `OpenNoRepairDialog`;
575
- 3. fails if Word cannot open it or sees zero tracked revisions;
576
- 4. accepts all revisions and reads `Document.Content.Text`;
577
- 5. closes without saving, reopens the untouched fixture, rejects all revisions,
578
- and reads the text again; and
579
- 6. compares both results with expectations that were not calculated by this
580
- library's own accept/reject implementation.
581
-
582
- Exact comparison is the default. The harness removes Word's terminal paragraph
583
- mark, normalizes CR/LF representation, and removes the characters Word exposes
584
- as table-cell and footnote/endnote reference boundaries; case-specific normalized comparison requires an
585
- explicit reason. Documents are never saved by the automated differential.
586
-
587
- For synthetic fixtures, expectations come directly from `original`, `modified`,
588
- and any explicit full-document expectations in the case. For SuperDoc fixtures,
589
- Word first reads the declared story from the original pinned source document;
590
- every target in a single or multi-change scenario must occur exactly once, and
591
- the accepted expectation is formed by applying those replacements to Word's own
592
- source text. Header scenarios use Word's header stories rather than body text.
593
- The corpus packager hashes every package part other than the one intentionally
594
- replaced by the scenario.
595
-
596
- This proves that Word can consume the package, recognizes the revision markup,
597
- and resolves Accept All and Reject All to the intended text. It does **not**
598
- prove that the document looks right on the page. `Content.Text` cannot detect
599
- bad pagination, awkward revision balloons, shifted table widths, broken tab
600
- alignment, font substitution, changed list indentation, clipped headers,
601
- visually stale fields, or a comment/footnote marker that is technically present
602
- but poorly placed.
603
-
604
- ### Microsoft Word visual review guide
605
-
606
- Human visual review complements—not replaces—the automated differential. The
607
- automated differential proves Word revision semantics by comparing text after
608
- Accept All and Reject All, but this review checks the layout, typography, and
609
- interaction details that `Document.Content.Text` cannot see.
610
-
611
- Review is required for:
612
-
613
- - every new or materially changed synthetic Word case;
614
- - the first case for a new structure, operation type, or related package part;
615
- - any engine change affecting reconstruction, formatting, lists, tables,
616
- fields, tabs/breaks, comments, notes, headers/footers, or revision metadata;
617
- - any case that needs normalized rather than exact text comparison; and
618
- - any automated Word failure whose cause is not immediately textual.
619
-
620
- #### Prepare the review set
621
-
622
- Run the automated lanes first:
623
-
624
- ```powershell
625
- npm run test:word
626
- npm run test:corpus:word
627
- npm run report:word:coverage
628
- npm run review:word:prepare -- --cycle=0
629
- ```
630
-
631
- The last command writes a pending review manifest under ignored
632
- `tmp/word-manual-review/` containing changed catalogue families, the rotating
633
- 20% synthetic release sample, and legal/administrative corpus representatives.
634
- It only prepares the selection: it never records a visual pass or human
635
- sign-off. Use a new cycle number for each release rotation.
636
-
637
- Synthetic documents are generated under `tmp/word-validation/`; reviewed
638
- SuperDoc results are under `tmp/superdoc-word-fixtures/`. Do not commit generated
639
- or downloaded `.docx` files.
640
-
641
- Selection rules:
642
- - every new or changed case;
643
- - every case required by the triggers above;
644
- - at least 20% of unchanged synthetic cases, rotating from the prior release (so
645
- every retained synthetic case receives a human review over five release cycles);
646
- - at least one legal and one administrative SuperDoc result; and
647
- - representative list, table, formatted-run, and anchor/field/content-control
648
- structures.
649
-
650
- A major release or a change to package assembly requires a full visual sweep of
651
- affected structure families.
652
-
653
- Record the installed Word version and build from **File → Account → About Word**.
654
- Differences in rendering can be version-specific.
655
-
656
- #### Configure Word
657
-
658
- For the tracked-change inspection:
659
-
660
- 1. Open the generated fixture directly in desktop Word.
661
- 2. On **Review**, select **All Markup**.
662
- 3. Under **Show Markup**, enable insertions/deletions, formatting, comments, and
663
- all reviewers relevant to the case.
664
- 4. Use the expected balloons/inline display for the document and enable
665
- paragraph marks when checking whitespace, tabs, breaks, lists, and empty
666
- paragraphs.
667
- 5. Do not overwrite the generated fixture. Work on disposable copies if a
668
- saved accepted or rejected view is useful.
669
-
670
- #### Inspect each case (3-view checklist)
671
-
672
- ##### 1. Tracked-change view
673
-
674
- - The document opens without a repair, conversion, or unreadable-content prompt.
675
- - Word shows the expected revision count and author attribution.
676
- - Insertions and deletions are anchored at the intended words or paragraph
677
- marks; a small edit has not become an unexplained whole-paragraph rewrite.
678
- - Untargeted text and surrounding revisions remain unchanged.
679
- - Existing bold, italic, underline, highlighting, fonts, styles, and language
680
- settings remain visually consistent.
681
- - Spaces, tabs, manual breaks, paragraph spacing, indentation, and alignment
682
- look intentional with formatting marks visible.
683
- - Lists retain numbering, levels, continuation, indentation, and marker style.
684
- - Tables retain widths, borders, merged cells, row heights, and alignment.
685
- - Bookmarks, hyperlinks, fields, content controls, comments, and note references
686
- remain in the correct visible location and still behave when activated.
687
- - Headers, footers, section boundaries, page breaks, and pagination remain
688
- stable around the edit.
689
- - Revision balloons and comment balloons point to the correct content and do
690
- not obscure or displace unrelated layout unexpectedly.
691
-
692
- ##### 2. Accept All view
693
-
694
- On a disposable copy, choose **Accept All Changes** and verify:
695
-
696
- - The resulting visible text expresses the intended edit.
697
- - No deletion residue, empty revision wrapper, unexpected blank line, or stale
698
- formatting remains.
699
- - Lists, tables, fields, links, comments, notes, and page layout still work.
700
- - Untargeted content is visually unchanged.
701
-
702
- Close the copy without replacing the generated fixture.
703
-
704
- ##### 3. Reject All view
705
-
706
- Reopen a fresh copy, choose **Reject All Changes**, and verify:
707
-
708
- - The original visible text and formatting are restored.
709
- - Original list numbering, table layout, fields, anchors, and pagination return.
710
- - No content introduced by the edit remains.
711
-
712
- #### Record the visual review result
713
-
714
- Keep the review report with release-validation artifacts. Screenshots may be
715
- stored under ignored `tmp/word-manual-review/<date>/` when useful, but do not
716
- commit corpus document images or document contents without checking rights and
717
- sensitivity.
718
-
719
- Suggested report template:
720
-
721
- ```markdown
722
- # Word visual review — <release/date>
723
-
724
- - Reviewer:
725
- - Review date:
726
- - Word version/build:
727
- - Automated synthetic result:
728
- - Automated corpus result:
729
- - Review type: Human sign-off | AI visual preflight
730
-
731
- | Case | Why selected | All Markup | Accept All | Reject All | Result | Notes |
732
- |---|---|---|---|---|---|---|
733
- | example-case | New table structure | Pass | Pass | Pass | Pass | No layout shift |
734
-
735
- ## Failures or follow-ups
736
-
737
- - None.
738
- ```
739
-
740
- A **Pass** requires all three views to pass. Record **Fail** if rendering or
741
- interaction is wrong even when automated text comparison passes. Turn a failure
742
- into a fixed regression case when possible; otherwise record the exact harness
743
- or Word-version limitation in the active reliability plan.
744
-
745
-
746
- ### AI-assisted Word visual preflight
747
-
748
- Generate repeatable three-view PDF evidence for every layout-sensitive
749
- synthetic fixture with:
750
-
751
- ```powershell
752
- npm run test:word:visual
753
- ```
754
-
755
- The command writes PDFs and `manifest.json` under ignored
756
- `tmp/word-visual-review/rendered/` storage. It verifies that Word opened each
757
- fixture and produced a non-empty, paginated rendering, but leaves the manifest
758
- certification and every visual judgment pending. To render only named cases,
759
- invoke `scripts/word-com-visual-suite.ps1 -Case case-one,case-two` directly.
760
-
761
- An AI agent with Windows computer control may also open the generated fixtures
762
- in the installed desktop Word application, switch among All Markup, Accept All,
763
- and Reject All views, capture screenshots, and inspect them for visible
764
- regressions. This is a useful intermediate oracle because it exercises the same
765
- real UI a person sees rather than only `Document.Content.Text`.
766
-
767
- Run an AI visual preflight:
768
-
769
- - after a phase that changes reconstruction, formatting, list/table behavior,
770
- package assembly, or revision display;
771
- - after every five to ten new Word fixtures;
772
- - for all newly introduced structure families;
773
- - when automated Word passes but the XML change is unusually broad; and
774
- - before asking a human to perform the release sample, so obvious failures are
775
- found first.
776
-
777
- For the focused real-document additions, run:
778
-
779
- ```powershell
780
- npm run test:corpus:word:visual
781
- ```
782
-
783
- This first runs the exact Word differential, then writes 120 PDFs and a pending
784
- manifest under ignored `tmp/superdoc-word-visual-review/rendered/` storage: three
785
- views for 22 list-focused cases, 16 table-focused cases, and two page-header
786
- cases. The long-document set includes 6,000+ word council minutes,
787
- an 8,000+ word zoning resolution, and a 59,000+ word prospectus with 180 tables.
788
- Each invocation packages its fixtures in a process-specific directory so
789
- a Word process left behind by an RPC disconnect cannot lock the next run's
790
- inputs. The runner ignores `RPC_E_DISCONNECTED` during post-export cleanup and
791
- restarts Word once when a disconnect interrupts an actual render. After Word
792
- finishes, the command rebuilds `docs/test-comparison-dashboard.html` from that
793
- exact process-specific fixture directory. The `Verified cached ...` messages
794
- refer only to the pinned source-document downloads; edited comparison DOCX
795
- files are regenerated on every run.
796
-
797
- The AI should use the same selection rules and checklist as a human, inspect at
798
- least the changed cases plus representative legal and administrative samples,
799
- and save screenshots under ignored `tmp/word-manual-review/<date>/` storage.
800
- Its report must identify itself as **AI visual preflight**, record the Word
801
- version/build when available, list the exact cases and views inspected, and
802
- separate observed facts from uncertain visual judgments.
803
-
804
- AI visual review is advisory. It can catch missing or misplaced revisions,
805
- unexpected whole-paragraph markup, obvious pagination shifts, broken tables,
806
- lost indentation, clipped headers, and visibly misplaced anchors. It may miss
807
- subtle font metrics, accessibility issues, field behavior that requires domain
808
- knowledge, or a legally meaningful formatting distinction. An AI pass does not
809
- satisfy the human release sign-off, and sensitive/private documents must not be
810
- opened for AI review without explicit authorization.
811
-
812
- ### Multimodal LLM visual inspection (real-document spot checks)
813
-
814
- Evaluating subtle visual defects—such as shifted table borders, unnatural run
815
- breaks, font substitution, clipping headers, or awkward revision balloons—requires
816
- interpreting rendered document pages visually rather than relying solely on
817
- string assertions or raw XML checks. A multimodal Large Language Model (MLLM)
818
- with vision capabilities can review rendered page images across the three
819
- revision views (`allMarkup`, `acceptAll`, and `rejectAll`) and detect regressions
820
- that are invisible to plain-text diffs.
821
-
822
- #### Why multimodal inspection is kept separate
823
-
824
- Passing high-resolution, multi-page document images through a vision-capable LLM
825
- is **expensive, computationally intensive, and time-consuming**:
826
-
827
- - Each document scenario produces three full-document rendering views (often 1
828
- to 20+ pages per view).
829
- - Sending dozens of high-resolution page images consumes significant token
830
- budgets and model context.
831
- - Latency per case is orders of magnitude higher than programmatic XML parsing
832
- or COM automation.
833
-
834
- For these reasons, multimodal visual inspection **forms a separate check from the
835
- rest of the test suite**. It is never executed on every commit, local test run, or
836
- full batch pipeline.
837
-
838
- #### Sampling strategy: Random real-document spot checks
839
-
840
- Instead of exhaustive or continuous execution, multimodal visual inspection is
841
- conducted as a **random spot check of real documents here and there only**:
842
-
843
- 1. **Focus on authentic real documents:** Spot checks prioritize the
844
- [SuperDoc real-document corpus](#superdoc-real-document-word-tests) rather than
845
- simple synthetic fixtures. Real documents contain organic complexities—such
846
- as multi-column headers, nested bullet styles, non-uniform table borders, and
847
- mixed-font legal numbering—where visual layout errors are most likely to hide.
848
- 2. **Small random sampling:** A typical visual spot-check run selects a random
849
- sample of **2 to 5 real document scenarios** (for example, one administrative
850
- board agenda, one municipal minutes multi-table document, and one complex
851
- zoning ordinance).
852
- 3. **Execution cadence:** Spot checks are run on-demand:
853
- - periodically during active engine refactoring;
854
- - when introducing major changes to run splitting, table reconciliation, or
855
- list reconstruction; or
856
- - as an advisory audit prior to a release cycle.
857
-
858
- #### Multimodal inspection workflow
859
-
860
- When performing a multimodal visual spot check:
861
-
862
- 1. **Render target pages to images:** Render the selected scenario in Word to
863
- PDFs using `scripts/word-com-corpus-visual-suite.ps1 -Case <scenario-name>`,
864
- then convert the relevant pages (or contact sheets) to PNG images using the
865
- visual inspection helper:
866
- ```powershell
867
- node scripts/inspect-visual-evidence.mjs --contact-sheets
868
- ```
869
- 2. **Submit to the multimodal model:** Provide the vision model with the side-by-side
870
- page renders of `allMarkup`, `acceptAll`, and `rejectAll` along with the target
871
- diff and context.
872
- 3. **Targeted visual checklist:** Prompt the model to evaluate specific layout risks:
873
- - **Table cell integrity:** Did column widths shift? Are table borders intact?
874
- Did multi-line cell text collapse or overlap?
875
- - **Typography & Font metrics:** Did any run revert to default font size (e.g.
876
- dropping from 14pt Georgia to 12pt Normal)? Did bold, italic, or underline
877
- bleed into neighboring text?
878
- - **List alignment:** Are bullet indents consistent with existing items? Are
879
- there ghost markers or blank list rows?
880
- - **Page apparatus:** Are headers, footers, and page numbers in their expected
881
- margins without clipping or wrapping anomalies?
882
- 4. **Triaging findings:** If the multimodal model flags a visual abnormality,
883
- treat it as an advisory signal:
884
- - Verify the defect manually in Microsoft Word.
885
- - Trace the visual defect to its underlying OOXML cause (per the
886
- [visual failure diagnosis guidelines](#diagnose-visual-failures-in-the-generated-ooxml-first)).
887
- - Add a minimal semantic regression test in
888
- `tests/visual_failure_regression_tests.mjs` to ensure the issue is permanently
889
- prevented in the automated suite (`npm test`).
890
-
891
- ## SuperDoc real-document Word tests
892
-
893
- The corpus lane references selected documents from SuperDoc's
894
- [docx-corpus](https://docxcorp.us/) under ODC-By 1.0. Source documents are never
895
- committed. `tests/corpus/superdoc-english-legal-administrative.json` pins each
896
- reviewed source and observed SHA-256; `tests/corpus/superdoc-word-scenarios.json`
897
- defines named deterministic scenarios and records the structural coverage they
898
- add. A source may support multiple scenarios when each one adds a distinct
899
- behavioral claim.
900
-
901
- To add a corpus case:
902
-
903
- 1. Select an English legal or administrative document that adds a structure or
904
- task not already represented. Review its content and record why it is
905
- suitable.
906
- 2. Add an explicit pinned reference and observed hash to the manifest. Do not
907
- use a floating or bulk corpus download.
908
- 3. Fetch only that reference with
909
- `npm run corpus:fetch:superdoc -- --id <pinned-id>`.
910
- 4. Inspect the document in Word and choose a target that occurs exactly once in
911
- Word's source text.
912
- 5. Add the deterministic operation, shape, coverage labels, and review note to
913
- `superdoc-word-scenarios.json`. Use `key` plus `sourceId` when adding another
914
- scenario for an already pinned source. Use `operations` for a multi-change
915
- atomic batch, or `part: "word/header<N>.xml"` for a page-header revision.
916
- 6. Run `npm test` for manifest/catalogue checks and `npm run test:corpus:word`
917
- for package hashing plus the Word Accept All/Reject All differential.
918
-
919
- The corpus packager starts from the original `.docx`, replaces only the declared
920
- revision part (`word/document.xml` by default, or a named header part), and
921
- verifies every untouched ZIP part byte-for-byte before Word opens the result.
922
- The Word oracle derives multi-change expectations by applying each independently
923
- declared replacement to Word's source text and reads header-story text separately
924
- ## Release validation and independent oracles
925
-
926
- This package works on OOXML strings and intentionally leaves `.docx` zip
927
- packaging to consumers (release *tooling* assembles minimal `.docx` fixtures
928
- with a script-local zip writer; the published library has no zip dependency).
929
-
930
- The test suite verifies the accept/reject round-trip invariant using the
931
- library's own transforms. Because a shared misconception between the
932
- generator and the resolver would pass those tests silently, release
933
- validation adds **independent oracles**: Microsoft Word, LibreOffice, and
934
- the ECMA-376 schemas.
935
-
936
- ### Pre-release automated check sequence
937
-
938
- ```bash
939
- npm test # includes tests/roundtrip_fuzz_tests.mjs (seeded, deterministic)
940
- npm run test:isolation
941
- npm run check:types
942
- npm run lint
943
- ```
944
-
945
- The fuzz harness generates random paragraph structures and edits, asserting the
946
- round-trip invariant plus `validateRedlineOoxml` on each case. Tune or reproduce with:
947
-
948
- ```bash
949
- FUZZ_SEED=<seed> FUZZ_ITERATIONS=<n> node tests/roundtrip_fuzz_tests.mjs
950
- ```
951
-
952
- A failing case prints its exact reproduction command.
953
-
954
- ### Runtime guardrail
955
-
956
- `validateRedlineOoxml(oxml)` (exported from `index.js`) runs structural
957
- invariants at runtime and returns `{ valid, issues }`. Downstream packagers
958
- should call it before writing engine output into `word/document.xml`.
959
-
960
- ### Export fixtures
961
-
962
- ```bash
963
- node scripts/export-validation-fixtures.mjs
964
- ```
965
-
966
- Writes to `tmp/validation-docx/`, per case:
967
-
968
- - `<name>.document.xml` — generated `word/document.xml` payload
969
- - `<name>.docx` — minimal assembled package
970
- - `<name>.expected.json` — expected accept-all / reject-all plain text,
971
- derived from edit *intent* (not from this library's transforms), so
972
- external consumers act as independent oracles.
973
-
974
- ### Schema validation (ECMA-376 transitional XSD)
975
-
976
- ```bash
977
- node scripts/export-validation-fixtures.mjs
978
- bash scripts/validate-fixtures-xsd.sh
979
- ```
980
-
981
- Downloads (and caches in `.cache/ooxml-schemas/`) the transitional
982
- wordprocessingml schemas from ECMA-376 Part 4, patches the `xml:` namespace
983
- import to resolve offline, and validates every `*.document.xml` fixture with
984
- `xmllint`. Requires `curl`, `unzip`, and `xmllint` (`libxml2-utils` on
985
- Debian/Ubuntu; available on Windows via conda/msys).
986
-
987
- ### LibreOffice consumer check
988
-
989
- ```bash
990
- cd tmp/validation-docx
991
- soffice --headless --convert-to pdf --outdir converted *.docx
992
- ```
993
-
994
- Verifies that a second independent OOXML consumer parses and converts the
995
- fixtures without error.
996
-
997
- ### Continuous validation
998
-
999
- `.github/workflows/validation.yml` runs nightly (and on demand via
1000
- `workflow_dispatch`):
1001
-
1002
- 1. **xsd-schema** — exports fixtures and validates them against the
1003
- ECMA-376 transitional `wml.xsd`.
1004
- 2. **libreoffice** — exports fixtures and converts them with headless
1005
- LibreOffice.
1006
- 3. **fuzz-extended** — 20,000 fuzz round-trip cases with a date-derived
1007
- seed, so every night explores new inputs. A failure log includes the
1008
- exact `FUZZ_SEED` reproduction command.
1009
-
1010
- The Word differential check stays manual because it requires desktop Word;
1011
- run it before tagging a release.
1012
-
1013
- ### JavaScript coverage baselines
1014
-
1015
- ```bash
1016
- npm run test:coverage
1017
- ```
1018
-
1019
- The command runs the complete JavaScript test suite under c8 and prints a
1020
- per-file report. Coverage answers "which implementation paths did the automated
1021
- JavaScript tests execute?" It does not prove that executed paths are correct,
1022
- that generated OOXML opens in Word, or that the real-document corpus is broad
1023
- enough. Treat it as a map for finding thinly tested code; Word, schema,
1024
- LibreOffice, fuzz, and corpus checks provide different evidence.
1025
-
1026
- Historical reference snapshots:
1027
-
1028
- - **Initial Phase 7 baseline (2026-08-29)**:
1029
- | Metric | Coverage |
1030
- |---|---:|
1031
- | Lines / statements | 79.57% |
1032
- | Functions | 80.07% |
1033
- | Branches | 69.22% |
1034
-
1035
- - **Post-Phase-5 snapshot (2026-08-30)**:
1036
- 79.96% lines/statements, 80.95% functions, and 69.52% branches.
1037
-
1038
- - **Post-Phase-5 snapshot (2026-09-04)**:
1039
- 89.81% statements/lines, 93.04% functions, and 77.34% branches repo-wide.
1040
- The Node surface reports 99.61% statements/lines; `document-inspection.js`
1041
- reports 97.64% statements/lines and 100% functions; `paragraph-text.js` reports
1042
- 100% statements/lines/functions.
1043
-
1044
- Preserve snapshots so progress is visible over time rather than presenting
1045
- coverage as a single pass/fail quality score.
1046
-
1047
- ## Choosing where a new test belongs
1048
-
1049
- - A fixed bug always gets an automated regression.
1050
- - A broad engine rule also gets deterministic fuzz coverage.
1051
- - A claim about what Word renders or accepts gets a synthetic Word case.
1052
- - A claim involving package complexity or realistic authoring structures gets
1053
- a reviewed corpus case.
1054
- - A structural XML rule should be checked by runtime validation and, where
1055
- practical, the ECMA-376 XSD lane.
1056
-
1057
- Keep expectations independent of the code under test. In particular, never use
1058
- this library's Accept/Reject helpers to calculate the expected result for the
1059
- Word differential.
1
+ # Testing Guide
2
+
3
+ This project uses several test lanes because no single oracle can prove that a
4
+ tracked-change document is correct. JavaScript assertions can verify exact XML
5
+ and return contracts; Microsoft Word can verify what the primary consumer
6
+ actually accepts and rejects; real documents expose structures that small
7
+ fixtures rarely contain.
8
+
9
+ ## Test lanes at a glance
10
+
11
+ | Lane | Command | What it proves | What it does not prove |
12
+ |---|---|---|---|
13
+ | Automated regression suite | `npm test` | API behavior, exact text invariants, OOXML structure, deterministic fuzz cases, and catalogue integrity | That desktop Word accepts the generated package |
14
+ | Isolation, types, and lint | `npm run test:isolation`, `npm run check:types`, `npm run lint` | Runtime boundaries, declaration alignment, and static repository rules | Document correctness |
15
+ | JavaScript coverage | `npm run test:coverage` | Which source lines, functions, and branches the automated suite executes | That an executed path is correct |
16
+ | Synthetic Word differential | `npm run test:word` | Word opens generated packages, sees revisions, and produces the intended text after Accept All and Reject All | The diversity of real-world packages |
17
+ | Word visual evidence | `npm run test:word:visual` | Word renders layout-sensitive fixtures in All Markup, Accept All, and Reject All views and writes a pending review manifest | That the rendered pages are visually correct until a reviewer inspects them |
18
+ | SuperDoc Word corpus | `npm run test:corpus:word` | The same Word differential on 60 scenarios drawn from 23 reviewed, pinned real English legal/administrative documents while untouched package parts remain byte-identical | Every possible DOCX producer or document type |
19
+ | SuperDoc visual evidence | `npm run test:corpus:word:visual` | Word renders 40 focused list, table, long-document, and page-header scenarios in All Markup, Accept All, and Reject All views | Human visual sign-off |
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
+ | 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
+ | 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 [Release validation and independent oracles](#release-validation-and-independent-oracles) | Schema conformance and acceptance by a second consumer | Word-specific revision semantics (see the Word revision semantics row) |
24
+ | Word revision semantics | `node tests/word_revision_semantics_tests.mjs` | The Reject All / Accept All projection in `core/word-revision-semantics.js` and the library's reject/accept agree with recorded desktop Word 16 results for each paragraph-mark revision shape (inserted/deleted marks mid-document, last paragraph, table cells, rows, lists, `w:pPrChange`, mixed authors); no Word needed to run | That Word still behaves as recorded (regenerate the expectations) |
25
+ | Revision fidelity verifier | `node tests/revision_fidelity_verifier_tests.mjs` | Post-apply Reject All invariants (`REJECT_*`), `ACCEPT_NEIGHBORS` locality and `UNTRACKED_CREATED_REVISION` for untracked edits, accept-first scoping, modes, baseline cache, and the `REVISION_FIDELITY_VIOLATION` payload through the bare runner and `openDocx` | Desktop Word behavior beyond the recorded semantics |
26
+ | Revision fidelity view cache | `node tests/revision_fidelity_view_cache_tests.mjs` | Session reuse of verified projections as the next baseline: an untracked edit between tracked ones, a refused operation and an unverified commit verify exactly like uncached single operations | Performance numbers (see `scripts/benchmark-operation-session.mjs`) |
27
+ | Paragraph-mark and property layering | `node tests/paragraph_mark_single_site_tests.mjs`, `node tests/current_properties_usage_tests.mjs` | Tracked paragraph-mark `w:ins`/`w:del` is constructed only in `engine/paragraph-mark-revisions.js`; current properties are read only through `core/current-properties.js` (allowlists carry written reasons) | Behavior of the allowed sites |
28
+ | 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 |
29
+ | Agent CLI | `node tests/agent_cli_tests.mjs`, `node tests/agent_cli_protocol_tests.mjs`, `node tests/agent_cli_help_tests.mjs`, `node tests/agent_cli_inspection_context_tests.mjs`, `node tests/agent_cli_output_budget_tests.mjs` | JSON contracts, command-specific help, contextual pagination, composable progressive/atomic profiles, deduplicated receipts, compact stdout, file/stdin Unicode fidelity, complete-success exits, source safety, command families, and schema readability | Cross-platform CI beyond the current runner |
30
+ | Example agent session | `node tests/agent_session_example_tests.mjs`, `node tests/localized_edit_example_tests.mjs` | A non-package sample wrapper delegates to the Node facade, binds and refreshes opaque handles across package revisions, returns context windows, compiles exact localized edits, preserves accepted/rejected lifecycle and atomic rollback, and supports review resolution | LLM/provider latency or a production tool-server transport |
31
+ | Batch compilation and recovery | `node tests/batch_source_binding_tests.mjs`, `node tests/error_recovery_contract_tests.mjs`, `node tests/capture_dependency_graph_tests.mjs` | Batch-start source identity survives structural index drift and savepoint restoration; true overlap and capture fan-out fail before mutation; recovery envelopes, retry bases, bounded CLI diagnostics, and complete-success exits remain stable | Whether a model follows the returned recovery action correctly |
32
+ | Agent documentation contract | `node tests/agent_documentation_contract_tests.mjs` | The ordinary fast start stays within its 40–60 line/600-word budget, examples lead with scoped contextual extraction, and packaged skill-authoring guidance separates invariants, policies, transports, recovery, and presentation while the development wrapper stays outside package files | Whether a particular model reads or follows the instructions |
33
+ | 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 |
34
+ | 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 |
35
+ | 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 |
36
+ | 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 |
37
+ | 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 |
38
+ | Universal document facade & ZIP | `node tests/universal_docx_document_tests.mjs`, `node tests/universal_zip_archive_tests.mjs`, `node tests/universal_facade_edge_cases_tests.mjs` | Universal `Uint8Array` binary handling, `fflate` compression/decompression, optimistic concurrency, and complete-DOCX operations without Node built-ins | Desktop Word rendering |
39
+ | Sandbox bundle isolation | `node tests/sandbox_bundle_isolation_tests.mjs` | Zero-dependency standalone ESM/CJS bundles execute in restricted VM sandboxes without `process`, `Buffer`, or `require`, with zero Node built-in imports | Web Worker latency |
40
+ | Existing-revision policy `auto` | `node tests/existing_revisions_auto_tests.mjs`, `node tests/existing_revisions_auto_paths_tests.mjs` | Per-target resolution to `merge-same-author` or `slice-cross-author` (mixed, unattributed and deletion-only history, whole `targetEnd` ranges, table cells), equivalence with the explicit policy through `applyRedlineToOxml` and the runner, preflight reporting, untracked edits, `openDocx`, `--existing-revisions auto`, schema and the CLI capability | Desktop Word rendering |
41
+ | Comment reply paraIds | `node tests/comment_reply_para_id_tests.mjs` | A reply's `w14:paraId` avoids body paragraph ids (case-insensitive) and stays deterministic without a collision | Word's own id allocation |
42
+ | Inspection comparison | `node tests/inspection_comparison_tests.mjs` | `compareDocumentInspections` ignores ids, dates, comment order and run splits and reports text, review-segment (author, kind, position), table, list-label and comment (text, author, anchor, resolved, thread) differences, capped at 10; works on `openDocx` inspections | A host's own write path |
43
+ | Literal underscores and plain text | `node tests/literal_underscore_text_tests.mjs` | Signature lines and `snake_case` survive table reconciliation and rewrites; CommonMark underscore flanking; a caller's `structuredContent: false` keeps inline Markdown literal | Desktop Word rendering |
44
+ | Cross-author revision slicing | `node tests/cross_author_slicing_fixtures_tests.mjs`, `node tests/cross_author_carrier_splitting_tests.mjs`, `node tests/cross_author_slicing_synthetic_tests.mjs`, `node tests/cross_author_slicing_real_tests.mjs`, `node tests/cross_author_slicing_hyperlink_roundtrip_tests.mjs`, `node tests/cross_author_slicing_insertion_stress_tests.mjs` | Word-native `<w:ins>` splitting, `<w:del>` nesting, multi-author stacking, straddle boundaries, synthetic matrix (SYN-01..12d), strict package differential replay (PKG-01..06), exact hyperlink/NBSP round trips, and 76 insertion stress scenarios | Non-Word consumers beyond OpenXML XSD validation |
45
+
46
+ The package-facade regression opens a real ZIP buffer, adds a comment beside an
47
+ existing high ID, validates OPC wiring, and checks an unrelated binary part is
48
+ unchanged after extraction. Its failure case requires byte-exact rollback. The
49
+ inspection regression covers revisions, structural characters, heading
50
+ context, comment joining, and a numbering start override.
51
+
52
+ The CLI regression executes the recommended extract → preflight → apply →
53
+ validate flow and also covers accept, reject, comment deletion, output collision
54
+ refusal, and source-byte preservation. CLI stdout is captured and parsed as one
55
+ JSON value so diagnostic logging cannot corrupt agent output.
56
+
57
+ The edge suites deliberately use structures omitted from the basic happy-path
58
+ fixtures: `w:moveFrom`/`w:moveTo`, deleted text, structural breaks and hyphens,
59
+ comments crossing paragraph boundaries, orphan definitions, outline headings,
60
+ footnote/endnote references, nested Roman/alphabetic numbering, sequential
61
+ commit-then-rollback calls, selective comment authors, existing high anchor
62
+ IDs, encrypted/unsupported ZIP entries, `--force`, and `--in-place`.
63
+
64
+ The Phase 2 boundary regression is intentionally structural as well as
65
+ behavioral. It prevents the public runner from accumulating implementation
66
+ logic again, verifies that internal operation code does not import the root
67
+ entry point, and checks that failed atomic work can retain the exact source
68
+ string while working maps and sets remain isolated until commit.
69
+
70
+ The checked post-Phase-5 2026-09-04 coverage run reports 89.81%
71
+ statements/lines, 77.34% branches, and 93.04% functions repository-wide; this
72
+ is above every recorded pre-refactor dimension. Targeting performance can be
73
+ measured in Node with `npm run benchmark:targeting` and against the native DOM
74
+ by serving the repository locally and opening
75
+ `scripts/benchmark-targeting-browser.html`; its `paragraphs`, `operations`, and
76
+ `iterations` query parameters reproduce the checked fixture sizes.
77
+ The Node surface reports 99.61% statements/lines; `document-inspection.js`
78
+ reports 97.64% statements/lines and 100% functions; `paragraph-text.js` reports
79
+ 100% statements/lines/functions.
80
+
81
+ Run the observational live-session benchmark with:
82
+
83
+ ```powershell
84
+ npm run benchmark:session
85
+ ```
86
+
87
+ It writes `tmp/benchmarks/operation-session-latest.json`. The checked
88
+ 1,000-paragraph/10-operation run reduced full-document parse/serialization from
89
+ 10/10 to 1/1 and measured a 1.58x median speedup. Timing and heap figures are
90
+ not correctness gates. Per-operation DOM savepoints are retained because
91
+ redline accuracy, no-op isolation, and rollback fidelity take precedence over
92
+ the aspirational speed target.
93
+
94
+ Run the observational agent-protocol benchmark with:
95
+
96
+ ```powershell
97
+ npm run benchmark:agent
98
+ ```
99
+
100
+ It writes `tmp/benchmarks/agent-workflow-latest.json` and compares canonical
101
+ stateless Node, legacy file-based CLI, compact stdin/profile CLI, and the
102
+ development-only session example. It records native/in-process CLI time, heap,
103
+ serialized request bytes, protocol calls, instruction words/bytes, exact
104
+ accepted/rejected text, comment preservation, ordering, recovery, and
105
+ cross-author attribution. It does not measure or estimate LLM reasoning, exact
106
+ tokens, tool transport, Claude, or OpenCode wall time; collect those separately
107
+ in the calling harness. The checked WP-07 results are summarized in the
108
+ [agent protocol rollout audit](validation-reports/2026-09-12-agent-protocol-rollout.md).
109
+
110
+ Run the CLI discovery/output benchmark with:
111
+
112
+ ```powershell
113
+ npm run benchmark:agent-cli
114
+ ```
115
+
116
+ It records command-specific help size, broad inspect/search response size,
117
+ context-window call count, pagination metadata, case-insensitive match parity,
118
+ and native elapsed time in `tmp/benchmarks/agent-cli-discovery-latest.json`.
119
+ The checked WP-00 through WP-02 before/after measurements are in the
120
+ [agent CLI discovery baseline](validation-reports/2026-09-12-agent-cli-discovery-baseline.md).
121
+ WP-03 through WP-05 profile, output-size, workflow, documentation, and package
122
+ results are in the
123
+ [agent CLI efficiency rollout audit](validation-reports/2026-09-13-agent-cli-efficiency-rollout.md).
124
+
125
+ ## Cross-author revision slicing test suite
126
+
127
+ The cross-author revision slicing subsystem introduces six complementary test lanes:
128
+
129
+ 1. **Native Word Desktop Fixtures (`tests/cross_author_slicing_fixtures_tests.mjs`)**:
130
+ Verifies 36 reference `.docx` and `.xml` files generated via Microsoft Word Desktop 365 COM automation across 6 golden scenarios (`insert-interior`, `delete-interior`, `delete-boundary-start`, `delete-boundary-end`, `delete-straddle-baseline-insertion`, and `multi-author-stacked`) in pending, accepted, and rejected states. Proves Word-native OOXML patterns (carrier `<w:ins>` splitting for insertions, direct `<w:del>` nesting inside `<w:ins>` for deletions) and lifecycle parity under `acceptTrackedChangesInOoxml` and `rejectTrackedChangesInOoxml`.
131
+ 2. **Carrier Splitting Unit Suite (`tests/cross_author_carrier_splitting_tests.mjs`)**:
132
+ Tests `splitTrackChangeCarrier` across interior and boundary cuts, multi-run formatting preservation, stable metadata cloning, independent revision ID allocation via `RevisionIdAllocator`, and immutable input handling.
133
+ 3. **Synthetic Boundary Matrix (`tests/cross_author_slicing_synthetic_tests.mjs`)**:
134
+ Executes the full SYN-01 through SYN-12d test matrix covering pure interior inserts, pure interior deletes, boundary start/end cuts, full-content annihilation, baseline/insertion straddles, multi-carrier straddles, formatting preservation, paired replacements, 3-author stacked revisions, and selective author accept/reject lifecycle oracles.
135
+ 4. **Checked-In Word Package Differential Suite (`tests/cross_author_slicing_real_tests.mjs`)**:
136
+ Reconstructs the pre-mutation document packages, applies `slice-cross-author` via the strict `openDocx` package facade, enforces atomic package validation, and compares engine `AcceptAll` and `RejectAll` output against Word Desktop-generated golden packages (PKG-01 through PKG-06).
137
+ 5. **Hyperlink and Exact-Text Regression (`tests/cross_author_slicing_hyperlink_roundtrip_tests.mjs`)**:
138
+ Reproduces the repeated-phrase, hyperlink-boundary, and NBSP-to-space failure through both the low-level engine and document operation runner. It requires exact accepted-view equality, preserves both hyperlink relationship containers, and proves that an unreconstructable slicing mutation returns `PATCH_ROUNDTRIP_MISMATCH` with the original OOXML unchanged.
139
+ 6. **Insertion Stress Matrix (`tests/cross_author_slicing_insertion_stress_tests.mjs`)**:
140
+ Runs 76 deterministic scenarios across carrier start/end/interior positions, repeated phrases, multi-run and formatted carriers, spaces/tabs/NBSP, XML-sensitive characters, emoji/combining Unicode, hyperlink interiors and boundaries, bookmarks, comments, nested deletions, adjacent authors, mixed same/foreign-author carriers, multiple structural containers, consecutive reviewer rounds, and the atomic document runner. Every supported case requires exact current-view, Accept-All, Reject-Current, schema, metadata, and non-empty-wrapper invariants; unsupported nested structures must fail without throwing or returning changed output.
141
+
142
+ ## Coverage matrix and test selection
143
+
144
+ Run the deterministic task-by-structure report before choosing the next Word
145
+ case:
146
+
147
+ ```powershell
148
+ npm run report:word:coverage
149
+ npm run report:word:coverage -- --json
150
+ npm run report:test:dashboard
151
+ ```
152
+
153
+ The dashboard command writes a self-contained interactive report to
154
+ `docs/test-comparison-dashboard.html`. It compares task/structure cells,
155
+ synthetic and real-document coverage, independent oracles, visual-render
156
+ eligibility, and planned high-priority gaps from the live catalogues. It also
157
+ embeds source, tracked, accepted, and rejected packages for every synthetic
158
+ fixture and, when the pinned corpus is downloaded, all 60 reviewed real legal
159
+ and administrative scenarios. Its `docx-preview` workbench supports arbitrary left/right states,
160
+ comparison presets, synchronized scrolling, revision metadata, expected text,
161
+ and local downloads; no fixture upload or file picker is required. Real cases
162
+ are labeled in the document selector and selected by default when available.
163
+ Use **Hide sidebar** beside the dashboard filters to remove the coverage-detail
164
+ column and expand the comparison workbench to the full browser width. The
165
+ button becomes **Show sidebar**, and the preference is stored locally so the
166
+ wide document view survives a reload.
167
+ Without the local corpus, generation remains offline-safe and embeds only the
168
+ synthetic previews.
169
+
170
+ The report combines all synthetic and reviewed SuperDoc scenarios. Synthetic
171
+ metadata is declared in `tests/fixtures/word-task-coverage.mjs`; SuperDoc
172
+ metadata is declared in `tests/corpus/superdoc-word-coverage.json`. Descriptive
173
+ `task`, `shape`, and `coverage` labels remain on the original cases as review
174
+ notes, while the matrix uses shared task, structure, and oracle vocabularies.
175
+
176
+ Catalogue tests reject unknown vocabulary labels, missing oracle or manual-review
177
+ metadata, duplicate identities, structural claims unsupported by fixture XML or
178
+ reviewed corpus labels, and uncovered high-priority cells without a recorded
179
+ plan or exclusion. The dispositions live in
180
+ `tests/fixtures/coverage-matrix-priorities.json` and require both a reason and a
181
+ dependency.
182
+
183
+ Choose new cases from uncovered high-priority cells first, then prefer a new
184
+ task/structure combination over another case in a dense cell. A higher count is
185
+ not itself a reason to add a fixture. Adding a catalogue case automatically
186
+ changes the report because it reads the live metadata.
187
+
188
+ ## Reliability improvement process used for this test expansion
189
+
190
+ The current corpus, visual runners, and comparison dashboard were built as one
191
+ feedback loop rather than as separate test features. The working sequence is:
192
+
193
+ 1. inventory the claimed behavior and existing test lanes;
194
+ 2. add realistic cases that expose weak structures;
195
+ 3. assert the smallest machine-checkable invariant at the engine level;
196
+ 4. package the result into the original real document without changing
197
+ unrelated parts;
198
+ 5. ask Word to accept and reject the revisions;
199
+ 6. render All Markup, Accept All, and Reject All views;
200
+ 7. inspect the actual pages at both contact-sheet and full-page scale; and
201
+ 8. turn every visual defect into a focused regression plus a corpus assertion.
202
+
203
+ This ordering matters. A valid `<w:ins>` or `<w:del>` tree does not establish
204
+ that the inserted run inherited the correct font, list indentation, hyperlink
205
+ wrapper, table-cell formatting, or header styling. Conversely, a good-looking
206
+ PDF does not prove that Reject All restores the exact source. Each stage answers
207
+ a different question, and a case is useful only when its intended claim is
208
+ explicit.
209
+
210
+ ### Start from coverage claims, not fixture count
211
+
212
+ Read the task-by-structure matrix and select a real document that adds a missing
213
+ or weak combination. The expansion deliberately favored bullets, nested lists,
214
+ table cells, headers, long documents, and atomic multi-change batches because
215
+ these structures cross more OOXML boundaries than a single plain paragraph.
216
+ Several scenarios reuse a pinned source when a new batch exercises a genuinely
217
+ different structure; this increases behavioral coverage without pretending
218
+ that another copy of the same document is a new source.
219
+
220
+ For long real documents, record structural minimums such as word count, table
221
+ count, list-paragraph count, section count, and related header/footer parts.
222
+ `scripts/prepare-superdoc-word-corpus.mjs` checks these declarations against the
223
+ source package. This prevents a scenario description such as “table-heavy” or
224
+ “multi-section” from silently drifting away from the actual fixture.
225
+
226
+ ### Use multi-change batches to exercise interaction effects
227
+
228
+ Single replacements remain valuable for isolating engine behavior, but real
229
+ editing sessions usually contain several changes. The larger corpus scenarios
230
+ therefore use `operations` to apply independent edits atomically across one
231
+ document. Targets are chosen from different list items or table cells when
232
+ possible. This catches failures caused by earlier replacements shifting later
233
+ anchors, revision-ID allocation across a batch, and formatting state leaking
234
+ between operations.
235
+
236
+ The batch runner must either produce all requested revisions or roll back the
237
+ document. Word then checks the complete accepted and rejected story, not merely
238
+ the first target. Untouched ZIP parts are hashed and compared with the pinned
239
+ source, so success cannot come from rebuilding or normalizing the rest of the
240
+ package.
241
+
242
+ ### Expand weak list and table cells with structural operations
243
+
244
+ The list/table expansion was driven directly by the task-by-structure matrix;
245
+ it did not add more plain replacement cases. The current catalogue contains 125
246
+ Word cases: 65 synthetic fixtures and 60 reviewed real-document scenarios. The
247
+ `List Change × List` cell now contains 27 cases; the
248
+ `Table Reconciliation × Table` cell contains 11 cases.
249
+
250
+ The added synthetic cases isolate one structural decision at a time:
251
+
252
+ - append one list item;
253
+ - append several adjacent items;
254
+ - add a nested child;
255
+ - insert into the middle of an explicit list range;
256
+ - preserve upper- and lower-Roman numbering;
257
+ - preserve upper-letter, parenthesized lower-letter, dash, and symbol bullets;
258
+ - expand a manually numbered heading whose `w:numId="0"` suppresses numbering
259
+ into separate, positively numbered list paragraphs;
260
+ - update one table cell;
261
+ - insert a table row;
262
+ - delete a table row; and
263
+ - update several cells in one reconciliation.
264
+
265
+ The mixed-attachment fixture replaces one anchor with explicit headings,
266
+ ordinary paragraphs, a Markdown table, and a numbered list. It verifies real
267
+ `w:pStyle`, `w:tbl`, and positive-numbering structures plus independent Word
268
+ Accept All and Reject All results; literal table pipes are forbidden.
269
+
270
+ The real-document cases repeat those claims against native numbering and table
271
+ markup from municipal notices, board agendas, a long zoning ordinance, PPG
272
+ minutes, action tables, and invoice tables. They include consecutive and nested
273
+ list insertions, a long-document definition insertion, single- and multi-cell
274
+ updates, row insertion/deletion, and invoice row changes. Reusing a reviewed,
275
+ hash-pinned source is intentional when the scenario exercises a different
276
+ operation; the unit of coverage is the behavioral claim, not the download.
277
+
278
+ These cases declare `operation.type` as `list-change` or
279
+ `table-reconciliation`. Their accepted/rejected expectations use explicit
280
+ contains/absent assertions when a Markdown table payload or a structural list
281
+ edit cannot be compared meaningfully to the raw Word source as one exact text
282
+ string. Structural expectations still verify real properties such as native
283
+ list paragraphs, tables, headers/footers, sections, and minimum document size.
284
+ The catalogue tests require these expectations and reject unknown operation or
285
+ coverage labels.
286
+
287
+ List-style cases do not infer their claim from visible text alone. Synthetic
288
+ fixtures declare the required `w:numFmt` values and use complete, schema-ordered
289
+ numbering parts: every `w:abstractNum` must precede the first concrete `w:num`
290
+ mapping. That ordering is regression-tested because Word otherwise opens the
291
+ package but silently substitutes its fallback bullet. Real scenarios declare a
292
+ `listStyleExpectation` with the source level, number format, and, where useful,
293
+ the exact `w:lvlText`. Corpus preparation resolves the target paragraph's
294
+ `w:numId` through `word/numbering.xml` and fails before editing if the pinned
295
+ source no longer has that native style. The current real cases exercise upper
296
+ and lower Roman, upper and lower letter, parenthesized lower letter, dash, and
297
+ symbol bullets across bylaws, agendas, healthcare minutes, and long council
298
+ minutes.
299
+
300
+ ### Diagnose visual failures in the generated OOXML first
301
+
302
+ When a page looks wrong, compare the relevant source and tracked
303
+ `word/document.xml` or related part before changing the renderer. Locate the
304
+ visible text, inspect its nearest `w:r`, `w:rPr`, wrapper, paragraph properties,
305
+ and neighboring runs, then compare those properties with the source. This
306
+ separates an engine defect from a Word, PDF, or `docx-preview` display issue.
307
+
308
+ Three defects illustrate the approach:
309
+
310
+ - The council header date replacement inherited `w:vertAlign="superscript"`
311
+ from the trailing ordinal suffix. The fix anchors replacement formatting at
312
+ the beginning of the deleted range, and the regression explicitly forbids
313
+ `vertAlign` on the inserted runs.
314
+ - The prospectus filing-date replacement crossed normal runs and a hyperlink.
315
+ A forward-only property lookup had already moved past the beginning of the
316
+ deletion when the insertion asked for its formatting, so the new run had no
317
+ `w:rPr`. Word and SuperDoc correctly fell back from the surrounding 10 pt to
318
+ the document default of 12 pt. The lookup now supports that deliberate
319
+ backward query. The focused regression requires `w:sz="20"` and
320
+ `w:szCs="20"`, and the real prospectus scenario requires those values on
321
+ every inserted text run.
322
+ - A wholly inserted list item originally tracked only its text run. Accept All
323
+ looked correct and text-only Reject All assertions passed, but Word retained
324
+ the untracked paragraph mark as an empty bullet or number. Inserted list
325
+ paragraphs now track both their text and paragraph mark. The regression
326
+ rejects the change and asserts the exact original paragraph count, text, and
327
+ numbering, while the real Word PDFs confirm that no ghost marker remains.
328
+
329
+ These assertions test the semantic cause rather than a screenshot pixel. The
330
+ visual render remains necessary to confirm that the corrected properties
331
+ produce the intended page. The automated test suite in
332
+ `tests/visual_failure_regression_tests.mjs` codifies these guards directly in
333
+ the regression test pipeline (`npm test`) to prevent regressions before DOCX
334
+ packages reach Word.
335
+
336
+ ### Regenerate packages; do not reason from stale outputs
337
+
338
+ Source downloads are content-addressed and may be reused. Generated tracked,
339
+ accepted, rejected, PDF, and dashboard artifacts are outputs and must be
340
+ rebuilt after an engine or scenario change. A `Verified cached ...` line from
341
+ the corpus fetcher means only that the pinned source `.docx` already matches its
342
+ recorded hash; it does not mean the edited package was reused.
343
+
344
+ Use this sequence for a corpus-affecting engine change:
345
+
346
+ ```powershell
347
+ npm test
348
+ npm run check:types
349
+ npm run lint
350
+ npm run test:isolation
351
+ npm run test:corpus:word
352
+ npm run test:corpus:word:visual
353
+ npm run report:test:dashboard
354
+ ```
355
+
356
+ For faster iteration, `prepare-superdoc-word-corpus.mjs` accepts
357
+ `--input-dir` and `--output-dir`, and the visual PowerShell runner accepts
358
+ `-Case <scenario-key>` plus custom fixture/output directories. Run the complete
359
+ lane before handoff even when a focused render passed.
360
+
361
+ ### Treat Word COM cleanup separately from render success
362
+
363
+ Word sometimes disconnects a COM document proxy after successfully exporting
364
+ a PDF and raises `RPC_E_DISCONNECTED` during `Document.Close()`. The visual
365
+ runner uses safe close/quit helpers: a disconnect during post-export cleanup is
366
+ reported but does not discard a valid, non-empty PDF; a disconnect during the
367
+ actual render causes Word to restart and the case to retry once. Other COM
368
+ exceptions still fail the run. This distinction avoids both false failures and
369
+ false passes.
370
+
371
+ Each visual invocation uses a process-specific fixture directory. That keeps a
372
+ Word process left behind by an earlier disconnect from locking or contaminating
373
+ the next set of generated DOCX files.
374
+
375
+ ### Review both the whole document and the changed page
376
+
377
+ For a long document, create low-resolution contact sheets for every page in all
378
+ three revision states and inspect them for pagination changes, blank pages,
379
+ clipping, table displacement, or large typography shifts. Then inspect every
380
+ changed page at full resolution. Contact sheets are good at document-wide
381
+ layout; they are not reliable for a 10 pt versus 12 pt difference or a leaked
382
+ superscript flag.
383
+
384
+ Record the exact scenario, Word version/build, revision view, page, observed
385
+ fact, and whether the judgment is certain. The visual manifest remains pending
386
+ until a reviewer makes that judgment; successful PDF export alone is not a
387
+ visual pass.
388
+
389
+ ### Keep the dashboard generated and test its controls
390
+
391
+ `scripts/generate-test-dashboard.mjs` owns the dashboard markup, styles, and
392
+ behavior. Do not hand-edit `docs/test-comparison-dashboard.html`; rebuild it
393
+ with `npm run report:test:dashboard`. The report embeds the four DOCX states so
394
+ it remains self-contained and can show the exact generated packages without a
395
+ file picker or server.
396
+
397
+ Dashboard changes require generator-level assertions in
398
+ `tests/test_dashboard_report_tests.mjs`. The sidebar-width control, for
399
+ example, is checked for its accessible relationship, full-width CSS state, and
400
+ persisted preference. DOCX package parsing remains covered separately by
401
+ `tests/docxjs_dashboard_rendering_tests.mjs`. When browser control is available,
402
+ also click the control in the generated page and verify that the sidebar is
403
+ hidden, both document panes widen, the label changes, and reloading preserves
404
+ the selected state.
405
+
406
+ ### Definition of done for a discovered real-document defect
407
+
408
+ A visual defect is complete only when all of the following are true:
409
+
410
+ - the source and faulty generated OOXML have been compared;
411
+ - the engine cause is understood rather than masked in CSS or the renderer;
412
+ - a minimal automated regression reproduces the structural boundary;
413
+ - Accept All yields the requested text and Reject All restores the source;
414
+ - the affected real corpus scenario has an appropriate structural or
415
+ formatting assertion;
416
+ - its source, tracked, accepted, and rejected packages have been regenerated;
417
+ - desktop Word differential passes;
418
+ - the changed page and the document-wide three-view render have been inspected;
419
+ - the self-contained comparison dashboard has been rebuilt; and
420
+ - the full automated, isolation, type, lint, and whitespace checks pass.
421
+
422
+ Prepare, but do not approve, a human review sample with:
423
+
424
+ ```powershell
425
+ npm run review:word:prepare -- --cycle=0
426
+ ```
427
+
428
+ The ignored `tmp/word-manual-review/review-manifest-cycle-0.json` selects all
429
+ catalogue families changed in the worktree, a rotating 20% synthetic sample,
430
+ and one legal plus one administrative SuperDoc case. Every All Markup, Accept
431
+ All, Reject All, and human sign-off field starts as `pending`; the helper cannot
432
+ turn them into passes. Increment `--cycle` between releases to rotate the
433
+ unchanged sample.
434
+
435
+ ## Automated JavaScript tests
436
+
437
+ Files matching `tests/*.mjs` are discovered by `scripts/run-tests.mjs`. Tests
438
+ use `assert/strict` and each runs in a separate Node process. The runner uses up
439
+ to four workers by default while preserving filename-sorted reporting. Set
440
+ `DOCX_TEST_CONCURRENCY=1` for deterministic serial reproduction, or another
441
+ positive integer for an explicit worker bound. Word, visual, and corpus lanes
442
+ remain separate serial commands. Shared OOXML assertions belong in
443
+ `tests/helpers/ooxml-assertions.mjs`; XML-provider setup belongs in
444
+ `tests/setup-xml-provider.mjs`.
445
+
446
+ Measure serial and parallel execution on the same machine with:
447
+
448
+ ```powershell
449
+ npm run benchmark:tests
450
+ ```
451
+
452
+ The benchmark performs paired warmups/runs and writes its JSON result to
453
+ `tmp/benchmarks/test-runner-latest.json`. The checked Node 24/Windows x64 run
454
+ reduced median time from 18.02 seconds to 7.96 seconds (55.80%). Twenty
455
+ consecutive four-worker runs completed without a failure.
456
+
457
+ For function-level gap work, run:
458
+
459
+ ```powershell
460
+ npm run test:coverage
461
+ npm run coverage:gaps
462
+ npm run coverage:gaps -- --json
463
+ ```
464
+
465
+ The coverage command emits both the text summary and detailed Istanbul JSON.
466
+ The gap report includes only runtime production roots, lists each uncovered
467
+ function with its file and declaration line, assigns the Phase 3 P0/P1/P2
468
+ priority, and fails if a targeted file drops below the checked covered-function
469
+ or covered-branch baseline in `tests/coverage-data/phase3-baseline.json`. Because V8
470
+ discovers new branch sites when a formerly cold function first executes, review
471
+ covered counts and behavior assertions alongside percentages. Do not add ignore
472
+ annotations or call private code merely to improve a score; classify a retained
473
+ gap with a reachability or environment reason.
474
+
475
+ For an engine regression:
476
+
477
+ 1. Add the smallest fixed case that reproduces the bug and asserts the exact
478
+ result or structured error.
479
+ 2. Assert both sides of tracked changes: accepting must produce the requested
480
+ text and rejecting must restore the original text exactly.
481
+ 3. Assert structural invariants with `validateRedlineOoxml` and the shared OOXML
482
+ helpers. Do not use a whitespace-collapsing reader as the oracle.
483
+ 4. If engine behavior changed, add the same shape to the deterministic fuzz
484
+ corpus so nearby inputs are exercised too.
485
+ 5. Run `npm test`, isolation, types, and lint.
486
+
487
+ For a mixed attachment regression, also run the content through
488
+ `planStructuredReplacement(...)`. Assert the declared block sequence, reject
489
+ malformed table grammar, and verify that the result contains real `w:tbl`,
490
+ heading styles, positive list numbering IDs, valid non-nested revisions, and
491
+ independent Accept/Reject outcomes. Do not treat absence of literal pipe text as
492
+ sufficient proof of a table.
493
+
494
+ ## Synthetic Microsoft Word tests
495
+
496
+ Run only one Word job at a time. `scripts/word-com-suite.ps1` and
497
+ `scripts/word-com-corpus-suite.ps1` begin by force-killing every windowless
498
+ `WINWORD` process to clear orphans from aborted runs, which also kills the
499
+ invisible Word instance of any other COM job running concurrently.
500
+
501
+ The runtime library contains no COM or Word dependency. Word automation exists
502
+ only in Windows development scripts and is intentionally excluded from package
503
+ runtime paths.
504
+
505
+ The synthetic catalogue is `tests/fixtures/word-task-cases.mjs`. During
506
+ `npm run test:word`:
507
+
508
+ 1. `scripts/export-validation-fixtures.mjs` applies each operation, runs the
509
+ library's structural validator, checks any case-specific `requiredElements`,
510
+ and builds a minimal `.docx` under ignored `tmp/word-validation/` storage.
511
+ 2. Expected Accept All and Reject All text is derived from the test's edit
512
+ intent, not from the library's own revision-management functions.
513
+ 3. `scripts/word-com-differential.ps1` opens each fixture through desktop Word
514
+ without a repair dialog and confirms Word sees at least one revision.
515
+ 4. Word accepts every revision in one fresh document and rejects every revision
516
+ in another. Both resulting texts are compared with the intent-derived
517
+ expectations.
518
+
519
+ To add a synthetic Word case:
520
+
521
+ 1. Add a unique lowercase-hyphenated entry to `WORD_TASK_CASES` with
522
+ `category: 'legal'` or `'administrative'`, a distinct `task`, and `original`
523
+ and `modified` text.
524
+ 2. Use `sourceDocumentXml` when the case needs bookmarks, hyperlinks, tables,
525
+ content controls, prior revisions, or another deliberate structure.
526
+ 3. Add `requiredElements`, such as `{ bookmarkStart: 1, bookmarkEnd: 1 }`, when
527
+ retaining a structure is part of the claim. These are minimum namespace-aware
528
+ element counts checked before Word runs.
529
+ 4. Supply `expectedAcceptedText` and `expectedRejectedText` when the source is a
530
+ multi-paragraph document or Word exposes structural separators such as table
531
+ row boundaries.
532
+ 5. Run `node tests/word_task_catalog_tests.mjs`, then `npm run test:word` on a
533
+ Windows machine with desktop Word installed.
534
+
535
+ ### Related-part fixture schema
536
+
537
+ The script-only packager supports optional numbering plus comments, footnotes,
538
+ endnotes, headers, footers, and external hyperlinks. Add them to a catalogue
539
+ case through `packageParts`:
540
+
541
+ ```js
542
+ {
543
+ sourceDocumentXml,
544
+ packageParts: {
545
+ commentsXml,
546
+ footnotesXml,
547
+ endnotesXml,
548
+ headers: [{
549
+ partName: 'header1.xml',
550
+ relationshipId: 'rIdHeader1',
551
+ xml: headerXml
552
+ }],
553
+ footers: [{
554
+ partName: 'footer1.xml',
555
+ relationshipId: 'rIdFooter1',
556
+ xml: footerXml
557
+ }],
558
+ externalHyperlinks: [{
559
+ relationshipId: 'rIdPolicy',
560
+ target: 'https://example.com/policy'
561
+ }]
562
+ }
563
+ }
564
+ ```
565
+
566
+ `createCommentsPart`, `createNotesPart`, and `createHeaderFooterPart` in
567
+ `tests/fixtures/word-package-parts.mjs` provide small escaped constructors for
568
+ the common XML parts. Header/footer `partName` and `relationshipId` values have
569
+ deterministic defaults, but explicit values make the corresponding
570
+ `w:headerReference`, `w:footerReference`, or `w:hyperlink` easier to audit.
571
+
572
+ Before ZIP emission, the packager rejects malformed XML, duplicate relationship
573
+ IDs or part names, missing relationship targets, undefined comment/note IDs,
574
+ and note parts without separator IDs `-1` and `0`. It generates the required
575
+ content-type overrides and document relationships. Every supplied related part
576
+ is compared byte-for-byte with the packaged entry, recorded as SHA-256 in the
577
+ case sidecar, and rechecked by the Word differential before opening the DOCX.
578
+ These helpers remain under `scripts/` and `tests/`; none are shipped through the
579
+ runtime entry point.
580
+
581
+ List cases may declare `expectedAcceptedListStrings` / `expectedRejectedListStrings` (per-paragraph
582
+ `ListFormat.ListString`) and `expectedAcceptedListGroupSizes` (per-paragraph
583
+ `ListFormat.List.ListParagraphs.Count`, 0 for a non-list paragraph). The group sizes tell one list from two lists
584
+ that merely read `A.` and `B.` through start values (R2).
585
+
586
+ ### What the automated Word differential proves
587
+
588
+ The Word process is real desktop Microsoft Word, but it is driven invisibly
589
+ through COM. For each fixture, the script:
590
+
591
+ 1. starts `Word.Application` with alerts and the window disabled;
592
+ 2. opens the generated package with `OpenNoRepairDialog`;
593
+ 3. fails if Word cannot open it or sees zero tracked revisions;
594
+ 4. accepts all revisions and reads `Document.Content.Text`;
595
+ 5. closes without saving, reopens the untouched fixture, rejects all revisions,
596
+ and reads the text again; and
597
+ 6. compares both results with expectations that were not calculated by this
598
+ library's own accept/reject implementation.
599
+
600
+ Where the case has a source package the script also compares the paragraph count after
601
+ Reject All with the source package's paragraph count and fails on a mismatch, so
602
+ a leftover or extra paragraph mark is caught even when the text matches.
603
+
604
+ Accept All and Reject All are issued through `Document.Revisions.AcceptAll()` /
605
+ `RejectAll()` (per-range `Revisions` for header stories). Do not use
606
+ `Document.AcceptAllRevisions()` / `RejectAllRevisions()` in COM scripts: when
607
+ the last body paragraph has a tracked-inserted mark they can leave that mark and
608
+ an extra empty paragraph behind, unlike the ribbon command and Word users.
609
+
610
+ Exact comparison is the default. The harness removes Word's terminal paragraph
611
+ mark, normalizes CR/LF representation, and removes the characters Word exposes
612
+ as table-cell and footnote/endnote reference boundaries; case-specific normalized comparison requires an
613
+ explicit reason. Documents are never saved by the automated differential.
614
+
615
+ For synthetic fixtures, expectations come directly from `original`, `modified`,
616
+ and any explicit full-document expectations in the case. For SuperDoc fixtures,
617
+ Word first reads the declared story from the original pinned source document;
618
+ every target in a single or multi-change scenario must occur exactly once, and
619
+ the accepted expectation is formed by applying those replacements to Word's own
620
+ source text. Header scenarios use Word's header stories rather than body text.
621
+ The corpus packager hashes every package part other than the one intentionally
622
+ replaced by the scenario.
623
+
624
+ This proves that Word can consume the package, recognizes the revision markup,
625
+ and resolves Accept All and Reject All to the intended text. It does **not**
626
+ prove that the document looks right on the page. `Content.Text` cannot detect
627
+ bad pagination, awkward revision balloons, shifted table widths, broken tab
628
+ alignment, font substitution, changed list indentation, clipped headers,
629
+ visually stale fields, or a comment/footnote marker that is technically present
630
+ but poorly placed.
631
+
632
+ ### Microsoft Word visual review guide
633
+
634
+ Human visual review complements—not replaces—the automated differential. The
635
+ automated differential proves Word revision semantics by comparing text after
636
+ Accept All and Reject All, but this review checks the layout, typography, and
637
+ interaction details that `Document.Content.Text` cannot see.
638
+
639
+ Review is required for:
640
+
641
+ - every new or materially changed synthetic Word case;
642
+ - the first case for a new structure, operation type, or related package part;
643
+ - any engine change affecting reconstruction, formatting, lists, tables,
644
+ fields, tabs/breaks, comments, notes, headers/footers, or revision metadata;
645
+ - any case that needs normalized rather than exact text comparison; and
646
+ - any automated Word failure whose cause is not immediately textual.
647
+
648
+ #### Prepare the review set
649
+
650
+ Run the automated lanes first:
651
+
652
+ ```powershell
653
+ npm run test:word
654
+ npm run test:corpus:word
655
+ npm run report:word:coverage
656
+ npm run review:word:prepare -- --cycle=0
657
+ ```
658
+
659
+ The last command writes a pending review manifest under ignored
660
+ `tmp/word-manual-review/` containing changed catalogue families, the rotating
661
+ 20% synthetic release sample, and legal/administrative corpus representatives.
662
+ It only prepares the selection: it never records a visual pass or human
663
+ sign-off. Use a new cycle number for each release rotation.
664
+
665
+ Synthetic documents are generated under `tmp/word-validation/`; reviewed
666
+ SuperDoc results are under `tmp/superdoc-word-fixtures/`. Do not commit generated
667
+ or downloaded `.docx` files.
668
+
669
+ Selection rules:
670
+ - every new or changed case;
671
+ - every case required by the triggers above;
672
+ - at least 20% of unchanged synthetic cases, rotating from the prior release (so
673
+ every retained synthetic case receives a human review over five release cycles);
674
+ - at least one legal and one administrative SuperDoc result; and
675
+ - representative list, table, formatted-run, and anchor/field/content-control
676
+ structures.
677
+
678
+ A major release or a change to package assembly requires a full visual sweep of
679
+ affected structure families.
680
+
681
+ Record the installed Word version and build from **File → Account → About Word**.
682
+ Differences in rendering can be version-specific.
683
+
684
+ #### Configure Word
685
+
686
+ For the tracked-change inspection:
687
+
688
+ 1. Open the generated fixture directly in desktop Word.
689
+ 2. On **Review**, select **All Markup**.
690
+ 3. Under **Show Markup**, enable insertions/deletions, formatting, comments, and
691
+ all reviewers relevant to the case.
692
+ 4. Use the expected balloons/inline display for the document and enable
693
+ paragraph marks when checking whitespace, tabs, breaks, lists, and empty
694
+ paragraphs.
695
+ 5. Do not overwrite the generated fixture. Work on disposable copies if a
696
+ saved accepted or rejected view is useful.
697
+
698
+ #### Inspect each case (3-view checklist)
699
+
700
+ ##### 1. Tracked-change view
701
+
702
+ - The document opens without a repair, conversion, or unreadable-content prompt.
703
+ - Word shows the expected revision count and author attribution.
704
+ - Insertions and deletions are anchored at the intended words or paragraph
705
+ marks; a small edit has not become an unexplained whole-paragraph rewrite.
706
+ - Untargeted text and surrounding revisions remain unchanged.
707
+ - Existing bold, italic, underline, highlighting, fonts, styles, and language
708
+ settings remain visually consistent.
709
+ - Spaces, tabs, manual breaks, paragraph spacing, indentation, and alignment
710
+ look intentional with formatting marks visible.
711
+ - Lists retain numbering, levels, continuation, indentation, and marker style.
712
+ - Tables retain widths, borders, merged cells, row heights, and alignment.
713
+ - Bookmarks, hyperlinks, fields, content controls, comments, and note references
714
+ remain in the correct visible location and still behave when activated.
715
+ - Headers, footers, section boundaries, page breaks, and pagination remain
716
+ stable around the edit.
717
+ - Revision balloons and comment balloons point to the correct content and do
718
+ not obscure or displace unrelated layout unexpectedly.
719
+
720
+ ##### 2. Accept All view
721
+
722
+ On a disposable copy, choose **Accept All Changes** and verify:
723
+
724
+ - The resulting visible text expresses the intended edit.
725
+ - No deletion residue, empty revision wrapper, unexpected blank line, or stale
726
+ formatting remains.
727
+ - Lists, tables, fields, links, comments, notes, and page layout still work.
728
+ - Untargeted content is visually unchanged.
729
+
730
+ Close the copy without replacing the generated fixture.
731
+
732
+ ##### 3. Reject All view
733
+
734
+ Reopen a fresh copy, choose **Reject All Changes**, and verify:
735
+
736
+ - The original visible text and formatting are restored.
737
+ - Original list numbering, table layout, fields, anchors, and pagination return.
738
+ - No content introduced by the edit remains.
739
+
740
+ #### Record the visual review result
741
+
742
+ Keep the review report with release-validation artifacts. Screenshots may be
743
+ stored under ignored `tmp/word-manual-review/<date>/` when useful, but do not
744
+ commit corpus document images or document contents without checking rights and
745
+ sensitivity.
746
+
747
+ Suggested report template:
748
+
749
+ ```markdown
750
+ # Word visual review — <release/date>
751
+
752
+ - Reviewer:
753
+ - Review date:
754
+ - Word version/build:
755
+ - Automated synthetic result:
756
+ - Automated corpus result:
757
+ - Review type: Human sign-off | AI visual preflight
758
+
759
+ | Case | Why selected | All Markup | Accept All | Reject All | Result | Notes |
760
+ |---|---|---|---|---|---|---|
761
+ | example-case | New table structure | Pass | Pass | Pass | Pass | No layout shift |
762
+
763
+ ## Failures or follow-ups
764
+
765
+ - None.
766
+ ```
767
+
768
+ A **Pass** requires all three views to pass. Record **Fail** if rendering or
769
+ interaction is wrong even when automated text comparison passes. Turn a failure
770
+ into a fixed regression case when possible; otherwise record the exact harness
771
+ or Word-version limitation in the active reliability plan.
772
+
773
+
774
+ ### AI-assisted Word visual preflight
775
+
776
+ Generate repeatable three-view PDF evidence for every layout-sensitive
777
+ synthetic fixture with:
778
+
779
+ ```powershell
780
+ npm run test:word:visual
781
+ ```
782
+
783
+ The command writes PDFs and `manifest.json` under ignored
784
+ `tmp/word-visual-review/rendered/` storage. It verifies that Word opened each
785
+ fixture and produced a non-empty, paginated rendering, but leaves the manifest
786
+ certification and every visual judgment pending. To render only named cases,
787
+ invoke `scripts/word-com-visual-suite.ps1 -Case case-one,case-two` directly.
788
+
789
+ An AI agent with Windows computer control may also open the generated fixtures
790
+ in the installed desktop Word application, switch among All Markup, Accept All,
791
+ and Reject All views, capture screenshots, and inspect them for visible
792
+ regressions. This is a useful intermediate oracle because it exercises the same
793
+ real UI a person sees rather than only `Document.Content.Text`.
794
+
795
+ Run an AI visual preflight:
796
+
797
+ - after a phase that changes reconstruction, formatting, list/table behavior,
798
+ package assembly, or revision display;
799
+ - after every five to ten new Word fixtures;
800
+ - for all newly introduced structure families;
801
+ - when automated Word passes but the XML change is unusually broad; and
802
+ - before asking a human to perform the release sample, so obvious failures are
803
+ found first.
804
+
805
+ For the focused real-document additions, run:
806
+
807
+ ```powershell
808
+ npm run test:corpus:word:visual
809
+ ```
810
+
811
+ This first runs the exact Word differential, then writes 120 PDFs and a pending
812
+ manifest under ignored `tmp/superdoc-word-visual-review/rendered/` storage: three
813
+ views for 22 list-focused cases, 16 table-focused cases, and two page-header
814
+ cases. The long-document set includes 6,000+ word council minutes,
815
+ an 8,000+ word zoning resolution, and a 59,000+ word prospectus with 180 tables.
816
+ Each invocation packages its fixtures in a process-specific directory so
817
+ a Word process left behind by an RPC disconnect cannot lock the next run's
818
+ inputs. The runner ignores `RPC_E_DISCONNECTED` during post-export cleanup and
819
+ restarts Word once when a disconnect interrupts an actual render. After Word
820
+ finishes, the command rebuilds `docs/test-comparison-dashboard.html` from that
821
+ exact process-specific fixture directory. The `Verified cached ...` messages
822
+ refer only to the pinned source-document downloads; edited comparison DOCX
823
+ files are regenerated on every run.
824
+
825
+ The AI should use the same selection rules and checklist as a human, inspect at
826
+ least the changed cases plus representative legal and administrative samples,
827
+ and save screenshots under ignored `tmp/word-manual-review/<date>/` storage.
828
+ Its report must identify itself as **AI visual preflight**, record the Word
829
+ version/build when available, list the exact cases and views inspected, and
830
+ separate observed facts from uncertain visual judgments.
831
+
832
+ AI visual review is advisory. It can catch missing or misplaced revisions,
833
+ unexpected whole-paragraph markup, obvious pagination shifts, broken tables,
834
+ lost indentation, clipped headers, and visibly misplaced anchors. It may miss
835
+ subtle font metrics, accessibility issues, field behavior that requires domain
836
+ knowledge, or a legally meaningful formatting distinction. An AI pass does not
837
+ satisfy the human release sign-off, and sensitive/private documents must not be
838
+ opened for AI review without explicit authorization.
839
+
840
+ ### Multimodal LLM visual inspection (real-document spot checks)
841
+
842
+ Evaluating subtle visual defects—such as shifted table borders, unnatural run
843
+ breaks, font substitution, clipping headers, or awkward revision balloons—requires
844
+ interpreting rendered document pages visually rather than relying solely on
845
+ string assertions or raw XML checks. A multimodal Large Language Model (MLLM)
846
+ with vision capabilities can review rendered page images across the three
847
+ revision views (`allMarkup`, `acceptAll`, and `rejectAll`) and detect regressions
848
+ that are invisible to plain-text diffs.
849
+
850
+ #### Why multimodal inspection is kept separate
851
+
852
+ Passing high-resolution, multi-page document images through a vision-capable LLM
853
+ is **expensive, computationally intensive, and time-consuming**:
854
+
855
+ - Each document scenario produces three full-document rendering views (often 1
856
+ to 20+ pages per view).
857
+ - Sending dozens of high-resolution page images consumes significant token
858
+ budgets and model context.
859
+ - Latency per case is orders of magnitude higher than programmatic XML parsing
860
+ or COM automation.
861
+
862
+ For these reasons, multimodal visual inspection **forms a separate check from the
863
+ rest of the test suite**. It is never executed on every commit, local test run, or
864
+ full batch pipeline.
865
+
866
+ #### Sampling strategy: Random real-document spot checks
867
+
868
+ Instead of exhaustive or continuous execution, multimodal visual inspection is
869
+ conducted as a **random spot check of real documents here and there only**:
870
+
871
+ 1. **Focus on authentic real documents:** Spot checks prioritize the
872
+ [SuperDoc real-document corpus](#superdoc-real-document-word-tests) rather than
873
+ simple synthetic fixtures. Real documents contain organic complexities—such
874
+ as multi-column headers, nested bullet styles, non-uniform table borders, and
875
+ mixed-font legal numbering—where visual layout errors are most likely to hide.
876
+ 2. **Small random sampling:** A typical visual spot-check run selects a random
877
+ sample of **2 to 5 real document scenarios** (for example, one administrative
878
+ board agenda, one municipal minutes multi-table document, and one complex
879
+ zoning ordinance).
880
+ 3. **Execution cadence:** Spot checks are run on-demand:
881
+ - periodically during active engine refactoring;
882
+ - when introducing major changes to run splitting, table reconciliation, or
883
+ list reconstruction; or
884
+ - as an advisory audit prior to a release cycle.
885
+
886
+ #### Multimodal inspection workflow
887
+
888
+ When performing a multimodal visual spot check:
889
+
890
+ 1. **Render target pages to images:** Render the selected scenario in Word to
891
+ PDFs using `scripts/word-com-corpus-visual-suite.ps1 -Case <scenario-name>`,
892
+ then convert the relevant pages (or contact sheets) to PNG images using the
893
+ visual inspection helper:
894
+ ```powershell
895
+ node scripts/inspect-visual-evidence.mjs --contact-sheets
896
+ ```
897
+ 2. **Submit to the multimodal model:** Provide the vision model with the side-by-side
898
+ page renders of `allMarkup`, `acceptAll`, and `rejectAll` along with the target
899
+ diff and context.
900
+ 3. **Targeted visual checklist:** Prompt the model to evaluate specific layout risks:
901
+ - **Table cell integrity:** Did column widths shift? Are table borders intact?
902
+ Did multi-line cell text collapse or overlap?
903
+ - **Typography & Font metrics:** Did any run revert to default font size (e.g.
904
+ dropping from 14pt Georgia to 12pt Normal)? Did bold, italic, or underline
905
+ bleed into neighboring text?
906
+ - **List alignment:** Are bullet indents consistent with existing items? Are
907
+ there ghost markers or blank list rows?
908
+ - **Page apparatus:** Are headers, footers, and page numbers in their expected
909
+ margins without clipping or wrapping anomalies?
910
+ 4. **Triaging findings:** If the multimodal model flags a visual abnormality,
911
+ treat it as an advisory signal:
912
+ - Verify the defect manually in Microsoft Word.
913
+ - Trace the visual defect to its underlying OOXML cause (per the
914
+ [visual failure diagnosis guidelines](#diagnose-visual-failures-in-the-generated-ooxml-first)).
915
+ - Add a minimal semantic regression test in
916
+ `tests/visual_failure_regression_tests.mjs` to ensure the issue is permanently
917
+ prevented in the automated suite (`npm test`).
918
+
919
+ ## SuperDoc real-document Word tests
920
+
921
+ The corpus lane references selected documents from SuperDoc's
922
+ [docx-corpus](https://docxcorp.us/) under ODC-By 1.0. Source documents are never
923
+ committed. `tests/corpus/superdoc-english-legal-administrative.json` pins each
924
+ reviewed source and observed SHA-256; `tests/corpus/superdoc-word-scenarios.json`
925
+ defines named deterministic scenarios and records the structural coverage they
926
+ add. A source may support multiple scenarios when each one adds a distinct
927
+ behavioral claim.
928
+
929
+ To add a corpus case:
930
+
931
+ 1. Select an English legal or administrative document that adds a structure or
932
+ task not already represented. Review its content and record why it is
933
+ suitable.
934
+ 2. Add an explicit pinned reference and observed hash to the manifest. Do not
935
+ use a floating or bulk corpus download.
936
+ 3. Fetch only that reference with
937
+ `npm run corpus:fetch:superdoc -- --id <pinned-id>`.
938
+ 4. Inspect the document in Word and choose a target that occurs exactly once in
939
+ Word's source text.
940
+ 5. Add the deterministic operation, shape, coverage labels, and review note to
941
+ `superdoc-word-scenarios.json`. Use `key` plus `sourceId` when adding another
942
+ scenario for an already pinned source. Use `operations` for a multi-change
943
+ atomic batch, or `part: "word/header<N>.xml"` for a page-header revision.
944
+ 6. Run `npm test` for manifest/catalogue checks and `npm run test:corpus:word`
945
+ for package hashing plus the Word Accept All/Reject All differential.
946
+
947
+ The corpus packager starts from the original `.docx`, replaces only the declared
948
+ revision part (`word/document.xml` by default, or a named header part), and
949
+ verifies every untouched ZIP part byte-for-byte before Word opens the result.
950
+ The Word oracle derives multi-change expectations by applying each independently
951
+ declared replacement to Word's source text and reads header-story text separately
952
+ ## Release validation and independent oracles
953
+
954
+ This package works on OOXML strings and intentionally leaves `.docx` zip
955
+ packaging to consumers (release *tooling* assembles minimal `.docx` fixtures
956
+ with a script-local zip writer; the published library has no zip dependency).
957
+
958
+ The test suite verifies the accept/reject round-trip invariant using the
959
+ library's own transforms. Because a shared misconception between the
960
+ generator and the resolver would pass those tests silently, release
961
+ validation adds **independent oracles**: Microsoft Word, LibreOffice, and
962
+ the ECMA-376 schemas.
963
+
964
+ ### Pre-release automated check sequence
965
+
966
+ ```bash
967
+ npm test # includes tests/roundtrip_fuzz_tests.mjs (seeded, deterministic)
968
+ npm run test:isolation
969
+ npm run check:types
970
+ npm run lint
971
+ ```
972
+
973
+ The fuzz harness generates random paragraph structures and edits, asserting the
974
+ round-trip invariant plus `validateRedlineOoxml` on each case. Tune or reproduce with:
975
+
976
+ ```bash
977
+ FUZZ_SEED=<seed> FUZZ_ITERATIONS=<n> node tests/roundtrip_fuzz_tests.mjs
978
+ ```
979
+
980
+ A failing case prints its exact reproduction command.
981
+
982
+ ### Reject-roundtrip matrix
983
+
984
+ `tests/reject_roundtrip_matrix_tests.mjs` (generators in
985
+ `tests/helpers/operation-generators.mjs`, fixtures in
986
+ `tests/helpers/reject-roundtrip-fixtures.mjs`) applies seeded, generated
987
+ operations (word/sentence/paragraph/range replace, insert-after, delete,
988
+ `replacements`, format, paragraph-format, plain-to-list, last-paragraph) to the
989
+ nested-lists fixture and synthetic documents (hyperlinks, breaks/tabs, simple
990
+ fields, tables, multi-author revisions, empty list items). Each case runs
991
+ through both `applyOperationsToDocumentXml` and `openDocx` with default options.
992
+ A `REVISION_FIDELITY_VIOLATION` refusal, a rejected view that differs from the
993
+ source, a missing intended accepted text, or bare/packaged divergence fails the
994
+ run; other refusals are counted as skipped. The default run (fixed seed, 150
995
+ cases, about 3 s) is part of `npm test`. Extended lane and replay:
996
+
997
+ ```bash
998
+ FUZZ_SEED=<seed> MATRIX_ITERATIONS=<n> node tests/reject_roundtrip_matrix_tests.mjs
999
+ ```
1000
+
1001
+ `MATRIX_VERBOSE=1` prints each skipped refusal; `MATRIX_MAX_REPORTED=<n>` raises
1002
+ the failure print limit. Known unfixable cases go in
1003
+ `tests/fixtures/reject-roundtrip-allowlist.json` (`{"entries":[{"fixture","kind","path","reason"}]}`);
1004
+ the file does not exist while the allowlist is empty.
1005
+
1006
+ The nightly `matrix-extended` job in `.github/workflows/validation.yml` runs 1000 cases with a date-derived `FUZZ_SEED`;
1007
+ a failure prints the seed, fixture, kind, operation and verifier violations. `tests/list_parser_diagnostics_tests.mjs` asserts that the
1008
+ list conversion paths emit no `console.warn`/`console.error` parser diagnostics under the pure-JS fallback and the explicit xmldom provider.
1009
+
1010
+ ### Word revision semantics matrix
1011
+
1012
+ `tests/fixtures/word-revision-semantics/` holds small packages (`cases.json`
1013
+ lists them) covering each paragraph-mark revision shape, and
1014
+ `expectations.json`, what desktop Word 16 produced on Reject All and Accept All
1015
+ for each. `tests/word_revision_semantics_tests.mjs` checks the projection and
1016
+ the library's reject/accept against those recorded results and needs no Word.
1017
+ `KNOWN_DIVERGENCES` in that test lists current differences from Word; the suite
1018
+ fails when a listed divergence disappears or an unlisted one appears.
1019
+
1020
+ Regenerate the packages and re-record expectations after changing the case list
1021
+ or when Word's behavior may have changed (requires desktop Word; use `pwsh`,
1022
+ PowerShell 7, because Windows PowerShell 5.1 hangs in `SaveAs2` over COM here):
1023
+
1024
+ ```bash
1025
+ node scripts/generate-word-revision-semantics-matrix.mjs
1026
+ pwsh -NoProfile -ExecutionPolicy Bypass -File scripts/word-revision-semantics-matrix.ps1
1027
+ ```
1028
+
1029
+ Review the `expectations.json` diff before committing it.
1030
+
1031
+ ### Revision fidelity verification in tests
1032
+
1033
+ Every tracked operation is verified after it is applied (default `enforce`).
1034
+ Diagnostic lanes may override the default without code changes by setting
1035
+ `DOCX_REDLINE_REVISION_FIDELITY=warn` (apply and report) or `off`. An explicit
1036
+ `verifyRevisionFidelity` option or CLI `--revision-fidelity` flag wins over the
1037
+ environment variable. Use it only to investigate a refusal; the reject
1038
+ round-trip matrix and `tests/revision_fidelity_verifier_tests.mjs` run with the
1039
+ default.
1040
+
1041
+ ### Runtime guardrail
1042
+
1043
+ `validateRedlineOoxml(oxml)` (exported from `index.js`) runs structural
1044
+ invariants at runtime and returns `{ valid, issues }`. Downstream packagers
1045
+ should call it before writing engine output into `word/document.xml`.
1046
+
1047
+ ### Export fixtures
1048
+
1049
+ ```bash
1050
+ node scripts/export-validation-fixtures.mjs
1051
+ ```
1052
+
1053
+ Writes to `tmp/validation-docx/`, per case:
1054
+
1055
+ - `<name>.document.xml` — generated `word/document.xml` payload
1056
+ - `<name>.docx` — minimal assembled package
1057
+ - `<name>.expected.json` — expected accept-all / reject-all plain text,
1058
+ derived from edit *intent* (not from this library's transforms), so
1059
+ external consumers act as independent oracles.
1060
+
1061
+ ### Schema validation (ECMA-376 transitional XSD)
1062
+
1063
+ ```bash
1064
+ node scripts/export-validation-fixtures.mjs
1065
+ bash scripts/validate-fixtures-xsd.sh
1066
+ ```
1067
+
1068
+ Downloads (and caches in `.cache/ooxml-schemas/`) the transitional
1069
+ wordprocessingml schemas from ECMA-376 Part 4, patches the `xml:` namespace
1070
+ import to resolve offline, and validates every `*.document.xml` fixture with
1071
+ `xmllint`. Requires `curl`, `unzip`, and `xmllint` (`libxml2-utils` on
1072
+ Debian/Ubuntu; available on Windows via conda/msys).
1073
+
1074
+ ### LibreOffice consumer check
1075
+
1076
+ ```bash
1077
+ cd tmp/validation-docx
1078
+ soffice --headless --convert-to pdf --outdir converted *.docx
1079
+ ```
1080
+
1081
+ Verifies that a second independent OOXML consumer parses and converts the
1082
+ fixtures without error.
1083
+
1084
+ ### Continuous validation
1085
+
1086
+ `.github/workflows/validation.yml` runs nightly (and on demand via
1087
+ `workflow_dispatch`):
1088
+
1089
+ 1. **xsd-schema** — exports fixtures and validates them against the
1090
+ ECMA-376 transitional `wml.xsd`.
1091
+ 2. **libreoffice** — exports fixtures and converts them with headless
1092
+ LibreOffice.
1093
+ 3. **fuzz-extended** — 20,000 fuzz round-trip cases with a date-derived
1094
+ seed, so every night explores new inputs. A failure log includes the
1095
+ exact `FUZZ_SEED` reproduction command.
1096
+
1097
+ The Word differential check stays manual because it requires desktop Word;
1098
+ run it before tagging a release.
1099
+
1100
+ ### JavaScript coverage baselines
1101
+
1102
+ ```bash
1103
+ npm run test:coverage
1104
+ ```
1105
+
1106
+ The command runs the complete JavaScript test suite under c8 and prints a
1107
+ per-file report. Coverage answers "which implementation paths did the automated
1108
+ JavaScript tests execute?" It does not prove that executed paths are correct,
1109
+ that generated OOXML opens in Word, or that the real-document corpus is broad
1110
+ enough. Treat it as a map for finding thinly tested code; Word, schema,
1111
+ LibreOffice, fuzz, and corpus checks provide different evidence.
1112
+
1113
+ Historical reference snapshots:
1114
+
1115
+ - **Initial Phase 7 baseline (2026-08-29)**:
1116
+ | Metric | Coverage |
1117
+ |---|---:|
1118
+ | Lines / statements | 79.57% |
1119
+ | Functions | 80.07% |
1120
+ | Branches | 69.22% |
1121
+
1122
+ - **Post-Phase-5 snapshot (2026-08-30)**:
1123
+ 79.96% lines/statements, 80.95% functions, and 69.52% branches.
1124
+
1125
+ - **Post-Phase-5 snapshot (2026-09-04)**:
1126
+ 89.81% statements/lines, 93.04% functions, and 77.34% branches repo-wide.
1127
+ The Node surface reports 99.61% statements/lines; `document-inspection.js`
1128
+ reports 97.64% statements/lines and 100% functions; `paragraph-text.js` reports
1129
+ 100% statements/lines/functions.
1130
+
1131
+ Preserve snapshots so progress is visible over time rather than presenting
1132
+ coverage as a single pass/fail quality score.
1133
+
1134
+ ## Choosing where a new test belongs
1135
+
1136
+ - A fixed bug always gets an automated regression.
1137
+ - A broad engine rule also gets deterministic fuzz coverage.
1138
+ - A claim about what Word renders or accepts gets a synthetic Word case.
1139
+ - A claim involving package complexity or realistic authoring structures gets
1140
+ a reviewed corpus case.
1141
+ - A structural XML rule should be checked by runtime validation and, where
1142
+ practical, the ECMA-376 XSD lane.
1143
+
1144
+ Keep expectations independent of the code under test. In particular, never use
1145
+ this library's Accept/Reject helpers to calculate the expected result for the
1146
+ Word differential.