@beyondwork/docx-react-component 1.0.58 → 1.0.60

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 (135) hide show
  1. package/README.md +2 -2
  2. package/package.json +2 -1
  3. package/src/api/awareness-identity-types.ts +4 -2
  4. package/src/api/comment-negotiation-types.ts +4 -1
  5. package/src/api/external-custody-types.ts +16 -0
  6. package/src/api/internal/build-ref-projections.ts +108 -0
  7. package/src/api/package-version.ts +1 -1
  8. package/src/api/participants-types.ts +11 -1
  9. package/src/api/public-types.ts +980 -10
  10. package/src/api/scope-metadata-resolver-types.ts +6 -0
  11. package/src/compare/diff-engine.ts +3 -0
  12. package/src/core/commands/formatting-commands.ts +1 -0
  13. package/src/core/commands/index.ts +225 -16
  14. package/src/core/commands/legacy-form-field-commands.ts +181 -0
  15. package/src/core/commands/table-structure-commands.ts +149 -31
  16. package/src/core/selection/mapping.ts +20 -0
  17. package/src/core/state/editor-state.ts +4 -1
  18. package/src/index.ts +28 -0
  19. package/src/io/docx-session.ts +22 -3
  20. package/src/io/export/export-session.ts +11 -7
  21. package/src/io/export/ooxml-namespaces.ts +47 -0
  22. package/src/io/export/reattach-preserved-parts.ts +4 -16
  23. package/src/io/export/serialize-comments.ts +3 -131
  24. package/src/io/export/serialize-ffdata.ts +89 -0
  25. package/src/io/export/serialize-headers-footers.ts +5 -0
  26. package/src/io/export/serialize-main-document.ts +224 -34
  27. package/src/io/export/serialize-numbering.ts +22 -2
  28. package/src/io/export/serialize-revisions.ts +99 -0
  29. package/src/io/export/serialize-tables.ts +9 -0
  30. package/src/io/export/split-review-boundaries.ts +1 -0
  31. package/src/io/export/table-properties-xml.ts +14 -0
  32. package/src/io/load-scheduler.ts +70 -28
  33. package/src/io/normalize/normalize-text.ts +13 -0
  34. package/src/io/ooxml/_mini-xml.ts +198 -0
  35. package/src/io/ooxml/canonicalize-payload.ts +1 -4
  36. package/src/io/ooxml/chart/chart-style-table.ts +4 -3
  37. package/src/io/ooxml/chart/parse-chart-space.ts +2 -4
  38. package/src/io/ooxml/chart/parse-series.ts +2 -1
  39. package/src/io/ooxml/chart/resolve-color.ts +2 -2
  40. package/src/io/ooxml/chart/types.ts +6 -434
  41. package/src/io/ooxml/comment-presentation-payload.ts +6 -5
  42. package/src/io/ooxml/highlight-colors.ts +8 -5
  43. package/src/io/ooxml/parse-anchor.ts +68 -53
  44. package/src/io/ooxml/parse-comments.ts +14 -142
  45. package/src/io/ooxml/parse-complex-content.ts +3 -106
  46. package/src/io/ooxml/parse-drawing.ts +100 -195
  47. package/src/io/ooxml/parse-ffdata.ts +93 -0
  48. package/src/io/ooxml/parse-fields.ts +7 -146
  49. package/src/io/ooxml/parse-fill.ts +88 -8
  50. package/src/io/ooxml/parse-font-table.ts +5 -105
  51. package/src/io/ooxml/parse-footnotes.ts +28 -152
  52. package/src/io/ooxml/parse-headers-footers.ts +106 -212
  53. package/src/io/ooxml/parse-inline-media.ts +3 -200
  54. package/src/io/ooxml/parse-main-document.ts +180 -217
  55. package/src/io/ooxml/parse-numbering.ts +154 -335
  56. package/src/io/ooxml/parse-object.ts +147 -0
  57. package/src/io/ooxml/parse-ole-relationship.ts +82 -0
  58. package/src/io/ooxml/parse-paragraph-formatting.ts +7 -10
  59. package/src/io/ooxml/parse-picture-sdt.ts +85 -0
  60. package/src/io/ooxml/parse-picture.ts +72 -42
  61. package/src/io/ooxml/parse-revisions.ts +285 -51
  62. package/src/io/ooxml/parse-settings.ts +6 -99
  63. package/src/io/ooxml/parse-shapes.ts +25 -140
  64. package/src/io/ooxml/parse-styles.ts +3 -218
  65. package/src/io/ooxml/parse-tables.ts +76 -256
  66. package/src/io/ooxml/parse-theme.ts +1 -4
  67. package/src/io/ooxml/property-grab-bag.ts +5 -47
  68. package/src/io/ooxml/workflow-payload.ts +6 -1
  69. package/src/io/ooxml/xml-element-serialize.ts +32 -0
  70. package/src/io/ooxml/xml-parser.ts +183 -0
  71. package/src/legal/bookmarks.ts +1 -1
  72. package/src/legal/cross-references.ts +1 -1
  73. package/src/legal/defined-terms.ts +1 -1
  74. package/src/legal/{_document-root.ts → document-root.ts} +8 -0
  75. package/src/legal/signature-blocks.ts +1 -1
  76. package/src/model/canonical-document.ts +159 -6
  77. package/src/model/chart-types.ts +439 -0
  78. package/src/model/snapshot.ts +5 -1
  79. package/src/review/store/comment-remapping.ts +24 -11
  80. package/src/review/store/revision-actions.ts +482 -2
  81. package/src/review/store/revision-store.ts +15 -0
  82. package/src/review/store/revision-types.ts +76 -0
  83. package/src/runtime/collab/remote-cursor-awareness.ts +24 -0
  84. package/src/runtime/collab/runtime-collab-sync.ts +33 -0
  85. package/src/runtime/diagnostics/build-diagnostic.ts +153 -0
  86. package/src/runtime/diagnostics/code-metadata-table.ts +230 -0
  87. package/src/runtime/document-runtime.ts +821 -54
  88. package/src/runtime/document-search.ts +115 -0
  89. package/src/runtime/edit-ops/index.ts +18 -2
  90. package/src/runtime/footnote-resolver.ts +130 -0
  91. package/src/runtime/layout/layout-engine-instance.ts +31 -4
  92. package/src/runtime/layout/layout-engine-version.ts +37 -1
  93. package/src/runtime/layout/page-graph.ts +14 -1
  94. package/src/runtime/layout/resolved-formatting-state.ts +21 -0
  95. package/src/runtime/numbering-prefix.ts +17 -0
  96. package/src/runtime/query-scopes.ts +108 -10
  97. package/src/runtime/resolved-numbering-geometry.ts +37 -6
  98. package/src/runtime/revision-runtime.ts +27 -1
  99. package/src/runtime/selection/post-edit-validator.ts +60 -6
  100. package/src/runtime/structure-ops/index.ts +20 -4
  101. package/src/runtime/surface-projection.ts +290 -21
  102. package/src/runtime/table-schema.ts +6 -0
  103. package/src/runtime/theme-color-resolver.ts +2 -2
  104. package/src/runtime/units.ts +9 -0
  105. package/src/runtime/workflow-rail-segments.ts +4 -0
  106. package/src/ui/WordReviewEditor.tsx +187 -43
  107. package/src/ui/editor-runtime-boundary.ts +10 -0
  108. package/src/ui/editor-shell-view.tsx +4 -1
  109. package/src/ui/headless/chrome-registry.ts +53 -0
  110. package/src/ui/headless/selection-tool-resolver.ts +11 -1
  111. package/src/ui-tailwind/chrome/chrome-preset-model.ts +13 -0
  112. package/src/ui-tailwind/chrome/tw-command-palette-mount.tsx +96 -0
  113. package/src/ui-tailwind/chrome/tw-context-menu.tsx +2 -1
  114. package/src/ui-tailwind/chrome/tw-image-context-toolbar.tsx +5 -4
  115. package/src/ui-tailwind/chrome/tw-mode-dock.tsx +6 -2
  116. package/src/ui-tailwind/chrome/use-container-breakpoint.ts +111 -0
  117. package/src/ui-tailwind/chrome-overlay/tw-chrome-overlay.tsx +0 -9
  118. package/src/ui-tailwind/chrome-overlay/tw-object-selection-overlay.tsx +1 -0
  119. package/src/ui-tailwind/chrome-overlay/tw-page-stack-overlay-layer.tsx +6 -7
  120. package/src/ui-tailwind/editor-surface/pm-schema.ts +87 -25
  121. package/src/ui-tailwind/editor-surface/pm-state-from-snapshot.ts +9 -0
  122. package/src/ui-tailwind/editor-surface/shape-renderer.ts +76 -14
  123. package/src/ui-tailwind/editor-surface/tw-page-block-view.helpers.ts +18 -1
  124. package/src/ui-tailwind/editor-surface/tw-page-block-view.tsx +2 -0
  125. package/src/ui-tailwind/editor-surface/tw-table-node-view.tsx +18 -2
  126. package/src/ui-tailwind/index.ts +9 -0
  127. package/src/ui-tailwind/page-chrome-model.ts +77 -5
  128. package/src/ui-tailwind/page-stack/tw-page-stack-chrome-layer.tsx +56 -1
  129. package/src/ui-tailwind/page-stack/tw-region-block-renderer.tsx +2 -0
  130. package/src/ui-tailwind/review/tw-comment-sidebar.tsx +116 -113
  131. package/src/ui-tailwind/review/tw-review-rail-footer.tsx +2 -2
  132. package/src/ui-tailwind/theme/tokens.ts +14 -0
  133. package/src/ui-tailwind/toolbar/tw-shell-header.tsx +5 -0
  134. package/src/ui-tailwind/tw-review-workspace.tsx +29 -87
  135. package/src/validation/diagnostics.ts +1 -0
@@ -261,6 +261,22 @@ export interface RuntimeCollabSyncHandle {
261
261
  * enforces write permissions per §7 of the lane plan.
262
262
  */
263
263
  getWorkflowShared(): WorkflowSharedHandle;
264
+ /**
265
+ * R3 — AbortSignal that fires whenever a remote command batch is applied
266
+ * locally. Long-running idle tasks (e.g. Phase 2.5 prerender cache
267
+ * warmups, Phase 2.2 ranged-snapshot prep) should pass this into
268
+ * `loadScheduler.scheduleIdle(task, { signal })` so remote arrivals
269
+ * cancel stale prep before the document moves under it. The signal is
270
+ * recycled after each drain — callers must call this getter on every
271
+ * `scheduleIdle` call rather than caching the result. On `destroy()`
272
+ * the current signal also aborts so consumers detect shutdown.
273
+ *
274
+ * Stability: advanced-supported (v2.0.0). See
275
+ * `docs/reference/public-api.md` §createRuntimeCollabSync for the
276
+ * consumer contract and `docs/wiki/collaboration.md` §"Remote-activity
277
+ * abort signal (R3)" for the end-to-end narrative.
278
+ */
279
+ getRemoteActivitySignal(): AbortSignal;
264
280
  }
265
281
 
266
282
  export function createRuntimeCommandAppliedBridge(): RuntimeCommandAppliedBridge {
@@ -300,8 +316,19 @@ export function createRuntimeCollabSync(
300
316
  let rafHandle: number | null = null;
301
317
  let timeoutHandle: ReturnType<typeof setTimeout> | null = null;
302
318
 
319
+ // R3 — remote-activity abort controller. Recycled on every scheduled
320
+ // drain so consumers that registered an idle task with the prior
321
+ // signal get cancelled before the upcoming commit lands. First signal
322
+ // is created eagerly so `getRemoteActivitySignal()` is always safe to
323
+ // call before any remote traffic.
324
+ let remoteActivityController = new AbortController();
325
+
303
326
  function scheduleDrain(): void {
304
327
  if (rafHandle !== null || timeoutHandle !== null) return;
328
+ // Abort the prior signal BEFORE firing the host hook so any consumer
329
+ // work that branches on the hook sees the fresh signal.
330
+ remoteActivityController.abort();
331
+ remoteActivityController = new AbortController();
305
332
  options.onRemoteReplayScheduled?.();
306
333
  if (typeof requestAnimationFrame === "function") {
307
334
  rafHandle = requestAnimationFrame(() => {
@@ -572,6 +599,9 @@ export function createRuntimeCollabSync(
572
599
  workflowShared.destroy();
573
600
  runtime.setSharedWorkflowState(null);
574
601
  listeners.clear();
602
+ // R3 — abort the current remote-activity signal so any lingering
603
+ // idle consumers detect the shutdown.
604
+ remoteActivityController.abort();
575
605
  },
576
606
  subscribe(listener) {
577
607
  listeners.add(listener);
@@ -602,6 +632,9 @@ export function createRuntimeCollabSync(
602
632
  getWorkflowShared() {
603
633
  return workflowShared;
604
634
  },
635
+ getRemoteActivitySignal() {
636
+ return remoteActivityController.signal;
637
+ },
605
638
  };
606
639
 
607
640
  function onYEventsChange(event: Y.YArrayEvent<CommandEvent>): void {
@@ -0,0 +1,153 @@
1
+ // v2.0.0 — centralized diagnostic factory. Every error / warning /
2
+ // blocked-command path in the runtime builds its `EditorDiagnostic`
3
+ // through `buildDiagnostic()` so the LLM-metadata defaults from
4
+ // `code-metadata-table.ts` stay consistent.
5
+ //
6
+ // Emitters may override any llmMetadata field on a per-call basis.
7
+
8
+ import type {
9
+ EditorAnchorProjection,
10
+ EditorDiagnostic,
11
+ EditorDiagnosticCode,
12
+ EditorDiagnosticLlmMetadata,
13
+ EditorDiagnosticTechnical,
14
+ } from "../../api/public-types.ts";
15
+ import { CODE_METADATA_TABLE } from "./code-metadata-table.ts";
16
+
17
+ export interface BuildDiagnosticParams {
18
+ code: EditorDiagnosticCode;
19
+ /** Optional override of the technical payload. `message` + `source` default from the table. */
20
+ technical?: Partial<EditorDiagnosticTechnical> & { message?: string };
21
+ /** Optional override of llmMetadata fields. Spreads over table defaults. */
22
+ llmMetadata?: Partial<EditorDiagnosticLlmMetadata>;
23
+ /** Severity override — defaults to the table's severity for this code. */
24
+ severity?: EditorDiagnostic["severity"];
25
+ /** Convenience: short-hand for technical.affectedAnchor. */
26
+ affectedAnchor?: EditorAnchorProjection;
27
+ /** Convenience: short-hand for technical.cause; accepts a raw Error. */
28
+ cause?: Error | EditorDiagnosticTechnical["cause"];
29
+ /** Convenience: short-hand for technical.details. */
30
+ details?: Record<string, unknown>;
31
+ /** Explicit diagnosticId; auto-generated when omitted. */
32
+ diagnosticId?: string;
33
+ /** Explicit emittedAt ISO string; `new Date().toISOString()` when omitted. */
34
+ emittedAt?: string;
35
+ }
36
+
37
+ let diagnosticIdCounter = 0;
38
+
39
+ const nextDiagnosticId = (): string => {
40
+ diagnosticIdCounter = (diagnosticIdCounter + 1) | 0;
41
+ return `diag-${Date.now().toString(36)}-${diagnosticIdCounter.toString(36)}`;
42
+ };
43
+
44
+ const normalizeCause = (
45
+ cause: BuildDiagnosticParams["cause"],
46
+ ): EditorDiagnosticTechnical["cause"] | undefined => {
47
+ if (!cause) return undefined;
48
+ if (cause instanceof Error) {
49
+ return {
50
+ message: cause.message,
51
+ name: cause.name,
52
+ stack: cause.stack,
53
+ };
54
+ }
55
+ return cause;
56
+ };
57
+
58
+ export function buildDiagnostic(params: BuildDiagnosticParams): EditorDiagnostic {
59
+ const row = CODE_METADATA_TABLE[params.code];
60
+ if (!row) {
61
+ throw new Error(
62
+ `buildDiagnostic: unknown EditorDiagnosticCode "${params.code}" — add a row to code-metadata-table.ts`,
63
+ );
64
+ }
65
+
66
+ const severity = params.severity ?? row.severity;
67
+ const technical: EditorDiagnosticTechnical = {
68
+ message: params.technical?.message ?? row.llmMetadata.userSummary,
69
+ source: params.technical?.source ?? row.source,
70
+ stack: params.technical?.stack,
71
+ cause: normalizeCause(params.cause ?? params.technical?.cause),
72
+ details: params.details ?? params.technical?.details,
73
+ affectedAnchor: params.affectedAnchor ?? params.technical?.affectedAnchor,
74
+ featureEntryId: params.technical?.featureEntryId,
75
+ };
76
+
77
+ const llmMetadata: EditorDiagnosticLlmMetadata = {
78
+ userSummary: params.llmMetadata?.userSummary ?? row.llmMetadata.userSummary,
79
+ recoveryClass:
80
+ params.llmMetadata?.recoveryClass ?? row.llmMetadata.recoveryClass,
81
+ remediation: params.llmMetadata?.remediation ?? row.llmMetadata.remediation,
82
+ echoedInput: params.llmMetadata?.echoedInput,
83
+ docsUrl: params.llmMetadata?.docsUrl ?? row.llmMetadata.docsUrl,
84
+ tags: params.llmMetadata?.tags ?? row.llmMetadata.tags,
85
+ };
86
+
87
+ return {
88
+ diagnosticId: params.diagnosticId ?? nextDiagnosticId(),
89
+ severity,
90
+ code: params.code,
91
+ technical,
92
+ llmMetadata,
93
+ emittedAt: params.emittedAt ?? new Date().toISOString(),
94
+ };
95
+ }
96
+
97
+ /**
98
+ * Convenience: build a diagnostic whose legacy `EditorError.code` is one of
99
+ * the pre-v2.0.0 codes, by mapping to the canonical v2.0.0 code.
100
+ */
101
+ export function buildDiagnosticFromLegacyErrorCode(
102
+ legacyCode:
103
+ | "import_failed"
104
+ | "export_failed"
105
+ | "package_corrupt"
106
+ | "validation_failed"
107
+ | "datastore_failed"
108
+ | "internal_invariant",
109
+ params: Omit<BuildDiagnosticParams, "code"> = {},
110
+ ): EditorDiagnostic {
111
+ const mapping: Record<string, EditorDiagnosticCode> = {
112
+ import_failed: "import.failed",
113
+ export_failed: "export.failed",
114
+ package_corrupt: "import.package_corrupt",
115
+ validation_failed: "validation.failed",
116
+ datastore_failed: "datastore.failed",
117
+ internal_invariant: "runtime.internal_invariant",
118
+ };
119
+ return buildDiagnostic({ ...params, code: mapping[legacyCode] });
120
+ }
121
+
122
+ /**
123
+ * Convenience: build a diagnostic whose legacy `EditorWarning.code` is one
124
+ * of the pre-v2.0.0 codes.
125
+ */
126
+ export function buildDiagnosticFromLegacyWarningCode(
127
+ legacyCode:
128
+ | "unsupported_ooxml_preserved"
129
+ | "unsupported_ooxml_locked"
130
+ | "import_normalized"
131
+ | "export_roundtrip_risk"
132
+ | "comment_anchor_detached"
133
+ | "revision_anchor_detached"
134
+ | "workflow_scope_invalidated"
135
+ | "large_document_degraded"
136
+ | "font_substitution"
137
+ | "image_missing",
138
+ params: Omit<BuildDiagnosticParams, "code"> = {},
139
+ ): EditorDiagnostic {
140
+ const mapping: Record<string, EditorDiagnosticCode> = {
141
+ unsupported_ooxml_preserved: "import.unsupported_ooxml_preserved",
142
+ unsupported_ooxml_locked: "import.unsupported_ooxml_locked",
143
+ import_normalized: "import.normalized",
144
+ export_roundtrip_risk: "export.roundtrip_risk",
145
+ comment_anchor_detached: "review.comment_anchor_detached",
146
+ revision_anchor_detached: "review.revision_anchor_detached",
147
+ workflow_scope_invalidated: "runtime.workflow_scope_invalidated",
148
+ large_document_degraded: "runtime.large_document_degraded",
149
+ font_substitution: "runtime.font_substitution",
150
+ image_missing: "runtime.image_missing",
151
+ };
152
+ return buildDiagnostic({ ...params, code: mapping[legacyCode] });
153
+ }
@@ -0,0 +1,230 @@
1
+ // v2.0.0 — canonical LLM-metadata + remediation defaults for every
2
+ // EditorDiagnosticCode. One row per code. The build-diagnostic factory
3
+ // reads this table to populate `EditorDiagnostic.llmMetadata` defaults;
4
+ // emitters may override any field on a per-call basis.
5
+ //
6
+ // Stability: advanced-supported. Changes ship with an updated snapshot
7
+ // in test/api/diagnostic-shape.test.ts.
8
+
9
+ import type {
10
+ EditorDiagnosticCode,
11
+ EditorDiagnosticLlmMetadata,
12
+ EditorDiagnosticSource,
13
+ } from "../../api/public-types.ts";
14
+
15
+ const DOCS_BASE = "docs/wiki/error-catalog.md";
16
+
17
+ interface CodeMetadataRow {
18
+ readonly source: EditorDiagnosticSource;
19
+ readonly severity: "fatal" | "error" | "warning" | "info";
20
+ readonly llmMetadata: EditorDiagnosticLlmMetadata;
21
+ }
22
+
23
+ const row = (
24
+ source: EditorDiagnosticSource,
25
+ severity: "fatal" | "error" | "warning" | "info",
26
+ userSummary: string,
27
+ recoveryClass: EditorDiagnosticLlmMetadata["recoveryClass"],
28
+ remediation: EditorDiagnosticLlmMetadata["remediation"],
29
+ tags: string[],
30
+ anchor: string,
31
+ ): CodeMetadataRow => ({
32
+ source,
33
+ severity,
34
+ llmMetadata: {
35
+ userSummary,
36
+ recoveryClass,
37
+ remediation,
38
+ tags,
39
+ docsUrl: `${DOCS_BASE}#${anchor}`,
40
+ },
41
+ });
42
+
43
+ export const CODE_METADATA_TABLE: Readonly<
44
+ Record<EditorDiagnosticCode, CodeMetadataRow>
45
+ > = {
46
+ "import.failed": row("import", "error",
47
+ "The document could not be opened — the .docx package appears to be corrupt or incomplete.",
48
+ "requires-input",
49
+ { kind: "fallback", suggestion: "Ask the user to re-export the document from Word and retry." },
50
+ ["import", "package"], "import-failed"),
51
+ "import.package_corrupt": row("import", "error",
52
+ "The .docx package structure is invalid and cannot be loaded.",
53
+ "unrecoverable",
54
+ { kind: "escalate", to: "author", reason: "Package corruption — source must be regenerated." },
55
+ ["import", "package", "corrupt"], "import-package-corrupt"),
56
+ "import.normalized": row("import", "info",
57
+ "The document was loaded successfully with minor automatic repairs.",
58
+ "retry-safe", { kind: "none" }, ["import", "normalized"], "import-normalized"),
59
+ "import.unsupported_ooxml_preserved": row("import", "info",
60
+ "Some advanced content was preserved but cannot be edited — it will round-trip unchanged on export.",
61
+ "retry-safe", { kind: "none" }, ["import", "preserve-only"], "import-unsupported-preserved"),
62
+ "import.unsupported_ooxml_locked": row("import", "warning",
63
+ "Some content is locked for editing because the editor cannot render it.",
64
+ "requires-human",
65
+ { kind: "escalate", to: "reviewer", reason: "Locked content region requires reviewer acknowledgment." },
66
+ ["import", "locked"], "import-unsupported-locked"),
67
+ "export.failed": row("export", "error",
68
+ "The document could not be saved — a serialization step failed.",
69
+ "retry-with-backoff",
70
+ { kind: "retry", afterMs: 250, maxAttempts: 2 },
71
+ ["export"], "export-failed"),
72
+ "export.roundtrip_risk": row("export", "warning",
73
+ "The document was saved, but some content may not open identically in other editors.",
74
+ "requires-human",
75
+ { kind: "escalate", to: "author", reason: "Roundtrip-risk content flagged; author should verify." },
76
+ ["export", "roundtrip"], "export-roundtrip-risk"),
77
+ "review.comment_anchor_detached": row("review", "info",
78
+ "A comment's anchor text was removed; the comment is now detached but still preserved.",
79
+ "retry-safe", { kind: "none" }, ["review", "comment"], "review-comment-anchor-detached"),
80
+ "review.revision_anchor_detached": row("review", "info",
81
+ "A tracked change's anchor was invalidated by a structural edit.",
82
+ "retry-safe", { kind: "none" }, ["review", "tracked-change"], "review-revision-anchor-detached"),
83
+ "runtime.workflow_scope_invalidated": row("runtime", "warning",
84
+ "A workflow scope lost its trusted anchor and is no longer enforcing against live text.",
85
+ "requires-input",
86
+ {
87
+ kind: "fallback",
88
+ suggestion:
89
+ "Use warning.details.scopeId + warning.details.lastKnownRange to relocate the intended text, then reapply the scope.",
90
+ },
91
+ ["runtime", "workflow", "scope"], "runtime-workflow-scope-invalidated"),
92
+ "runtime.large_document_degraded": row("runtime", "warning",
93
+ "The document is large enough that some rendering optimizations have been relaxed.",
94
+ "retry-safe", { kind: "none" }, ["runtime", "perf"], "runtime-large-document-degraded"),
95
+ "runtime.font_substitution": row("runtime", "info",
96
+ "A requested font was unavailable; a substitute was used.",
97
+ "retry-safe", { kind: "none" }, ["runtime", "font"], "runtime-font-substitution"),
98
+ "runtime.image_missing": row("runtime", "warning",
99
+ "An embedded image could not be loaded — a placeholder is shown.",
100
+ "requires-input",
101
+ { kind: "fallback", suggestion: "Verify the image media part exists in the package." },
102
+ ["runtime", "image"], "runtime-image-missing"),
103
+ "runtime.internal_invariant": row("runtime", "fatal",
104
+ "The editor reached an unexpected state and cannot continue safely.",
105
+ "unrecoverable",
106
+ { kind: "escalate", to: "admin", reason: "Internal invariant violated — file a bug with the full diagnostic." },
107
+ ["runtime", "bug"], "runtime-internal-invariant"),
108
+ "host.adapter_rejected": row("host", "error",
109
+ "A host integration call failed.",
110
+ "retry-with-backoff",
111
+ { kind: "retry", afterMs: 500, maxAttempts: 3 },
112
+ ["host", "adapter"], "host-adapter-rejected"),
113
+ "host.load_rejected": row("host", "error",
114
+ "Loading the document from the host failed.",
115
+ "retry-with-backoff",
116
+ { kind: "retry", afterMs: 500, maxAttempts: 3 },
117
+ ["host", "load"], "host-load-rejected"),
118
+ "host.save_rejected": row("host", "error",
119
+ "Saving the document to the host failed.",
120
+ "retry-with-backoff",
121
+ { kind: "retry", afterMs: 500, maxAttempts: 3 },
122
+ ["host", "save"], "host-save-rejected"),
123
+ "host.chart_preview_rejected": row("host", "warning",
124
+ "A chart preview could not be rendered by the host — a badge is shown instead.",
125
+ "retry-safe", { kind: "none" }, ["host", "chart"], "host-chart-preview-rejected"),
126
+ "datastore.failed": row("datastore", "error",
127
+ "A datastore operation failed.",
128
+ "retry-with-backoff",
129
+ { kind: "retry", afterMs: 500, maxAttempts: 3 },
130
+ ["datastore"], "datastore-failed"),
131
+ "datastore.load_rejected": row("datastore", "error",
132
+ "The datastore could not load the requested snapshot.",
133
+ "retry-with-backoff",
134
+ { kind: "retry", afterMs: 500, maxAttempts: 3 },
135
+ ["datastore", "load"], "datastore-load-rejected"),
136
+ "datastore.save_rejected": row("datastore", "error",
137
+ "The datastore could not save the snapshot.",
138
+ "retry-with-backoff",
139
+ { kind: "retry", afterMs: 500, maxAttempts: 3 },
140
+ ["datastore", "save"], "datastore-save-rejected"),
141
+ "validation.failed": row("validation", "error",
142
+ "A document validation invariant failed.",
143
+ "requires-input",
144
+ { kind: "fallback", suggestion: "Inspect technical.details.reason for the failing invariant." },
145
+ ["validation"], "validation-failed"),
146
+ "validation.invariant_violated": row("validation", "fatal",
147
+ "A critical document invariant was violated.",
148
+ "unrecoverable",
149
+ { kind: "escalate", to: "admin", reason: "Invariant violated — document state may be corrupt." },
150
+ ["validation", "bug"], "validation-invariant-violated"),
151
+ "api.invalid_regex": row("api", "error",
152
+ "The search pattern is not a valid regular expression.",
153
+ "requires-input",
154
+ { kind: "fallback", suggestion: "Retry with a valid JavaScript regex, or disable the regex flag." },
155
+ ["api", "search"], "api-invalid-regex"),
156
+ "api.invalid_anchor": row("api", "error",
157
+ "The provided position is invalid for this document.",
158
+ "requires-input",
159
+ { kind: "fallback", suggestion: "Resolve a fresh anchor via getCurrentLocation() or findFirstText()." },
160
+ ["api", "anchor"], "api-invalid-anchor"),
161
+ "api.invalid_argument": row("api", "error",
162
+ "An argument passed to the editor API is invalid.",
163
+ "requires-input",
164
+ { kind: "fallback", suggestion: "Check technical.details.argument for the offending field." },
165
+ ["api"], "api-invalid-argument"),
166
+ "api.metadata_resolver_missing": row("api", "error",
167
+ "External metadata mode requires a resolver — register one via setScopeMetadataResolver() first.",
168
+ "requires-resolver",
169
+ { kind: "fallback", suggestion: "Call setScopeMetadataResolver(...) then retry." },
170
+ ["api", "metadata", "resolver"], "api-metadata-resolver-missing"),
171
+ "api.metadata_resolver_rejected": row("api", "error",
172
+ "The registered metadata resolver rejected a request.",
173
+ "retry-with-backoff",
174
+ { kind: "retry", afterMs: 500, maxAttempts: 2 },
175
+ ["api", "metadata", "resolver"], "api-metadata-resolver-rejected"),
176
+ "api.scope_not_found": row("api", "error",
177
+ "The requested workflow scope does not exist in the current overlay.",
178
+ "requires-input",
179
+ { kind: "fallback", suggestion: "Call queryScopes() to enumerate available scopeIds." },
180
+ ["api", "scope"], "api-scope-not-found"),
181
+ "api.collab_role_restricted": row("api", "error",
182
+ "The current user's role does not permit this action.",
183
+ "requires-permission",
184
+ { kind: "escalate", to: "author", reason: "Role-gated action; requires elevated permissions." },
185
+ ["api", "collab", "role"], "api-collab-role-restricted"),
186
+ "api.workflow_comment_only": row("api", "warning",
187
+ "This location only allows comments — edits are blocked by the active workflow overlay.",
188
+ "requires-input",
189
+ { kind: "fallback", suggestion: "Use addComment() instead, or target a scoped location." },
190
+ ["api", "workflow"], "api-workflow-comment-only"),
191
+ "api.command_blocked": row("api", "warning",
192
+ "This command is blocked by the current workflow policy.",
193
+ "requires-input",
194
+ { kind: "fallback", suggestion: "Check technical.details.reasons for the blocking policy." },
195
+ ["api", "workflow", "blocked"], "api-command-blocked"),
196
+ // Lane 8 Track H (v2.x) — agent-helper widening.
197
+ "api.projection_unavailable": row("api", "error",
198
+ "A text projection could not be built — the runtime is still loading or the story target is unknown.",
199
+ "retry-with-backoff",
200
+ { kind: "retry", afterMs: 100, maxAttempts: 3 },
201
+ ["api", "agent-helpers", "projection"], "api-projection-unavailable"),
202
+ "api.batch_edit_collision": row("api", "error",
203
+ "A batched-edit operation targeted a range that had already been mutated by an earlier edit in the batch.",
204
+ "requires-input",
205
+ { kind: "fallback", suggestion: "Rebuild the batch with non-overlapping ranges, or switch to mode: \"atomic\" to abort on conflict." },
206
+ ["api", "agent-helpers", "batch"], "api-batch-edit-collision"),
207
+ "api.workflow_patch_invalid": row("api", "error",
208
+ "A workflow-overlay patch referenced an unknown scopeId or candidateId.",
209
+ "requires-input",
210
+ { kind: "fallback", suggestion: "Call getWorkflowOverlay() to enumerate live ids before patching." },
211
+ ["api", "agent-helpers", "workflow"], "api-workflow-patch-invalid"),
212
+ "api.change_id_unknown": row("api", "warning",
213
+ "The provided changeId did not match any comment or tracked-change revision.",
214
+ "requires-input",
215
+ { kind: "fallback", suggestion: "Call findAllChanges() to enumerate live change ids." },
216
+ ["api", "agent-helpers", "change-lookup"], "api-change-id-unknown"),
217
+ "api.ai_explanation_anchor_invalid": row("api", "error",
218
+ "An AI-explanation scope could not be anchored — the referenced change is detached or the overlay refused the range.",
219
+ "requires-input",
220
+ { kind: "fallback", suggestion: "Filter findAllChanges() by liveOnly: true before attaching explanations." },
221
+ ["api", "agent-helpers", "ai-annotation"], "api-ai-explanation-anchor-invalid"),
222
+ };
223
+
224
+ /**
225
+ * Every key in this object is a valid {@link EditorDiagnosticCode}. Exported
226
+ * so test harnesses can assert exhaustiveness without importing the
227
+ * internal row shape.
228
+ */
229
+ export const KNOWN_DIAGNOSTIC_CODES: ReadonlyArray<EditorDiagnosticCode> =
230
+ Object.freeze(Object.keys(CODE_METADATA_TABLE) as EditorDiagnosticCode[]);