@ansonlai/docx-redline-js 0.5.4 → 0.6.1

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 (55) hide show
  1. package/AGENTS.md +82 -697
  2. package/ARCHITECTURE.md +13 -1
  3. package/CHANGELOG.md +8 -0
  4. package/README.md +177 -45
  5. package/core/paragraph-targeting.js +14 -2
  6. package/dist/docx-redline-js.esm.js +184 -51
  7. package/dist/docx-redline-js.esm.js.map +3 -3
  8. package/dist/docx-redline-js.esm.min.js +77 -77
  9. package/dist/docx-redline-js.esm.min.js.map +4 -4
  10. package/docs/AGENT_FAST_START.md +59 -0
  11. package/docs/AGENT_KNOWLEDGE_BASE.md +878 -0
  12. package/docs/SKILL_AUTHORING.md +126 -0
  13. package/docs/TESTING.md +35 -1
  14. package/docs/schemas/document-operations.schema.json +5 -1
  15. package/docs/validation-reports/2026-09-12-agent-cli-discovery-baseline.md +56 -0
  16. package/docs/validation-reports/2026-09-12-agent-protocol-rollout.md +86 -0
  17. package/docs/validation-reports/2026-09-13-agent-cli-efficiency-rollout.md +86 -0
  18. package/engine/oxml-engine.js +80 -13
  19. package/engine/run-builders.js +5 -15
  20. package/index.d.ts +28 -3
  21. package/node/cli-help.js +209 -0
  22. package/node/cli.js +323 -65
  23. package/node/docx-document.js +120 -69
  24. package/node/index.d.ts +6 -2
  25. package/package.json +15 -3
  26. package/scripts/generate-cross-author-slicing-fixtures.ps1 +25 -25
  27. package/services/batch-operation-orchestrator.js +215 -120
  28. package/services/document-inspection.js +89 -11
  29. package/services/document-operation-applier.js +52 -34
  30. package/services/document-operation-contract.js +10 -6
  31. package/services/document-operation-mutations.js +51 -5
  32. package/services/document-operation-session.js +4 -0
  33. package/services/error-recovery.js +174 -0
  34. package/services/operation-batch-compiler.js +394 -0
  35. package/services/operation-preflight.js +91 -72
  36. package/services/standalone-operation-runner.d.ts +17 -1
  37. package/docs/plans/2026-09-05-structural-revisions-and-fidelity-oracles.md +0 -1669
  38. package/docs/plans/2026-09-08-cross-author-revision-slicing.md +0 -1399
  39. package/docs/plans/completed/2026-03-01-release-0.1.4-design.md +0 -33
  40. package/docs/plans/completed/2026-03-01-release-0.1.4.md +0 -110
  41. package/docs/plans/completed/2026-05-31-architectural changes.md +0 -593
  42. package/docs/plans/completed/2026-08-02-reliability-improvements.md +0 -1155
  43. package/docs/plans/completed/2026-08-30-reliability-testing-improvements.md +0 -488
  44. package/docs/plans/completed/2026-09-01-performance-and-complexity-reduction.md +0 -669
  45. package/docs/plans/completed/2026-09-03-agent-friendly-document-workflows.md +0 -427
  46. package/docs/plans/completed/2026-09-04-comment-anchor-and-cli-reliability.md +0 -519
  47. package/docs/plans/completed/PERFORMANCE-CONSOLIDATION.md +0 -69
  48. package/docs/plans/completed/structural-revision-capability-matrix.md +0 -115
  49. package/docs/test-comparison-dashboard.html +0 -4338
  50. package/docs/validation-reports/2026-08-30-phase-1-word-visual-preflight.md +0 -22
  51. package/docs/validation-reports/2026-08-30-phase-2-word-visual-preflight.md +0 -24
  52. package/docs/validation-reports/2026-08-30-phase-3-coverage.md +0 -73
  53. package/docs/validation-reports/2026-09-02-multilevel-bullets-visual-review.md +0 -82
  54. package/docs/validation-reports/2026-09-02-multimodal-visual-samples.md +0 -114
  55. package/docs/validation-reports/2026-09-02-visual-failures-preflight.md +0 -79
@@ -1,1669 +0,0 @@
1
- # Structural Revisions, Stable Addressing, and Fidelity Oracles
2
-
3
- **Status:** Proposed — implementation-ready specification
4
- **Date:** 2026-09-05
5
- **Target releases:** v0.5.0–v1.0.0
6
- **Priority:** Correct accepted/rejected results and document fidelity over throughput or review-pane cosmetics
7
-
8
- ## 1. Purpose
9
-
10
- The package already performs surgical text redlining, paragraph-mark revision
11
- generation, tracked run formatting, structured-content planning, strict
12
- preflight, atomic live-DOM batches, and package validation. The next releases
13
- should harden and extend those capabilities without creating a second mutation
14
- model or weakening existing rollback and targeting guarantees.
15
-
16
- This plan focuses on the remaining gaps:
17
-
18
- 1. Prove what is preserved before expanding mutation behavior.
19
- 2. Give callers precise revision-view addressing without pretending that All
20
- Markup is one unambiguous string.
21
- 3. Make replacement metadata and insertion affinity explicit while preserving
22
- structural containers.
23
- 4. Complete paragraph-boundary and formatting-revision semantics already
24
- partially implemented.
25
- 5. Add package concurrency guards, dependent batch steps, and commit-aware
26
- receipts.
27
- 6. Promote strict application targeting only through a documented major-version
28
- migration.
29
-
30
- The plan does not introduce an editor tree, layout engine, HTML round-trip, or
31
- second document authority. The OOXML package and the current live DOM remain
32
- authoritative.
33
-
34
- ## 2. Current Baseline
35
-
36
- Before implementation, tests and documentation must acknowledge what exists.
37
- Treat these as foundations to extend, not greenfield work:
38
-
39
- - `engine/run-builders.js` creates `w:ins`, `w:del`, paragraph-mark revisions,
40
- and `w:rPrChange` snapshots.
41
- - `services/revision-comment-management.js` accepts and rejects text, move,
42
- paragraph-mark, and property-change revisions.
43
- - `core/paragraph-text.js` already extracts accepted and rejected revision
44
- views.
45
- - `services/operation-preflight.js` is strict by default and reports ambiguous
46
- targets and anchors.
47
- - The Node facade and CLI default to strict target application; the lower-level
48
- application API remains permissive for compatibility.
49
- - `DocumentOperationSession` owns one live DOM and one document-scoped
50
- `RevisionIdAllocator`, with per-operation DOM and allocator savepoints.
51
- - Batch execution uses an immutable start-of-batch target-reference snapshot,
52
- comment-first scheduling, and one final serialization.
53
- - `planStructuredReplacement` validates headings, paragraphs, lists, and tables
54
- before a large structural insertion is applied.
55
- - Word differential, visual, package, round-trip, and real-document tests are
56
- already part of the verification stack.
57
-
58
- ### Required baseline audit
59
-
60
- Create a short capability matrix before feature work begins. For each proposed
61
- behavior, label it `implemented`, `partial`, `missing`, or `intentionally
62
- unsupported`, and link the production code and regression test. At minimum the
63
- matrix must cover:
64
-
65
- - text insertion, deletion, and replacement;
66
- - paragraph insertion, deletion, split, and boundary removal;
67
- - run and paragraph property revisions;
68
- - hyperlinks, bookmarks, comments, fields, SDTs, tabs, breaks, and notes;
69
- - lists and table structural revisions;
70
- - accepted/rejected text extraction;
71
- - target resolution, batch savepoints, and package artifact wiring.
72
-
73
- No phase may duplicate an implemented path under a new option without a written
74
- migration and compatibility reason.
75
-
76
- ## 3. Non-Negotiable Invariants
77
-
78
- ### 3.1 Lifecycle correctness
79
-
80
- For every generated revision:
81
-
82
- - Accept All produces the requested final document.
83
- - Reject All restores the intended source document.
84
- - Selective author acceptance/rejection does not consume another author's work.
85
- - Repeating acceptance or rejection is idempotent.
86
- - Paragraph count, order, list membership, tables, comments, and section
87
- ownership are checked in addition to plain text.
88
-
89
- ### 3.2 Preservation envelope
90
-
91
- “Untouched” has three distinct meanings and must not be conflated:
92
-
93
- 1. **Untouched package entry:** compare the uncompressed entry payload
94
- byte-for-byte. ZIP container bytes are not expected to match after repacking.
95
- 2. **Untouched subtree inside a modified XML part:** compare using a
96
- namespace-aware canonical structural digest.
97
- 3. **Intentionally modified region:** validate against an operation-specific
98
- mutation envelope and lifecycle oracle.
99
-
100
- An operation must declare which XML nodes, ancestor containers, relationships,
101
- content types, and companion parts it is permitted to change. Unexpected drift
102
- outside that envelope fails the test.
103
-
104
- ### 3.3 Structural safety over presentation heuristics
105
-
106
- Bookmarks, comment ranges, hyperlinks, fields, SDTs, proofing markers, drawings,
107
- and revision boundaries must not be moved or deleted merely to influence how
108
- Word groups a change in the Reviewing Pane.
109
-
110
- ### 3.4 Refusal over guessing
111
-
112
- Ambiguous targets, anchors, capture selections, revision views, or structural
113
- ownership must produce structured errors. Fuzzy or first-match fallback is a
114
- legacy compatibility behavior, never an agent recommendation.
115
-
116
- ### 3.5 One mutation authority
117
-
118
- All public package operations continue through the existing session, allocator,
119
- savepoint, artifact, and validation paths. New functionality must not introduce
120
- parallel ID allocation, string-only package mutation, or a second batch state.
121
-
122
- ## 4. Compatibility Matrix
123
-
124
- | Initiative | Initial behavior | Classification | Default change |
125
- |---|---|---|---|
126
- | Fidelity and mutation-envelope oracles | Test-only | Non-breaking | None |
127
- | Explicit accepted/rejected addressing | Additive option; omitted means current behavior | Non-breaking | None |
128
- | Preserving existing revisions during addressed edits | New opt-in policy | Additive, high-risk | No automatic default |
129
- | Paired replacement metadata | Opt-in serialization policy | Observable semantic change | Consider only after Word evidence |
130
- | Explicit insertion affinity | Additive; omitted means legacy resolution | Non-breaking | No implicit `left` default |
131
- | Completed paragraph-boundary handling | New support for previously refused/fallback cases | Internal semantic change | Route only proven cases |
132
- | Author-aware formatting coalescing | Opt-in policy | Observable semantic change | Preserve current behavior initially |
133
- | Package revision token | Additive precondition | Non-breaking | No check when omitted |
134
- | Capture chaining | Additive batch feature | Non-breaking for independent batches | Dependency scheduling only when used |
135
- | Mutation receipts | Additive result field | Non-breaking | Existing result fields remain authoritative |
136
- | Strict lower-level application by default | Default behavior change | Breaking | v1.0.0 only |
137
-
138
- “Non-breaking” means more than an unchanged TypeScript signature. Changes to
139
- generated OOXML, formatting inheritance, accepted/rejected structure, revision
140
- count, author attribution, warnings, or Word presentation are observable and
141
- must be tested and documented.
142
-
143
- ## 5. Milestone 0 — Fidelity Oracle Foundation
144
-
145
- **Target:** v0.5.0, before new mutation semantics
146
- **Classification:** Non-breaking, test and validation infrastructure
147
-
148
- ### 5.1 Part inventory and mutation envelopes
149
-
150
- Add a fixture helper that inventories every package entry and records:
151
-
152
- - entry URI and SHA-256 of its uncompressed bytes;
153
- - content type and relationship ownership;
154
- - XML root namespace and canonical digest for XML parts;
155
- - known volatile metadata that a test intentionally ignores.
156
-
157
- Each operation family declares allowed effects. Examples:
158
-
159
- - Text-only body edit: `word/document.xml` only.
160
- - List creation: document XML, numbering XML, numbering relationship, and
161
- content type when absent.
162
- - Comment creation: document XML, comments XML, comments relationship, and
163
- content type when absent.
164
- - Header edit: selected header part only, plus artifacts explicitly required by
165
- that edit.
166
-
167
- The oracle must fail when an undeclared part changes, even if the resulting DOCX
168
- opens successfully.
169
-
170
- ### 5.2 Canonical subtree comparison
171
-
172
- Do not implement canonicalization by simple prefix replacement. Use a proven XML
173
- canonicalization implementation where possible. If a local OOXML subset is
174
- necessary, it must:
175
-
176
- - compare expanded names, not literal prefixes;
177
- - sort attributes by namespace URI and local name;
178
- - retain child order;
179
- - preserve text, tabs, breaks, and `xml:space` semantics;
180
- - preserve or correctly rewrite QName-valued attributes such as
181
- `mc:Ignorable`;
182
- - distinguish absent properties from explicit off-values when Word does;
183
- - normalize only syntax that is semantically equivalent.
184
-
185
- Untouched subtree identity cannot rely solely on `w14:paraId`: IDs may be absent
186
- or duplicated in real documents. Match nodes using a combination of stable IDs,
187
- canonical fingerprints, ancestor context, and ordered-neighbor evidence. Exclude
188
- the mutation target, its structurally affected ancestors, and any explicitly
189
- declared sibling boundary from the untouched set.
190
-
191
- ### 5.3 Required oracles
192
-
193
- Add tests for:
194
-
195
- - accepted and rejected text and structure;
196
- - revision ID uniqueness across every revision element supported by validation;
197
- - comment and numbering artifact integrity;
198
- - untouched entry byte equality;
199
- - untouched subtree canonical equality;
200
- - save-and-reopen semantic equality;
201
- - no-op and atomic rollback returning the exact original XML/package bytes;
202
- - Word open, Accept All, Reject All, and save/reopen behavior.
203
-
204
- Reviewing Pane grouping is a visual compatibility observation, not a schema or
205
- lifecycle oracle.
206
-
207
- ### Acceptance gate
208
-
209
- No later phase begins until representative paragraph, list, table, comment,
210
- header/footer, field, hyperlink, bookmark, drawing, and SDT fixtures pass the
211
- new preservation envelope.
212
-
213
- ## 6. Milestone 1 — Revision-View Addressing
214
-
215
- **Target:** v0.5.0
216
- **Classification:** Additive, with mutation support gated separately
217
-
218
- ### 6.1 Use semantic view names
219
-
220
- Expose revision addressing as:
221
-
222
- ```ts
223
- export type RevisionView = 'accepted' | 'rejected';
224
-
225
- export interface ParagraphTargetDescriptor {
226
- exactText?: string;
227
- paragraphId?: string;
228
- index?: number;
229
- occurrence?: number;
230
- fingerprint?: string;
231
- revisionView?: RevisionView; // Defaults to accepted/current behavior.
232
- }
233
- ```
234
-
235
- Do not call accepted text “No Markup or Original.” Original/rejected and
236
- accepted/final are different views. Do not expose a single `tracked` string made
237
- by concatenating deleted and inserted text; a replacement has two overlapping
238
- histories, not one stable user-facing coordinate line.
239
-
240
- For inspection that needs revision history, return segments instead:
241
-
242
- ```ts
243
- interface RevisionTextSegment {
244
- text: string;
245
- kind: 'baseline' | 'insertion' | 'deletion' | 'move_from' | 'move_to';
246
- author?: string;
247
- revisionId?: string;
248
- acceptedStart: number | null;
249
- rejectedStart: number | null;
250
- }
251
- ```
252
-
253
- Offsets use JavaScript UTF-16 code units. DOM mapping must cover `w:t`,
254
- `w:delText`, tabs, breaks, carriage returns, soft/no-break hyphens, entities,
255
- and revision/move ancestry.
256
-
257
- ### 6.2 Separate inspection from mutation policy
258
-
259
- Reading or locating text in a rejected view does not automatically authorize
260
- editing historical deletion markup. Initial scope:
261
-
262
- - Inspection and preflight support both views.
263
- - Comments may target a supported revision range only after schema and Word
264
- round-trip fixtures prove the anchor representation.
265
- - Text mutation of another pending deletion or insertion is refused until an
266
- explicit author-aware lifecycle policy exists.
267
- - The existing `reject-input` and `accept-all-first` policies remain unchanged.
268
- - If a future `preserve-input` policy is added, it must prohibit illegal nested
269
- revisions and define same-author coalescing separately.
270
-
271
- ### Acceptance criteria
272
-
273
- - The same descriptor resolves deterministically in accepted and rejected
274
- views.
275
- - Duplicate text remains ambiguous within the selected view.
276
- - Emoji, entities, tabs, breaks, moves, and mixed insert/delete replacements map
277
- to the correct DOM boundaries.
278
- - Omitting `revisionView` produces byte-for-byte current behavior.
279
-
280
- ## 7. Milestone 2 — Replacement Pairing and Insertion Affinity
281
-
282
- **Target:** v0.5.0 experimental; stabilize in v0.6.0
283
- **Classification:** Opt-in observable serialization behavior
284
-
285
- ### 7.1 Paired replacement metadata
286
-
287
- Model a delete-followed-by-insert diff as one internal replacement event when
288
- both sides share a safe parent and no structural boundary must be crossed. The
289
- event allocates distinct revision IDs but may share one author and timestamp.
290
-
291
- ```ts
292
- interface ReplacementRevisionEvent {
293
- deletionId: string;
294
- insertionId: string;
295
- author: string;
296
- date: string;
297
- }
298
- ```
299
-
300
- Rules:
301
-
302
- - `w:del` and `w:ins` IDs remain distinct and document-scoped.
303
- - Shared metadata is allocated once for the event.
304
- - Empty runs created solely by splitting are removed.
305
- - `xml:space="preserve"` is based on emitted text boundaries; consecutive
306
- interior spaces are also preserved defensively.
307
- - Pairing never crosses a hyperlink, field, SDT, comment, bookmark, move, or
308
- incompatible revision parent.
309
- - Required structural markers may remain between changes. Fidelity wins over
310
- visual grouping.
311
- - Word may present a paired event as a combined replacement, but the public API
312
- does not guarantee a particular balloon shape.
313
-
314
- ### 7.2 Separate formatting affinity from container affinity
315
-
316
- One `left | right` flag is insufficient for every boundary. Define:
317
-
318
- ```ts
319
- interface InsertionAffinity {
320
- formatting?: 'left' | 'right' | 'none';
321
- hyperlink?: 'inside' | 'outside' | 'preserve';
322
- revision?: 'coalesce_same_author' | 'separate';
323
- }
324
- ```
325
-
326
- Bookmarks and comments are ranges represented by markers, not formatting
327
- wrappers. Their membership is determined by the insertion point relative to
328
- start/end markers and must be tested independently.
329
-
330
- When affinity is omitted, retain the existing boundary behavior. Do not claim
331
- that a universal implicit `left` policy matches Word in every container.
332
-
333
- ### Acceptance criteria
334
-
335
- - Accepted and rejected content is identical with pairing enabled or disabled.
336
- - Pairing never removes or reorders structural markers.
337
- - Every formatting/hyperlink/comment/bookmark boundary combination has an
338
- explicit expected DOM position.
339
- - Word-version visual fixtures record presentation differences without making
340
- them correctness requirements.
341
-
342
- ## 8. Milestone 3 — Complete Paragraph-Boundary Semantics
343
-
344
- **Target:** v0.6.0
345
- **Classification:** Internal semantic change for newly supported cases
346
-
347
- The paragraph-mark primitives already exist. This phase completes the operation
348
- matrix and removes unsafe reconstruction only where native boundary revisions
349
- are proven.
350
-
351
- ### 8.1 Boundary removal versus paragraph deletion
352
-
353
- These operations must remain distinct:
354
-
355
- 1. **Remove the boundary between A and B while retaining both texts**
356
- - Keep both physical paragraphs in the pending-revision document.
357
- - Mark the paragraph mark ending A with `w:pPr/w:rPr/w:del`.
358
- - Do not mark B's retained text as deleted.
359
- - Accept joins the contents; Reject restores the boundary.
360
- 2. **Delete Paragraph B and its text**
361
- - Track B's content deletion.
362
- - Track the relevant paragraph mark so Reject does not leave a ghost
363
- paragraph, bullet, or number.
364
- - Accept removes B; Reject restores B exactly.
365
-
366
- For boundary acceptance, do not assume that A's paragraph properties win. The
367
- remaining paragraph mark normally determines final paragraph formatting. Build
368
- native Word fixtures for conflicting A/B styles, numbering, spacing, and
369
- section properties, then encode the observed ownership rule before implementing
370
- the merge algorithm.
371
-
372
- ### 8.2 Paragraph split
373
-
374
- When splitting A into A and B:
375
-
376
- - Split at a structurally safe run boundary.
377
- - Create B with the correct inherited paragraph properties.
378
- - Mark the newly inserted boundary—the paragraph mark ending A—with
379
- `w:pPr/w:rPr/w:ins`.
380
- - Move retained trailing runs without incorrectly marking their text inserted.
381
- - Accept keeps A and B; Reject moves B's retained content back into A and
382
- removes the inserted boundary.
383
-
384
- ### 8.3 Unsafe cases
385
-
386
- Refuse or retain the current proven fallback when the boundary owns or crosses:
387
-
388
- - `w:sectPr` with uncertain ownership;
389
- - table-cell terminal paragraphs;
390
- - incompatible list definitions;
391
- - overlapping bookmarks/comments;
392
- - fields, SDTs, drawings, notes, or unsupported revision nesting.
393
-
394
- ### Acceptance criteria
395
-
396
- - A full matrix covers split, join, delete, list-item insertion/deletion, and
397
- boundaries adjacent to structural content.
398
- - Word and internal Accept All/Reject All agree on paragraph order, text,
399
- paragraph properties, numbering, and section ownership.
400
- - Existing paragraph-mark regression tests remain valid and are expanded rather
401
- than replaced.
402
-
403
- ## 9. Milestone 4 — Complete Tracked Formatting Semantics
404
-
405
- **Target:** v0.6.0
406
- **Classification:** Existing behavior plus opt-in author-aware coalescing
407
-
408
- `w:rPrChange` generation and property-change lifecycle handling already exist.
409
- This phase closes coverage gaps and defines a public formatting operation model.
410
-
411
- ### 9.1 Operation contracts
412
-
413
- Distinguish character and paragraph formatting:
414
-
415
- ```ts
416
- interface CharacterFormatOperation {
417
- type: 'format';
418
- target: ParagraphTargetDescriptor;
419
- textToFormat: string;
420
- properties: {
421
- bold?: boolean;
422
- italic?: boolean;
423
- underline?: boolean;
424
- strike?: boolean;
425
- highlight?: string | null;
426
- };
427
- }
428
-
429
- interface ParagraphFormatOperation {
430
- type: 'paragraph-format';
431
- target: ParagraphTargetDescriptor;
432
- properties: {
433
- alignment?: 'left' | 'center' | 'right' | 'both';
434
- keepNext?: boolean;
435
- keepLines?: boolean;
436
- pageBreakBefore?: boolean;
437
- };
438
- }
439
- ```
440
-
441
- The property set may expand only with prior-state snapshot and lifecycle tests.
442
- The previous-state child of `w:rPrChange` or `w:pPrChange` contains the complete
443
- direct property state required to reverse the change, excluding nested change
444
- records.
445
-
446
- ### 9.2 Author-aware policy
447
-
448
- Preserve current formatting revision behavior by default. Add an experimental
449
- policy only after selective-author tests:
450
-
451
- ```ts
452
- formattingRevisionPolicy?: 'always' | 'coalesce-own-insertion';
453
- ```
454
-
455
- - `always`: current behavior; create the appropriate property revision.
456
- - `coalesce-own-insertion`: formatting inside the active author's pending
457
- insertion updates that insertion directly.
458
- - Formatting baseline text or another author's insertion creates a separate
459
- property revision attributed to the active author.
460
-
461
- ### Acceptance criteria
462
-
463
- - Bold/italic/underline/strike/highlight on/off transitions round-trip.
464
- - Paragraph-property changes round-trip independently of run properties.
465
- - Same-author and different-author pending insertions are tested separately.
466
- - Run splitting never duplicates an existing `w:rPrChange` ID.
467
- - Omitting the new policy retains current serialized behavior.
468
-
469
- ## 10. Milestone 5 — Versioned Concurrency Tokens
470
-
471
- **Target:** v0.7.0
472
- **Classification:** Additive precondition
473
-
474
- ### 10.1 Two token scopes
475
-
476
- Do not describe a three-part hash as a complete package revision.
477
-
478
- ```ts
479
- interface RevisionToken {
480
- algorithm: 'sha256';
481
- version: 1;
482
- scope: 'document-parts' | 'package';
483
- value: string;
484
- }
485
- ```
486
-
487
- - `document-parts`: hash a documented, sorted list of provided part names and
488
- exact strings/bytes. Suitable for lower-level APIs.
489
- - `package`: hash every relevant ZIP entry name plus its uncompressed bytes.
490
- Suitable for `openDocx` and CLI workflows.
491
-
492
- Use length-prefixed binary framing so part boundaries cannot collide. Include
493
- the token version and scope in the digest input. A package token necessarily
494
- requires reading the ZIP directory and selected entries; it can still reject
495
- before any mutation is staged.
496
-
497
- ### 10.2 Check location
498
-
499
- - Inspection returns the token and the covered part list.
500
- - Apply recomputes it from the exact incoming state before creating mutation
501
- savepoints or package artifacts.
502
- - Mismatch returns `REVISION_MISMATCH`, `hasChanges: false`, no artifacts, and
503
- the original bytes.
504
- - A token from one scope cannot satisfy another scope.
505
-
506
- ### Acceptance criteria
507
-
508
- - Changes to document text, comments, numbering, relationships, headers,
509
- footers, styles, notes, or media are detected by the package token.
510
- - ZIP recompression without uncompressed entry changes does not invalidate a
511
- package token.
512
- - Omitted preconditions preserve current behavior.
513
-
514
- ## 11. Milestone 6 — Dependency-Aware Capture Chaining
515
-
516
- **Target:** v0.7.0
517
- **Classification:** Additive batch behavior when captures are used
518
-
519
- ### 11.1 Contract
520
-
521
- ```json
522
- [
523
- {
524
- "operationId": "insert-privacy-section",
525
- "type": "replace",
526
- "target": { "exactText": "11. Term and Termination" },
527
- "modified": "11. Term and Termination\n\n12. Data Privacy\nService Provider shall maintain the required controls.",
528
- "structuredContent": true,
529
- "captureKey": "privacy-section"
530
- },
531
- {
532
- "operationId": "comment-controls",
533
- "type": "comment",
534
- "target": {
535
- "captureRef": "privacy-section",
536
- "select": "required controls"
537
- },
538
- "commentContent": "Confirm the evidence and audit cadence."
539
- }
540
- ]
541
- ```
542
-
543
- ### 11.2 Dependency scheduling
544
-
545
- Build a dependency graph before execution:
546
-
547
- - A `captureRef` depends on the operation exporting its `captureKey`.
548
- - Reject duplicate keys, missing references, forward references not resolvable
549
- by the graph, and cycles.
550
- - Preserve source order among independent operations.
551
- - Retain comment-first scheduling only when it does not violate dependencies.
552
- - `executionOrder` reports actual topological order.
553
-
554
- Static preflight reports capture-dependent targets as `deferred`, not missing.
555
- An optional mutation preview may execute against an isolated clone to resolve
556
- all dynamic selections without committing output.
557
-
558
- ### 11.3 Stable capture representation
559
-
560
- Do not store raw DOM nodes as the durable capture value. Savepoint restoration
561
- replaces the DOM tree and invalidates those references. A capture records a
562
- serializable session-local identity:
563
-
564
- ```ts
565
- interface CapturedEntity {
566
- captureKey: string;
567
- operationIndex: number;
568
- kind: 'paragraph' | 'range' | 'table' | 'list';
569
- generatedParagraphIds: string[];
570
- fingerprints: string[];
571
- expectedText: string[];
572
- structuralPathHints: string[];
573
- }
574
- ```
575
-
576
- Resolution reacquires nodes from the current session index and verifies text and
577
- fingerprint before mutation. The capture table participates in every savepoint:
578
- failed/no-op steps restore its exact prior state, and atomic rollback discards it.
579
- If a captured entity is replaced later, the executor must either update the
580
- capture deterministically or return `CAPTURE_STALE`.
581
-
582
- ### 11.4 Structured insertion guidance
583
-
584
- Large generated content must be analyzed before execution:
585
-
586
- 1. Run `planStructuredReplacement`.
587
- 2. Refuse malformed tables or ambiguous block boundaries.
588
- 3. Emit separate heading, paragraph, list, and table nodes.
589
- 4. Capture block identities, not a flattened combined string.
590
- 5. Apply dependent comments or formatting only after those nodes exist.
591
-
592
- ### Acceptance criteria
593
-
594
- - Insert-then-comment and insert-then-format work in one atomic batch.
595
- - Independent comments retain current comment-first safety.
596
- - Savepoint restoration never leaves stale node references or captures.
597
- - Failure of a dependent step rolls back the entire atomic batch and all
598
- artifacts.
599
-
600
- ## 12. Milestone 7 — Commit-Aware Mutation Receipts
601
-
602
- **Target:** v0.7.0
603
- **Classification:** Additive result metadata
604
-
605
- Receipts describe both what was attempted and what survived the transaction:
606
-
607
- ```ts
608
- interface MutationReceipt {
609
- operationIndex: number;
610
- operationId?: string;
611
- attemptedDisposition: 'applied' | 'no_change' | 'refused' | 'not_attempted';
612
- finalDisposition: 'applied' | 'no_change' | 'refused' | 'rolled_back' | 'not_attempted';
613
- committed: boolean;
614
- authorUsed?: string;
615
- revisionItems: Array<{
616
- id: string;
617
- kind: 'ins' | 'del' | 'move_from' | 'move_to' | 'rPrChange' | 'pPrChange' | 'structural';
618
- partName: string;
619
- }>;
620
- commentIds: string[];
621
- numberingIds: string[];
622
- relationshipIds: string[];
623
- affectedTargets: ResolvedDocumentTarget[];
624
- warnings: string[];
625
- }
626
- ```
627
-
628
- Rules:
629
-
630
- - Existing result fields and status strings remain unchanged.
631
- - Collectors are scoped to the operation savepoint.
632
- - IDs consumed by a failed/no-op step are absent after restore.
633
- - A later atomic failure changes earlier provisional receipts to
634
- `finalDisposition: 'rolled_back'` and `committed: false`.
635
- - No IDs are presented as durable handles unless the transaction commits.
636
- - Receipt generation must not add paragraph IDs or otherwise mutate the
637
- document solely for telemetry.
638
-
639
- ### Acceptance criteria
640
-
641
- - Receipts enumerate exact committed revision, comment, numbering, and
642
- relationship allocations.
643
- - No-op, refusal, stopped execution, per-step rollback, and whole-batch rollback
644
- are distinguishable.
645
- - Receipt contents agree with a fresh parse of the committed package.
646
-
647
- ## 13. Milestone 8 — Strict Application Migration
648
-
649
- **Target:** warning cycle in v0.7.x; default change in v1.0.0
650
- **Classification:** Breaking default change
651
-
652
- Preflight is already strict by default, and the Node facade/CLI already prefer
653
- strict application. The remaining migration concerns callers of permissive
654
- lower-level application APIs.
655
-
656
- ### Migration
657
-
658
- - v0.7.x: ambiguous permissive resolution succeeds only where legacy behavior
659
- currently permits it and emits `AMBIGUOUS_TARGET_HEURISTIC_USED`.
660
- - Documentation and generated agent examples always use strict application.
661
- - v1.0.0: lower-level application defaults to strict.
662
- - An explicit `strictTargets: false` compatibility escape hatch may remain for
663
- one major release, with warnings.
664
-
665
- Strict descriptors are conjunctive assertions, not a priority list: when both
666
- `paragraphId` and `exactText` are supplied, both must describe the same current
667
- target. An index or occurrence alone is not a concurrency guarantee.
668
-
669
- ### Acceptance criteria
670
-
671
- - Zero, duplicate, descriptor-conflict, stale fingerprint, and anchor ambiguity
672
- have distinct errors and candidate diagnostics.
673
- - Preflight and strict application resolve the same target or both refuse.
674
- - CLI and Node package workflows never silently choose candidate one.
675
-
676
- ## 14. Verification Matrix
677
-
678
- Every milestone runs:
679
-
680
- ```bash
681
- npm test
682
- npm run test:isolation
683
- npm run check:types
684
- node scripts/export-validation-fixtures.mjs
685
- ```
686
-
687
- Risk-specific gates:
688
-
689
- | Change | Required additional verification |
690
- |---|---|
691
- | Canonical fidelity | New fidelity-oracle suite plus real package corpus |
692
- | Revision addressing | Accepted/rejected view, entity, Unicode, move, and mixed-revision fixtures |
693
- | Pairing/affinity | Hyperlink, field, SDT, bookmark, comment, and multi-author fixtures |
694
- | Paragraph boundaries | Internal round trip plus desktop Word Accept/Reject and visual review |
695
- | Formatting revisions | Selective-author Accept/Reject and complete previous-state snapshots |
696
- | Concurrency tokens | Entry mutation/recompression/token-version matrix |
697
- | Capture chaining | Dependency graph, savepoint, stale capture, and atomic rollback matrix |
698
- | Receipts | Fresh-package audit of every reported committed ID |
699
-
700
- Desktop Word automation proves open/save and accepted/rejected outcomes. Visual
701
- inspection remains required for layout, paragraph marks, change bars, and
702
- Reviewing Pane presentation because those UI details are not fully exposed as a
703
- stable COM assertion surface.
704
-
705
- ## 15. Release Sequence
706
-
707
- ### v0.5.0 — Evidence and addressing
708
-
709
- - Current-capability audit.
710
- - Mutation envelopes and fidelity oracles.
711
- - Accepted/rejected revision-view inspection and targeting.
712
- - Experimental paired replacement metadata and explicit insertion affinity.
713
-
714
- ### v0.6.0 — Structural lifecycle completion
715
-
716
- - Proven paragraph boundary join/split/delete cases.
717
- - Complete character and paragraph formatting revision contracts.
718
- - Expanded selective-author and native Word lifecycle fixtures.
719
-
720
- ### v0.7.0 — Transactional agent workflows
721
-
722
- - Versioned document/package concurrency tokens.
723
- - Dependency-aware capture chaining.
724
- - Commit-aware mutation receipts.
725
- - Permissive-target deprecation warnings.
726
-
727
- ### v1.0.0 — Strict defaults
728
-
729
- - Strict lower-level application by default.
730
- - Compatibility flags and deprecated helpers reviewed under normal semver rules.
731
- - Full fidelity, lifecycle, package, and Word validation gates required for
732
- release.
733
-
734
- ## 16. Exit Criteria
735
-
736
- This program is complete when:
737
-
738
- 1. Every claimed existing and new capability has production-code and regression-
739
- test evidence.
740
- 2. Every operation declares and stays within its mutation envelope.
741
- 3. Accepted and rejected results match internal and desktop Word outcomes for
742
- text and structure.
743
- 4. Large structured insertions remain headings, paragraphs, lists, and tables
744
- rather than flattened text.
745
- 5. Dependent batch steps use stable captures and preserve atomic rollback.
746
- 6. Receipts never report rolled-back IDs as committed.
747
- 7. Stale package mutations fail before document state is changed.
748
- 8. Ambiguous agent operations fail closed under all recommended public workflows.
749
-
750
- ## 17. Coding-Agent Execution Protocol
751
-
752
- This section is normative. An implementation agent should follow it even when a
753
- work package appears simple.
754
-
755
- ### 17.1 Unit of work
756
-
757
- Implement exactly one work package from Section 18 at a time. Do not combine
758
- unrelated packages, opportunistically refactor adjacent modules, or change a
759
- default before the package that explicitly authorizes that change.
760
-
761
- For each work package:
762
-
763
- 1. Read `AGENTS.md`, `ARCHITECTURE.md`, `docs/TESTING.md`, and every production
764
- file listed under **Files to inspect**.
765
- 2. Run the listed baseline tests before editing. If they fail, record the
766
- pre-existing failure and stop unless the task explicitly includes repairing
767
- it.
768
- 3. Add the smallest failing regression test that expresses the requested
769
- invariant.
770
- 4. Implement through existing helpers and session state. Do not create a second
771
- parser, allocator, target resolver, or package writer.
772
- 5. Run the focused tests, then the shared gates in Section 17.5.
773
- 6. Update public declarations, schemas, README, architecture, testing guidance,
774
- and changelog only when the work package changes those contracts.
775
- 7. Report files changed, tests run, compatibility impact, and any deferred
776
- unsafe cases.
777
-
778
- ### 17.2 Mutation rules
779
-
780
- - Create Word elements through `createWordElement`.
781
- - Create revision metadata through the document-scoped allocator path already
782
- used by `createRevisionMetadata` and `RevisionIdAllocator`.
783
- - Never allocate revision IDs with module globals, timestamps, random numbers,
784
- or a fresh per-operation allocator inside a batch.
785
- - Mutate the live session DOM. String serialization is a boundary operation,
786
- not a mutation technique.
787
- - Retain per-operation DOM, allocator, runtime-context, artifact, capture, and
788
- receipt savepoints.
789
- - Never move or delete unknown OOXML merely because the current renderer ignores
790
- it.
791
- - Preserve source whitespace exactly. Replacement text is caller data and must
792
- not be trimmed, collapsed, Markdown-normalized, or line-ending-normalized.
793
- - Keep accepted/rejected text extraction centralized in
794
- `core/paragraph-text.js`.
795
- - Keep target selection centralized in `core/paragraph-targeting.js` and
796
- `services/operation-preflight.js`.
797
- - Keep package mutations in the Node/package layer. Browser-safe root imports
798
- must not acquire Node built-ins.
799
-
800
- ### 17.3 Error behavior
801
-
802
- Expected refusal is not an exception. Return the established error shape:
803
-
804
- ```ts
805
- {
806
- hasChanges: false,
807
- status: 'error',
808
- error: {
809
- code: 'STABLE_MACHINE_CODE',
810
- message: 'Actionable human-readable explanation.'
811
- }
812
- }
813
- ```
814
-
815
- An atomic batch failure additionally returns the original document, empty
816
- uncommitted artifacts, `rolledBack: true`, and per-operation attempt results.
817
- Never convert a structured refusal into `no_change`.
818
-
819
- New error codes introduced by this plan:
820
-
821
- | Code | Meaning |
822
- |---|---|
823
- | `UNSUPPORTED_REVISION_VIEW_MUTATION` | Requested view can be inspected but not safely mutated |
824
- | `UNSAFE_REVISION_NESTING` | Mutation would create unsupported nested/overlapping revisions |
825
- | `UNSUPPORTED_INSERTION_AFFINITY` | Requested container placement is not legal or deterministic |
826
- | `UNSAFE_PARAGRAPH_BOUNDARY` | Split/join crosses unsupported structural ownership |
827
- | `REVISION_MISMATCH` | Expected package/document token does not match current state |
828
- | `REVISION_TOKEN_SCOPE_MISMATCH` | Token scope/version cannot guard the selected API |
829
- | `DUPLICATE_CAPTURE_KEY` | More than one step exports the same capture key |
830
- | `CAPTURE_NOT_FOUND` | No step exports the requested capture |
831
- | `CAPTURE_DEPENDENCY_CYCLE` | Capture dependencies cannot be topologically ordered |
832
- | `AMBIGUOUS_CAPTURE_SELECTION` | `select` matches multiple locations inside a capture |
833
- | `CAPTURE_STALE` | Captured identity no longer resolves to the verified current entity |
834
-
835
- Add codes to runtime validation, TypeScript declarations, JSON schema, README,
836
- and tests in the same work package that first emits them.
837
-
838
- ### 17.4 Public-contract propagation checklist
839
-
840
- For every public option, operation field, result field, or error payload, inspect
841
- and update all applicable surfaces:
842
-
843
- - `index.js` and `index.d.ts`;
844
- - `services/document-operation-contract.js`;
845
- - `services/standalone-operation-runner.d.ts`;
846
- - `docs/schemas/document-operations.schema.json`;
847
- - `node/docx-document.js`;
848
- - CLI parsing/help/examples;
849
- - `README.md`, `ARCHITECTURE.md`, `docs/AGENT-WORKFLOW.md`,
850
- `docs/TESTING.md`, and `CHANGELOG.md`;
851
- - contract, schema, package facade, CLI, and type-export tests.
852
-
853
- Do not update only the JavaScript implementation and leave declarations or the
854
- operation schema behind.
855
-
856
- ### 17.5 Shared verification gates
857
-
858
- Run these after every work package:
859
-
860
- ```bash
861
- npm test
862
- npm run test:isolation
863
- npm run check:types
864
- ```
865
-
866
- Run these when OOXML generation or package plumbing changes:
867
-
868
- ```bash
869
- node scripts/export-validation-fixtures.mjs
870
- docx-redline validate <representative-output.docx>
871
- ```
872
-
873
- Run the relevant Word differential suite before completing any work package that
874
- changes paragraph marks, revision wrappers, run properties, numbering, tables,
875
- comments, relationships, or content types. Visual suites require human review of
876
- the generated evidence; successful rendering alone is not visual approval.
877
-
878
- ## 18. Ordered Implementation Work Packages
879
-
880
- ### WP-00 — Capability and Gap Audit [COMPLETED 2026-09-05]
881
-
882
- **Depends on:** None
883
- **Produces runtime changes:** No
884
- **Status:** Completed — audit matrix published at `docs/plans/structural-revision-capability-matrix.md` with all 67 baseline test suites verified passing.
885
-
886
- **Files to inspect**
887
-
888
- - `engine/run-builders.js`
889
- - `engine/surgical-run-splitting.js`
890
- - `engine/surgical-diff-application.js`
891
- - `engine/reconstruction-mode.js`
892
- - `pipeline/list-generation.js`
893
- - `pipeline/structured-content.js`
894
- - `core/paragraph-text.js`
895
- - `core/paragraph-targeting.js`
896
- - `services/document-operation-session.js`
897
- - `services/batch-operation-orchestrator.js`
898
- - `services/revision-comment-management.js`
899
- - `services/operation-preflight.js`
900
- - `node/docx-document.js`
901
-
902
- **Steps**
903
-
904
- 1. Create `docs/plans/structural-revision-capability-matrix.md`.
905
- 2. Add one row for every behavior named in Section 2.
906
- 3. For each row, record status, production symbol, existing test, missing case,
907
- and proposed work package.
908
- 4. Mark a capability `implemented` only when generation, validation, Accept All,
909
- and Reject All are covered.
910
- 5. Mark behavior that exists only for lists or only for whole-paragraph deletion
911
- as `partial`, not implemented.
912
- 6. Record current serialized output for one canonical fixture per partial area.
913
-
914
- **Tests**
915
-
916
- No new test is required, but all tests linked from the matrix must be executed.
917
-
918
- **Done when**
919
-
920
- No later work package describes existing behavior as absent, and every claimed
921
- gap has a reproducible fixture or test.
922
-
923
- ### WP-01 — Package Part Inventory Helper [COMPLETED 2026-09-05]
924
-
925
- **Depends on:** WP-00
926
- **Produces runtime changes:** No; test helper only
927
- **Status:** Completed — helper implemented in `tests/helpers/package-fidelity.mjs`, verified by `tests/package_fidelity_inventory_tests.mjs`.
928
-
929
- **Files to inspect**
930
-
931
- - `node/zip-archive.js`
932
- - `services/package-builder.js`
933
- - `services/standalone-docx-plumbing.js`
934
- - existing ZIP/package transaction tests
935
-
936
- **New files**
937
-
938
- - `tests/helpers/package-fidelity.mjs`
939
- - `tests/package_fidelity_inventory_tests.mjs`
940
-
941
- **Algorithm**
942
-
943
- 1. Read every ZIP entry as its uncompressed byte payload.
944
- 2. Normalize entry names to OPC forward-slash form; reject duplicate normalized
945
- names.
946
- 3. Sort by entry name for reporting only. Never treat archive order, compression
947
- method, CRC field position, or ZIP timestamps as content.
948
- 4. Return `{ entries: Map<name, { size, sha256, bytes }> }`.
949
- 5. Provide `comparePackageEntries(before, after, allowedChangedEntries)`.
950
- 6. Fail with a report containing unexpected changed, added, and removed entries.
951
- 7. Do not silently ignore `docProps`, relationships, media, content types, or
952
- custom XML.
953
-
954
- **Required tests**
955
-
956
- - Recompressing identical entries produces no content differences.
957
- - One-byte payload changes are detected.
958
- - Added and removed entries are reported separately.
959
- - Duplicate normalized paths are refused.
960
- - Binary media is compared without text decoding.
961
-
962
- **Done when**
963
-
964
- The helper can prove exact untouched entry payload preservation without claiming
965
- the entire ZIP byte stream remains identical.
966
-
967
- ### WP-02 — OOXML Canonical Subtree Helper [COMPLETED 2026-09-05]
968
-
969
- **Depends on:** WP-01
970
- **Produces runtime changes:** No; test helper only
971
- **Status:** Completed — canonical XML helper implemented in `tests/helpers/canonical-ooxml.mjs` supporting QName-valued attribute canonicalization, verified by `tests/canonical_ooxml_tests.mjs`.
972
-
973
- **Files to inspect**
974
-
975
- - `adapters/xml-adapter.js`
976
- - `core/word-xml.js`
977
- - `core/xml-query.js`
978
- - fixtures containing `mc:Ignorable`, alternate prefixes, drawings, and SDTs
979
-
980
- **New files**
981
-
982
- - `tests/helpers/canonical-ooxml.mjs`
983
- - `tests/canonical_ooxml_tests.mjs`
984
-
985
- **Steps**
986
-
987
- 1. Evaluate an existing standards-compliant canonicalization dependency before
988
- writing custom logic. Do not add a dependency without documenting bundle and
989
- browser impact.
990
- 2. Represent element and attribute names as `{ namespaceURI, localName }`.
991
- 3. Preserve element child order and exact character data.
992
- 4. Canonicalize namespace declarations without breaking QName-valued attribute
993
- contents.
994
- 5. Treat comments and processing instructions consistently and document whether
995
- they participate in the digest.
996
- 6. Return both canonical bytes and SHA-256 so failures can print a readable
997
- canonical diff.
998
-
999
- **Required tests**
1000
-
1001
- - Different harmless prefixes compare equal.
1002
- - Different attribute orders compare equal.
1003
- - Different child orders compare unequal.
1004
- - `w:b` absent and `w:b w:val="0"` compare unequal.
1005
- - Leading/trailing spaces and `xml:space` differences compare unequal when they
1006
- change Word text semantics.
1007
- - `mc:Ignorable` remains bound to the intended namespace prefixes.
1008
-
1009
- **Stop condition**
1010
-
1011
- If QName-valued attributes cannot be canonicalized safely, limit the first
1012
- oracle to exact serialized subtree bytes and document the limitation. Do not
1013
- ship a prefix-replacement approximation as canonical XML.
1014
-
1015
- ### WP-03 — Mutation Envelope Registry and Fidelity Suite [COMPLETED 2026-09-05]
1016
-
1017
- **Depends on:** WP-01, WP-02
1018
- **Produces runtime changes:** No unless a discovered fidelity bug is separately scoped
1019
- **Status:** Completed — mutation envelopes registry implemented in `tests/helpers/mutation-envelopes.mjs`, verified against the multi-feature fixture matrix in `tests/fidelity_oracle_tests.mjs`.
1020
-
1021
- **New files**
1022
-
1023
- - `tests/helpers/mutation-envelopes.mjs`
1024
- - `tests/fidelity_oracle_tests.mjs`
1025
-
1026
- **Steps**
1027
-
1028
- 1. Define envelopes by operation kind and structural route: surgical text,
1029
- reconstruction, list, structured content, table reconciliation, comment,
1030
- highlight, accept/reject, and package plumbing.
1031
- 2. Each envelope lists allowed part changes and affected subtree classes.
1032
- 3. Capture before-state entry hashes and candidate subtree identities.
1033
- 4. Apply exactly one operation.
1034
- 5. Reopen the output package before measuring after-state.
1035
- 6. Compare untouched package entries exactly.
1036
- 7. Reacquire untouched subtrees using IDs plus fingerprints and ordered context.
1037
- 8. Compare their canonical digests.
1038
- 9. Run internal Accept All and Reject All and compare expected structure.
1039
- 10. Emit a diagnostic that names the first unexpected entry or subtree.
1040
-
1041
- **Required fixture matrix**
1042
-
1043
- - normal paragraph surrounded by untouched paragraphs;
1044
- - hyperlink and bookmark adjacent to an edit;
1045
- - comment range adjacent to an edit;
1046
- - field-code paragraph;
1047
- - drawing and image relationship;
1048
- - SDT containing text;
1049
- - numbered and bulleted lists;
1050
- - existing table and newly inserted Markdown table;
1051
- - header, footer, footnote, and endnote package parts;
1052
- - documents with and without comments/numbering parts.
1053
-
1054
- **Done when**
1055
-
1056
- The suite passes current behavior or produces separately tracked fidelity bugs.
1057
- Do not weaken an envelope merely to make a failure disappear.
1058
-
1059
- ### WP-04 — Revision-View Segment Extraction [COMPLETED 2026-09-05]
1060
-
1061
- **Depends on:** WP-03
1062
- **Produces runtime changes:** Additive inspection API
1063
- **Status:** Completed — implemented `extractParagraphRevisionSegments` in `core/paragraph-text.js`, wired `segments` into `inspectDocumentParts` in `services/document-inspection.js`, exported types in `index.d.ts` and `index.js`, and verified by `tests/revision_view_segment_tests.mjs`.
1064
-
1065
- **Files to edit**
1066
-
1067
- - `core/paragraph-text.js`
1068
- - `services/document-inspection.js`
1069
- - public exports and declarations from Section 17.4
1070
-
1071
- **New tests**
1072
-
1073
- - `tests/revision_view_segment_tests.mjs`
1074
-
1075
- **Algorithm**
1076
-
1077
- 1. Add one depth-first paragraph walker that emits structural text pieces.
1078
- 2. Track ancestors for `ins`, `del`, `moveFrom`, and `moveTo`.
1079
- 3. Emit text for `w:t`, `w:delText`, tabs, breaks, carriage returns,
1080
- soft hyphens, and no-break hyphens.
1081
- 4. Compute accepted and rejected visibility using the existing canonical rules.
1082
- 5. Maintain accepted and rejected UTF-16 cursors independently.
1083
- 6. For a hidden segment, store `null` for that view's start.
1084
- 7. Merge adjacent pieces only when kind, author, revision ID, and DOM carrier are
1085
- compatible.
1086
- 8. Reimplement existing accepted/rejected string extraction as a projection of
1087
- the walker, or prove parity before retaining both implementations.
1088
-
1089
- **Required assertions**
1090
-
1091
- - Concatenating accepted-visible segments equals current accepted extraction.
1092
- - Concatenating rejected-visible segments equals current rejected extraction.
1093
- - Entity decoding does not alter DOM offsets.
1094
- - An emoji counts as two UTF-16 code units.
1095
- - A replacement exposes deletion and insertion as separate segments, never as
1096
- one concatenated “tracked” word.
1097
- - Move revisions map consistently with existing accept/reject behavior.
1098
-
1099
- **Compatibility gate**
1100
-
1101
- Existing extraction output must be byte-for-byte unchanged for all current
1102
- fixtures.
1103
-
1104
- ### WP-05 — Revision-View Target Resolution [COMPLETED 2026-09-05]
1105
-
1106
- **Depends on:** WP-04
1107
- **Produces runtime changes:** Additive target option for inspection/preflight
1108
- **Status:** Completed — implemented revisionView-aware caching and conjunctive descriptor resolution in `core/paragraph-targeting.js`, validation in `services/document-operation-contract.js`, preflight multi-view support in `services/operation-preflight.js`, mutation refusal with `UNSUPPORTED_REVISION_VIEW_MUTATION` in `services/document-operation-applier.js`, JSON schema updates, and verified by `tests/revision_view_target_tests.mjs`.
1109
-
1110
- **Files to edit**
1111
-
1112
- - `core/paragraph-targeting.js`
1113
- - `services/operation-preflight.js`
1114
- - `services/document-operation-contract.js`
1115
- - public contract surfaces from Section 17.4
1116
-
1117
- **Steps**
1118
-
1119
- 1. Add `revisionView` validation with only `accepted` and `rejected` values.
1120
- 2. Build or cache paragraph metadata per revision view; never reuse accepted
1121
- text for rejected matching.
1122
- 3. Resolve all supplied descriptor fields conjunctively.
1123
- 4. Calculate `occurrence` within the selected view.
1124
- 5. Include the selected view in candidate diagnostics and fingerprints.
1125
- 6. Restrict the initial implementation to inspection and preflight.
1126
- 7. If apply receives `revisionView: 'rejected'`, return
1127
- `UNSUPPORTED_REVISION_VIEW_MUTATION` until WP-06 explicitly supports that
1128
- operation kind.
1129
-
1130
- **Required tests**
1131
-
1132
- - Same phrase at different accepted/rejected locations.
1133
- - Phrase exists only in an insertion.
1134
- - Phrase exists only in a deletion.
1135
- - Duplicate phrase in one view but unique in the other.
1136
- - Descriptor fields disagree despite one individually matching.
1137
- - Invalid view is rejected by runtime contract and JSON schema.
1138
-
1139
- ### WP-06 — Existing-Revision Mutation Policy [COMPLETED 2026-09-05]
1140
-
1141
- **Depends on:** WP-05
1142
- **Produces runtime changes:** Opt-in only
1143
- **Status:** Completed — implemented policy matrix: allow schema-valid comments inside pending insertions across same and different authors with selective accept/reject preservation; refuse deletion comments, cross-container comment ranges, and deletion/move replacements with `UNSAFE_REVISION_NESTING`; refuse pending insertion replacements with `EXISTING_REVISIONS`; verified by `tests/existing_revision_mutation_policy_tests.mjs`.
1144
-
1145
- **Goal**
1146
-
1147
- Decide which mutations can safely preserve existing revisions. Do not add a
1148
- general `preserve-input` switch until the matrix below is implemented.
1149
-
1150
- **Policy matrix to implement and test**
1151
-
1152
- | Existing content | Same author | Different author | Initial action |
1153
- |---|---:|---:|---|
1154
- | Baseline text | n/a | n/a | Normal tracked mutation |
1155
- | Pending insertion, comment only | Yes | Yes | Allow if comment range is schema-valid |
1156
- | Pending insertion, text replacement | Yes | No | Refuse initially; later consider same-author coalescing |
1157
- | Pending deletion, comment only | Yes | Yes | Refuse until native Word fixture proves representation |
1158
- | Pending deletion, text replacement | Yes | No | Refuse with `UNSAFE_REVISION_NESTING` |
1159
- | Move source/destination | Any | Any | Refuse until move-specific lifecycle is designed |
1160
-
1161
- **Steps**
1162
-
1163
- 1. Generate native Word fixtures for every proposed allowed case.
1164
- 2. Inspect exact OOXML before copying the pattern.
1165
- 3. Add schema, Word open/save, selective-author Accept/Reject, and internal
1166
- lifecycle tests.
1167
- 4. Add only narrowly named policies for proven cases.
1168
- 5. Retain current `reject-input` default.
1169
-
1170
- **Stop condition**
1171
-
1172
- If internal and Word selective-author outcomes differ, keep the case refused.
1173
-
1174
- ### WP-07 — Replacement Event Metadata [COMPLETED 2026-09-05]
1175
-
1176
- **Depends on:** WP-03
1177
- **Produces runtime changes:** Opt-in paired metadata
1178
- **Status:** Completed — implemented opt-in `pairReplacements` with shared author/timestamp metadata, distinct document-allocated revision IDs, structural boundary safety fallback emitting `PAIRING_SKIPPED_STRUCTURAL_BOUNDARY`, lifecycle equivalence, schema and type declarations, and verified by `tests/replacement_event_metadata_tests.mjs`.
1179
-
1180
- **Files to inspect/edit**
1181
-
1182
- - `engine/surgical-diff-application.js`
1183
- - `engine/surgical-run-splitting.js`
1184
- - `engine/run-builders.js`
1185
- - `core/types.js`
1186
- - `core/redline-validation.js`
1187
-
1188
- **Steps**
1189
-
1190
- 1. Identify delete/insert token pairs before either wrapper is created.
1191
- 2. Confirm both sides can be emitted under the same legal parent.
1192
- 3. Allocate one event author/date and two unique revision IDs.
1193
- 4. Pass explicit metadata into builders; do not call the clock independently for
1194
- each half.
1195
- 5. Remove only empty runs introduced by the current split operation.
1196
- 6. Preserve all pre-existing zero-width structural nodes in their original order.
1197
- 7. Fall back to independent revisions when safe adjacency is impossible.
1198
- 8. Return a warning such as `PAIRING_SKIPPED_STRUCTURAL_BOUNDARY` only when the
1199
- caller explicitly requested pairing.
1200
-
1201
- **Tests**
1202
-
1203
- - Plain run replacement receives identical author/date and distinct IDs.
1204
- - Accepted/rejected output matches independent mode.
1205
- - Replacement across two formatting runs retains both formats.
1206
- - Hyperlink, comment, bookmark, field, SDT, and existing-revision boundaries
1207
- trigger safe fallback without node movement.
1208
- - Multiple replacements in one operation receive separate event pairs.
1209
- - Validation rejects duplicate revision IDs.
1210
-
1211
- **Word evidence**
1212
-
1213
- Record Word version, build, platform, displayed revision count, and screenshots.
1214
- Do not encode “one balloon” as a cross-version API guarantee.
1215
-
1216
- ### WP-08 — Insertion Affinity [COMPLETED 2026-09-05]
1217
-
1218
- **Depends on:** WP-07
1219
- **Produces runtime changes:** Additive explicit option
1220
- **Status:** Completed — implemented `InsertionAffinity` (`formatting`, `hyperlink`, `revision`, `bookmark`, `comment`), `describeInsertionBoundary` descriptor, strict validation returning `UNSUPPORTED_INSERTION_AFFINITY` for illegal positions, exact legacy fallback when omitted, schema and type declarations, and verified by `tests/insertion_affinity_tests.mjs`.
1221
-
1222
- **Files to inspect/edit**
1223
-
1224
- - `engine/surgical-spans.js`
1225
- - `engine/surgical-run-splitting.js`
1226
- - `engine/surgical-diff-application.js`
1227
- - hyperlink/bookmark/comment targeting helpers
1228
-
1229
- **Steps**
1230
-
1231
- 1. Create an internal boundary descriptor containing left/right carrier runs and
1232
- all open/closing structural boundaries at the insertion point.
1233
- 2. Apply formatting affinity independently from hyperlink/revision affinity.
1234
- 3. Treat bookmark and comment range markers as ordered boundaries, not wrappers.
1235
- 4. Validate requested affinity before mutation.
1236
- 5. If the requested placement is illegal or ambiguous, return
1237
- `UNSUPPORTED_INSERTION_AFFINITY`.
1238
- 6. When omitted, call the exact legacy path.
1239
-
1240
- **Minimum test grid**
1241
-
1242
- For insertion at the start and end of each structure, test left/right formatting
1243
- and legal inside/outside placement:
1244
-
1245
- - normal to bold run;
1246
- - paragraph start/end with no carrier on one side;
1247
- - hyperlink;
1248
- - bookmark range;
1249
- - comment range;
1250
- - field begin/separate/end sequence;
1251
- - SDT content;
1252
- - same-author insertion;
1253
- - different-author insertion.
1254
-
1255
- Assert exact parent, sibling order, inherited `w:rPr`, accepted/rejected text,
1256
- and preservation of every range marker.
1257
-
1258
- ### WP-09 — Paragraph Boundary Fixture Matrix
1259
-
1260
- **Depends on:** WP-03
1261
- **Produces runtime changes:** No; fixture/oracle work
1262
- **Status:** Completed — generated 10 Word-native golden fixture triples (30 DOCX files and 30 extracted XML parts in `tests/fixtures/paragraph-boundaries/`) covering split, boundary deletion, middle paragraph deletion, blank paragraph insertion, style boundaries, list levels, section breaks, table cells, bookmarks/comments, and multi-author revisions using desktop Word COM automation (`scripts/generate-paragraph-boundary-fixtures.ps1`). Validated all 10 fixtures and codified the empirical ownership rules in `tests/paragraph_boundary_matrix_tests.mjs`.
1263
-
1264
- **New files**
1265
-
1266
- - `tests/fixtures/paragraph-boundaries/`
1267
- - `tests/paragraph_boundary_matrix_tests.mjs`
1268
- - `scripts/generate-paragraph-boundary-fixtures.ps1`
1269
-
1270
- **Fixture creation**
1271
-
1272
- Use desktop Word to create golden examples for:
1273
-
1274
- - split one paragraph in the middle;
1275
- - delete only the boundary between two paragraphs;
1276
- - delete an entire middle paragraph;
1277
- - insert a blank paragraph;
1278
- - boundaries between different paragraph styles;
1279
- - boundaries with different list levels/numIds;
1280
- - boundary before a section break;
1281
- - first/last paragraph in a table cell;
1282
- - adjacent bookmark/comment ranges;
1283
- - same-author and multi-author revisions.
1284
-
1285
- For every golden fixture, save pending, accepted, and rejected copies. Extract
1286
- the relevant XML and record which paragraph owns the inserted/deleted mark and
1287
- which `w:pPr` survives acceptance.
1288
-
1289
- **Done when**
1290
-
1291
- The ownership rules are evidence, not assumptions in prose:
1292
- 1. **Paragraph Split**: Paragraph A owns the inserted paragraph mark (`w:pPr/w:rPr/w:ins`), Paragraph B contains the moved runs without false insertion wrappers; on rejection, Paragraph B content is folded back into Paragraph A and Paragraph B is removed.
1293
- 2. **Boundary Deletion**: Paragraph A owns the deleted paragraph mark (`w:pPr/w:rPr/w:del`), Paragraph B's content is untouched in pending state; on acceptance, Paragraph A's contents are prepended into Paragraph B, Paragraph A is removed, and Paragraph B's properties survive (with any style inheritance tracked via `w:pPrChange`); on rejection, Paragraph A's deleted mark is removed and both paragraphs remain distinct.
1294
- 3. **Paragraph Deletion**: The deleted paragraph owns both its paragraph mark deletion (`w:pPr/w:rPr/w:del`) and text deletions (`w:del/w:r/w:delText`). On acceptance, the paragraph is removed; on rejection, the paragraph and its runs are restored intact.
1295
- 4. **Section Breaks**: Embedded in `w:pPr/w:sectPr`; deleting the break marks the paragraph mark ending Paragraph A deleted.
1296
- 5. **Table Cells**: Cell terminal paragraph constraints must be preserved; internal cell paragraph boundary deletions follow the standard merge.
1297
- 6. **Bookmarks and Comments**: Range start and end markers are retained within their respective paragraph nodes across boundary revisions.
1298
-
1299
- ### WP-10 — Paragraph Boundary Implementation [COMPLETED 2026-09-05]
1300
-
1301
- **Depends on:** WP-09
1302
- **Produces runtime changes:** New proven structural routes
1303
- **Status:** Completed — implemented structural paragraph boundary mutations (split, join, blank and whole-paragraph deletions) with explicit schema safeguards (refusing sole table cell paragraph deletion, cross-cell joins, section break paragraph deletion, field instruction splits, open bookmark splits returning `UNSAFE_PARAGRAPH_BOUNDARY`). Verified across 11 unit tests in `tests/paragraph_boundary_implementation_tests.mjs`, all 10 Word-native golden fixture tests in `tests/paragraph_boundary_matrix_tests.mjs`, and full regression/fuzz suites (77/77 test suites passing).
1304
-
1305
- **Files to inspect/edit**
1306
-
1307
- - `engine/reconstruction-mode.js`
1308
- - `engine/reconstruction-writer.js`
1309
- - `engine/run-builders.js`
1310
- - `services/revision-comment-management.js`
1311
- - `services/document-operation-mutations.js`
1312
-
1313
- **Implementation order**
1314
-
1315
- 1. Boundary removal retaining both texts.
1316
- 2. Paragraph split retaining all source text.
1317
- 3. Entire paragraph deletion.
1318
- 4. Blank paragraph insertion/deletion.
1319
- 5. List paragraph cases.
1320
- 6. Only then consider section/table-cell cases supported by WP-09 evidence.
1321
-
1322
- **Boundary removal algorithm**
1323
-
1324
- 1. Resolve adjacent A/B paragraphs under the same legal parent.
1325
- 2. Verify no unsupported boundary ownership.
1326
- 3. Mark the paragraph mark ending A as deleted.
1327
- 4. Leave B text active and in B while the revision is pending.
1328
- 5. On acceptance, merge using the golden-fixture property ownership rule.
1329
- 6. On rejection, remove only the boundary deletion marker.
1330
-
1331
- **Split algorithm**
1332
-
1333
- 1. Resolve a safe UTF-16 split boundary using surgical span mapping.
1334
- 2. Clone only the paragraph properties proven to transfer.
1335
- 3. Move trailing retained nodes into B without wrapping them as inserted text.
1336
- 4. Mark the paragraph mark ending A as inserted.
1337
- 5. On rejection, move retained B content back into A in exact order and remove B.
1338
-
1339
- **Required safeguards**
1340
-
1341
- - Never create an empty numbered/bulleted ghost paragraph after Reject All.
1342
- - Never place `w:sectPr` on both paragraphs.
1343
- - Never split a field instruction or move only one bookmark/comment boundary.
1344
- - Preserve tabs, breaks, hyperlinks, drawings, and SDTs as nodes.
1345
- - Return `UNSAFE_PARAGRAPH_BOUNDARY` before mutation for unsupported cases.
1346
-
1347
- ### WP-11 — Formatting Contract Completion [COMPLETED 2026-09-05]
1348
-
1349
- **Depends on:** WP-03, WP-06
1350
- **Produces runtime changes:** Additive operations; current default retained
1351
- **Status:** Completed — implemented explicit character-format (`format`, `character-format`) and paragraph-format (`paragraph-format`) operations with tracked revisions (`w:rPrChange`, `w:pPrChange`), schema ordering (`RPR_SCHEMA_ORDER`, `PPR_SCHEMA_ORDER`), direct prior state snapshots excluding nested change records, fresh ID preservation on run clones, author-aware coalescing policy (`coalesce-own-insertion`), and no-op handling without ID consumption. Verified in `tests/formatting_contract_tests.mjs` and all 78 test suites.
1352
-
1353
- **Files to inspect/edit**
1354
-
1355
- - `engine/format-application.js`
1356
- - `engine/formatting-removal.js`
1357
- - `engine/run-builders.js`
1358
- - `engine/rpr-helpers.js`
1359
- - `services/document-operation-contract.js`
1360
- - `services/revision-comment-management.js`
1361
-
1362
- **Steps**
1363
-
1364
- 1. Inventory current Markdown format hints and highlight behavior.
1365
- 2. Define explicit character-format and paragraph-format operations in runtime,
1366
- declarations, and schema.
1367
- 3. Split only the requested character span.
1368
- 4. Snapshot complete direct prior properties without nested change records.
1369
- 5. Apply explicit on/off properties in schema order.
1370
- 6. Generate one property change per resulting changed carrier.
1371
- 7. Preserve fresh unique IDs when a run containing history is cloned.
1372
- 8. Implement paragraph `w:pPrChange` generation separately from run formatting.
1373
- 9. Add author-aware coalescing only after the WP-06 selective-author policy
1374
- permits it.
1375
-
1376
- **Tests**
1377
-
1378
- - Every supported property: absent→on, on→off, and value→different value.
1379
- - Partial-run and multi-run spans.
1380
- - Character style plus direct formatting.
1381
- - Existing `w:rPrChange` and `w:pPrChange` history.
1382
- - Same/different author pending insertion.
1383
- - Accept/reject one author while retaining another's property change.
1384
- - No requested property change returns `no_change` without consuming an ID.
1385
-
1386
- ### WP-12 — Revision Token Core [COMPLETED 2026-09-05]
1387
-
1388
- **Depends on:** WP-01
1389
- **Produces runtime changes:** Additive inspection utility
1390
- **Status:** Completed — implemented binary framing revision token generator in `services/revision-token.js` (`buildRevisionTokenFraming`, `computeRevisionToken`, `computeRevisionTokenSync`, `computeDocumentPartsRevisionToken`, `validateRevisionToken`), package-scoped token computation in `node/docx-document.js` (`computePackageRevisionToken`, `DocxDocument.prototype.getRevisionToken`, `DocxDocument.prototype.revisionToken`), integrated into `inspectDocumentParts`, browser-safe with 0 Node imports in root graph, verified in `tests/revision_token_tests.mjs` and all 79 test suites.
1391
-
1392
- **New/edited files**
1393
-
1394
- - `services/revision-token.js` or an equivalently focused leaf module
1395
- - `services/document-inspection.js`
1396
- - `node/docx-document.js`
1397
- - browser/root dependency isolation tests
1398
-
1399
- **Binary framing**
1400
-
1401
- Hash this conceptual sequence using UTF-8 names and raw payload bytes:
1402
-
1403
- ```text
1404
- magic = "docx-redline-revision-token\0"
1405
- version = uint32be(1)
1406
- scopeLength + scope
1407
- entryCount
1408
- for each entry sorted by normalized name:
1409
- nameLength + name
1410
- payloadLength + payload
1411
- ```
1412
-
1413
- Use fixed-width unsigned big-endian lengths or another unambiguous documented
1414
- encoding. Reject duplicate normalized entry names. The browser implementation
1415
- may use Web Crypto asynchronously; the Node package path may use `node:crypto`
1416
- behind a Node-only module. Do not import Node crypto from the browser-safe root
1417
- graph.
1418
-
1419
- **Tests**
1420
-
1421
- - Stable across ZIP compression/order/timestamp changes.
1422
- - Changes when any entry name or payload changes.
1423
- - Stable regardless of Map/object enumeration order.
1424
- - Different scope or version produces a different token.
1425
- - Binary zero bytes and non-ASCII entry names frame correctly.
1426
-
1427
- ### WP-13 — Revision Token Enforcement [COMPLETED 2026-09-05]
1428
-
1429
- **Depends on:** WP-12
1430
- **Produces runtime changes:** Additive apply precondition
1431
- **Status:** Completed — added `expectedRevision` precondition support across package facade (`DocxDocument.prototype.applyOperations`), batch runner (`applyOperationsToDocumentXml`), single-op runner (`applyOperationToDocumentXml`), CLI (`--expected-revision` and JSON file wrapper), and document-operations schema. Validates token syntax, enforces scope isolation (`REVISION_TOKEN_SCOPE_MISMATCH`), timing-safe token equality comparison with `areRevisionTokensEqual`, structured rejection with `REVISION_MISMATCH` before mutating state or creating artifacts in both atomic and non-atomic modes. Verified in `tests/revision_token_enforcement_tests.mjs` and all 80 test suites.
1432
-
1433
- **Steps**
1434
-
1435
- 1. Add `expectedRevision` to package and applicable lower-level options.
1436
- 2. Validate token syntax, version, algorithm, and scope before comparison.
1437
- 3. Recompute from the exact input supplied to apply.
1438
- 4. Perform comparison before creating session mutations or companion artifacts.
1439
- 5. Use a timing-safe equality primitive where available; correctness must not
1440
- depend on it.
1441
- 6. Return `REVISION_TOKEN_SCOPE_MISMATCH` for incompatible scopes and
1442
- `REVISION_MISMATCH` for unequal valid tokens.
1443
- 7. Ensure atomic and non-atomic modes both leave input untouched on mismatch.
1444
-
1445
- **Tests**
1446
-
1447
- - Matching token applies.
1448
- - Stale token refuses.
1449
- - Token from another document refuses.
1450
- - Parts token cannot guard package API and vice versa.
1451
- - No operation result or artifact is partially produced.
1452
- - Omitting the option follows the byte-for-byte legacy path.
1453
-
1454
- ### WP-14 — Capture Contract and Dependency Graph [COMPLETED 2026-09-05]
1455
-
1456
- **Depends on:** WP-05
1457
- **Produces runtime changes:** Validation and scheduling for capture batches
1458
- **Status:** Completed — added `operationId`, `captureKey`, `target.captureRef`, and `target.select` to document operation contract, TypeScript declarations, and JSON schema. Implemented `buildOperationDependencyPlan` performing Kahn topological sort with comment priority among ready nodes, duplicate key detection (`DUPLICATE_CAPTURE_KEY`), missing producer detection (`CAPTURE_NOT_FOUND`), and cycle detection (`CAPTURE_DEPENDENCY_CYCLE`). Integrated dependency graph planning into `applyOperationsToDocumentXml` and `orderOperationsForStableTargets`. Updated `preflightOperations` to validate dependency graph and mark capture consumers as `status: 'deferred'` without searching static DOM. Ensured batch `results` remain sorted by original operation index while `executionOrder` reports actual execution order. Verified in `tests/capture_dependency_graph_tests.mjs` and all 81 test suites.
1459
-
1460
- **Files to inspect/edit**
1461
-
1462
- - `services/document-operation-contract.js`
1463
- - `services/operation-preflight.js`
1464
- - `services/batch-operation-orchestrator.js`
1465
- - public contract surfaces from Section 17.4
1466
-
1467
- **Steps**
1468
-
1469
- 1. Validate `operationId`, `captureKey`, and `target.captureRef` as non-empty,
1470
- bounded strings.
1471
- 2. Build `captureKey -> source operation index`.
1472
- 3. Reject duplicates and missing producers.
1473
- 4. Add dependency edges from consumer to producer.
1474
- 5. Perform a stable topological sort using original operation index as the tie
1475
- breaker.
1476
- 6. Apply existing comment-first priority only among currently ready independent
1477
- nodes.
1478
- 7. Detect cycles before constructing `DocumentOperationSession` mutations.
1479
- 8. Preflight static targets immediately and mark capture consumers `deferred`.
1480
-
1481
- **Tests**
1482
-
1483
- - Linear dependency, diamond dependency, independent comments, duplicate key,
1484
- missing producer, self-cycle, multi-node cycle, and stable tie order.
1485
- - `results` remain sorted by original index while `executionOrder` reports actual
1486
- execution.
1487
- - Batches without capture fields preserve current scheduling exactly.
1488
-
1489
- ### WP-15 — Capture Storage, Resolution, and Savepoints [COMPLETED 2026-09-05]
1490
-
1491
- **Depends on:** WP-14
1492
- **Produces runtime changes:** Capture execution
1493
- **Status:** Completed — added `captureTable` to `DocumentOperationSession` with savepoint clone/restore and atomic rollback clearing. Implemented `deriveCapturedEntity`, `invalidateAffectedCaptures`, and `resolveTargetFromCapture` in `services/capture-engine.js`. Wired capture resolution and live/removed mutation node tracking into `services/document-operation-mutations.js` and `services/document-operation-applier.js`. Verified all 10 required routes (plain paragraph, structured content, lists, tables, producer no-ops, producer failures, consumer rollback, staleness detection, ambiguous selection, non-atomic isolation) in `tests/capture_resolution_tests.mjs` and all 82 test suites.
1494
-
1495
- **Files to inspect/edit**
1496
-
1497
- - `services/document-operation-session.js`
1498
- - `services/document-operation-applier.js`
1499
- - `services/document-operation-mutations.js`
1500
- - structured/list/table replacement paths
1501
-
1502
- **Steps**
1503
-
1504
- 1. Add `captureTable` to the session.
1505
- 2. Clone/restore it with every savepoint.
1506
- 3. After a successful changed producer, derive captures from the actual imported
1507
- live-DOM nodes—not from the requested text alone.
1508
- 4. Store serializable identity evidence; do not store raw nodes across
1509
- savepoints.
1510
- 5. Reacquire candidates from the current paragraph/table index.
1511
- 6. Verify fingerprints and expected text before resolving `select`.
1512
- 7. Require a unique `select` match within the capture.
1513
- 8. Invalidate or deterministically update captures affected by later structural
1514
- replacement.
1515
- 9. Discard all captures on atomic rollback.
1516
-
1517
- **Required route tests**
1518
-
1519
- - Plain paragraph insertion → comment.
1520
- - Structured heading/paragraph insertion → comment.
1521
- - List insertion → format one item.
1522
- - Table insertion → target one cell only if the capture schema supports cells.
1523
- - Producer no-op, producer failure, consumer failure, stale capture, ambiguous
1524
- selection, and later replacement of captured content.
1525
-
1526
- ### WP-16 — Receipt Collector [COMPLETED 2026-09-05]
1527
-
1528
- **Depends on:** WP-03
1529
- **Produces runtime changes:** Internal telemetry collector
1530
- **Status:** Completed — added `ReceiptCollector` in `services/receipt-collector.js` owned by `DocumentOperationSession` with savepoint snapshots and restore. Instrumented `core/types.js` (`createRevisionMetadata`, `createReplacementRevisionEvent`), `engine/run-builders.js` (`createTrackChange`, `markParagraphMark`, `snapshotAndAttachRPrChange`, `snapshotAndAttachPPrChange`), `engine/reconstruction-writer.js`, `engine/table-mode.js`, `pipeline/serialization.js`, `services/comment-engine.js`, `services/document-operation-mutations.js`, `services/batch-operation-orchestrator.js`, and `services/standalone-docx-plumbing.js` to record revisions, comments, numbering, relationships, and targets directly at point of allocation/attachment. Verified via `tests/receipt_collector_tests.mjs` and all 83 test suites.
1531
-
1532
- **Files to inspect/edit**
1533
-
1534
- - `core/types.js`
1535
- - revision builders
1536
- - comment engine
1537
- - numbering helpers
1538
- - package relationship/content-type helpers
1539
- - `services/document-operation-session.js`
1540
-
1541
- **Steps**
1542
-
1543
- 1. Add a session-owned collector with `beginOperation(index)`, record methods,
1544
- `commitOperation()`, and `restore(savepoint)`.
1545
- 2. Record IDs at the successful allocation/attachment point, not by reparsing a
1546
- partially serialized string.
1547
- 3. Associate every item with kind and part name.
1548
- 4. Snapshot collector state in operation savepoints.
1549
- 5. Ensure failed/no-op allocations disappear on restore.
1550
- 6. Keep the collector internal until WP-17 proves reconciliation with output.
1551
-
1552
- **Tests**
1553
-
1554
- - Text replacement records distinct deletion/insertion IDs.
1555
- - Formatting records property-change IDs.
1556
- - Comment/list/package plumbing records companion IDs.
1557
- - Failed and no-op operations leave collector state unchanged.
1558
-
1559
- ### WP-17 — Public Commit-Aware Receipts
1560
-
1561
- **Depends on:** WP-16
1562
- **Produces runtime changes:** Additive result field
1563
- **Status:** Completed — added `MutationReceipt` and `MutationReceiptRevisionItem` interfaces to types/index. Instrumented batch orchestrator and single-operation applier to attach receipts to every item result and top-level batch results (`receipts`). Implemented output reconciliation via `reconcileReceiptsAgainstOutput` verifying all committed IDs are present in serialized XML or failing the transaction. Supported atomic rollback marking prior items `rolled_back` with `committed: false`. Added `tests/mutation_receipt_tests.mjs` verifying all required test cases. All 84 test suites pass.
1564
-
1565
- **Steps**
1566
-
1567
- 1. Convert internal collector records to the receipt schema in Section 12.
1568
- 2. Attach provisional receipts to item results.
1569
- 3. At successful transaction commit, set applied receipts `committed: true`.
1570
- 4. On atomic rollback, rewrite every provisionally applied receipt to
1571
- `finalDisposition: 'rolled_back'` and `committed: false`.
1572
- 5. Mark unexecuted steps `not_attempted` when `continueOnError: false`.
1573
- 6. Reopen committed output and reconcile every reported durable ID.
1574
- 7. If reconciliation fails, treat it as a transaction error rather than return
1575
- inaccurate telemetry.
1576
-
1577
- **Tests**
1578
-
1579
- - Successful atomic and non-atomic batches.
1580
- - Early stop and continue-on-error.
1581
- - Later failure rolling back earlier success.
1582
- - Mixed text/comment/list/table/format operations.
1583
- - Existing IDs are never incorrectly reported as newly allocated.
1584
-
1585
- ### WP-18 — Strict Application Warning Cycle
1586
-
1587
- **Depends on:** WP-05
1588
- **Produces runtime changes:** Warning only before v1.0.0
1589
- **Status:** Completed — added `AMBIGUOUS_TARGET_HEURISTIC_USED` warning in `core/paragraph-targeting.js` triggered only when permissive resolution chooses among multiple identical candidate paragraphs. Integrated candidate count and migration guidance into warnings in `document-operation-applier.js`, `batch-operation-orchestrator.js`, and `operation-preflight.js`. Updated `README.md`, `AGENTS.md`, `ARCHITECTURE.md`, and `CHANGELOG.md` with migration guidance and strict descriptor examples. Created `tests/strict_application_warning_tests.mjs` verifying all warning cycle invariants. All 85 test suites pass.
1590
-
1591
- **Steps**
1592
-
1593
- 1. Identify every public application entry point and its current strict default.
1594
- 2. Add a warning only when permissive resolution actually chooses among multiple
1595
- candidates—not merely when `strictTargets: false` is present.
1596
- 3. Include candidate count and migration guidance without exposing document
1597
- content beyond existing diagnostics policy.
1598
- 4. Ensure preflight and strict application share the same candidate resolver.
1599
- 5. Update recommended examples to strict descriptors.
1600
-
1601
- **v1.0.0 task**
1602
-
1603
- Change the remaining permissive defaults only in a dedicated major-version
1604
- work package. Update semver notes, migration examples, and compatibility tests at
1605
- that time; do not silently make the change during v0.x implementation.
1606
-
1607
- ## 19. Test Fixture Naming and Assertions
1608
-
1609
- Use names that state the invariant, not the implementation. Examples:
1610
-
1611
- - `paragraph-boundary-join-retains-second-text`
1612
- - `paragraph-split-reject-restores-source-properties`
1613
- - `replacement-pair-preserves-comment-boundary`
1614
- - `rejected-view-target-is-ambiguous`
1615
- - `capture-savepoint-reacquires-current-node`
1616
- - `atomic-receipt-marks-prior-success-rolled-back`
1617
-
1618
- Every structural fixture should assert at least:
1619
-
1620
- 1. operation status and error code;
1621
- 2. generated revision element location and unique IDs;
1622
- 3. exact accepted text;
1623
- 4. exact rejected text;
1624
- 5. accepted paragraph/table/list structure;
1625
- 6. rejected paragraph/table/list structure;
1626
- 7. preservation of nearby unknown/structural nodes;
1627
- 8. validation result;
1628
- 9. package artifact presence/absence;
1629
- 10. no unexpected mutation-envelope drift.
1630
-
1631
- Avoid assertions that merely search the serialized XML for a tag. Parse the
1632
- output and assert parent, child, sibling order, namespace, attributes, and
1633
- affected text. Regex assertions may supplement but not replace DOM assertions.
1634
-
1635
- ## 20. Implementation Reporting Template
1636
-
1637
- The implementing agent should finish each work package with this report:
1638
-
1639
- ```text
1640
- Work package: WP-XX — Name
1641
- Outcome: complete | partial | blocked
1642
-
1643
- Behavior implemented:
1644
- - ...
1645
-
1646
- Compatibility:
1647
- - Public API change: ...
1648
- - Default behavior change: none | ...
1649
- - Serialized OOXML change: none | ...
1650
-
1651
- Files changed:
1652
- - path — purpose
1653
-
1654
- Tests added:
1655
- - test — invariant
1656
-
1657
- Verification run:
1658
- - command — result
1659
-
1660
- Deferred or refused cases:
1661
- - case — reason and error code
1662
-
1663
- Manual Word evidence:
1664
- - not required | artifact path and review status
1665
- ```
1666
-
1667
- Do not report a work package complete when focused tests pass but shared gates,
1668
- package validation, required Word evidence, declarations, schemas, or docs remain
1669
- unfinished.