@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
@@ -24,10 +24,21 @@ import type {
24
24
  } from "./_scope-dependencies.ts";
25
25
 
26
26
  import { AI_EXPLANATION_METADATA_ID } from "./attach-explanation.ts";
27
+ import { deriveScopeCapabilities } from "./capabilities.ts";
28
+ import { deriveScopeContentControlEvidence } from "./content-control-evidence.ts";
27
29
  import { AI_ISSUE_METADATA_ID } from "./create-issue.ts";
28
30
  import type { EnumeratedScope } from "./enumerate-scopes.ts";
31
+ import {
32
+ deriveScopeGeometryEvidence,
33
+ type ScopeGeometryEvidenceProvider,
34
+ } from "./geometry-evidence.ts";
35
+ import {
36
+ deriveScopeLayoutEvidence,
37
+ type ScopeLayoutEvidenceProvider,
38
+ } from "./layout-evidence.ts";
29
39
  import { buildScopePositionMap, type ScopePositionRange } from "./position-map.ts";
30
40
  import { rangesOverlap, resolveScopeRange } from "./scope-range.ts";
41
+ import { deriveScopeVisualization } from "./visualization.ts";
31
42
  import type {
32
43
  AIExplanationSummary,
33
44
  AIIssueSummary,
@@ -86,6 +97,16 @@ export interface EvidenceInputs {
86
97
  * `runtime.getWorkflowMetadataSnapshot().entries` through here.
87
98
  */
88
99
  readonly workflowMetadataEntries?: readonly WorkflowMetadataEntry[];
100
+ /**
101
+ * Layer-05 geometry facet subset. When present, evidence includes the
102
+ * current replacement envelope or an explicit requires-rehydration status.
103
+ */
104
+ readonly geometry?: ScopeGeometryEvidenceProvider;
105
+ /**
106
+ * Optional lower-layer layout evidence seam. Missing provider or missing
107
+ * rows are surfaced explicitly in `ScopeBundleEvidence.layout`.
108
+ */
109
+ readonly layout?: ScopeLayoutEvidenceProvider;
89
110
  }
90
111
 
91
112
  function normalizeSeverity(raw: unknown): AIIssueSummary["severity"] {
@@ -98,6 +119,38 @@ function normalizeStatus(raw: unknown): AIIssueSummary["status"] {
98
119
  return "open";
99
120
  }
100
121
 
122
+ function projectIssueTransition(raw: unknown): AIIssueSummary["lastTransition"] | undefined {
123
+ const value = raw as
124
+ | {
125
+ action?: unknown;
126
+ actorId?: unknown;
127
+ at?: unknown;
128
+ origin?: unknown;
129
+ fromStatus?: unknown;
130
+ toStatus?: unknown;
131
+ }
132
+ | undefined;
133
+ if (!value || typeof value !== "object") return undefined;
134
+ if (value.action !== "resolve" && value.action !== "reopen") return undefined;
135
+ if (typeof value.actorId !== "string") return undefined;
136
+ if (typeof value.at !== "string") return undefined;
137
+ if (typeof value.origin !== "string") return undefined;
138
+ if (value.fromStatus !== "open" && value.fromStatus !== "resolved") {
139
+ return undefined;
140
+ }
141
+ if (value.toStatus !== "open" && value.toStatus !== "resolved") {
142
+ return undefined;
143
+ }
144
+ return {
145
+ action: value.action,
146
+ actorId: value.actorId,
147
+ at: value.at,
148
+ origin: value.origin,
149
+ fromStatus: value.fromStatus,
150
+ toStatus: value.toStatus,
151
+ };
152
+ }
153
+
101
154
  function projectAIExplanation(
102
155
  entry: WorkflowMetadataEntry,
103
156
  ): AIExplanationSummary | null {
@@ -127,9 +180,18 @@ function projectAIIssue(
127
180
  severity?: unknown;
128
181
  status?: unknown;
129
182
  createdAtUtc?: string;
183
+ statusUpdatedAtUtc?: string;
184
+ resolvedAtUtc?: string;
185
+ resolvedBy?: string;
186
+ reopenedAtUtc?: string;
187
+ reopenedBy?: string;
188
+ lifecycle?: unknown[];
130
189
  }
131
190
  | undefined;
132
191
  if (!value || typeof value.summary !== "string") return null;
192
+ const lastTransition = Array.isArray(value.lifecycle)
193
+ ? projectIssueTransition(value.lifecycle[value.lifecycle.length - 1])
194
+ : undefined;
133
195
  return {
134
196
  issueId:
135
197
  typeof value.issueId === "string" && value.issueId.length > 0
@@ -141,6 +203,22 @@ function projectAIIssue(
141
203
  ...(typeof value.createdAtUtc === "string"
142
204
  ? { createdAtUtc: value.createdAtUtc }
143
205
  : {}),
206
+ ...(typeof value.statusUpdatedAtUtc === "string"
207
+ ? { statusUpdatedAtUtc: value.statusUpdatedAtUtc }
208
+ : {}),
209
+ ...(typeof value.resolvedAtUtc === "string"
210
+ ? { resolvedAtUtc: value.resolvedAtUtc }
211
+ : {}),
212
+ ...(typeof value.resolvedBy === "string"
213
+ ? { resolvedBy: value.resolvedBy }
214
+ : {}),
215
+ ...(typeof value.reopenedAtUtc === "string"
216
+ ? { reopenedAtUtc: value.reopenedAtUtc }
217
+ : {}),
218
+ ...(typeof value.reopenedBy === "string"
219
+ ? { reopenedBy: value.reopenedBy }
220
+ : {}),
221
+ ...(lastTransition ? { lastTransition } : {}),
144
222
  };
145
223
  }
146
224
 
@@ -221,11 +299,24 @@ export function composeEvidence(inputs: EvidenceInputs): ScopeBundleEvidence {
221
299
  aiExplanations.sort((a, b) => a.explanationId.localeCompare(b.explanationId));
222
300
  aiIssues.sort((a, b) => a.issueId.localeCompare(b.issueId));
223
301
 
302
+ const layout = deriveScopeLayoutEvidence(scope.handle.scopeId, inputs.layout);
303
+ const geometry = deriveScopeGeometryEvidence(scope.handle.scopeId, inputs.geometry);
304
+ const contentControls = deriveScopeContentControlEvidence(document, selfRange);
305
+
224
306
  return {
225
307
  formattingSummary: formattingSummaryOf(scope),
226
308
  reviewItemIds,
227
309
  overlappingWorkflowScopeIds,
228
310
  compatibilityFlags,
311
+ layout,
312
+ geometry,
313
+ visualization: deriveScopeVisualization(scope),
314
+ contentControls,
315
+ capabilities: deriveScopeCapabilities(scope, {
316
+ layout,
317
+ geometry,
318
+ contentControls,
319
+ }),
229
320
  ...(inputs.workflowMetadataEntries
230
321
  ? { aiExplanations, aiIssues }
231
322
  : {}),
@@ -156,6 +156,7 @@ export function applyScopeFormatting(
156
156
  if (!resolvedScope || !resolvedEnumerated) {
157
157
  const validation: ValidationResult = {
158
158
  safe: false,
159
+ posture: "hard-refusal",
159
160
  blockedReasons: Object.freeze([
160
161
  `scope-not-resolvable:${proposed.targetHandle.scopeId}`,
161
162
  ]),
@@ -211,6 +212,7 @@ export function applyScopeFormatting(
211
212
  : `compile-refused:${resolvedScope.kind}:formatting-not-implemented`;
212
213
  const refused: ValidationResult = {
213
214
  safe: false,
215
+ posture: "hard-refusal",
214
216
  blockedReasons: Object.freeze([blocker]),
215
217
  warnings: verdict.warnings,
216
218
  };
@@ -0,0 +1,130 @@
1
+ /**
2
+ * Scope geometry evidence projection.
3
+ *
4
+ * Layer 08 consumes the Layer 05 replacement-envelope seam when it is wired,
5
+ * but it does not compute geometry itself. Cold or missing geometry remains
6
+ * explicit evidence, not a synthesized rectangle.
7
+ */
8
+
9
+ import type {
10
+ ScopeGeometryEvidence,
11
+ ScopeGeometryEvidencePrecision,
12
+ ScopeGeometryEvidenceRect,
13
+ ScopeLayoutContinuationEvidence,
14
+ } from "./semantic-scope-types.ts";
15
+
16
+ type GeometrySpace = ScopeGeometryEvidenceRect["space"];
17
+
18
+ interface EnvelopeRectLike {
19
+ readonly leftPx: number;
20
+ readonly topPx: number;
21
+ readonly widthPx: number;
22
+ readonly heightPx: number;
23
+ readonly space: GeometrySpace;
24
+ readonly precision?: ScopeGeometryEvidencePrecision;
25
+ }
26
+
27
+ interface EnvelopeLike {
28
+ readonly scopeRects: readonly EnvelopeRectLike[];
29
+ readonly pageIds?: readonly string[];
30
+ readonly continuation?: ScopeLayoutContinuationEvidence;
31
+ readonly attachPoint: {
32
+ readonly xPx: number;
33
+ readonly yPx: number;
34
+ readonly space: GeometrySpace;
35
+ };
36
+ readonly confidence: "exact" | "medium" | "detached";
37
+ readonly linesCrossed: number;
38
+ readonly rehydrationStatus?: "realized" | "requires-rehydration" | "unavailable";
39
+ readonly precision?: ScopeGeometryEvidencePrecision;
40
+ readonly sourceIdentity?: {
41
+ readonly storyKey?: string;
42
+ readonly blockPath?: string;
43
+ readonly tableKey?: string;
44
+ readonly rowKey?: string;
45
+ readonly cellKey?: string;
46
+ readonly scopeKey?: string;
47
+ readonly scopeId?: string;
48
+ readonly objectKey?: string;
49
+ readonly inlinePath?: string;
50
+ readonly objectKind?: string;
51
+ readonly editPosture?: string;
52
+ readonly joinKind?: string;
53
+ };
54
+ }
55
+
56
+ export interface ScopeGeometryEvidenceProvider {
57
+ getReplacementEnvelope(scopeId: string): EnvelopeLike | null;
58
+ }
59
+
60
+ function mapRect(rect: EnvelopeRectLike): ScopeGeometryEvidenceRect {
61
+ return {
62
+ x: rect.leftPx,
63
+ y: rect.topPx,
64
+ width: rect.widthPx,
65
+ height: rect.heightPx,
66
+ space: rect.space,
67
+ ...(rect.precision ? { precision: rect.precision } : {}),
68
+ };
69
+ }
70
+
71
+ export function deriveScopeGeometryEvidence(
72
+ scopeId: string,
73
+ provider?: ScopeGeometryEvidenceProvider,
74
+ ): ScopeGeometryEvidence {
75
+ if (!provider) {
76
+ return {
77
+ status: "unavailable",
78
+ requiresRehydration: false,
79
+ reason: "geometry-facet-unavailable",
80
+ };
81
+ }
82
+
83
+ const envelope = provider.getReplacementEnvelope(scopeId);
84
+ if (!envelope) {
85
+ return {
86
+ status: "requires-rehydration",
87
+ requiresRehydration: true,
88
+ reason: "replacement-envelope-unavailable",
89
+ };
90
+ }
91
+
92
+ if (envelope.rehydrationStatus === "unavailable") {
93
+ return {
94
+ status: "unavailable",
95
+ requiresRehydration: false,
96
+ reason: "replacement-envelope-unavailable",
97
+ ...(envelope.pageIds ? { pageIds: Object.freeze([...envelope.pageIds]) } : {}),
98
+ ...(envelope.continuation
99
+ ? { continuationState: { ...envelope.continuation } }
100
+ : {}),
101
+ ...(envelope.sourceIdentity
102
+ ? { sourceIdentity: { ...envelope.sourceIdentity } }
103
+ : {}),
104
+ };
105
+ }
106
+
107
+ const requiresRehydration =
108
+ envelope.rehydrationStatus === "requires-rehydration";
109
+ return {
110
+ status: requiresRehydration ? "requires-rehydration" : "available",
111
+ requiresRehydration,
112
+ ...(requiresRehydration ? { reason: "replacement-envelope-requires-rehydration" } : {}),
113
+ ...(envelope.pageIds ? { pageIds: Object.freeze([...envelope.pageIds]) } : {}),
114
+ confidence: envelope.confidence,
115
+ ...(envelope.precision ? { precision: envelope.precision } : {}),
116
+ rects: Object.freeze(envelope.scopeRects.map(mapRect)),
117
+ attachPoint: {
118
+ x: envelope.attachPoint.xPx,
119
+ y: envelope.attachPoint.yPx,
120
+ space: envelope.attachPoint.space,
121
+ },
122
+ linesCrossed: envelope.linesCrossed,
123
+ ...(envelope.continuation
124
+ ? { continuationState: { ...envelope.continuation } }
125
+ : {}),
126
+ ...(envelope.sourceIdentity
127
+ ? { sourceIdentity: { ...envelope.sourceIdentity } }
128
+ : {}),
129
+ };
130
+ }
@@ -26,8 +26,24 @@ export type {
26
26
  ReplacementOperationKind,
27
27
  ReplacementPreservePolicy,
28
28
  ReplacementScope,
29
+ ScopeReplacementOperationKind,
29
30
  ScopeActionOperationKind,
30
31
  ScopeActionProposal,
32
+ ScopeCapabilities,
33
+ ScopeCapabilityStatus,
34
+ ScopeCapabilityVerdict,
35
+ ScopeContentControlEvidence,
36
+ ScopeContentControlEvidenceEntry,
37
+ ScopeContentControlEvidenceStatus,
38
+ ScopeEvidenceAvailability,
39
+ ScopeGeometryEvidence,
40
+ ScopeGeometryEvidencePrecision,
41
+ ScopeGeometryEvidenceRect,
42
+ ScopeGeometryEvidenceStatus,
43
+ ScopeLayoutContinuationEvidence,
44
+ ScopeLayoutEvidence,
45
+ ScopeVisualizationClass,
46
+ ScopeVisualizationHint,
31
47
  RuntimeOperationPlan,
32
48
  RuntimeOperationStep,
33
49
  RuntimeOperationStepKind,
@@ -35,6 +51,7 @@ export type {
35
51
  ScopeFormattingAction,
36
52
  ScopeFormattingClearTarget,
37
53
  ScopeFormattingScope,
54
+ ScopeActionPosture,
38
55
  ScopeBundle,
39
56
  ScopeBundleEvidence,
40
57
  ScopeBundleNeighborhood,
@@ -56,6 +73,33 @@ export type {
56
73
  ValidationIssue,
57
74
  ValidationResult,
58
75
  } from "./semantic-scope-types.ts";
76
+ export { deriveScopeCapabilities } from "./capabilities.ts";
77
+ export type { ScopeCapabilityContext } from "./capabilities.ts";
78
+ export {
79
+ deriveScopeContentControlEvidence,
80
+ findContentControlsIntersectingRange,
81
+ } from "./content-control-evidence.ts";
82
+ export {
83
+ deriveScopeGeometryEvidence,
84
+ type ScopeGeometryEvidenceProvider,
85
+ } from "./geometry-evidence.ts";
86
+ export {
87
+ deriveScopeLayoutEvidence,
88
+ collectTopLevelTableBlockIdsFromRenderSnapshot,
89
+ createRuntimeTableFrameLayoutEvidenceProvider,
90
+ type RuntimeTableFrameLayoutEvidenceOptions,
91
+ type ScopeLayoutEvidenceEntry,
92
+ type ScopeLayoutEvidenceProvider,
93
+ type ScopeRuntimeLayoutEvidenceSource,
94
+ type ScopeRuntimeLayoutFragment,
95
+ type ScopeRuntimeRenderSnapshotLike,
96
+ } from "./layout-evidence.ts";
97
+ export {
98
+ MULTI_PARAGRAPH_REPLACEMENT_REFUSAL,
99
+ multiParagraphReplacementBlockers,
100
+ } from "./multi-paragraph-refusal.ts";
101
+ export type { MultiParagraphReplacementShape } from "./multi-paragraph-refusal.ts";
102
+ export { deriveScopeVisualization } from "./visualization.ts";
59
103
 
60
104
  export {
61
105
  buildParagraphIndexMap,
@@ -153,6 +197,15 @@ export {
153
197
  type IssueSeverity,
154
198
  type IssueStatus,
155
199
  } from "./create-issue.ts";
200
+ export {
201
+ resolveIssueLifecycleTarget,
202
+ transitionScopeIssueLifecycle,
203
+ type IssueLifecycleAction,
204
+ type IssueLifecycleRuntime,
205
+ type IssueLifecycleTarget,
206
+ type TransitionScopeIssueLifecycleInputs,
207
+ type TransitionScopeIssueLifecycleResult,
208
+ } from "./issue-lifecycle.ts";
156
209
  export {
157
210
  createScopeCompilerService,
158
211
  type ApplyFormattingRequest,
@@ -161,5 +214,6 @@ export {
161
214
  type CompileScopeByIdResult,
162
215
  type CompilerServiceRuntime,
163
216
  type CreateIssueRequest,
217
+ type TransitionIssueLifecycleRequest,
164
218
  type ScopeCompilerService,
165
219
  } from "./compiler-service.ts";
@@ -0,0 +1,224 @@
1
+ /**
2
+ * Layer 08 issue lifecycle binding.
3
+ *
4
+ * Layer 06 owns durable `ai.issue` metadata status transitions. This module
5
+ * adds the scope-compiler binding that future L09 `resolveIssue` /
6
+ * `reopenIssue` APIs need: find the issue's scope, require that scope to still
7
+ * compile, preserve the existing metadata entry id, and emit scope-shaped audit
8
+ * evidence for the lifecycle transition.
9
+ */
10
+
11
+ import type { TelemetryBus } from "../debug/telemetry-bus.ts";
12
+ import {
13
+ projectAIIssueLifecycleReadback,
14
+ transitionAIIssueLifecycle,
15
+ type AIIssueLifecycleAction,
16
+ type AIIssueLifecycleAuditEntry,
17
+ type AIIssueStatus,
18
+ } from "../workflow/ai-issue-lifecycle.ts";
19
+ import type {
20
+ WorkflowEventOrigin,
21
+ WorkflowMetadataEntry,
22
+ WorkflowMetadataSnapshot,
23
+ } from "./_scope-dependencies.ts";
24
+ import type { CanonicalDocumentEnvelope } from "../../core/state/editor-state.ts";
25
+ import type {
26
+ ReplacementScope,
27
+ ScopeActionAudit,
28
+ SemanticScope,
29
+ ValidationResult,
30
+ } from "./semantic-scope-types.ts";
31
+
32
+ export type IssueLifecycleAction = AIIssueLifecycleAction;
33
+
34
+ export interface IssueLifecycleRuntime {
35
+ getCanonicalDocument(): CanonicalDocumentEnvelope;
36
+ getWorkflowMetadataSnapshot(): WorkflowMetadataSnapshot;
37
+ setWorkflowMetadataEntries(
38
+ entries: readonly WorkflowMetadataEntry[],
39
+ origin?: WorkflowEventOrigin,
40
+ ): void;
41
+ }
42
+
43
+ export interface IssueLifecycleTarget {
44
+ readonly issueId: string;
45
+ readonly entryId: string;
46
+ readonly scopeId: string;
47
+ readonly status: AIIssueStatus;
48
+ }
49
+
50
+ export interface TransitionScopeIssueLifecycleInputs {
51
+ readonly runtime: IssueLifecycleRuntime;
52
+ readonly documentBefore: CanonicalDocumentEnvelope;
53
+ readonly targetScopeSnapshot: SemanticScope;
54
+ readonly issueId: string;
55
+ readonly action: IssueLifecycleAction;
56
+ readonly actorId: string;
57
+ readonly origin: "ui" | "agent" | "host";
58
+ readonly emittedAtUtc: string;
59
+ readonly bus?: TelemetryBus;
60
+ }
61
+
62
+ export type TransitionScopeIssueLifecycleResult =
63
+ | {
64
+ readonly transitioned: true;
65
+ readonly issueId: string;
66
+ readonly entryId: string;
67
+ readonly scopeId: string;
68
+ readonly fromStatus: AIIssueStatus;
69
+ readonly toStatus: AIIssueStatus;
70
+ readonly lifecycleAudit: AIIssueLifecycleAuditEntry;
71
+ readonly targetScopeSnapshot: SemanticScope;
72
+ readonly audit: ScopeActionAudit;
73
+ }
74
+ | {
75
+ readonly transitioned: false;
76
+ readonly issueId: string;
77
+ readonly reason: string;
78
+ readonly blockers: readonly string[];
79
+ readonly scopeId?: string;
80
+ readonly targetScopeSnapshot?: SemanticScope;
81
+ };
82
+
83
+ function documentHash(doc: CanonicalDocumentEnvelope): string {
84
+ let textLength = 0;
85
+ for (const block of doc.content.children) {
86
+ if (block.type !== "paragraph") continue;
87
+ for (const child of block.children) {
88
+ if (child.type === "text") textLength += child.text.length;
89
+ }
90
+ }
91
+ return `blocks:${doc.content.children.length}|text:${textLength}`;
92
+ }
93
+
94
+ function freezeBlocker(reason: string): readonly string[] {
95
+ return Object.freeze([reason]);
96
+ }
97
+
98
+ export function resolveIssueLifecycleTarget(
99
+ snapshot: WorkflowMetadataSnapshot,
100
+ issueIdOrEntryId: string,
101
+ ): IssueLifecycleTarget | null {
102
+ const readback = projectAIIssueLifecycleReadback(snapshot.entries).find(
103
+ (issue) =>
104
+ issue.issueId === issueIdOrEntryId || issue.entryId === issueIdOrEntryId,
105
+ );
106
+ if (!readback || !readback.scopeId) return null;
107
+ return {
108
+ issueId: readback.issueId,
109
+ entryId: readback.entryId,
110
+ scopeId: readback.scopeId,
111
+ status: readback.status,
112
+ };
113
+ }
114
+
115
+ function buildLifecycleAudit(
116
+ inputs: TransitionScopeIssueLifecycleInputs,
117
+ transition: {
118
+ readonly issueId: string;
119
+ readonly entryId: string;
120
+ readonly fromStatus: AIIssueStatus;
121
+ readonly toStatus: AIIssueStatus;
122
+ readonly audit: AIIssueLifecycleAuditEntry;
123
+ },
124
+ ): ScopeActionAudit {
125
+ const snapshot = inputs.targetScopeSnapshot;
126
+ const proposed: ReplacementScope = {
127
+ targetHandle: snapshot.handle,
128
+ operation: "annotate",
129
+ proposedContent: {
130
+ kind: "structured",
131
+ structured: {
132
+ metadataKind: "issue-lifecycle",
133
+ issueId: transition.issueId,
134
+ entryId: transition.entryId,
135
+ action: inputs.action,
136
+ fromStatus: transition.fromStatus,
137
+ toStatus: transition.toStatus,
138
+ },
139
+ },
140
+ proposedAtUtc: inputs.emittedAtUtc,
141
+ };
142
+ const validation: ValidationResult = {
143
+ safe: true,
144
+ posture: "supported",
145
+ blockedReasons: Object.freeze([]),
146
+ warnings: Object.freeze([]),
147
+ };
148
+ return {
149
+ actionId:
150
+ inputs.action === "resolve" ? "ai.resolveIssue" : "ai.reopenIssue",
151
+ actorId: inputs.actorId,
152
+ origin: inputs.origin,
153
+ documentHashBefore: documentHash(inputs.documentBefore),
154
+ documentHashAfter: documentHash(inputs.runtime.getCanonicalDocument()),
155
+ targetScopeSnapshot: snapshot,
156
+ proposed,
157
+ compiledOperations: Object.freeze([
158
+ {
159
+ kind:
160
+ inputs.action === "resolve"
161
+ ? "metadata-issue-resolve"
162
+ : "metadata-issue-reopen",
163
+ summary: `${inputs.action} issue ${transition.issueId} on scope ${snapshot.handle.scopeId}`,
164
+ },
165
+ ]),
166
+ validation,
167
+ emittedAtUtc: inputs.emittedAtUtc,
168
+ };
169
+ }
170
+
171
+ export function transitionScopeIssueLifecycle(
172
+ inputs: TransitionScopeIssueLifecycleInputs,
173
+ ): TransitionScopeIssueLifecycleResult {
174
+ const target = resolveIssueLifecycleTarget(
175
+ inputs.runtime.getWorkflowMetadataSnapshot(),
176
+ inputs.issueId,
177
+ );
178
+ if (!target) {
179
+ const reason = `issue-not-found:${inputs.issueId}`;
180
+ return {
181
+ transitioned: false,
182
+ issueId: inputs.issueId,
183
+ reason,
184
+ blockers: freezeBlocker(reason),
185
+ };
186
+ }
187
+
188
+ const transition = transitionAIIssueLifecycle(inputs.runtime, {
189
+ issueId: target.issueId,
190
+ action: inputs.action,
191
+ actorId: inputs.actorId,
192
+ at: inputs.emittedAtUtc,
193
+ origin: { source: inputs.origin, at: inputs.emittedAtUtc },
194
+ });
195
+
196
+ if (!transition.transitioned) {
197
+ const reason = `${transition.reason}:${transition.issueId}`;
198
+ return {
199
+ transitioned: false,
200
+ issueId: transition.issueId,
201
+ reason,
202
+ blockers: freezeBlocker(reason),
203
+ scopeId: target.scopeId,
204
+ targetScopeSnapshot: inputs.targetScopeSnapshot,
205
+ };
206
+ }
207
+
208
+ const audit = buildLifecycleAudit(inputs, transition);
209
+ inputs.bus?.emitLazy("scope", () => ({
210
+ type: "scope.action_audit",
211
+ payload: audit,
212
+ }));
213
+ return {
214
+ transitioned: true,
215
+ issueId: transition.issueId,
216
+ entryId: transition.entryId,
217
+ scopeId: target.scopeId,
218
+ fromStatus: transition.fromStatus,
219
+ toStatus: transition.toStatus,
220
+ lifecycleAudit: transition.audit,
221
+ targetScopeSnapshot: inputs.targetScopeSnapshot,
222
+ audit,
223
+ };
224
+ }