@beyondwork/docx-react-component 1.0.105 → 1.0.108

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 (193) 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 +10 -2
  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-reference.ts +28 -0
  11. package/src/api/v3/ai/_audit-time.ts +5 -0
  12. package/src/api/v3/ai/_pe2-evidence.ts +310 -6
  13. package/src/api/v3/ai/attach.ts +29 -4
  14. package/src/api/v3/ai/bundle.ts +6 -2
  15. package/src/api/v3/ai/inspect.ts +6 -2
  16. package/src/api/v3/ai/replacement.ts +112 -18
  17. package/src/api/v3/ai/resolve.ts +2 -2
  18. package/src/api/v3/ai/review.ts +177 -3
  19. package/src/api/v3/index.ts +8 -0
  20. package/src/api/v3/runtime/collab.ts +462 -0
  21. package/src/api/v3/runtime/document.ts +503 -20
  22. package/src/api/v3/runtime/geometry.ts +97 -0
  23. package/src/api/v3/runtime/layout.ts +744 -0
  24. package/src/api/v3/runtime/perf-probe.ts +14 -0
  25. package/src/api/v3/runtime/viewport.ts +9 -8
  26. package/src/api/v3/ui/_types.ts +202 -55
  27. package/src/api/v3/ui/chrome-preset-model.ts +5 -5
  28. package/src/api/v3/ui/debug.ts +115 -2
  29. package/src/api/v3/ui/index.ts +17 -0
  30. package/src/api/v3/ui/overlays.ts +0 -8
  31. package/src/api/v3/ui/surface.ts +56 -0
  32. package/src/api/v3/ui/viewport.ts +119 -9
  33. package/src/core/commands/image-commands.ts +1 -0
  34. package/src/core/commands/index.ts +6 -0
  35. package/src/core/schema/text-schema.ts +43 -5
  36. package/src/core/selection/mapping.ts +8 -1
  37. package/src/core/selection/review-anchors.ts +5 -1
  38. package/src/core/state/text-transaction.ts +8 -2
  39. package/src/io/export/serialize-revisions.ts +149 -1
  40. package/src/io/normalize/normalize-text.ts +6 -0
  41. package/src/io/ooxml/parse-bookmark-references.ts +55 -0
  42. package/src/io/ooxml/parse-fields.ts +24 -2
  43. package/src/io/ooxml/parse-headers-footers.ts +38 -5
  44. package/src/io/ooxml/parse-main-document.ts +153 -9
  45. package/src/io/ooxml/parse-numbering.ts +20 -0
  46. package/src/io/ooxml/parse-revisions.ts +19 -8
  47. package/src/io/opc/package-reader.ts +98 -8
  48. package/src/model/anchor.ts +4 -3
  49. package/src/model/canonical-document.ts +220 -2
  50. package/src/model/canonical-hash.ts +221 -0
  51. package/src/model/canonical-layout-inputs.ts +245 -6
  52. package/src/model/layout/index.ts +1 -0
  53. package/src/model/layout/page-graph-types.ts +147 -1
  54. package/src/model/review/revision-types.ts +14 -3
  55. package/src/preservation/store.ts +20 -4
  56. package/src/review/README.md +1 -1
  57. package/src/review/store/revision-actions.ts +14 -2
  58. package/src/runtime/collab/event-types.ts +67 -1
  59. package/src/runtime/collab/runtime-collab-sync.ts +177 -5
  60. package/src/runtime/diagnostics/layout-guard-warning.ts +18 -0
  61. package/src/runtime/document-heading-outline.ts +147 -0
  62. package/src/runtime/document-navigation.ts +8 -243
  63. package/src/runtime/document-runtime.ts +279 -115
  64. package/src/runtime/edit-dispatch/dispatch-text-command.ts +11 -0
  65. package/src/runtime/formatting/layout-inputs.ts +38 -5
  66. package/src/runtime/formatting/numbering/geometry.ts +28 -2
  67. package/src/runtime/geometry/adjacent-geometry-intake.ts +835 -0
  68. package/src/runtime/geometry/caret-geometry.ts +5 -6
  69. package/src/runtime/geometry/geometry-facet.ts +60 -10
  70. package/src/runtime/geometry/geometry-index.ts +661 -16
  71. package/src/runtime/geometry/geometry-types.ts +59 -0
  72. package/src/runtime/geometry/hit-test.ts +11 -1
  73. package/src/runtime/geometry/overlay-rects.ts +5 -3
  74. package/src/runtime/geometry/project-anchors.ts +1 -1
  75. package/src/runtime/geometry/word-layout-v2-line-intake.ts +323 -0
  76. package/src/runtime/layout/index.ts +6 -0
  77. package/src/runtime/layout/layout-engine-instance.ts +6 -1
  78. package/src/runtime/layout/layout-engine-version.ts +188 -16
  79. package/src/runtime/layout/layout-facet-types.ts +6 -0
  80. package/src/runtime/layout/page-graph.ts +23 -4
  81. package/src/runtime/layout/paginated-layout-engine.ts +149 -15
  82. package/src/runtime/layout/project-block-fragments.ts +351 -14
  83. package/src/runtime/layout/public-facet.ts +162 -24
  84. package/src/runtime/layout/table-row-continuation-contract.ts +107 -0
  85. package/src/runtime/layout/table-row-split.ts +92 -35
  86. package/src/runtime/prerender/cache-envelope.ts +2 -2
  87. package/src/runtime/prerender/cache-key.ts +5 -4
  88. package/src/runtime/prerender/customxml-cache.ts +0 -1
  89. package/src/runtime/render/render-kernel.ts +1 -1
  90. package/src/runtime/revision-runtime.ts +112 -10
  91. package/src/runtime/scopes/_scope-dependencies.ts +1 -0
  92. package/src/runtime/scopes/action-validation.ts +22 -2
  93. package/src/runtime/scopes/capabilities.ts +316 -0
  94. package/src/runtime/scopes/compile-scope-bundle.ts +14 -0
  95. package/src/runtime/scopes/compiler-service.ts +108 -4
  96. package/src/runtime/scopes/content-control-evidence.ts +79 -0
  97. package/src/runtime/scopes/create-issue.ts +5 -5
  98. package/src/runtime/scopes/evidence.ts +91 -0
  99. package/src/runtime/scopes/formatting/apply.ts +2 -0
  100. package/src/runtime/scopes/geometry-evidence.ts +130 -0
  101. package/src/runtime/scopes/index.ts +54 -0
  102. package/src/runtime/scopes/issue-lifecycle.ts +224 -0
  103. package/src/runtime/scopes/layout-evidence.ts +374 -0
  104. package/src/runtime/scopes/multi-paragraph-refusal.ts +37 -0
  105. package/src/runtime/scopes/preservation-boundary.ts +7 -1
  106. package/src/runtime/scopes/replacement/apply.ts +97 -34
  107. package/src/runtime/scopes/scope-kinds/paragraph.ts +108 -12
  108. package/src/runtime/scopes/semantic-scope-types.ts +242 -3
  109. package/src/runtime/scopes/visualization.ts +28 -0
  110. package/src/runtime/surface-projection.ts +44 -5
  111. package/src/runtime/telemetry/perf-probe.ts +216 -0
  112. package/src/runtime/virtualized-rendering.ts +36 -1
  113. package/src/runtime/workflow/ai-issue-lifecycle.ts +253 -0
  114. package/src/runtime/workflow/coordinator.ts +39 -11
  115. package/src/runtime/workflow/derived-scope-resolver.ts +63 -9
  116. package/src/runtime/workflow/index.ts +4 -0
  117. package/src/runtime/workflow/overlay-lane-types.ts +58 -0
  118. package/src/runtime/workflow/overlay-lanes.ts +386 -0
  119. package/src/runtime/workflow/overlay-store.ts +2 -2
  120. package/src/runtime/workflow/redline-posture-calibration.ts +257 -0
  121. package/src/runtime/workflow/word-field-matrix-calibration.ts +231 -0
  122. package/src/session/_sync-legacy.ts +17 -27
  123. package/src/session/import/loader.ts +6 -4
  124. package/src/session/import/source-package-evidence.ts +186 -2
  125. package/src/session/index.ts +5 -6
  126. package/src/session/session.ts +30 -56
  127. package/src/session/types.ts +8 -13
  128. package/src/shell/session-bootstrap.ts +155 -81
  129. package/src/ui/WordReviewEditor.tsx +520 -12
  130. package/src/ui/editor-shell-view.tsx +14 -4
  131. package/src/ui/editor-surface-controller.tsx +5 -3
  132. package/src/ui/headless/selection-tool-resolver.ts +1 -2
  133. package/src/ui/presence-overlay-lane.ts +130 -0
  134. package/src/ui/ui-controller-factory.ts +17 -0
  135. package/src/ui-tailwind/chrome/build-context-menu-entries.ts +5 -1
  136. package/src/ui-tailwind/chrome/editor-action-registry.ts +105 -5
  137. package/src/ui-tailwind/chrome/editor-actions-to-palette.ts +7 -0
  138. package/src/ui-tailwind/chrome/layer-debug-contracts.ts +208 -0
  139. package/src/ui-tailwind/chrome/resolve-target-kind.ts +13 -0
  140. package/src/ui-tailwind/chrome/tw-alert-banner.tsx +11 -3
  141. package/src/ui-tailwind/chrome/tw-command-palette.tsx +36 -6
  142. package/src/ui-tailwind/chrome/tw-context-menu.tsx +6 -1
  143. package/src/ui-tailwind/chrome/tw-display-mode-selector.tsx +42 -109
  144. package/src/ui-tailwind/chrome/tw-inline-find-bar.tsx +26 -6
  145. package/src/ui-tailwind/chrome/tw-navigation-command-bar.tsx +328 -0
  146. package/src/ui-tailwind/chrome/tw-object-context-toolbar.tsx +8 -4
  147. package/src/ui-tailwind/chrome/tw-runtime-repl-dialog.tsx +129 -1
  148. package/src/ui-tailwind/chrome/tw-selection-tool-host.tsx +19 -5
  149. package/src/ui-tailwind/chrome/tw-selection-tool-structure.tsx +5 -1
  150. package/src/ui-tailwind/chrome/tw-workspace-chrome-host.tsx +28 -12
  151. package/src/ui-tailwind/chrome-overlay/tw-chrome-overlay.tsx +30 -3
  152. package/src/ui-tailwind/chrome-overlay/tw-object-selection-overlay.tsx +116 -10
  153. package/src/ui-tailwind/chrome-overlay/tw-page-stack-overlay-layer.tsx +223 -94
  154. package/src/ui-tailwind/chrome-overlay/tw-presence-overlay-lane.tsx +157 -0
  155. package/src/ui-tailwind/chrome-overlay/tw-review-overlay-lane-markers.tsx +259 -0
  156. package/src/ui-tailwind/chrome-overlay/tw-scope-card-layer.tsx +5 -2
  157. package/src/ui-tailwind/chrome-overlay/tw-substrate-overlay-lanes.tsx +314 -0
  158. package/src/ui-tailwind/debug/README.md +4 -1
  159. package/src/ui-tailwind/debug/layer11-consumer-readiness.ts +272 -0
  160. package/src/ui-tailwind/debug/layer11-word-field-matrix-evidence.ts +160 -0
  161. package/src/ui-tailwind/editor-surface/perf-probe.ts +14 -215
  162. package/src/ui-tailwind/editor-surface/pm-decorations.ts +42 -0
  163. package/src/ui-tailwind/editor-surface/pm-position-map.ts +38 -2
  164. package/src/ui-tailwind/editor-surface/pm-schema.ts +14 -4
  165. package/src/ui-tailwind/editor-surface/pm-state-from-snapshot.ts +34 -5
  166. package/src/ui-tailwind/editor-surface/runtime-decoration-plugin.ts +9 -19
  167. package/src/ui-tailwind/editor-surface/surface-build-keys.ts +2 -2
  168. package/src/ui-tailwind/editor-surface/tw-page-block-view.helpers.ts +145 -0
  169. package/src/ui-tailwind/editor-surface/tw-page-block-view.tsx +16 -11
  170. package/src/ui-tailwind/editor-surface/tw-prosemirror-surface.tsx +8 -10
  171. package/src/ui-tailwind/editor-surface/tw-table-node-view.tsx +3 -0
  172. package/src/ui-tailwind/page-stack/tw-page-chrome-entry.tsx +4 -2
  173. package/src/ui-tailwind/page-stack/tw-page-stack-chrome-layer.tsx +60 -20
  174. package/src/ui-tailwind/page-stack/tw-region-block-renderer.tsx +16 -11
  175. package/src/ui-tailwind/review/tw-health-panel.tsx +36 -17
  176. package/src/ui-tailwind/review/tw-review-rail.tsx +7 -4
  177. package/src/ui-tailwind/review-workspace/diagnostics-visibility.ts +44 -0
  178. package/src/ui-tailwind/review-workspace/page-shell-metrics.ts +11 -0
  179. package/src/ui-tailwind/review-workspace/tw-review-workspace-rail.tsx +16 -1
  180. package/src/ui-tailwind/review-workspace/types.ts +26 -12
  181. package/src/ui-tailwind/review-workspace/use-diagnostics-signal.ts +40 -11
  182. package/src/ui-tailwind/review-workspace/use-layout-facet-render-signal.ts +2 -1
  183. package/src/ui-tailwind/review-workspace/use-page-markers.ts +15 -26
  184. package/src/ui-tailwind/review-workspace/use-scope-card-state.ts +35 -18
  185. package/src/ui-tailwind/review-workspace/use-selection-toolbar-placement.ts +41 -32
  186. package/src/ui-tailwind/review-workspace/use-status-bar-page-facts.ts +2 -1
  187. package/src/ui-tailwind/review-workspace/use-workspace-side-effects.ts +2 -1
  188. package/src/ui-tailwind/status/tw-status-bar.tsx +6 -5
  189. package/src/ui-tailwind/toolbar/tw-role-action-region.tsx +52 -80
  190. package/src/ui-tailwind/toolbar/tw-shell-header.tsx +12 -48
  191. package/src/ui-tailwind/toolbar/tw-toolbar-icon-button.tsx +9 -4
  192. package/src/ui-tailwind/toolbar/tw-toolbar.tsx +328 -361
  193. package/src/ui-tailwind/tw-review-workspace.tsx +152 -286
@@ -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
+ }
@@ -0,0 +1,374 @@
1
+ /**
2
+ * Scope layout evidence projection.
3
+ *
4
+ * Layer 08 only consumes layout evidence supplied by lower layers. When the
5
+ * seam is absent or cold, the bundle reports that explicitly instead of
6
+ * deriving page slices or continuation state from canonical content.
7
+ */
8
+
9
+ import type {
10
+ ScopeLayoutContinuationEvidence,
11
+ ScopeLayoutEvidence,
12
+ ScopeTableFrameEvidence,
13
+ ScopeTableFramePageEvidence,
14
+ } from "./semantic-scope-types.ts";
15
+
16
+ export interface ScopeLayoutEvidenceEntry {
17
+ readonly status?: ScopeLayoutEvidence["status"];
18
+ readonly completeness?: ScopeLayoutEvidence["completeness"];
19
+ readonly reason?: string;
20
+ readonly pageSliceIds?: readonly string[];
21
+ readonly layoutObjectIds?: readonly string[];
22
+ readonly continuationState?: ScopeLayoutContinuationEvidence;
23
+ readonly divergenceIds?: readonly string[];
24
+ readonly tableFrame?: ScopeTableFrameEvidence;
25
+ }
26
+
27
+ export interface ScopeLayoutEvidenceProvider {
28
+ getScopeLayoutEvidence(scopeId: string): ScopeLayoutEvidenceEntry | null;
29
+ }
30
+
31
+ export interface ScopeRuntimeLayoutFragment {
32
+ readonly fragmentId: string;
33
+ readonly blockId: string;
34
+ readonly pageId: string;
35
+ readonly pageIndex: number;
36
+ readonly kind?: "whole" | "paragraph-slice" | "table-slice";
37
+ readonly tableRowRange?: {
38
+ readonly from: number;
39
+ readonly to: number;
40
+ readonly totalRows: number;
41
+ };
42
+ readonly continuation?: {
43
+ readonly kind: string;
44
+ readonly continuesFromPreviousPage?: boolean;
45
+ readonly continuesToNextPage?: boolean;
46
+ readonly repeatedHeaderRowIndexes?: readonly number[];
47
+ readonly splitRowCarry?: readonly {
48
+ readonly rowIndex: number;
49
+ readonly continuesFromPreviousPage: boolean;
50
+ readonly continuesToNextPage: boolean;
51
+ }[];
52
+ readonly verticalMergeCarry?: readonly {
53
+ readonly columnIndex: number;
54
+ readonly restartRowIndex: number;
55
+ }[];
56
+ };
57
+ readonly layoutObject?: {
58
+ readonly objectId?: string;
59
+ };
60
+ }
61
+
62
+ export interface ScopeRuntimeLayoutEvidenceSource {
63
+ getPageCount(): number;
64
+ getFragmentsForPage(pageIndex: number): readonly ScopeRuntimeLayoutFragment[];
65
+ }
66
+
67
+ export interface RuntimeTableFrameLayoutEvidenceOptions {
68
+ readonly layout: ScopeRuntimeLayoutEvidenceSource;
69
+ readonly tableBlockIdsByBlockIndex?: ReadonlyMap<number, string>;
70
+ }
71
+
72
+ export interface ScopeRuntimeRenderSnapshotLike {
73
+ readonly surface?: {
74
+ readonly blocks?: readonly {
75
+ readonly kind?: string;
76
+ readonly blockId?: string;
77
+ }[];
78
+ };
79
+ }
80
+
81
+ function freezeList<T>(values: readonly T[] | undefined): readonly T[] | undefined {
82
+ return values ? Object.freeze([...values]) : undefined;
83
+ }
84
+
85
+ function unique<T>(values: readonly T[]): readonly T[] {
86
+ return Object.freeze([...new Set(values)]);
87
+ }
88
+
89
+ function parseTableFamilyScopeId(scopeId: string):
90
+ | { readonly scopeKind: "table"; readonly blockIndex: number }
91
+ | { readonly scopeKind: "table-row"; readonly blockIndex: number; readonly rowIndex: number }
92
+ | {
93
+ readonly scopeKind: "table-cell";
94
+ readonly blockIndex: number;
95
+ readonly rowIndex: number;
96
+ readonly cellIndex: number;
97
+ }
98
+ | null {
99
+ const table = /^table:(\d+)$/.exec(scopeId);
100
+ if (table) return { scopeKind: "table", blockIndex: Number(table[1]) };
101
+ const row = /^row:(\d+):(\d+)$/.exec(scopeId);
102
+ if (row) {
103
+ return {
104
+ scopeKind: "table-row",
105
+ blockIndex: Number(row[1]),
106
+ rowIndex: Number(row[2]),
107
+ };
108
+ }
109
+ const cell = /^cell:(\d+):(\d+):(\d+)$/.exec(scopeId);
110
+ if (cell) {
111
+ return {
112
+ scopeKind: "table-cell",
113
+ blockIndex: Number(cell[1]),
114
+ rowIndex: Number(cell[2]),
115
+ cellIndex: Number(cell[3]),
116
+ };
117
+ }
118
+ return null;
119
+ }
120
+
121
+ function candidateTableBlockIds(
122
+ blockIndex: number,
123
+ mapped?: ReadonlyMap<number, string>,
124
+ ): readonly string[] {
125
+ const ids: string[] = [];
126
+ const mappedId = mapped?.get(blockIndex);
127
+ if (mappedId) ids.push(mappedId);
128
+ ids.push(`table-${blockIndex}`, `table:${blockIndex}`, `block-${blockIndex}`);
129
+ return unique(ids);
130
+ }
131
+
132
+ function rowInFragment(
133
+ fragment: ScopeRuntimeLayoutFragment,
134
+ rowIndex: number,
135
+ ): boolean {
136
+ const range = fragment.tableRowRange;
137
+ if (range && rowIndex >= range.from && rowIndex < range.to) return true;
138
+ return fragment.continuation?.repeatedHeaderRowIndexes?.includes(rowIndex) === true;
139
+ }
140
+
141
+ function fragmentMatchesScope(
142
+ fragment: ScopeRuntimeLayoutFragment,
143
+ parsed: NonNullable<ReturnType<typeof parseTableFamilyScopeId>>,
144
+ ): boolean {
145
+ if (parsed.scopeKind === "table") return true;
146
+ return rowInFragment(fragment, parsed.rowIndex);
147
+ }
148
+
149
+ function collectTableFragments(
150
+ layout: ScopeRuntimeLayoutEvidenceSource,
151
+ blockIds: readonly string[],
152
+ parsed: NonNullable<ReturnType<typeof parseTableFamilyScopeId>>,
153
+ ): readonly ScopeRuntimeLayoutFragment[] {
154
+ const matches: ScopeRuntimeLayoutFragment[] = [];
155
+ const blockIdSet = new Set(blockIds);
156
+ for (let pageIndex = 0; pageIndex < layout.getPageCount(); pageIndex += 1) {
157
+ for (const fragment of layout.getFragmentsForPage(pageIndex)) {
158
+ if (!blockIdSet.has(fragment.blockId)) continue;
159
+ if (fragment.kind !== undefined && fragment.kind !== "table-slice" && fragment.kind !== "whole") {
160
+ continue;
161
+ }
162
+ if (!fragmentMatchesScope(fragment, parsed)) continue;
163
+ matches.push(fragment);
164
+ }
165
+ }
166
+ return Object.freeze(matches);
167
+ }
168
+
169
+ function projectTableFramePage(
170
+ fragment: ScopeRuntimeLayoutFragment,
171
+ ): ScopeTableFramePageEvidence {
172
+ const repeated = fragment.continuation?.repeatedHeaderRowIndexes;
173
+ const splitRowCarry = fragment.continuation?.splitRowCarry;
174
+ const carry = fragment.continuation?.verticalMergeCarry;
175
+ return {
176
+ pageId: fragment.pageId,
177
+ pageIndex: fragment.pageIndex,
178
+ fragmentId: fragment.fragmentId,
179
+ ...(fragment.tableRowRange ? { rowRange: { ...fragment.tableRowRange } } : {}),
180
+ ...(fragment.continuation?.continuesFromPreviousPage !== undefined
181
+ ? { continuesFromPreviousPage: fragment.continuation.continuesFromPreviousPage }
182
+ : {}),
183
+ ...(fragment.continuation?.continuesToNextPage !== undefined
184
+ ? { continuesToNextPage: fragment.continuation.continuesToNextPage }
185
+ : {}),
186
+ ...(repeated ? { repeatedHeaderRowIndexes: unique(repeated) } : {}),
187
+ ...(splitRowCarry ? { splitRowCarry: Object.freeze(splitRowCarry.map((item) => ({ ...item }))) } : {}),
188
+ ...(carry ? { verticalMergeCarry: Object.freeze(carry.map((item) => ({ ...item }))) } : {}),
189
+ };
190
+ }
191
+
192
+ function projectTableFrame(
193
+ blockId: string,
194
+ parsed: NonNullable<ReturnType<typeof parseTableFamilyScopeId>>,
195
+ fragments: readonly ScopeRuntimeLayoutFragment[],
196
+ ): ScopeTableFrameEvidence {
197
+ const pageIds = unique(fragments.map((fragment) => fragment.pageId));
198
+ const pageSliceIds = unique(fragments.map((fragment) => fragment.fragmentId));
199
+ const layoutObjectIds = unique(
200
+ fragments
201
+ .map((fragment) => fragment.layoutObject?.objectId)
202
+ .filter((objectId): objectId is string => typeof objectId === "string" && objectId.length > 0),
203
+ );
204
+ const rowRangesByPage = Object.freeze(fragments.map(projectTableFramePage));
205
+ const repeatedHeaderRowIndexes = unique(
206
+ fragments.flatMap((fragment) => fragment.continuation?.repeatedHeaderRowIndexes ?? []),
207
+ );
208
+ const verticalMergeCarry = Object.freeze(
209
+ fragments
210
+ .flatMap((fragment) => fragment.continuation?.verticalMergeCarry ?? [])
211
+ .map((item) => ({ ...item })),
212
+ );
213
+ const splitRowCarry = Object.freeze(
214
+ fragments
215
+ .flatMap((fragment) => fragment.continuation?.splitRowCarry ?? [])
216
+ .map((item) => ({ ...item })),
217
+ );
218
+ return {
219
+ source: "runtime.layout.table-frame-continuation",
220
+ blockId,
221
+ scopeKind: parsed.scopeKind,
222
+ ...(parsed.scopeKind === "table-row" || parsed.scopeKind === "table-cell"
223
+ ? { rowIndex: parsed.rowIndex }
224
+ : {}),
225
+ ...(parsed.scopeKind === "table-cell" ? { cellIndex: parsed.cellIndex } : {}),
226
+ ...(pageIds.length > 0 ? { pageIds } : {}),
227
+ ...(pageSliceIds.length > 0 ? { pageSliceIds } : {}),
228
+ ...(layoutObjectIds.length > 0 ? { layoutObjectIds } : {}),
229
+ ...(rowRangesByPage.length > 0 ? { rowRangesByPage } : {}),
230
+ ...(repeatedHeaderRowIndexes.length > 0 ? { repeatedHeaderRowIndexes } : {}),
231
+ ...(splitRowCarry.length > 0 ? { splitRowCarry } : {}),
232
+ ...(verticalMergeCarry.length > 0 ? { verticalMergeCarry } : {}),
233
+ };
234
+ }
235
+
236
+ function continuationFromTableFrame(
237
+ fragments: readonly ScopeRuntimeLayoutFragment[],
238
+ ): ScopeLayoutContinuationEvidence {
239
+ const pageIds = unique(fragments.map((fragment) => fragment.pageId));
240
+ return {
241
+ ...(pageIds.length > 0 ? { pageIds } : {}),
242
+ pageCount: pageIds.length,
243
+ crossesPageBoundary: pageIds.length > 1,
244
+ continuedFromPreviousPage: fragments.some(
245
+ (fragment) => fragment.continuation?.continuesFromPreviousPage === true,
246
+ ),
247
+ continuesToNextPage: fragments.some(
248
+ (fragment) => fragment.continuation?.continuesToNextPage === true,
249
+ ),
250
+ };
251
+ }
252
+
253
+ export function createRuntimeTableFrameLayoutEvidenceProvider(
254
+ options: RuntimeTableFrameLayoutEvidenceOptions,
255
+ ): ScopeLayoutEvidenceProvider {
256
+ return {
257
+ getScopeLayoutEvidence(scopeId) {
258
+ const parsed = parseTableFamilyScopeId(scopeId);
259
+ if (!parsed) return null;
260
+
261
+ const blockIds = candidateTableBlockIds(
262
+ parsed.blockIndex,
263
+ options.tableBlockIdsByBlockIndex,
264
+ );
265
+ const fragments = collectTableFragments(options.layout, blockIds, parsed);
266
+ if (fragments.length === 0) return null;
267
+
268
+ const blockId = fragments[0]?.blockId ?? blockIds[0] ?? `table-${parsed.blockIndex}`;
269
+ const tableFrame = projectTableFrame(blockId, parsed, fragments);
270
+ const pageSliceIds = tableFrame.pageSliceIds;
271
+ const layoutObjectIds = tableFrame.layoutObjectIds;
272
+ const cellScoped = parsed.scopeKind === "table-cell";
273
+ return {
274
+ status: "available",
275
+ completeness: cellScoped ? "partial" : "complete",
276
+ reason: cellScoped
277
+ ? "l04-table-frame-row-level-evidence"
278
+ : "l04-table-frame-continuation",
279
+ ...(pageSliceIds ? { pageSliceIds } : {}),
280
+ ...(layoutObjectIds ? { layoutObjectIds } : {}),
281
+ continuationState: continuationFromTableFrame(fragments),
282
+ tableFrame,
283
+ };
284
+ },
285
+ };
286
+ }
287
+
288
+ export function collectTopLevelTableBlockIdsFromRenderSnapshot(
289
+ snapshot: ScopeRuntimeRenderSnapshotLike | null | undefined,
290
+ ): ReadonlyMap<number, string> {
291
+ const out = new Map<number, string>();
292
+ const blocks = snapshot?.surface?.blocks ?? [];
293
+ blocks.forEach((block, blockIndex) => {
294
+ if (block.kind === "table" && typeof block.blockId === "string") {
295
+ out.set(blockIndex, block.blockId);
296
+ }
297
+ });
298
+ return out;
299
+ }
300
+
301
+ function cloneTableFrame(
302
+ tableFrame: ScopeTableFrameEvidence,
303
+ ): ScopeTableFrameEvidence {
304
+ return {
305
+ ...tableFrame,
306
+ ...(tableFrame.pageIds ? { pageIds: freezeList(tableFrame.pageIds) } : {}),
307
+ ...(tableFrame.pageSliceIds ? { pageSliceIds: freezeList(tableFrame.pageSliceIds) } : {}),
308
+ ...(tableFrame.layoutObjectIds ? { layoutObjectIds: freezeList(tableFrame.layoutObjectIds) } : {}),
309
+ ...(tableFrame.rowRangesByPage
310
+ ? {
311
+ rowRangesByPage: Object.freeze(
312
+ tableFrame.rowRangesByPage.map((row) => ({
313
+ ...row,
314
+ ...(row.rowRange ? { rowRange: { ...row.rowRange } } : {}),
315
+ ...(row.repeatedHeaderRowIndexes
316
+ ? { repeatedHeaderRowIndexes: freezeList(row.repeatedHeaderRowIndexes) }
317
+ : {}),
318
+ ...(row.splitRowCarry
319
+ ? { splitRowCarry: Object.freeze(row.splitRowCarry.map((item) => ({ ...item }))) }
320
+ : {}),
321
+ ...(row.verticalMergeCarry
322
+ ? { verticalMergeCarry: Object.freeze(row.verticalMergeCarry.map((item) => ({ ...item }))) }
323
+ : {}),
324
+ })),
325
+ ),
326
+ }
327
+ : {}),
328
+ ...(tableFrame.repeatedHeaderRowIndexes
329
+ ? { repeatedHeaderRowIndexes: freezeList(tableFrame.repeatedHeaderRowIndexes) }
330
+ : {}),
331
+ ...(tableFrame.splitRowCarry
332
+ ? { splitRowCarry: Object.freeze(tableFrame.splitRowCarry.map((item) => ({ ...item }))) }
333
+ : {}),
334
+ ...(tableFrame.verticalMergeCarry
335
+ ? { verticalMergeCarry: Object.freeze(tableFrame.verticalMergeCarry.map((item) => ({ ...item }))) }
336
+ : {}),
337
+ };
338
+ }
339
+
340
+ export function deriveScopeLayoutEvidence(
341
+ scopeId: string,
342
+ provider?: ScopeLayoutEvidenceProvider,
343
+ ): ScopeLayoutEvidence {
344
+ if (!provider) {
345
+ return {
346
+ status: "unavailable",
347
+ completeness: "unavailable",
348
+ reason: "layout-evidence-provider-unavailable",
349
+ };
350
+ }
351
+
352
+ const entry = provider.getScopeLayoutEvidence(scopeId);
353
+ if (!entry) {
354
+ return {
355
+ status: "requires-rehydration",
356
+ completeness: "requires-rehydration",
357
+ reason: "scope-layout-evidence-unavailable",
358
+ };
359
+ }
360
+
361
+ const status = entry.status ?? "available";
362
+ return {
363
+ status,
364
+ completeness: entry.completeness ?? (status === "available" ? "complete" : status),
365
+ ...(entry.reason ? { reason: entry.reason } : {}),
366
+ ...(entry.pageSliceIds ? { pageSliceIds: freezeList(entry.pageSliceIds) } : {}),
367
+ ...(entry.layoutObjectIds ? { layoutObjectIds: freezeList(entry.layoutObjectIds) } : {}),
368
+ ...(entry.continuationState
369
+ ? { continuationState: { ...entry.continuationState } }
370
+ : {}),
371
+ ...(entry.divergenceIds ? { divergenceIds: freezeList(entry.divergenceIds) } : {}),
372
+ ...(entry.tableFrame ? { tableFrame: cloneTableFrame(entry.tableFrame) } : {}),
373
+ };
374
+ }