@beyondwork/docx-react-component 1.0.106 → 1.0.109

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 (190) hide show
  1. package/package.json +19 -5
  2. package/src/api/geometry-overlay-rects.ts +5 -0
  3. package/src/api/package-version.ts +1 -1
  4. package/src/api/page-anchor-id.ts +5 -0
  5. package/src/api/public-types.ts +16 -9
  6. package/src/api/table-node-specs.ts +6 -0
  7. package/src/api/v3/_create.ts +2 -1
  8. package/src/api/v3/_page-anchor-id.ts +52 -0
  9. package/src/api/v3/_runtime-handle.ts +92 -1
  10. package/src/api/v3/ai/_audit-time.ts +5 -0
  11. package/src/api/v3/ai/_pe2-evidence.ts +38 -0
  12. package/src/api/v3/ai/attach.ts +7 -2
  13. package/src/api/v3/ai/replacement.ts +101 -18
  14. package/src/api/v3/ai/resolve.ts +2 -2
  15. package/src/api/v3/ai/review.ts +177 -3
  16. package/src/api/v3/index.ts +1 -0
  17. package/src/api/v3/runtime/collab.ts +462 -0
  18. package/src/api/v3/runtime/document.ts +503 -20
  19. package/src/api/v3/runtime/geometry.ts +97 -0
  20. package/src/api/v3/runtime/layout.ts +744 -0
  21. package/src/api/v3/runtime/perf-probe.ts +14 -0
  22. package/src/api/v3/runtime/viewport.ts +9 -8
  23. package/src/api/v3/ui/_types.ts +149 -55
  24. package/src/api/v3/ui/chrome-preset-model.ts +5 -5
  25. package/src/api/v3/ui/debug.ts +115 -2
  26. package/src/api/v3/ui/index.ts +13 -0
  27. package/src/api/v3/ui/overlays.ts +0 -8
  28. package/src/api/v3/ui/surface.ts +56 -0
  29. package/src/api/v3/ui/viewport.ts +22 -9
  30. package/src/core/commands/image-commands.ts +1 -0
  31. package/src/core/commands/index.ts +6 -0
  32. package/src/core/schema/text-schema.ts +43 -5
  33. package/src/core/selection/mapping.ts +8 -1
  34. package/src/core/selection/review-anchors.ts +5 -1
  35. package/src/core/state/text-transaction.ts +8 -2
  36. package/src/io/export/serialize-revisions.ts +149 -1
  37. package/src/io/normalize/normalize-text.ts +6 -0
  38. package/src/io/ooxml/parse-bookmark-references.ts +55 -0
  39. package/src/io/ooxml/parse-fields.ts +24 -2
  40. package/src/io/ooxml/parse-headers-footers.ts +38 -5
  41. package/src/io/ooxml/parse-main-document.ts +153 -9
  42. package/src/io/ooxml/parse-numbering.ts +20 -0
  43. package/src/io/ooxml/parse-revisions.ts +19 -8
  44. package/src/io/opc/package-reader.ts +98 -8
  45. package/src/model/anchor.ts +4 -3
  46. package/src/model/canonical-document.ts +220 -2
  47. package/src/model/canonical-hash.ts +221 -0
  48. package/src/model/canonical-layout-inputs.ts +245 -6
  49. package/src/model/layout/index.ts +1 -0
  50. package/src/model/layout/page-graph-types.ts +118 -1
  51. package/src/model/review/revision-types.ts +14 -3
  52. package/src/preservation/store.ts +20 -4
  53. package/src/review/README.md +1 -1
  54. package/src/review/store/revision-actions.ts +14 -2
  55. package/src/runtime/collab/event-types.ts +67 -1
  56. package/src/runtime/collab/runtime-collab-sync.ts +177 -5
  57. package/src/runtime/diagnostics/layout-guard-warning.ts +18 -0
  58. package/src/runtime/document-heading-outline.ts +147 -0
  59. package/src/runtime/document-navigation.ts +8 -243
  60. package/src/runtime/document-runtime.ts +240 -97
  61. package/src/runtime/edit-dispatch/dispatch-text-command.ts +11 -0
  62. package/src/runtime/formatting/layout-inputs.ts +38 -5
  63. package/src/runtime/formatting/numbering/geometry.ts +28 -2
  64. package/src/runtime/geometry/adjacent-geometry-intake.ts +835 -0
  65. package/src/runtime/geometry/caret-geometry.ts +5 -6
  66. package/src/runtime/geometry/geometry-facet.ts +60 -10
  67. package/src/runtime/geometry/geometry-index.ts +591 -20
  68. package/src/runtime/geometry/geometry-types.ts +59 -0
  69. package/src/runtime/geometry/hit-test.ts +11 -1
  70. package/src/runtime/geometry/overlay-rects.ts +5 -3
  71. package/src/runtime/geometry/project-anchors.ts +1 -1
  72. package/src/runtime/geometry/word-layout-v2-line-intake.ts +323 -0
  73. package/src/runtime/layout/index.ts +6 -0
  74. package/src/runtime/layout/layout-engine-instance.ts +6 -1
  75. package/src/runtime/layout/layout-engine-version.ts +181 -16
  76. package/src/runtime/layout/layout-facet-types.ts +6 -0
  77. package/src/runtime/layout/page-graph.ts +21 -4
  78. package/src/runtime/layout/paginated-layout-engine.ts +139 -15
  79. package/src/runtime/layout/project-block-fragments.ts +265 -7
  80. package/src/runtime/layout/public-facet.ts +78 -24
  81. package/src/runtime/layout/table-row-continuation-contract.ts +107 -0
  82. package/src/runtime/layout/table-row-split.ts +92 -35
  83. package/src/runtime/prerender/cache-envelope.ts +2 -2
  84. package/src/runtime/prerender/cache-key.ts +5 -4
  85. package/src/runtime/prerender/customxml-cache.ts +0 -1
  86. package/src/runtime/render/render-kernel.ts +1 -1
  87. package/src/runtime/revision-runtime.ts +112 -10
  88. package/src/runtime/scopes/_scope-dependencies.ts +1 -0
  89. package/src/runtime/scopes/action-validation.ts +22 -2
  90. package/src/runtime/scopes/capabilities.ts +316 -0
  91. package/src/runtime/scopes/compile-scope-bundle.ts +14 -0
  92. package/src/runtime/scopes/compiler-service.ts +108 -4
  93. package/src/runtime/scopes/content-control-evidence.ts +79 -0
  94. package/src/runtime/scopes/create-issue.ts +5 -5
  95. package/src/runtime/scopes/evidence.ts +91 -0
  96. package/src/runtime/scopes/formatting/apply.ts +2 -0
  97. package/src/runtime/scopes/geometry-evidence.ts +130 -0
  98. package/src/runtime/scopes/index.ts +54 -0
  99. package/src/runtime/scopes/issue-lifecycle.ts +224 -0
  100. package/src/runtime/scopes/layout-evidence.ts +374 -0
  101. package/src/runtime/scopes/multi-paragraph-refusal.ts +37 -0
  102. package/src/runtime/scopes/preservation-boundary.ts +7 -1
  103. package/src/runtime/scopes/replacement/apply.ts +97 -34
  104. package/src/runtime/scopes/scope-kinds/paragraph.ts +108 -12
  105. package/src/runtime/scopes/semantic-scope-types.ts +242 -3
  106. package/src/runtime/scopes/visualization.ts +28 -0
  107. package/src/runtime/surface-projection.ts +44 -5
  108. package/src/runtime/telemetry/perf-probe.ts +216 -0
  109. package/src/runtime/virtualized-rendering.ts +36 -1
  110. package/src/runtime/workflow/ai-issue-lifecycle.ts +253 -0
  111. package/src/runtime/workflow/coordinator.ts +39 -11
  112. package/src/runtime/workflow/derived-scope-resolver.ts +63 -9
  113. package/src/runtime/workflow/index.ts +3 -0
  114. package/src/runtime/workflow/overlay-lane-types.ts +58 -0
  115. package/src/runtime/workflow/overlay-lanes.ts +168 -10
  116. package/src/runtime/workflow/overlay-store.ts +2 -2
  117. package/src/runtime/workflow/redline-posture-calibration.ts +257 -0
  118. package/src/runtime/workflow/word-field-matrix-calibration.ts +231 -0
  119. package/src/session/_sync-legacy.ts +17 -27
  120. package/src/session/import/loader.ts +6 -4
  121. package/src/session/import/source-package-evidence.ts +186 -2
  122. package/src/session/index.ts +5 -6
  123. package/src/session/session.ts +30 -56
  124. package/src/session/types.ts +8 -13
  125. package/src/shell/session-bootstrap.ts +155 -81
  126. package/src/ui/WordReviewEditor.tsx +520 -12
  127. package/src/ui/editor-shell-view.tsx +14 -4
  128. package/src/ui/editor-surface-controller.tsx +5 -3
  129. package/src/ui/headless/selection-tool-resolver.ts +1 -2
  130. package/src/ui/presence-overlay-lane.ts +0 -1
  131. package/src/ui/ui-controller-factory.ts +7 -0
  132. package/src/ui-tailwind/chrome/build-context-menu-entries.ts +5 -1
  133. package/src/ui-tailwind/chrome/editor-action-registry.ts +105 -5
  134. package/src/ui-tailwind/chrome/editor-actions-to-palette.ts +7 -0
  135. package/src/ui-tailwind/chrome/layer-debug-contracts.ts +208 -0
  136. package/src/ui-tailwind/chrome/resolve-target-kind.ts +13 -0
  137. package/src/ui-tailwind/chrome/tw-alert-banner.tsx +11 -3
  138. package/src/ui-tailwind/chrome/tw-command-palette.tsx +36 -6
  139. package/src/ui-tailwind/chrome/tw-context-menu.tsx +6 -1
  140. package/src/ui-tailwind/chrome/tw-display-mode-selector.tsx +42 -109
  141. package/src/ui-tailwind/chrome/tw-inline-find-bar.tsx +26 -6
  142. package/src/ui-tailwind/chrome/tw-navigation-command-bar.tsx +328 -0
  143. package/src/ui-tailwind/chrome/tw-object-context-toolbar.tsx +8 -4
  144. package/src/ui-tailwind/chrome/tw-runtime-repl-dialog.tsx +129 -1
  145. package/src/ui-tailwind/chrome/tw-selection-tool-host.tsx +19 -5
  146. package/src/ui-tailwind/chrome/tw-selection-tool-structure.tsx +5 -1
  147. package/src/ui-tailwind/chrome/tw-workspace-chrome-host.tsx +28 -12
  148. package/src/ui-tailwind/chrome-overlay/tw-chrome-overlay.tsx +30 -3
  149. package/src/ui-tailwind/chrome-overlay/tw-object-selection-overlay.tsx +116 -10
  150. package/src/ui-tailwind/chrome-overlay/tw-page-stack-overlay-layer.tsx +223 -94
  151. package/src/ui-tailwind/chrome-overlay/tw-presence-overlay-lane.tsx +157 -0
  152. package/src/ui-tailwind/chrome-overlay/tw-review-overlay-lane-markers.tsx +259 -0
  153. package/src/ui-tailwind/chrome-overlay/tw-scope-card-layer.tsx +5 -2
  154. package/src/ui-tailwind/chrome-overlay/tw-substrate-overlay-lanes.tsx +314 -0
  155. package/src/ui-tailwind/debug/README.md +4 -1
  156. package/src/ui-tailwind/debug/layer11-consumer-readiness.ts +272 -0
  157. package/src/ui-tailwind/debug/layer11-word-field-matrix-evidence.ts +160 -0
  158. package/src/ui-tailwind/editor-surface/perf-probe.ts +14 -215
  159. package/src/ui-tailwind/editor-surface/pm-decorations.ts +42 -0
  160. package/src/ui-tailwind/editor-surface/pm-position-map.ts +38 -2
  161. package/src/ui-tailwind/editor-surface/pm-schema.ts +14 -4
  162. package/src/ui-tailwind/editor-surface/pm-state-from-snapshot.ts +34 -5
  163. package/src/ui-tailwind/editor-surface/runtime-decoration-plugin.ts +9 -19
  164. package/src/ui-tailwind/editor-surface/surface-build-keys.ts +2 -2
  165. package/src/ui-tailwind/editor-surface/tw-page-block-view.helpers.ts +145 -0
  166. package/src/ui-tailwind/editor-surface/tw-page-block-view.tsx +16 -11
  167. package/src/ui-tailwind/editor-surface/tw-prosemirror-surface.tsx +8 -10
  168. package/src/ui-tailwind/editor-surface/tw-table-node-view.tsx +3 -0
  169. package/src/ui-tailwind/page-stack/tw-page-chrome-entry.tsx +4 -2
  170. package/src/ui-tailwind/page-stack/tw-page-stack-chrome-layer.tsx +60 -20
  171. package/src/ui-tailwind/page-stack/tw-region-block-renderer.tsx +16 -11
  172. package/src/ui-tailwind/review/tw-health-panel.tsx +36 -17
  173. package/src/ui-tailwind/review/tw-review-rail.tsx +7 -4
  174. package/src/ui-tailwind/review-workspace/diagnostics-visibility.ts +44 -0
  175. package/src/ui-tailwind/review-workspace/page-shell-metrics.ts +11 -0
  176. package/src/ui-tailwind/review-workspace/tw-review-workspace-rail.tsx +16 -1
  177. package/src/ui-tailwind/review-workspace/types.ts +26 -12
  178. package/src/ui-tailwind/review-workspace/use-diagnostics-signal.ts +40 -11
  179. package/src/ui-tailwind/review-workspace/use-layout-facet-render-signal.ts +2 -1
  180. package/src/ui-tailwind/review-workspace/use-page-markers.ts +15 -26
  181. package/src/ui-tailwind/review-workspace/use-scope-card-state.ts +35 -18
  182. package/src/ui-tailwind/review-workspace/use-selection-toolbar-placement.ts +41 -32
  183. package/src/ui-tailwind/review-workspace/use-status-bar-page-facts.ts +2 -1
  184. package/src/ui-tailwind/review-workspace/use-workspace-side-effects.ts +2 -1
  185. package/src/ui-tailwind/status/tw-status-bar.tsx +6 -5
  186. package/src/ui-tailwind/toolbar/tw-role-action-region.tsx +52 -80
  187. package/src/ui-tailwind/toolbar/tw-shell-header.tsx +12 -48
  188. package/src/ui-tailwind/toolbar/tw-toolbar-icon-button.tsx +9 -4
  189. package/src/ui-tailwind/toolbar/tw-toolbar.tsx +328 -361
  190. package/src/ui-tailwind/tw-review-workspace.tsx +152 -286
@@ -0,0 +1,316 @@
1
+ /**
2
+ * Scope action capability projection.
3
+ *
4
+ * This module reports what the shipped L08 compiler/runtime can lower today.
5
+ * It does not validate a specific payload and does not authorize a mutation;
6
+ * every apply path still re-runs validation and compile.
7
+ */
8
+
9
+ import type {
10
+ ScopeContentControlEvidence,
11
+ ScopeGeometryEvidence,
12
+ ScopeLayoutEvidence,
13
+ ScopeCapabilities,
14
+ ScopeCapabilityVerdict,
15
+ SemanticScope,
16
+ SemanticScopeKind,
17
+ } from "./semantic-scope-types.ts";
18
+ import {
19
+ MULTI_PARAGRAPH_REPLACEMENT_REFUSAL,
20
+ multiParagraphReplacementBlockers,
21
+ } from "./multi-paragraph-refusal.ts";
22
+
23
+ const PARAGRAPH_LIKE = new Set<SemanticScopeKind>([
24
+ "paragraph",
25
+ "heading",
26
+ "list-item",
27
+ ]);
28
+
29
+ const FORMATTING_SUPPORTED = new Set<SemanticScopeKind>([
30
+ "paragraph",
31
+ "heading",
32
+ "list-item",
33
+ "scope",
34
+ ]);
35
+
36
+ const METADATA_SUPPORTED = new Set<SemanticScopeKind>([
37
+ "paragraph",
38
+ "heading",
39
+ "list-item",
40
+ "table",
41
+ "table-row",
42
+ "table-cell",
43
+ "scope",
44
+ ]);
45
+
46
+ export interface ScopeCapabilityContext {
47
+ readonly layout?: ScopeLayoutEvidence;
48
+ readonly geometry?: ScopeGeometryEvidence;
49
+ readonly contentControls?: ScopeContentControlEvidence;
50
+ }
51
+
52
+ function freezeList(values: readonly string[]): readonly string[] {
53
+ return Object.freeze([...values]);
54
+ }
55
+
56
+ function supported(
57
+ reason: string,
58
+ warnings: readonly string[] = [],
59
+ ): ScopeCapabilityVerdict {
60
+ const hasWarnings = warnings.length > 0;
61
+ return {
62
+ supported: true,
63
+ status: hasWarnings ? "degraded" : "supported",
64
+ posture: hasWarnings ? "warn-and-proceed" : "supported",
65
+ reason,
66
+ ...(hasWarnings ? { warnings: freezeList(warnings) } : {}),
67
+ };
68
+ }
69
+
70
+ function unsupported(
71
+ reason: string,
72
+ blockers: readonly string[] = [reason],
73
+ warnings: readonly string[] = [],
74
+ ): ScopeCapabilityVerdict {
75
+ return {
76
+ supported: false,
77
+ status: "unsupported",
78
+ posture: "hard-refusal",
79
+ reason,
80
+ blockers: freezeList(blockers),
81
+ ...(warnings.length > 0 ? { warnings: freezeList(warnings) } : {}),
82
+ };
83
+ }
84
+
85
+ function blocked(
86
+ reason: string,
87
+ blockers: readonly string[] = [reason],
88
+ ): ScopeCapabilityVerdict {
89
+ return {
90
+ supported: false,
91
+ status: "blocked",
92
+ posture: "hard-refusal",
93
+ reason,
94
+ blockers: freezeList(blockers),
95
+ };
96
+ }
97
+
98
+ function guardBlocker(scope: SemanticScope): string | null {
99
+ switch (scope.workflow.effectiveMode) {
100
+ case "view":
101
+ return "guard:view-mode-active";
102
+ case "comment":
103
+ return "guard:comment-only";
104
+ case "blocked": {
105
+ const [first] = scope.workflow.blockedReasons ?? [];
106
+ return first ? `guard:block-${first}` : "guard:block-scope-overlay";
107
+ }
108
+ default:
109
+ return null;
110
+ }
111
+ }
112
+
113
+ function operationRefusal(scope: SemanticScope): string {
114
+ return `compile-refused:${scope.kind}`;
115
+ }
116
+
117
+ function contentControlBlockers(
118
+ context: ScopeCapabilityContext | undefined,
119
+ ): readonly string[] {
120
+ const entries = context?.contentControls?.entries ?? [];
121
+ if (entries.length === 0) return [];
122
+ return freezeList(entries.map((entry) => `preserve:content-control:${entry.evidenceId}`));
123
+ }
124
+
125
+ function evidenceWarnings(
126
+ context: ScopeCapabilityContext | undefined,
127
+ ): readonly string[] {
128
+ const warnings: string[] = [];
129
+ if (context?.layout) {
130
+ if (
131
+ context.layout.status === "requires-rehydration" ||
132
+ context.layout.status === "unavailable" ||
133
+ context.layout.completeness !== "complete"
134
+ ) {
135
+ warnings.push(`layout:${context.layout.completeness}`);
136
+ }
137
+ }
138
+ if (context?.geometry) {
139
+ if (context.geometry.requiresRehydration) {
140
+ warnings.push("geometry:requires-rehydration");
141
+ } else if (context.geometry.status === "unavailable") {
142
+ warnings.push("geometry:unavailable");
143
+ }
144
+ if (context.geometry.continuationState?.crossesPageBoundary) {
145
+ warnings.push("geometry:crosses-page-boundary");
146
+ }
147
+ }
148
+ return freezeList(warnings);
149
+ }
150
+
151
+ function insertCapability(
152
+ scope: SemanticScope,
153
+ edge: "before" | "after",
154
+ context?: ScopeCapabilityContext,
155
+ ): ScopeCapabilityVerdict {
156
+ const guard = guardBlocker(scope);
157
+ if (guard) return blocked(guard);
158
+
159
+ if (!PARAGRAPH_LIKE.has(scope.kind)) {
160
+ return unsupported(operationRefusal(scope));
161
+ }
162
+
163
+ if (
164
+ scope.replaceability.level === "blocked" ||
165
+ scope.replaceability.level === "preserve-only"
166
+ ) {
167
+ const reason = scope.replaceability.reason
168
+ ? `replaceability:${scope.replaceability.reason}`
169
+ : `replaceability:${scope.replaceability.level}`;
170
+ return unsupported(reason);
171
+ }
172
+
173
+ return supported(
174
+ `compile-supported:paragraph-like:insert-${edge}`,
175
+ [
176
+ ...(scope.workflow.effectiveMode === "suggest" ? ["guard:suggest-mode"] : []),
177
+ ...evidenceWarnings(context),
178
+ ],
179
+ );
180
+ }
181
+
182
+ function replaceTextCapability(
183
+ scope: SemanticScope,
184
+ context?: ScopeCapabilityContext,
185
+ ): ScopeCapabilityVerdict {
186
+ const guard = guardBlocker(scope);
187
+ if (guard) return blocked(guard);
188
+
189
+ const contentControls = contentControlBlockers(context);
190
+ if (contentControls.length > 0) {
191
+ return blocked("preserve:content-control", contentControls);
192
+ }
193
+
194
+ if (!PARAGRAPH_LIKE.has(scope.kind)) {
195
+ const reason =
196
+ scope.kind === "scope" && scope.replaceability.reason
197
+ ? MULTI_PARAGRAPH_REPLACEMENT_REFUSAL
198
+ : `compile-refused:${scope.kind}`;
199
+ return unsupported(
200
+ reason,
201
+ scope.kind === "scope"
202
+ ? multiParagraphReplacementBlockers("text")
203
+ : [reason],
204
+ evidenceWarnings(context),
205
+ );
206
+ }
207
+
208
+ if (
209
+ scope.replaceability.level === "blocked" ||
210
+ scope.replaceability.level === "preserve-only" ||
211
+ scope.replaceability.level === "formatting-only"
212
+ ) {
213
+ const reason = scope.replaceability.reason
214
+ ? `replaceability:${scope.replaceability.reason}`
215
+ : `replaceability:${scope.replaceability.level}`;
216
+ return unsupported(reason);
217
+ }
218
+
219
+ return supported(
220
+ "compile-supported:paragraph-like:text-replace",
221
+ [
222
+ ...(scope.workflow.effectiveMode === "suggest" ? ["guard:suggest-mode"] : []),
223
+ ...evidenceWarnings(context),
224
+ ],
225
+ );
226
+ }
227
+
228
+ function replaceFragmentCapability(
229
+ scope: SemanticScope,
230
+ context?: ScopeCapabilityContext,
231
+ ): ScopeCapabilityVerdict {
232
+ const guard = guardBlocker(scope);
233
+ if (guard) return blocked(guard);
234
+
235
+ const contentControls = contentControlBlockers(context);
236
+ if (contentControls.length > 0) {
237
+ return blocked("preserve:content-control", contentControls);
238
+ }
239
+
240
+ if (!PARAGRAPH_LIKE.has(scope.kind)) {
241
+ const reason =
242
+ scope.kind === "scope" && scope.replaceability.reason
243
+ ? MULTI_PARAGRAPH_REPLACEMENT_REFUSAL
244
+ : `compile-refused:${scope.kind}`;
245
+ return unsupported(
246
+ reason,
247
+ scope.kind === "scope"
248
+ ? multiParagraphReplacementBlockers("fragment")
249
+ : [reason],
250
+ evidenceWarnings(context),
251
+ );
252
+ }
253
+
254
+ if (scope.workflow.effectiveMode === "suggest") {
255
+ return unsupported(
256
+ `compile-refused:${scope.kind}:structured-suggesting-not-implemented`,
257
+ [`compile-refused:${scope.kind}:structured-suggesting-not-implemented`],
258
+ ["guard:suggest-mode"],
259
+ );
260
+ }
261
+
262
+ return supported(
263
+ "compile-supported:paragraph-like:fragment-replace",
264
+ evidenceWarnings(context),
265
+ );
266
+ }
267
+
268
+ function formattingCapability(
269
+ scope: SemanticScope,
270
+ context?: ScopeCapabilityContext,
271
+ ): ScopeCapabilityVerdict {
272
+ const guard = guardBlocker(scope);
273
+ if (guard) return blocked(guard);
274
+
275
+ if (!FORMATTING_SUPPORTED.has(scope.kind)) {
276
+ return unsupported(`compile-refused:${scope.kind}:formatting-not-implemented`);
277
+ }
278
+
279
+ if (scope.workflow.effectiveMode === "suggest") {
280
+ return unsupported(
281
+ `compile-refused:${scope.kind}:formatting-suggesting-not-implemented`,
282
+ [`compile-refused:${scope.kind}:formatting-suggesting-not-implemented`],
283
+ ["guard:suggest-mode"],
284
+ );
285
+ }
286
+
287
+ return supported("compile-supported:scope-formatting", evidenceWarnings(context));
288
+ }
289
+
290
+ function metadataCapability(scope: SemanticScope): ScopeCapabilityVerdict {
291
+ if (METADATA_SUPPORTED.has(scope.kind)) {
292
+ return supported(
293
+ scope.kind === "table" ||
294
+ scope.kind === "table-row" ||
295
+ scope.kind === "table-cell"
296
+ ? "metadata-supported:derived-table-family"
297
+ : "metadata-supported:scope-id",
298
+ );
299
+ }
300
+ return unsupported(`scope-not-resolvable:${scope.handle.scopeId}`);
301
+ }
302
+
303
+ export function deriveScopeCapabilities(
304
+ scope: SemanticScope,
305
+ context: ScopeCapabilityContext = {},
306
+ ): ScopeCapabilities {
307
+ return {
308
+ canReplaceText: replaceTextCapability(scope, context),
309
+ canReplaceFragment: replaceFragmentCapability(scope, context),
310
+ canInsertBefore: insertCapability(scope, "before", context),
311
+ canInsertAfter: insertCapability(scope, "after", context),
312
+ canApplyFormatting: formattingCapability(scope, context),
313
+ canClearFormattingLayer: formattingCapability(scope, context),
314
+ canAttachMetadata: metadataCapability(scope),
315
+ };
316
+ }
@@ -17,6 +17,7 @@ import type {
17
17
  WorkflowMetadataEntry,
18
18
  WorkflowOverlay,
19
19
  } from "./_scope-dependencies.ts";
20
+ import type { ScopeGeometryEvidenceProvider } from "./geometry-evidence.ts";
20
21
 
21
22
  import {
22
23
  buildParagraphIndexMap,
@@ -26,6 +27,7 @@ import {
26
27
  import type { EnumeratedScope } from "./enumerate-scopes.ts";
27
28
  import { enumerateScopes } from "./enumerate-scopes.ts";
28
29
  import { composeEvidence } from "./evidence.ts";
30
+ import type { ScopeLayoutEvidenceProvider } from "./layout-evidence.ts";
29
31
  import type {
30
32
  ScopeBundle,
31
33
  ScopeBundleNeighborhood,
@@ -47,6 +49,16 @@ export interface ScopeBundleInputs {
47
49
  * to walk the metadata snapshot.
48
50
  */
49
51
  readonly workflowMetadataEntries?: readonly WorkflowMetadataEntry[];
52
+ /**
53
+ * Optional Layer-05 replacement-envelope read seam. When omitted, bundle
54
+ * evidence records geometry as unavailable rather than synthesizing rects.
55
+ */
56
+ readonly geometry?: ScopeGeometryEvidenceProvider;
57
+ /**
58
+ * Optional lower-layer layout evidence seam. When absent, bundle evidence
59
+ * records layout as unavailable instead of deriving page slices in L08.
60
+ */
61
+ readonly layout?: ScopeLayoutEvidenceProvider;
50
62
  }
51
63
 
52
64
  /**
@@ -126,6 +138,8 @@ export function compileScopeBundle(
126
138
  ...(inputs.workflowMetadataEntries
127
139
  ? { workflowMetadataEntries: inputs.workflowMetadataEntries }
128
140
  : {}),
141
+ ...(inputs.geometry ? { geometry: inputs.geometry } : {}),
142
+ ...(inputs.layout ? { layout: inputs.layout } : {}),
129
143
  });
130
144
  return {
131
145
  scope,
@@ -37,10 +37,12 @@ import type {
37
37
  WorkflowMetadataEntry,
38
38
  WorkflowMetadataSnapshot,
39
39
  WorkflowOverlay,
40
+ WorkflowEventOrigin,
40
41
  } from "./_scope-dependencies.ts";
41
42
  import type { CanonicalDocumentEnvelope } from "../../core/state/editor-state.ts";
42
43
  import type { TelemetryBus } from "../debug/telemetry-bus.ts";
43
44
  import type { AIAction } from "../workflow/ai-action-policy.ts";
45
+ import type { ScopeGeometryEvidenceProvider } from "./geometry-evidence.ts";
44
46
 
45
47
  import {
46
48
  buildParagraphIndexMap,
@@ -49,6 +51,12 @@ import {
49
51
  } from "./compile-scope.ts";
50
52
  import { compileScopeBundleById } from "./compile-scope-bundle.ts";
51
53
  import { enumerateScopes, type EnumeratedScope } from "./enumerate-scopes.ts";
54
+ import {
55
+ collectTopLevelTableBlockIdsFromRenderSnapshot,
56
+ createRuntimeTableFrameLayoutEvidenceProvider,
57
+ type ScopeRuntimeLayoutEvidenceSource,
58
+ type ScopeRuntimeRenderSnapshotLike,
59
+ } from "./layout-evidence.ts";
52
60
  import {
53
61
  attachExplanation,
54
62
  type AttachExplanationResult,
@@ -59,6 +67,12 @@ import {
59
67
  type IssueSeverity,
60
68
  type IssueStatus,
61
69
  } from "./create-issue.ts";
70
+ import {
71
+ resolveIssueLifecycleTarget,
72
+ transitionScopeIssueLifecycle,
73
+ type IssueLifecycleAction,
74
+ type TransitionScopeIssueLifecycleResult,
75
+ } from "./issue-lifecycle.ts";
62
76
  import {
63
77
  applyScopeReplacement,
64
78
  type ApplyScopeReplacementResult,
@@ -69,12 +83,12 @@ import {
69
83
  } from "./formatting/apply.ts";
70
84
  import { proposeReplacement } from "./replacement/propose.ts";
71
85
  import type {
72
- ReplacementOperationKind,
73
86
  ReplacementPreservePolicy,
74
87
  ReplacementScope,
75
88
  RuntimeOperationPlan,
76
89
  ScopeFormattingAction,
77
90
  ScopeFormattingScope,
91
+ ScopeReplacementOperationKind,
78
92
  ScopeBundle,
79
93
  ScopeHandle,
80
94
  SemanticScope,
@@ -98,10 +112,16 @@ export interface CompilerServiceRuntime {
98
112
  getInteractionGuardSnapshot(): InteractionGuardSnapshot;
99
113
  getCompatibilityReport(): CompatibilityReport;
100
114
  getWorkflowMetadataSnapshot(): WorkflowMetadataSnapshot;
101
- setWorkflowMetadataEntries(entries: readonly WorkflowMetadataEntry[]): void;
115
+ setWorkflowMetadataEntries(
116
+ entries: readonly WorkflowMetadataEntry[],
117
+ origin?: WorkflowEventOrigin,
118
+ ): void;
119
+ getRenderSnapshot?(): ScopeRuntimeRenderSnapshotLike;
102
120
  getSessionState(): ScopeSessionState;
103
121
  applyScopeReplacement(plan: RuntimeOperationPlan): void;
104
122
  applyScopeFormatting(plan: RuntimeOperationPlan): boolean;
123
+ readonly geometry?: ScopeGeometryEvidenceProvider;
124
+ readonly layout?: ScopeRuntimeLayoutEvidenceSource;
105
125
  debug?: { bus: TelemetryBus };
106
126
  }
107
127
 
@@ -113,7 +133,7 @@ export interface CompileScopeByIdResult {
113
133
 
114
134
  export interface ApplyReplacementRequest {
115
135
  readonly targetScopeId: string;
116
- readonly operation: ReplacementOperationKind;
136
+ readonly operation: ScopeReplacementOperationKind;
117
137
  /**
118
138
  * Flat text payload. Shorthand for
119
139
  * `proposedContent: {kind: "text", text}`. Ignored when
@@ -165,6 +185,14 @@ export interface CreateIssueRequest {
165
185
  readonly status?: IssueStatus;
166
186
  }
167
187
 
188
+ export interface TransitionIssueLifecycleRequest {
189
+ readonly issueId: string;
190
+ readonly action: IssueLifecycleAction;
191
+ readonly actorId: string;
192
+ readonly origin: "ui" | "agent" | "host";
193
+ readonly emittedAtUtc: string;
194
+ }
195
+
168
196
  export interface ScopeCompilerService {
169
197
  /**
170
198
  * Enumerate + compile every live scope in the document. Per-call the
@@ -197,7 +225,7 @@ export interface ScopeCompilerService {
197
225
  buildReplacementScope(
198
226
  targetHandle: ScopeHandle,
199
227
  input: {
200
- readonly operation: ReplacementOperationKind;
228
+ readonly operation: ScopeReplacementOperationKind;
201
229
  readonly proposedText?: string;
202
230
  readonly formatting?: ReplacementScope["formatting"];
203
231
  readonly preserve?: ReplacementPreservePolicy;
@@ -238,6 +266,17 @@ export interface ScopeCompilerService {
238
266
  * overlay-only constraint as `attachExplanation`.
239
267
  */
240
268
  createIssue(request: CreateIssueRequest): CreateIssueResult;
269
+
270
+ /**
271
+ * Resolve/reopen an existing `ai.issue` metadata entry through the L06
272
+ * durable lifecycle substrate, but only after its scope target still compiles
273
+ * through Layer 08. This is intentionally not a public L09 API yet; it is the
274
+ * scope/evidence join that L09 will consume when `ai.resolveIssue` /
275
+ * `ai.reopenIssue` graduate.
276
+ */
277
+ transitionIssueLifecycle(
278
+ request: TransitionIssueLifecycleRequest,
279
+ ): TransitionScopeIssueLifecycleResult;
241
280
  }
242
281
 
243
282
  /**
@@ -324,12 +363,24 @@ export function createScopeCompilerService(
324
363
  // operations go through `attachScopeMetadata` via this same
325
364
  // runtime surface, so the snapshot is always fresh here.
326
365
  const workflowMetadataEntries = runtime.getWorkflowMetadataSnapshot().entries;
366
+ const layout =
367
+ runtime.layout
368
+ ? createRuntimeTableFrameLayoutEvidenceProvider({
369
+ layout: runtime.layout,
370
+ tableBlockIdsByBlockIndex:
371
+ collectTopLevelTableBlockIdsFromRenderSnapshot(
372
+ runtime.getRenderSnapshot?.(),
373
+ ),
374
+ })
375
+ : undefined;
327
376
  return compileScopeBundleById(scopeId, {
328
377
  document,
329
378
  ...(overlay ? { overlay } : {}),
330
379
  nowUtc,
331
380
  catalog: document.styles,
332
381
  workflowMetadataEntries,
382
+ ...(runtime.geometry ? { geometry: runtime.geometry } : {}),
383
+ ...(layout ? { layout } : {}),
333
384
  });
334
385
  },
335
386
 
@@ -361,6 +412,7 @@ export function createScopeCompilerService(
361
412
  reason: "scope-not-resolvable",
362
413
  validation: {
363
414
  safe: false,
415
+ posture: "hard-refusal",
364
416
  blockedReasons: Object.freeze([
365
417
  `scope-not-resolvable:${request.targetScopeId}`,
366
418
  ]),
@@ -400,6 +452,7 @@ export function createScopeCompilerService(
400
452
  runtime.getInteractionGuardSnapshot(),
401
453
  getCompatibilityReport: () => runtime.getCompatibilityReport(),
402
454
  applyScopeReplacement: (plan) => runtime.applyScopeReplacement(plan),
455
+ verifyReadback: true,
403
456
  },
404
457
  proposed,
405
458
  ...(request.actionId ? { actionId: request.actionId } : {}),
@@ -418,6 +471,7 @@ export function createScopeCompilerService(
418
471
  reason: "scope-not-resolvable",
419
472
  validation: {
420
473
  safe: false,
474
+ posture: "hard-refusal",
421
475
  blockedReasons: Object.freeze([
422
476
  `scope-not-resolvable:${request.targetScopeId}`,
423
477
  ]),
@@ -498,5 +552,55 @@ export function createScopeCompilerService(
498
552
  },
499
553
  );
500
554
  },
555
+
556
+ transitionIssueLifecycle(
557
+ request: TransitionIssueLifecycleRequest,
558
+ ): TransitionScopeIssueLifecycleResult {
559
+ const target = resolveIssueLifecycleTarget(
560
+ runtime.getWorkflowMetadataSnapshot(),
561
+ request.issueId,
562
+ );
563
+ if (!target) {
564
+ const reason = `issue-not-found:${request.issueId}`;
565
+ return {
566
+ transitioned: false,
567
+ issueId: request.issueId,
568
+ reason,
569
+ blockers: Object.freeze([reason]),
570
+ };
571
+ }
572
+
573
+ const compiled = this.compileScopeById(target.scopeId);
574
+ if (!compiled) {
575
+ const reason = `scope-not-resolvable:${target.scopeId}`;
576
+ return {
577
+ transitioned: false,
578
+ issueId: target.issueId,
579
+ reason,
580
+ blockers: Object.freeze([reason]),
581
+ scopeId: target.scopeId,
582
+ };
583
+ }
584
+
585
+ const documentBefore = runtime.getCanonicalDocument();
586
+ const result = transitionScopeIssueLifecycle({
587
+ runtime: {
588
+ getCanonicalDocument: () => runtime.getCanonicalDocument(),
589
+ getWorkflowMetadataSnapshot: () =>
590
+ runtime.getWorkflowMetadataSnapshot(),
591
+ setWorkflowMetadataEntries: (entries, origin) =>
592
+ runtime.setWorkflowMetadataEntries([...entries], origin),
593
+ },
594
+ documentBefore,
595
+ targetScopeSnapshot: compiled.scope,
596
+ issueId: target.issueId,
597
+ action: request.action,
598
+ actorId: request.actorId,
599
+ origin: request.origin,
600
+ emittedAtUtc: request.emittedAtUtc,
601
+ ...(runtime.debug?.bus ? { bus: runtime.debug.bus } : {}),
602
+ });
603
+ return result;
604
+ },
501
605
  };
502
606
  }
@@ -0,0 +1,79 @@
1
+ /**
2
+ * Content-control / SDT evidence for scope bundles and preservation checks.
3
+ *
4
+ * SDTs are package-backed wrappers, not L08 scope truth. This helper only
5
+ * reports overlap with a scope range so capability and validation surfaces can
6
+ * stay preserve-first until L02/L04 expose richer source/layout refs.
7
+ */
8
+
9
+ import type {
10
+ CanonicalDocument,
11
+ DocumentRootNode,
12
+ SdtNode,
13
+ } from "../../model/canonical-document.ts";
14
+ import type { CanonicalDocumentEnvelope } from "../../core/state/editor-state.ts";
15
+
16
+ import { computeBlockPositions, type ScopePositionRange } from "./position-map.ts";
17
+ import { rangesOverlap } from "./scope-range.ts";
18
+ import type {
19
+ ScopeContentControlEvidence,
20
+ ScopeContentControlEvidenceEntry,
21
+ } from "./semantic-scope-types.ts";
22
+
23
+ function documentRoot(
24
+ document: Pick<CanonicalDocument, "content"> | CanonicalDocumentEnvelope,
25
+ ): DocumentRootNode {
26
+ const envelope = document as CanonicalDocumentEnvelope;
27
+ return "content" in envelope
28
+ ? (envelope.content as DocumentRootNode)
29
+ : (document as unknown as DocumentRootNode);
30
+ }
31
+
32
+ function entryForSdt(
33
+ blockIndex: number,
34
+ sdt: SdtNode,
35
+ ): ScopeContentControlEvidenceEntry {
36
+ const typePart = sdt.properties.sdtType ?? "unknown";
37
+ return {
38
+ evidenceId: `sdt:${blockIndex}:${typePart}`,
39
+ blockIndex,
40
+ ...(sdt.properties.sdtType ? { sdtType: sdt.properties.sdtType } : {}),
41
+ ...(sdt.properties.alias ? { alias: sdt.properties.alias } : {}),
42
+ ...(sdt.properties.tag ? { tag: sdt.properties.tag } : {}),
43
+ ...(sdt.properties.lock ? { lock: sdt.properties.lock } : {}),
44
+ };
45
+ }
46
+
47
+ export function findContentControlsIntersectingRange(
48
+ document: Pick<CanonicalDocument, "content"> | CanonicalDocumentEnvelope,
49
+ range: ScopePositionRange | null,
50
+ ): readonly ScopeContentControlEvidenceEntry[] {
51
+ if (!range) return Object.freeze([]);
52
+
53
+ const root = documentRoot(document);
54
+ const blockRanges = computeBlockPositions(document);
55
+ const entries: ScopeContentControlEvidenceEntry[] = [];
56
+
57
+ for (const blockRange of blockRanges) {
58
+ const block = root.children[blockRange.blockIndex];
59
+ if (!block || block.type !== "sdt") continue;
60
+ if (!rangesOverlap(range, { from: blockRange.from, to: blockRange.to })) {
61
+ continue;
62
+ }
63
+ entries.push(entryForSdt(blockRange.blockIndex, block));
64
+ }
65
+
66
+ return Object.freeze(entries);
67
+ }
68
+
69
+ export function deriveScopeContentControlEvidence(
70
+ document: Pick<CanonicalDocument, "content"> | CanonicalDocumentEnvelope,
71
+ range: ScopePositionRange | null,
72
+ ): ScopeContentControlEvidence {
73
+ const entries = findContentControlsIntersectingRange(document, range);
74
+ return {
75
+ status: entries.length > 0 ? "present" : "none",
76
+ count: entries.length,
77
+ entries,
78
+ };
79
+ }
@@ -9,10 +9,9 @@
9
9
  * Issues are metadata records rather than a dedicated overlay section
10
10
  * in this slice — matches the Slice-6 plan's "issue store is a new
11
11
  * section on the workflow overlay" intent without requiring an
12
- * overlay-schema minor bump. When CLM workblocks need richer lifecycle
13
- * (resolve/reopen transitions, assigned owners), a follow-up sub-slice
14
- * promotes issues to a first-class overlay kind and the storage
15
- * migrates — the metadataId stays stable so callers don't break.
12
+ * overlay-schema minor bump. Layer 06 owns the durable lifecycle substrate for
13
+ * the stable metadataId; L08/L09 still own scope/API shaped resolve/reopen
14
+ * exposure.
16
15
  *
17
16
  * Scope coverage (coord-06 §13c shipped 2026-04-23): both overlay
18
17
  * (workflow) scopes AND derived paragraph / heading / list-item
@@ -28,9 +27,10 @@ import type {
28
27
  MetadataWriterRuntime,
29
28
  } from "../workflow/metadata-writer.ts";
30
29
  import { attachScopeMetadata } from "../workflow/metadata-writer.ts";
30
+ import { AI_ISSUE_METADATA_ID } from "../workflow/ai-issue-lifecycle.ts";
31
31
 
32
32
  /** Stable metadataId for AI-authored issues. */
33
- export const AI_ISSUE_METADATA_ID = "ai.issue" as const;
33
+ export { AI_ISSUE_METADATA_ID };
34
34
 
35
35
  export type IssueSeverity = "info" | "warning" | "error";
36
36
  export type IssueStatus = "open" | "resolved";