@ansonlai/docx-redline-js 0.7.2 → 0.8.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 (53) hide show
  1. package/AGENTS.md +15 -10
  2. package/ARCHITECTURE.md +30 -10
  3. package/CHANGELOG.md +539 -431
  4. package/README.md +331 -309
  5. package/adapters/xml-adapter.js +84 -56
  6. package/core/sha256.js +120 -0
  7. package/dist/docx-redline-js.esm.js +8982 -693
  8. package/dist/docx-redline-js.esm.js.map +4 -4
  9. package/dist/docx-redline-js.esm.min.js +102 -89
  10. package/dist/docx-redline-js.esm.min.js.map +4 -4
  11. package/dist/docx-redline.bundle.cjs +28305 -0
  12. package/dist/docx-redline.bundle.cjs.map +7 -0
  13. package/dist/docx-redline.bundle.js +28296 -0
  14. package/dist/docx-redline.bundle.js.map +7 -0
  15. package/docs/AGENT_FAST_START.md +2 -1
  16. package/docs/AGENT_KNOWLEDGE_BASE.md +480 -393
  17. package/docs/SKILL_AUTHORING.md +5 -2
  18. package/docs/TESTING.md +2 -1
  19. package/docs/schemas/document-operations.schema.json +29 -0
  20. package/document/docx-document.d.ts +39 -0
  21. package/document/docx-document.js +1010 -0
  22. package/document/zip-archive.js +258 -0
  23. package/engine/reconstruction-writer.js +18 -0
  24. package/index.d.ts +598 -531
  25. package/index.js +349 -340
  26. package/node/cli-help.js +5 -5
  27. package/node/cli.js +760 -754
  28. package/node/docx-document.js +11 -355
  29. package/node/index.d.ts +1 -1
  30. package/node/zip-archive.js +28 -38
  31. package/package.json +14 -11
  32. package/scripts/build.mjs +40 -5
  33. package/scripts/generate-word-comment-edge-case-fixture.ps1 +58 -0
  34. package/scripts/generate-word-comment-thread-fixture.ps1 +36 -0
  35. package/scripts/generate-word-header-footer-fixture.ps1 +32 -0
  36. package/scripts/generate-word-multi-section-fixture.ps1 +27 -0
  37. package/scripts/generate-word-package-integrity-cases.mjs +141 -0
  38. package/scripts/generate-word-thread-state-fixtures.ps1 +50 -0
  39. package/scripts/word-com-package-integrity.ps1 +98 -0
  40. package/services/batch-operation-orchestrator.js +262 -259
  41. package/services/comment-replies.js +96 -95
  42. package/services/comment-thread-parts.js +280 -0
  43. package/services/document-inspection.js +366 -365
  44. package/services/document-operation-applier.js +230 -202
  45. package/services/document-operation-contract.js +17 -2
  46. package/services/error-recovery.js +6 -0
  47. package/services/headers-footers.js +206 -0
  48. package/services/operation-batch-compiler.js +1 -0
  49. package/services/operation-preflight.js +231 -218
  50. package/services/package-parts.js +80 -0
  51. package/services/revision-token.js +11 -7
  52. package/services/standalone-docx-plumbing.js +631 -569
  53. package/services/standalone-operation-runner.d.ts +19 -3
package/README.md CHANGED
@@ -15,65 +15,65 @@ Converts AI-generated or programmatic text/markdown edits into valid Office Open
15
15
  - Comment management: delete comments by author or for all authors
16
16
  - Highlights: apply highlight colors to runs
17
17
  - Markdown and OOXML conversion in both directions
18
- - Status/error result fields for parse, targeting, and existing-revision failures
19
- - Localized exact replacements against inspected targets, plus optional fail-closed speculative CLI edits
20
- - Package plumbing helpers for numbering.xml, comments.xml, content types, and relationships
21
- - Zero host dependencies: works in Node.js, browsers, Deno, and similar JS runtimes with DOM parsing support
22
-
23
- ## What's New in 0.7.0
24
-
25
- Version 0.7.0 adds contract-8 localized edits. Agents can send a small
26
- `find`/`replace` request against an inspected target instead of reproducing a
27
- complete paragraph. An optional speculative form may omit the strong target
28
- when the source literal resolves uniquely; advanced scoping is described below.
29
-
30
- Speculative edits fail without writing when the anchor, paragraph, or source
31
- span is missing or ambiguous. Successful localized results include a committed
32
- `change` object with the selected legal location, bounded before/after excerpts,
33
- and actual accepted-view verification. Existing full-paragraph operations,
34
- tracked-change semantics, validation, rollback, and review policies remain
35
- available unchanged. See the [complete 0.7.0 release notes](./docs/releases/0.7.0.md).
18
+ - Status/error result fields for parse, targeting, and existing-revision failures
19
+ - Localized exact replacements against inspected targets, plus optional fail-closed speculative CLI edits
20
+ - Package plumbing helpers for numbering.xml, comments.xml, content types, and relationships
21
+ - Zero host dependencies: works in Node.js, browsers, Deno, and similar JS runtimes with DOM parsing support
22
+
23
+ ## What's New in 0.7.0
24
+
25
+ Version 0.7.0 adds contract-8 localized edits. Agents can send a small
26
+ `find`/`replace` request against an inspected target instead of reproducing a
27
+ complete paragraph. An optional speculative form may omit the strong target
28
+ when the source literal resolves uniquely; advanced scoping is described below.
29
+
30
+ Speculative edits fail without writing when the anchor, paragraph, or source
31
+ span is missing or ambiguous. Successful localized results include a committed
32
+ `change` object with the selected legal location, bounded before/after excerpts,
33
+ and actual accepted-view verification. Existing full-paragraph operations,
34
+ tracked-change semantics, validation, rollback, and review policies remain
35
+ available unchanged. See the [complete 0.7.0 release notes](./docs/releases/0.7.0.md).
36
36
  ## Documentation Index
37
37
 
38
- | Document | Description |
39
- |---|---|
40
- | **[README.md](./README.md)** | Library overview, installation, quick start, and public API reference |
41
- | **[docs/AGENT_FAST_START.md](./docs/AGENT_FAST_START.md)** | Compact ordinary-edit protocol for structured tools and shell-only agents |
42
- | **[AGENTS.md](./AGENTS.md)** | Short repository launch card for task routing and contributor verification |
43
- | **[docs/AGENT_KNOWLEDGE_BASE.md](./docs/AGENT_KNOWLEDGE_BASE.md)** | Full agent reference, CLI workflow, operation examples, error recovery, options, and gotchas |
38
+ | Document | Description |
39
+ |---|---|
40
+ | **[README.md](./README.md)** | Library overview, installation, quick start, and public API reference |
41
+ | **[docs/AGENT_FAST_START.md](./docs/AGENT_FAST_START.md)** | Compact ordinary-edit protocol for structured tools and shell-only agents |
42
+ | **[AGENTS.md](./AGENTS.md)** | Short repository launch card for task routing and contributor verification |
43
+ | **[docs/AGENT_KNOWLEDGE_BASE.md](./docs/AGENT_KNOWLEDGE_BASE.md)** | Full agent reference, CLI workflow, operation examples, error recovery, options, and gotchas |
44
44
  | **[ARCHITECTURE.md](./ARCHITECTURE.md)** | Contributor architecture, module responsibilities, end-to-end data flow, and contracts |
45
45
  | **[docs/TESTING.md](./docs/TESTING.md)** | Complete testing guide, test lanes, independent oracle validation, and Word visual review checklist |
46
- | **[CHANGELOG.md](./CHANGELOG.md)** | Release history, breaking changes, and migration notes |
47
-
48
- ## Repository Layout
49
-
50
- The package exposes three levels of API:
51
-
52
- | Level | Entry point | Use it for |
53
- |---|---|---|
54
- | Host-independent OOXML API | `index.js` | Paragraph/range transforms and exported OOXML utilities in browsers, Node.js, or another DOM-capable runtime |
55
- | Standalone document XML runner | `services/standalone-operation-runner.js` | Applying operations to a complete `word/document.xml` string |
56
- | Node/DOCX API and CLI | `node/index.js`, `bin/docx-redline.js` | Reading, changing, validating, and writing complete `.docx` ZIP packages |
57
-
58
- Implementation folders have distinct roles: `core/` holds shared OOXML and
59
- targeting primitives; `pipeline/` handles ingestion, diffing, markdown, lists,
60
- and serialization; `engine/` performs reconciliation; `services/` coordinates
61
- document operations and package artifacts; `node/` contains Node-only ZIP and
62
- whole-document code. Tests are directly runnable `tests/*.mjs` files, while
63
- `tests/helpers/` and `tests/fixtures/` contain support code and data.
64
-
65
- Contributors and coding agents should start with the routing table in
66
- [AGENTS.md](./AGENTS.md#pick-the-route) before exploring the tree. The full
67
- dependency and ownership map is in [ARCHITECTURE.md](./ARCHITECTURE.md).
68
-
69
- For document-operation JSON, choose operations by the desired output structure,
70
- not by the everyday meaning of the type name. `redline` and `replace` provide
71
- ordinary text replacement; `list-change` and `table-reconciliation` provide
72
- structural intent; and ordinary `insert` is a compatibility alias of the
73
- redline path unless it includes a rejected-view target and anchor. In every
74
- ordinary text-bearing operation, `modified` is the complete desired content for
75
- the target. See the [operation model](./docs/AGENT_KNOWLEDGE_BASE.md#operation-model-choose-by-output-shape)
76
- and the [JSON schema](./docs/schemas/document-operations.schema.json).
46
+ | **[CHANGELOG.md](./CHANGELOG.md)** | Release history, breaking changes, and migration notes |
47
+
48
+ ## Repository Layout
49
+
50
+ The package exposes three levels of API:
51
+
52
+ | Level | Entry point | Use it for |
53
+ |---|---|---|
54
+ | Host-independent OOXML API | `index.js` | Paragraph/range transforms and exported OOXML utilities in browsers, Node.js, or another DOM-capable runtime |
55
+ | Standalone document XML runner | `services/standalone-operation-runner.js` | Applying operations to a complete `word/document.xml` string |
56
+ | Node/DOCX API and CLI | `node/index.js`, `bin/docx-redline.js` | Reading, changing, validating, and writing complete `.docx` ZIP packages |
57
+
58
+ Implementation folders have distinct roles: `core/` holds shared OOXML and
59
+ targeting primitives; `pipeline/` handles ingestion, diffing, markdown, lists,
60
+ and serialization; `engine/` performs reconciliation; `services/` coordinates
61
+ document operations and package artifacts; `node/` contains Node-only ZIP and
62
+ whole-document code. Tests are directly runnable `tests/*.mjs` files, while
63
+ `tests/helpers/` and `tests/fixtures/` contain support code and data.
64
+
65
+ Contributors and coding agents should start with the routing table in
66
+ [AGENTS.md](./AGENTS.md#pick-the-route) before exploring the tree. The full
67
+ dependency and ownership map is in [ARCHITECTURE.md](./ARCHITECTURE.md).
68
+
69
+ For document-operation JSON, choose operations by the desired output structure,
70
+ not by the everyday meaning of the type name. `redline` and `replace` provide
71
+ ordinary text replacement; `list-change` and `table-reconciliation` provide
72
+ structural intent; and ordinary `insert` is a compatibility alias of the
73
+ redline path unless it includes a rejected-view target and anchor. In every
74
+ ordinary text-bearing operation, `modified` is the complete desired content for
75
+ the target. See the [operation model](./docs/AGENT_KNOWLEDGE_BASE.md#operation-model-choose-by-output-shape)
76
+ and the [JSON schema](./docs/schemas/document-operations.schema.json).
77
77
 
78
78
  ## Install
79
79
 
@@ -159,23 +159,23 @@ import { inspectDocumentParts } from '@ansonlai/docx-redline-js';
159
159
  const inventory = inspectDocumentParts({ documentXml, commentsXml, numberingXml });
160
160
  ```
161
161
 
162
- Inspection returns exact paragraph text, target IDs/fingerprints, headings,
163
- table/list context, revision authors, and joined comment anchors. Filters such
164
- as `search`, `indexes`, `range`, `revisedOnly`, `inTable`, and `skipEmpty`
165
- limit output. Search is a case-insensitive substring match; `around` adds nearby drafting
166
- context, while `limit` and `after` page direct hits. When a search yields 0
167
- matches in the active view, `selection.hint` provides guidance if matches exist
168
- in the alternate revision view (such as searching for deleted text to restore).
169
- Compact `extract --revised` output retains revision authors and marks paragraphs
170
- whose content is wholly hidden in the selected view as `deleted` or `inserted`.
171
- One such paragraph can be restored without restating it:
172
- `docx-redline apply input.docx --restore --target-id ID --output reviewed.docx`.
173
- Add one exact `--find`/`--replace` pair to make a localized correction while
174
- restoring. The shortcut requires a strong rejected-view paragraph ID and does
175
- not infer a range or adjacent content.
176
- `revisionView` accepts `accepted`, `rejected`, or `current`. Returned
177
- paragraphs identify matches versus context and include `humanReference` for
178
- user-facing summaries.
162
+ Inspection returns exact paragraph text, target IDs/fingerprints, headings,
163
+ table/list context, revision authors, and joined comment anchors. Filters such
164
+ as `search`, `indexes`, `range`, `revisedOnly`, `inTable`, and `skipEmpty`
165
+ limit output. Search is a case-insensitive substring match; `around` adds nearby drafting
166
+ context, while `limit` and `after` page direct hits. When a search yields 0
167
+ matches in the active view, `selection.hint` provides guidance if matches exist
168
+ in the alternate revision view (such as searching for deleted text to restore).
169
+ Compact `extract --revised` output retains revision authors and marks paragraphs
170
+ whose content is wholly hidden in the selected view as `deleted` or `inserted`.
171
+ One such paragraph can be restored without restating it:
172
+ `docx-redline apply input.docx --restore --target-id ID --output reviewed.docx`.
173
+ Add one exact `--find`/`--replace` pair to make a localized correction while
174
+ restoring. The shortcut requires a strong rejected-view paragraph ID and does
175
+ not infer a range or adjacent content.
176
+ `revisionView` accepts `accepted`, `rejected`, or `current`. Returned
177
+ paragraphs identify matches versus context and include `humanReference` for
178
+ user-facing summaries.
179
179
 
180
180
  For complete `.docx` buffers in Node:
181
181
 
@@ -188,69 +188,69 @@ const result = await document.applyOperations(operations, {
188
188
  const outputBuffer = result.toBuffer();
189
189
  ```
190
190
 
191
- The Node facade performs edits, artifact merges, package wiring, validation,
192
- and commit as one transaction. It defaults to strict targets and returns the
193
- untouched input with `written: false` on atomic failure. It is isolated from
194
- the root/browser dependency graph.
195
-
196
- Install `@xmldom/xmldom` alongside the package when using the Node facade or
197
- CLI; it remains an optional peer so browser consumers do not install a DOM shim.
198
-
199
- #### Example agent session wrapper (development only)
200
-
201
- [`examples/agent-session-wrapper.mjs`](./examples/agent-session-wrapper.mjs)
202
- demonstrates how a custom agent harness can keep one `DocxDocument` open, return
203
- short revision-bound target handles, apply safe defaults once, and translate a
204
- narrow edit request into canonical document operations:
205
-
206
- ```js
207
- import { readFile } from 'node:fs/promises';
208
- import { createExampleAgentSession } from './examples/agent-session-wrapper.mjs';
209
-
210
- const session = createExampleAgentSession(await readFile('contract.docx'), {
211
- profile: { author: 'Editor' }
212
- });
213
- const inspection = session.inspect({ search: 'termination', around: 2 });
214
- const clause = inspection.targets.find(target => target.role === 'match');
215
- const result = await session.applyEdits([{
216
- target: clause.handle,
217
- replacements: [{
218
- find: 'The Company may terminate',
219
- replace: 'Either party may terminate'
220
- }]
221
- }]);
222
- ```
223
-
224
- This file is a testable sample, not a package export or supported alternate
225
- mutation engine. It delegates to `@ansonlai/docx-redline-js/node`, is excluded
226
- from the published package files, and is intended to help MCP servers, Claude
227
- skills, OpenCode tools, and other custom harnesses design thin integrations.
228
- The sample binds each handle to the inspected package revision and view, returns
229
- new handles after successful mutations, and expands localized exact replacements
230
- into complete desired paragraph text before delegating to the canonical redline
231
- operation. Duplicate matches require an explicit `occurrence`; missing,
232
- ambiguous, overlapping, and conflicting patches fail before document mutation.
233
- From a source checkout, run `npm run benchmark:agent` to compare its native
234
- execution and serialized request size with a canonical stateless Node workflow.
235
- The example and its benchmark are excluded from the published package. The
236
- benchmark explicitly does not claim to measure LLM reasoning or provider/tool
237
- latency. Checked comparative results are in the
238
- [agent protocol rollout audit](./docs/validation-reports/2026-09-12-agent-protocol-rollout.md).
239
-
240
- > **Pre-1.0 integration warning:** Agent skills, MCP servers, and harnesses
241
- > should pin the exact `@ansonlai/docx-redline-js` release they were tested
242
- > against and declare the CLI contract version/capabilities they require. Do
243
- > not treat a newly installed release or contract as verified until the
244
- > integration's compatibility tests pass. See the
245
- > [skill-authoring contract](./docs/SKILL_AUTHORING.md#runtime-negotiation).
246
-
247
- ### Agent CLI
248
-
249
- ```bash
250
- # Default agent workflow: inspect once, then apply once
251
- docx-redline extract contract.docx --search "termination" --around 3
252
- node emit-operations.mjs | docx-redline apply contract.docx --operations - --profile agent --compact --output reviewed.docx
253
- ```
191
+ The Node facade performs edits, artifact merges, package wiring, validation,
192
+ and commit as one transaction. It defaults to strict targets and returns the
193
+ untouched input with `written: false` on atomic failure. It is isolated from
194
+ the root/browser dependency graph.
195
+
196
+ Install `@xmldom/xmldom` alongside the package when using the Node facade or
197
+ CLI; it remains an optional peer so browser consumers do not install a DOM shim.
198
+
199
+ #### Example agent session wrapper (development only)
200
+
201
+ [`examples/agent-session-wrapper.mjs`](./examples/agent-session-wrapper.mjs)
202
+ demonstrates how a custom agent harness can keep one `DocxDocument` open, return
203
+ short revision-bound target handles, apply safe defaults once, and translate a
204
+ narrow edit request into canonical document operations:
205
+
206
+ ```js
207
+ import { readFile } from 'node:fs/promises';
208
+ import { createExampleAgentSession } from './examples/agent-session-wrapper.mjs';
209
+
210
+ const session = createExampleAgentSession(await readFile('contract.docx'), {
211
+ profile: { author: 'Editor' }
212
+ });
213
+ const inspection = session.inspect({ search: 'termination', around: 2 });
214
+ const clause = inspection.targets.find(target => target.role === 'match');
215
+ const result = await session.applyEdits([{
216
+ target: clause.handle,
217
+ replacements: [{
218
+ find: 'The Company may terminate',
219
+ replace: 'Either party may terminate'
220
+ }]
221
+ }]);
222
+ ```
223
+
224
+ This file is a testable sample, not a package export or supported alternate
225
+ mutation engine. It delegates to `@ansonlai/docx-redline-js/node`, is excluded
226
+ from the published package files, and is intended to help MCP servers, Claude
227
+ skills, OpenCode tools, and other custom harnesses design thin integrations.
228
+ The sample binds each handle to the inspected package revision and view, returns
229
+ new handles after successful mutations, and expands localized exact replacements
230
+ into complete desired paragraph text before delegating to the canonical redline
231
+ operation. Duplicate matches require an explicit `occurrence`; missing,
232
+ ambiguous, overlapping, and conflicting patches fail before document mutation.
233
+ From a source checkout, run `npm run benchmark:agent` to compare its native
234
+ execution and serialized request size with a canonical stateless Node workflow.
235
+ The example and its benchmark are excluded from the published package. The
236
+ benchmark explicitly does not claim to measure LLM reasoning or provider/tool
237
+ latency. Checked comparative results are in the
238
+ [agent protocol rollout audit](./docs/validation-reports/2026-09-12-agent-protocol-rollout.md).
239
+
240
+ > **Pre-1.0 integration warning:** Agent skills, MCP servers, and harnesses
241
+ > should pin the exact `@ansonlai/docx-redline-js` release they were tested
242
+ > against and declare the CLI contract version/capabilities they require. Do
243
+ > not treat a newly installed release or contract as verified until the
244
+ > integration's compatibility tests pass. See the
245
+ > [skill-authoring contract](./docs/SKILL_AUTHORING.md#runtime-negotiation).
246
+
247
+ ### Agent CLI
248
+
249
+ ```bash
250
+ # Default agent workflow: inspect once, then apply once
251
+ docx-redline extract contract.docx --search "termination" --around 3
252
+ node emit-operations.mjs | docx-redline apply contract.docx --operations - --profile agent --compact --output reviewed.docx
253
+ ```
254
254
 
255
255
  ```bash
256
256
  # Inline one-liner edit (no operations file needed)
@@ -262,76 +262,78 @@ docx-redline apply contract.docx --target "Typo fix" --modified "Fixed typo" --n
262
262
  # Cross-author edit inside another reviewer's pending insertion
263
263
  docx-redline apply contract.docx --target "Another author's clause" --modified "Revised clause" --existing-revisions slice-cross-author --output reviewed.docx
264
264
 
265
- # High-assurance atomic batch with nonzero exit on any incomplete result
266
- docx-redline apply contract.docx --operations operations.json --atomic --require-complete --output reviewed.docx
267
-
268
- # Optional read-only audit before a deliberately staged batch
269
- docx-redline preflight contract.docx --operations operations.json --author "Editor"
270
- ```
265
+ # High-assurance atomic batch with nonzero exit on any incomplete result
266
+ docx-redline apply contract.docx --operations operations.json --atomic --require-complete --output reviewed.docx
267
+
268
+ # Optional read-only audit before a deliberately staged batch
269
+ docx-redline preflight contract.docx --operations operations.json --author "Editor"
270
+ ```
271
271
 
272
- All commands emit JSON on stdout. `apply` defaults:
272
+ All commands emit JSON on stdout. `apply` defaults:
273
273
  - **Author**: Defaults to `'AI Redliner'` (or `DOCX_REDLINE_AUTHOR` environment variable).
274
274
  - **Output overwrite**: Destination files provided via `--output` overwrite by default. Pass `--no-overwrite` or `--no-clobber` to safeguard existing destination files. The source input is never overwritten unless `--in-place` is specified.
275
275
  - **Existing revisions**: Defaults to `'merge-same-author'`. Pass `--existing-revisions slice-cross-author` to edit inside another reviewer's pending insertions with native carrier slicing.
276
- - **Transactionality**: Defaults to `atomic: false` (applies valid operations and reports any failures). Pass `--atomic` for all-or-nothing rollback on any operation error.
277
- - **Complete-success exit**: Pass `--require-complete` when a progressive `partial` result must exit with code `3`; errors exit with code `2`. Without the flag, partial results retain the legacy zero exit code, so always inspect `completion`.
278
- - **Agent profile**: `--profile agent` enables complete-success exit behavior while retaining the ordinary progressive transaction and existing-revision defaults. Compose it with `--atomic` or an explicit `--existing-revisions` policy when intended, and verify the resolved `effectiveOptions`.
279
- - **Operations from stdin**: `--operations -` reads the same array or `{ operations, expectedRevision }` envelope accepted from a file. Feed it from a JSON serializer or structured process API, not shell-interpolated legal text.
280
- - **Compact stdout**: `--compact` emits one-line mutation JSON. It changes serialization only, not operation semantics.
276
+ - **Transactionality**: Defaults to `atomic: false` (applies valid operations and reports any failures). Pass `--atomic` for all-or-nothing rollback on any operation error.
277
+ - **Complete-success exit**: Pass `--require-complete` when a progressive `partial` result must exit with code `3`; errors exit with code `2`. Without the flag, partial results retain the legacy zero exit code, so always inspect `completion`.
278
+ - **Agent profile**: `--profile agent` enables complete-success exit behavior while retaining the ordinary progressive transaction and existing-revision defaults. Compose it with `--atomic` or an explicit `--existing-revisions` policy when intended, and verify the resolved `effectiveOptions`.
279
+ - **Operations from stdin**: `--operations -` reads the same array or `{ operations, expectedRevision }` envelope accepted from a file. Feed it from a JSON serializer or structured process API, not shell-interpolated legal text.
280
+ - **Compact stdout**: `--compact` emits one-line mutation JSON. It changes serialization only, not operation semantics.
281
281
  - **Tracked changes**: Defaults to `generateRedlines: true`. Pass `--no-redlines` when clean direct text edits are desired.
282
- - **Inline edits**: Use `--target <text>` with `--modified <text>` or `--comment <text>` for quick one-liners without creating a JSON file.
283
- - **Localized edits**: After extraction, use `replacements` with the inspected `paragraphId` plus `fingerprint` to avoid restating a long paragraph. Retain `exactText` locally for drafting. Targetless `--find`/`--replace` is an optional speculative path described below.
284
- - **Invisible spaces**: Localized matching is case-sensitive and tries exact text first. If no exact span exists, ordinary spaces may match DOCX non-breaking spaces; ambiguous equivalent matches still fail closed, and `change.replacements[i].matchMode` reports `space_equivalent` when used.
285
- - **Compact mutation results**: `apply`, `accept`, `reject`, and `delete-comments` omit full OOXML/package payloads and inspection text from stdout. CLI operation results omit duplicate nested receipt bodies; the ordered top-level `receipts` array is authoritative. Successful exact target-match diagnostics are omitted and equivalent-whitespace matches retain only mode/count. Node and standalone-runner results remain unchanged. Use `validate` when full issue arrays are needed.
286
-
287
- Inspection commands default to 20 direct matches and a 48 KiB soft response
288
- budget when no explicit positional scope is supplied. When a search returns 0
289
- matches in the requested view, `selection.hint` provides immediate guidance if
290
- matches exist in the alternate view (such as searching for a deleted clause to
291
- restore). Use `--limit` with `--after <paragraph-index>` to continue, `--around N`
292
- (aliases `--context` and `-C`) to include nearby paragraphs, and `--all` only
293
- for deliberate unbounded inspection. Every retained target is complete; an
294
- individually oversized paragraph is returned whole with an `oversizeItem`
295
- marker. Machine `index` and `ref` fields are for targeting and pagination, not
296
- user-facing Word locations; use `humanReference`, `provision`, or
297
- `nearestHeading` in reports.
298
-
299
- `docx-redline version` reports contract version 8 and the additive
300
- `command-help-v1`, `inspection-context-v1`, `bounded-inspection-v1`,
301
- `human-document-references-v1`, `batch-start-source-binding`,
302
- `recovery-envelope-v1`, `require-complete-exit`, `operations-stdin`,
303
- `agent-safety-profile-v2`, `deduplicated-cli-receipts`, and
304
- `compact-cli-json-v1`, `localized-replacements-v1`,
305
- `speculative-search-apply-v1`, `localized-change-summary-v1`, and
306
- `restore-shortcuts-v1`, plus `localized-space-equivalence-v1` capabilities.
307
- Wrappers should negotiate only the
308
- capabilities they use. Run `docx-redline <command> --help` for that command's
309
- machine-readable options, behavior, exit codes, canonical GitHub documentation links, and compact examples.
310
-
311
- #### Optional speculative CLI path
312
-
313
- Harnesses with a measured confidence policy may attempt a targetless localized
314
- edit. This is not the default agent workflow:
315
-
316
- ```bash
317
- # Globally unique literal
318
- docx-redline apply contract.docx --find "thirty (30) days" --replace "sixty (60) days" --profile agent --output reviewed.docx
319
-
320
- # Optional directional scope using a longer, fairly unique nearby phrase
321
- docx-redline apply contract.docx --search "distinctive nearby heading or phrase" --context-range 1:3 --find "thirty (30) days" --replace "sixty (60) days" --profile agent --output reviewed.docx
322
- ```
323
-
324
- Generic or repeated anchors can widen the unioned scope until multiple
325
- paragraphs contain `find`, causing a safe `AMBIGUOUS_TARGET` failure and a
326
- recovery turn. `--around 3` is symmetric. On success,
327
- `change.context.anchorMatchCount > 1` signals a repeated anchor; confirm the
328
- returned location and bounded evidence. Matching remains case-sensitive and
329
- exact-first, with a narrow ordinary-space/NBSP fallback that still refuses
330
- ambiguity.
331
-
332
- See the [compact agent fast start](./docs/AGENT_FAST_START.md), the
333
- [skill/harness authoring contract](./docs/SKILL_AUTHORING.md), and the
334
- [operation JSON Schema](docs/schemas/document-operations.schema.json).
282
+ - **Inline edits**: Use `--target <text>` with `--modified <text>` or `--comment <text>` for quick one-liners without creating a JSON file.
283
+ - **Localized edits**: After extraction, use `replacements` with the inspected `paragraphId` plus `fingerprint` to avoid restating a long paragraph. Retain `exactText` locally for drafting. Targetless `--find`/`--replace` is an optional speculative path described below.
284
+ - **Invisible spaces**: Localized matching is case-sensitive and tries exact text first. If no exact span exists, ordinary spaces may match DOCX non-breaking spaces; ambiguous equivalent matches still fail closed, and `change.replacements[i].matchMode` reports `space_equivalent` when used.
285
+ - **Compact mutation results**: `apply`, `accept`, `reject`, and `delete-comments` omit full OOXML/package payloads and inspection text from stdout. CLI operation results omit duplicate nested receipt bodies; the ordered top-level `receipts` array is authoritative. Successful exact target-match diagnostics are omitted and equivalent-whitespace matches retain only mode/count. Node and standalone-runner results remain unchanged. Use `validate` when full issue arrays are needed.
286
+
287
+ Inspection commands default to 20 direct matches and a 48 KiB soft response
288
+ budget when no explicit positional scope is supplied. When a search returns 0
289
+ matches in the requested view, `selection.hint` provides immediate guidance if
290
+ matches exist in the alternate view (such as searching for a deleted clause to
291
+ restore). Use `--limit` with `--after <paragraph-index>` to continue, `--around N`
292
+ (aliases `--context` and `-C`) to include nearby paragraphs, and `--all` only
293
+ for deliberate unbounded inspection. Every retained target is complete; an
294
+ individually oversized paragraph is returned whole with an `oversizeItem`
295
+ marker. Machine `index` and `ref` fields are for targeting and pagination, not
296
+ user-facing Word locations; use `humanReference`, `provision`, or
297
+ `nearestHeading` in reports.
298
+
299
+ `docx-redline version` reports contract version 8 and the additive
300
+ `command-help-v1`, `inspection-context-v1`, `bounded-inspection-v1`,
301
+ `human-document-references-v1`, `batch-start-source-binding`,
302
+ `recovery-envelope-v1`, `require-complete-exit`, `operations-stdin`,
303
+ `agent-safety-profile-v2`, `deduplicated-cli-receipts`, and
304
+ `compact-cli-json-v1`, `localized-replacements-v1`,
305
+ `speculative-search-apply-v1`, `localized-change-summary-v1`, and
306
+ `restore-shortcuts-v1`, plus `localized-space-equivalence-v1` capabilities. Header/footer
307
+ `part` targeting, the `comment_resolve` operation and `delete-comments --comment-id` are advertised as
308
+ `header-footer-parts-v1`, `comment-resolve-operation-v1` and `delete-comments-by-id-v1`.
309
+ Wrappers should negotiate only the
310
+ capabilities they use. Run `docx-redline <command> --help` for that command's
311
+ machine-readable options, behavior, exit codes, canonical GitHub documentation links, and compact examples.
312
+
313
+ #### Optional speculative CLI path
314
+
315
+ Harnesses with a measured confidence policy may attempt a targetless localized
316
+ edit. This is not the default agent workflow:
317
+
318
+ ```bash
319
+ # Globally unique literal
320
+ docx-redline apply contract.docx --find "thirty (30) days" --replace "sixty (60) days" --profile agent --output reviewed.docx
321
+
322
+ # Optional directional scope using a longer, fairly unique nearby phrase
323
+ docx-redline apply contract.docx --search "distinctive nearby heading or phrase" --context-range 1:3 --find "thirty (30) days" --replace "sixty (60) days" --profile agent --output reviewed.docx
324
+ ```
325
+
326
+ Generic or repeated anchors can widen the unioned scope until multiple
327
+ paragraphs contain `find`, causing a safe `AMBIGUOUS_TARGET` failure and a
328
+ recovery turn. `--around 3` is symmetric. On success,
329
+ `change.context.anchorMatchCount > 1` signals a repeated anchor; confirm the
330
+ returned location and bounded evidence. Matching remains case-sensitive and
331
+ exact-first, with a narrow ordinary-space/NBSP fallback that still refuses
332
+ ambiguity.
333
+
334
+ See the [compact agent fast start](./docs/AGENT_FAST_START.md), the
335
+ [skill/harness authoring contract](./docs/SKILL_AUTHORING.md), and the
336
+ [operation JSON Schema](docs/schemas/document-operations.schema.json).
335
337
 
336
338
  ### Configuration (call once at startup)
337
339
 
@@ -365,10 +367,10 @@ Common result fields:
365
367
  | Field | Purpose |
366
368
  |-------|---------|
367
369
  | `status` | Operation status: `'ok'`, `'partial'`, `'no-op'`, or `'error'`. |
368
- | `error` | Present on failure; retains a stable `code` and adds recovery envelope version, stage, category, bounded context, and a machine-readable recovery action. |
369
- | `written` | CLI/facade boolean indicating whether the output file was successfully written to disk. |
370
- | `completion` | CLI-only boolean that is `true` only when a destination was written, top-level status is neither error nor partial, and no operation result failed. |
371
- | `rolledBack` | Present and `true` when an atomic batch encountered an error and rolled back all changes. |
370
+ | `error` | Present on failure; retains a stable `code` and adds recovery envelope version, stage, category, bounded context, and a machine-readable recovery action. |
371
+ | `written` | CLI/facade boolean indicating whether the output file was successfully written to disk. |
372
+ | `completion` | CLI-only boolean that is `true` only when a destination was written, top-level status is neither error nor partial, and no operation result failed. |
373
+ | `rolledBack` | Present and `true` when an atomic batch encountered an error and rolled back all changes. |
372
374
 
373
375
  Word diffs are deterministic by default (no wall-clock timeout). Inputs above
374
376
  the safe ceiling of 262,144 unique diff tokens return `DIFF_TOKEN_LIMIT` with
@@ -380,11 +382,11 @@ silent text loss.
380
382
  During multi-round legal negotiations, a reviewer often needs to edit text that was previously inserted by another reviewer whose revision is still pending. Pass `existingRevisions: 'slice-cross-author'` (or `--existing-revisions slice-cross-author` via CLI) to edit inside another author's pending insertion without erasing their attribution or requiring prior acceptance:
381
383
 
382
384
  ```js
383
- const result = await applyRedlineToOxml(paragraphOoxml, originalText, modifiedText, {
384
- generateRedlines: true,
385
- author: 'Reviewer B',
386
- existingRevisions: 'slice-cross-author'
387
- });
385
+ const result = await applyRedlineToOxml(paragraphOoxml, originalText, modifiedText, {
386
+ generateRedlines: true,
387
+ author: 'Reviewer B',
388
+ existingRevisions: 'slice-cross-author'
389
+ });
388
390
  ```
389
391
 
390
392
  The engine applies Microsoft Word Desktop-native tracked change structures:
@@ -497,25 +499,25 @@ an earlier block has already replaced.
497
499
  | `ensureCommentsExtendedArtifactsInZip(zip, commentsExtendedXml)` | Add or replace modern Word comment-thread metadata in a `.docx` package. |
498
500
  | `validateDocxPackage(zip)` | Validate `.docx` structural consistency. |
499
501
 
500
- Malformed OOXML never escapes these public transform APIs as a raw parser
501
- exception. Transforms return `status: 'error'` with `error.code === 'PARSE_ERROR'`;
502
- validators return a `PARSE_ERROR` issue. Recoverable XML parser
503
- diagnostics are forwarded through the configured logger and included in
504
- `warnings` where the result shape supports them.
505
-
506
- Cross-author slicing also verifies its exact accepted-view text before success.
507
- If a structural boundary prevents exact reconstruction, the transform returns
508
- `status: 'error'` with `error.code === 'PATCH_ROUNDTRIP_MISMATCH'`,
509
- `hasChanges: false`, and the original OOXML unchanged.
510
- Pure insertion-only slicing uses an exact character-local diff so repeated words
511
- cannot move an insertion to a different occurrence. Leading/trailing spaces,
512
- tabs, and non-breaking spaces are treated as real changes rather than no-ops.
513
- For text-bearing replacements, the exact accepted-view text of the resolved
514
- paragraph—not a space-normalized caller target—defines mutation offsets. Word-level replacement
515
- hunks that differ only by ordinary spaces and NBSPs are refined to character
516
- edits so unchanged hyperlinks and their relationship attributes stay in place.
517
- Runner results expose bounded `resolvedTarget.targetTextMatch` code-point
518
- diagnostics when equivalent whitespace was used to identify the target.
502
+ Malformed OOXML never escapes these public transform APIs as a raw parser
503
+ exception. Transforms return `status: 'error'` with `error.code === 'PARSE_ERROR'`;
504
+ validators return a `PARSE_ERROR` issue. Recoverable XML parser
505
+ diagnostics are forwarded through the configured logger and included in
506
+ `warnings` where the result shape supports them.
507
+
508
+ Cross-author slicing also verifies its exact accepted-view text before success.
509
+ If a structural boundary prevents exact reconstruction, the transform returns
510
+ `status: 'error'` with `error.code === 'PATCH_ROUNDTRIP_MISMATCH'`,
511
+ `hasChanges: false`, and the original OOXML unchanged.
512
+ Pure insertion-only slicing uses an exact character-local diff so repeated words
513
+ cannot move an insertion to a different occurrence. Leading/trailing spaces,
514
+ tabs, and non-breaking spaces are treated as real changes rather than no-ops.
515
+ For text-bearing replacements, the exact accepted-view text of the resolved
516
+ paragraph—not a space-normalized caller target—defines mutation offsets. Word-level replacement
517
+ hunks that differ only by ordinary spaces and NBSPs are refined to character
518
+ edits so unchanged hyperlinks and their relationship attributes stay in place.
519
+ Runner results expose bounded `resolvedTarget.targetTextMatch` code-point
520
+ diagnostics when equivalent whitespace was used to identify the target.
519
521
 
520
522
  ### Deep Imports
521
523
 
@@ -531,17 +533,17 @@ import {
531
533
  import { getParagraphText } from '@ansonlai/docx-redline-js/core/paragraph-targeting.js';
532
534
  ```
533
535
 
534
- Use `applyOperationsToDocumentXml(...)` for mixed batches. It stably runs comments before text-changing operations so replacements cannot invalidate their original anchors. Other operation types retain their relative order. Batch results retain each operation's original 1-based index and expose the actual `executionOrder`.
535
-
536
- Before mutation, the runner resolves strong source descriptors against the
537
- immutable batch-start document and binds them to session-local source
538
- identities. Independent edits therefore do not need to be manually sorted when
539
- an earlier structural rewrite changes later paragraph indexes or fingerprints.
540
- Targets that deliberately refer to uniquely created paragraph text are compiled
541
- into an internal capture dependency. True overlap is not guessed: incompatible
542
- writes to one source fail before mutation with `OVERLAPPING_SOURCE_TARGETS` or
543
- `REVISION_ORDER_CONFLICT`, and mutating capture fan-out without distinct
544
- selectors fails with `CAPTURE_FANOUT_CONFLICT`.
536
+ Use `applyOperationsToDocumentXml(...)` for mixed batches. It stably runs comments before text-changing operations so replacements cannot invalidate their original anchors. Other operation types retain their relative order. Batch results retain each operation's original 1-based index and expose the actual `executionOrder`.
537
+
538
+ Before mutation, the runner resolves strong source descriptors against the
539
+ immutable batch-start document and binds them to session-local source
540
+ identities. Independent edits therefore do not need to be manually sorted when
541
+ an earlier structural rewrite changes later paragraph indexes or fingerprints.
542
+ Targets that deliberately refer to uniquely created paragraph text are compiled
543
+ into an internal capture dependency. True overlap is not guessed: incompatible
544
+ writes to one source fail before mutation with `OVERLAPPING_SOURCE_TARGETS` or
545
+ `REVISION_ORDER_CONFLICT`, and mutating capture fan-out without distinct
546
+ selectors fails with `CAPTURE_FANOUT_CONFLICT`.
545
547
 
546
548
  Threaded replies use a comment operation with no body target:
547
549
 
@@ -550,9 +552,29 @@ Threaded replies use a comment operation with no body target:
550
552
  ```
551
553
 
552
554
  For complete `.docx` files, use `openDocx(...).applyOperations(...)`; it reads
553
- the existing comments parts and writes the required `commentsExtended.xml`
554
- relationship and content type. Inspection reports `paraId` and
555
- `parentCommentId` so callers can discover and verify the thread hierarchy.
555
+ the existing comments parts, writes the required `commentsExtended.xml`
556
+ relationship and content type, adds the reply's own range/reference markers to the body
557
+ (Word does not display a reply without them), and keeps `commentsIds.xml` and
558
+ `commentsExtensible.xml` in step when they exist. Inspection reports `paraId`, `parentCommentId` and `done`
559
+ so callers can discover and verify the thread hierarchy.
560
+
561
+ Resolve or reopen a thread with an operation (usable from the CLI through `apply --operations`) or the API:
562
+
563
+ ```js
564
+ { type: 'comment_resolve', commentId: 8 } // resolve
565
+ { type: 'comment_resolve', commentId: 8, resolved: false } // reopen
566
+ await doc.resolveComment(8, { resolved: true });
567
+ ```
568
+
569
+ Resolved is a thread-level state in Word, so any id in the thread updates the root and all replies. Delete
570
+ specific comments with `doc.deleteComments({ ids: [8] })` or `docx-redline delete-comments file.docx --comment-id 8`;
571
+ deleting a thread root also deletes its replies.
572
+
573
+ Headers and footers are edited by adding `part` to an operation: `{ type: 'replace', part: { kind: 'footer' }, target: { exactText: 'Draft v1' }, modified: 'Draft v2' }`.
574
+ `openDocx(...).inspect().headersFooters` lists the parts (path, kind, type, sections, text, whether they contain
575
+ fields). Fields such as `PAGE` are treated as atomic, comments are not allowed in headers or footers, and `accept`/`reject`
576
+ also resolve revisions inside them. Creating a header or footer that does not exist is not supported; see
577
+ [Known limitations](docs/plans/KNOWN_BUGS.md#4-headers-and-footers).
556
578
 
557
579
  A whole-paragraph `delete` that targets existing comment markup fails with
558
580
  `COMMENTED_CONTENT_DELETE`. In the Node facade and CLI, the error also includes
@@ -564,23 +586,23 @@ serialization. Accuracy remains the controlling constraint: each operation has
564
586
  an internal savepoint so an error or no-op cannot leak a partial edit or consumed
565
587
  revision ID into later operations.
566
588
 
567
- Batches are progressive by default (`atomic: false`): valid operations commit
568
- while failed operations remain unapplied and are reported in `results`. Pass
569
- `{ atomic: true }` when any operation error must return the original
570
- `documentXml`, `hasChanges: false`, empty package artifacts, and
571
- `rolledBack: true`. The default `continueOnError: true` still attempts the full
572
- batch so `results` describes every operation; use `{ continueOnError: false }`
573
- to stop after the first error.
574
-
575
- Every failed or partial mutation includes `retryPlan`. Its `base` is `original`
576
- after rollback/no commit and `output` after a progressive partial commit;
577
- `committedIndexes`, `failedIndexes`, and `unattemptedIndexes` identify the safe
578
- replay scope. Errors use recovery envelope version 1 and always report
579
- `sameArgumentsSafe: false`. Follow `error.recovery.action`; do not infer a retry
580
- from prose. Authorization-sensitive actions, such as resolving comments, are
581
- marked with `requiresUserAuthorization: true`. For `EXISTING_REVISIONS`, the
582
- non-normalizing surgical recommendation is `slice-cross-author`; accepting or
583
- rejecting prior revisions still requires explicit authority.
589
+ Batches are progressive by default (`atomic: false`): valid operations commit
590
+ while failed operations remain unapplied and are reported in `results`. Pass
591
+ `{ atomic: true }` when any operation error must return the original
592
+ `documentXml`, `hasChanges: false`, empty package artifacts, and
593
+ `rolledBack: true`. The default `continueOnError: true` still attempts the full
594
+ batch so `results` describes every operation; use `{ continueOnError: false }`
595
+ to stop after the first error.
596
+
597
+ Every failed or partial mutation includes `retryPlan`. Its `base` is `original`
598
+ after rollback/no commit and `output` after a progressive partial commit;
599
+ `committedIndexes`, `failedIndexes`, and `unattemptedIndexes` identify the safe
600
+ replay scope. Errors use recovery envelope version 1 and always report
601
+ `sameArgumentsSafe: false`. Follow `error.recovery.action`; do not infer a retry
602
+ from prose. Authorization-sensitive actions, such as resolving comments, are
603
+ marked with `requiresUserAuthorization: true`. For `EXISTING_REVISIONS`, the
604
+ non-normalizing surgical recommendation is `slice-cross-author`; accepting or
605
+ rejecting prior revisions still requires explicit authority.
584
606
 
585
607
  Comment anchors use exact matching first, then a unique ASCII-space/NBSP
586
608
  equivalent match that preserves source offsets and text. Missing anchors return
@@ -632,14 +654,14 @@ guidance. Callers should pass `{ strictTargets: true }` and use strict descripto
632
654
 
633
655
  ### Mutation Receipts
634
656
 
635
- Both single-operation (`applyOperationToDocumentXml`) and batch
636
- (`applyOperationsToDocumentXml`) results expose commit-aware **Mutation Receipts**
637
- (`result.receipt` on single results and per-item `results[i].receipt`, plus `result.receipts`
638
- for the full batch).
639
-
640
- This full receipt shape is retained by the Node facade and standalone runner.
641
- Only compact CLI JSON removes duplicate `results[i].receipt` bodies and keeps
642
- the ordered top-level `receipts` array.
657
+ Both single-operation (`applyOperationToDocumentXml`) and batch
658
+ (`applyOperationsToDocumentXml`) results expose commit-aware **Mutation Receipts**
659
+ (`result.receipt` on single results and per-item `results[i].receipt`, plus `result.receipts`
660
+ for the full batch).
661
+
662
+ This full receipt shape is retained by the Node facade and standalone runner.
663
+ Only compact CLI JSON removes duplicate `results[i].receipt` bodies and keeps
664
+ the ordered top-level `receipts` array.
643
665
 
644
666
  ```js
645
667
  const result = await applyOperationsToDocumentXml(documentXml, operations, 'Agent');
@@ -715,49 +737,49 @@ import { applyOperationToDocumentXml } from '@ansonlai/docx-redline-js/standalon
715
737
  const zip = await JSZip.loadAsync(docxBuffer);
716
738
  const documentXml = await zip.file('word/document.xml').async('string');
717
739
 
718
- const opResult = await applyOperationToDocumentXml(
740
+ const opResult = await applyOperationToDocumentXml(
719
741
  documentXml,
720
742
  { type: 'redline', target: 'old text', modified: 'new text' },
721
743
  'Editor'
722
- );
723
-
724
- // Restoring another reviewer's pending whole-paragraph deletion requires
725
- // explicit intent. The restored counterproposal becomes a separately tracked
726
- // sibling paragraph; a normal redline operation remains fail-closed.
727
- const restoration = await applyOperationToDocumentXml(
728
- documentXml,
729
- {
730
- type: 'restore',
731
- // restore targets default to the rejected view, where deleted text exists
732
- target: { paragraphId: '1A2B3C4D', exactText: 'Original deleted paragraph text.' },
733
- modified: 'Restored or adjusted paragraph text.'
734
- },
735
- 'Editor'
736
- );
737
-
738
- // A single restoration follows its deleted source paragraph. A range
739
- // restoration follows the complete deleted source block. Unchanged legacy
740
- // validation defects are retained as baseline issues; newly generated errors
741
- // fail closed before commit.
742
- // inspect/extract descriptors report their revisionView, and each fingerprint
743
- // is computed from the same view as exactText. Keep those fields together.
744
-
745
- // To insert run-level text at a location visible only in the rejected view,
746
- // provide explicit rejected-view intent and an exact anchor-relative offset.
747
- const deletedTextInsertion = await applyOperationToDocumentXml(
748
- documentXml,
749
- {
750
- type: 'insert',
751
- target: { paragraphId: '1A2B3C4D', revisionView: 'rejected' },
752
- anchor: { exactText: 'must pay', occurrence: 1, offset: 5 },
753
- modified: '[clarification] ',
754
- existingRevisions: 'slice-cross-author'
755
- },
756
- 'Editor'
757
- );
744
+ );
745
+
746
+ // Restoring another reviewer's pending whole-paragraph deletion requires
747
+ // explicit intent. The restored counterproposal becomes a separately tracked
748
+ // sibling paragraph; a normal redline operation remains fail-closed.
749
+ const restoration = await applyOperationToDocumentXml(
750
+ documentXml,
751
+ {
752
+ type: 'restore',
753
+ // restore targets default to the rejected view, where deleted text exists
754
+ target: { paragraphId: '1A2B3C4D', exactText: 'Original deleted paragraph text.' },
755
+ modified: 'Restored or adjusted paragraph text.'
756
+ },
757
+ 'Editor'
758
+ );
759
+
760
+ // A single restoration follows its deleted source paragraph. A range
761
+ // restoration follows the complete deleted source block. Unchanged legacy
762
+ // validation defects are retained as baseline issues; newly generated errors
763
+ // fail closed before commit.
764
+ // inspect/extract descriptors report their revisionView, and each fingerprint
765
+ // is computed from the same view as exactText. Keep those fields together.
766
+
767
+ // To insert run-level text at a location visible only in the rejected view,
768
+ // provide explicit rejected-view intent and an exact anchor-relative offset.
769
+ const deletedTextInsertion = await applyOperationToDocumentXml(
770
+ documentXml,
771
+ {
772
+ type: 'insert',
773
+ target: { paragraphId: '1A2B3C4D', revisionView: 'rejected' },
774
+ anchor: { exactText: 'must pay', occurrence: 1, offset: 5 },
775
+ modified: '[clarification] ',
776
+ existingRevisions: 'slice-cross-author'
777
+ },
778
+ 'Editor'
779
+ );
758
780
 
759
781
  // applyOperationToDocumentXml(...) returns a full w:document payload.
760
- zip.file('word/document.xml', opResult.documentXml);
782
+ zip.file('word/document.xml', opResult.documentXml);
761
783
 
762
784
  const fragmentResult = await applyRedlineToOxml(
763
785
  paragraphOoxml,
@@ -840,10 +862,10 @@ invariant with a fresh seed. See [Release validation in docs/TESTING.md](./docs/
840
862
 
841
863
  ## Architecture & Contributing
842
864
 
843
- - **[ARCHITECTURE.md](./ARCHITECTURE.md)**: Detailed module layout, end-to-end reconciliation flow, and contributor fast orientation.
844
- - **[AGENTS.md](./AGENTS.md)**: Fast-start routing and operational guardrails for AI coding agents.
845
- - **[docs/AGENT_FAST_START.md](./docs/AGENT_FAST_START.md)**: Minimal ordinary-edit contract for agent integrations.
846
- - **[docs/AGENT_KNOWLEDGE_BASE.md](./docs/AGENT_KNOWLEDGE_BASE.md)**: Full agent reference for APIs, operations, CLI automation, recovery, and gotchas.
865
+ - **[ARCHITECTURE.md](./ARCHITECTURE.md)**: Detailed module layout, end-to-end reconciliation flow, and contributor fast orientation.
866
+ - **[AGENTS.md](./AGENTS.md)**: Fast-start routing and operational guardrails for AI coding agents.
867
+ - **[docs/AGENT_FAST_START.md](./docs/AGENT_FAST_START.md)**: Minimal ordinary-edit contract for agent integrations.
868
+ - **[docs/AGENT_KNOWLEDGE_BASE.md](./docs/AGENT_KNOWLEDGE_BASE.md)**: Full agent reference for APIs, operations, CLI automation, recovery, and gotchas.
847
869
  - **[docs/TESTING.md](./docs/TESTING.md)**: Comprehensive testing model, test lanes, independent oracle checks, and visual review checklist.
848
870
  - **[CHANGELOG.md](./CHANGELOG.md)**: Version history, migration guides, and deprecation schedules.
849
871