@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
@@ -9,12 +9,10 @@
9
9
  * `composeScopeValidation`, and projects the richer compiler verdict
10
10
  * into the v3-facing `ValidationResult` shape.
11
11
  *
12
- * The v3 `ValidationResult` shape (`{ proposalId, safe, blockers? }`)
13
- * stays narrow by design. The compiler-side `ValidationResult`
14
- * (`{ safe, blockedReasons, warnings, approval }`) remains the authority
15
- * for internal consumers; the v3 projection trades richness for a
16
- * stable external contract. Slice 5's apply pipeline reads the
17
- * compiler verdict directly rather than the v3 projection.
12
+ * The v3 `ValidationResult` shape stays narrow by design. The compiler-side
13
+ * `ValidationResult` remains the authority for internal consumers; the v3
14
+ * projection carries only stable routing facts (`safe`, blockers, warnings,
15
+ * approval, and the hard-refusal / warn-and-proceed posture).
18
16
  */
19
17
 
20
18
  import type { RuntimeApiHandle } from "../_runtime-handle.ts";
@@ -26,8 +24,9 @@ import {
26
24
  createScopeCompilerService,
27
25
  } from "../../../runtime/scopes/index.ts";
28
26
  import type {
29
- ReplacementOperationKind,
30
27
  ReplacementPreservePolicy,
28
+ ScopeActionPosture,
29
+ ScopeReplacementOperationKind,
31
30
  ScopeFormattingAction,
32
31
  ValidationResult as CompilerValidationResult,
33
32
  } from "../../../runtime/scopes/index.ts";
@@ -37,10 +36,11 @@ import {
37
36
  projectAuditReference,
38
37
  type AiActionAuditReference,
39
38
  } from "./_audit-reference.ts";
39
+ import { currentAuditTimestamp } from "./_audit-time.ts";
40
40
 
41
41
  export interface ReplacementProposalInput {
42
42
  readonly targetScopeId: string;
43
- readonly operation: ReplacementOperationKind;
43
+ readonly operation: ScopeReplacementOperationKind;
44
44
  readonly proposedText?: string;
45
45
  readonly formatting?: TextFormattingDirective;
46
46
  readonly preserve?: ReplacementPreservePolicy;
@@ -94,6 +94,7 @@ export const proposeReplacementScopeMetadata: ApiV3FnMetadata = {
94
94
  export interface ValidationResult {
95
95
  readonly proposalId: string;
96
96
  readonly safe: boolean;
97
+ readonly posture?: ScopeActionPosture;
97
98
  readonly blockers?: readonly string[];
98
99
  readonly warnings?: readonly string[];
99
100
  readonly approvalRequired?: boolean;
@@ -125,6 +126,7 @@ export const validateReplacementScopeMetadata: ApiV3FnMetadata = {
125
126
  export interface ApplyResult {
126
127
  readonly proposalId: string;
127
128
  readonly applied: boolean;
129
+ readonly posture?: ScopeActionPosture;
128
130
  readonly reason?: string;
129
131
  readonly blockers?: readonly string[];
130
132
  readonly blockerDetails?: readonly ActionBlockerDetail[];
@@ -145,16 +147,20 @@ export interface ActionBlockerDetail {
145
147
  | "unsupported-scope-kind"
146
148
  | "unsupported-operation"
147
149
  | "unresolved-scope"
148
- | "policy-or-guard";
150
+ | "preservation-boundary"
151
+ | "policy-or-guard"
152
+ | "capability-prerequisite";
149
153
  readonly message: string;
150
154
  readonly nextStep: string;
151
155
  readonly scopeKind?: string;
152
156
  readonly operation?: string;
157
+ readonly fragmentId?: string;
158
+ readonly payloadKind?: string;
153
159
  }
154
160
 
155
161
  export interface ApplyReplacementScopeInput {
156
162
  readonly targetScopeId: string;
157
- readonly operation?: ReplacementOperationKind;
163
+ readonly operation?: ScopeReplacementOperationKind;
158
164
  /**
159
165
  * Flat text payload. Shorthand for
160
166
  * `proposedContent: {kind: "text", text}`. Ignored when
@@ -254,7 +260,7 @@ export const applyScopeActionMetadata: ApiV3FnMetadata = {
254
260
  export interface ValidateReplacementScopeInput {
255
261
  readonly proposalId?: string;
256
262
  readonly targetScopeId?: string;
257
- readonly operation?: ReplacementOperationKind;
263
+ readonly operation?: ScopeReplacementOperationKind;
258
264
  readonly proposedText?: string;
259
265
  /** Optional action id override; composer derives a default otherwise. */
260
266
  readonly actionId?: AIAction;
@@ -275,6 +281,7 @@ function projectValidationResult(
275
281
  return {
276
282
  proposalId,
277
283
  safe: verdict.safe,
284
+ ...(verdict.posture ? { posture: verdict.posture } : {}),
278
285
  ...(blockers ? { blockers } : {}),
279
286
  ...(warnings ? { warnings } : {}),
280
287
  ...(verdict.approval?.required ? { approvalRequired: true } : {}),
@@ -293,6 +300,22 @@ function blockerDetailFor(code: string): ActionBlockerDetail | null {
293
300
  };
294
301
  }
295
302
 
303
+ if (code.startsWith("preserve:opaque-fragment:")) {
304
+ const parts = code.split(":");
305
+ const payloadKind = parts.at(-1) ?? "unknown";
306
+ const fragmentId =
307
+ parts.length > 4 ? parts.slice(2, -1).join(":") : "unknown";
308
+ return {
309
+ code,
310
+ category: "preservation-boundary",
311
+ fragmentId,
312
+ payloadKind,
313
+ message: `Replacement crosses preserve-only opaque fragment ${fragmentId} (${payloadKind}).`,
314
+ nextStep:
315
+ "Retry with preserve:{opaqueFragments:true} only when preserving opaque payloads in place is acceptable, or attach an explanation/issue instead of mutating.",
316
+ };
317
+ }
318
+
296
319
  if (!code.startsWith("compile-refused:")) return null;
297
320
 
298
321
  const [, scopeKind = "unknown", ...rest] = code.split(":");
@@ -310,12 +333,45 @@ function blockerDetailFor(code: string): ActionBlockerDetail | null {
310
333
  };
311
334
  }
312
335
 
313
- if (scopeKind === "scope" && suffix === "multi-paragraph-replace-not-implemented") {
336
+ if (
337
+ suffix === "formatting-not-implemented" ||
338
+ suffix === "formatting-suggesting-not-implemented"
339
+ ) {
340
+ return {
341
+ code,
342
+ category: "unsupported-operation",
343
+ scopeKind,
344
+ operation: "formatting",
345
+ message:
346
+ suffix === "formatting-suggesting-not-implemented"
347
+ ? `Suggest-mode formatting is not implemented for ${scopeKind} scopes.`
348
+ : `Formatting actions are not implemented for ${scopeKind} scopes.`,
349
+ nextStep:
350
+ "Use a supported paragraph-like scope in direct-edit mode, or attach an explanation/issue instead of mutating.",
351
+ };
352
+ }
353
+
354
+ if (
355
+ scopeKind === "scope" &&
356
+ (suffix === "multi-paragraph-replace-not-implemented" ||
357
+ suffix === "multi-paragraph-text-replace-not-implemented" ||
358
+ suffix === "multi-paragraph-fragment-replace-not-implemented")
359
+ ) {
360
+ const operation =
361
+ suffix === "multi-paragraph-text-replace-not-implemented"
362
+ ? "replace-text"
363
+ : suffix === "multi-paragraph-fragment-replace-not-implemented"
364
+ ? "replace-fragment"
365
+ : "replace";
314
366
  return {
315
367
  code,
316
368
  category: "unsupported-scope-kind",
317
369
  scopeKind,
318
- message: "Multi-paragraph scope replacement is not implemented.",
370
+ operation,
371
+ message:
372
+ operation === "replace"
373
+ ? "Multi-paragraph scope replacement is not implemented."
374
+ : `Multi-paragraph scope ${operation} is not implemented.`,
319
375
  nextStep:
320
376
  "Split the request into paragraph-scoped replacements, or attach an explanation/issue until the multi-paragraph planner ships.",
321
377
  };
@@ -328,7 +384,7 @@ function blockerDetailFor(code: string): ActionBlockerDetail | null {
328
384
  scopeKind,
329
385
  message: `Flat text replacement is not implemented for ${scopeKind} scopes because it can break table structure.`,
330
386
  nextStep:
331
- "Use ai.attachExplanation or ai.createIssue for now, or wait for the Layer 08 table-family replacement planner.",
387
+ "Use ai.attachExplanation or ai.createIssue on this scopeId for metadata fallback. For realized table-cell text edits, a host can route through runtime.content.replaceText; whole-table and row edits wait for the Layer 08 table-family replacement planner.",
332
388
  };
333
389
  }
334
390
 
@@ -364,11 +420,34 @@ function blockerDetailFor(code: string): ActionBlockerDetail | null {
364
420
  };
365
421
  }
366
422
 
423
+ function capabilityBlockerDetailFor(code: string): ActionBlockerDetail | null {
424
+ if (!code.startsWith("capability:scope:")) return null;
425
+ const requirement = code.slice("capability:scope:".length);
426
+ const labels: Record<string, string> = {
427
+ "block-granular-replacement-lowering-required":
428
+ "block-granular replacement lowering",
429
+ "provenance:marker-backed-required": "marker-backed scope provenance",
430
+ "layout-completeness-required": "complete layout evidence",
431
+ "geometry-completeness-required": "complete geometry evidence",
432
+ "continuation-state-required": "page-continuation state",
433
+ "preservation-verdict-required": "preservation verdict evidence",
434
+ };
435
+ const label = labels[requirement] ?? requirement.replaceAll("-", " ");
436
+ return {
437
+ code,
438
+ category: "capability-prerequisite",
439
+ scopeKind: "scope",
440
+ message: `Multi-paragraph scope replacement requires ${label}.`,
441
+ nextStep:
442
+ "Keep the action blocked and use paragraph-scoped replacements or attach an explanation/issue until the Scopes2 planner provides this prerequisite.",
443
+ };
444
+ }
445
+
367
446
  function projectBlockerDetails(
368
447
  blockers: readonly string[],
369
448
  ): readonly ActionBlockerDetail[] | undefined {
370
449
  const details = blockers
371
- .map((code) => blockerDetailFor(code))
450
+ .map((code) => capabilityBlockerDetailFor(code) ?? blockerDetailFor(code))
372
451
  .filter((detail): detail is ActionBlockerDetail => detail !== null);
373
452
  return details.length > 0 ? Object.freeze(details) : undefined;
374
453
  }
@@ -406,6 +485,7 @@ export function createReplacementFamily(runtime: RuntimeApiHandle) {
406
485
  return {
407
486
  proposalId,
408
487
  safe: false,
488
+ posture: "hard-refusal",
409
489
  blockers: Object.freeze([
410
490
  "legacy-proposalId-only:pass-targetScopeId-to-validate-now",
411
491
  ]),
@@ -417,13 +497,14 @@ export function createReplacementFamily(runtime: RuntimeApiHandle) {
417
497
  return {
418
498
  proposalId,
419
499
  safe: false,
500
+ posture: "hard-refusal",
420
501
  blockers: Object.freeze([
421
502
  `scope-not-resolvable:${input.targetScopeId}`,
422
503
  ]),
423
504
  };
424
505
  }
425
506
 
426
- const operation: ReplacementOperationKind = input.operation ?? "replace";
507
+ const operation: ScopeReplacementOperationKind = input.operation ?? "replace";
427
508
  const proposedContent =
428
509
  typeof input.proposedText === "string"
429
510
  ? ({ kind: "text", text: input.proposedText } as const)
@@ -470,7 +551,7 @@ export function createReplacementFamily(runtime: RuntimeApiHandle) {
470
551
  ...(input.actionId ? { actionId: input.actionId } : {}),
471
552
  actorId: input.actorId ?? "agent",
472
553
  origin: input.origin ?? "agent",
473
- emittedAtUtc: new Date(0).toISOString(),
554
+ emittedAtUtc: currentAuditTimestamp(runtime),
474
555
  });
475
556
 
476
557
  emitUxResponse(runtime, {
@@ -487,6 +568,7 @@ export function createReplacementFamily(runtime: RuntimeApiHandle) {
487
568
  return {
488
569
  proposalId,
489
570
  applied: result.applied,
571
+ ...(result.validation.posture ? { posture: result.validation.posture } : {}),
490
572
  ...(result.reason ? { reason: result.reason } : {}),
491
573
  ...(result.validation.blockedReasons.length > 0
492
574
  ? { blockers: Object.freeze([...result.validation.blockedReasons]) }
@@ -518,7 +600,7 @@ export function createReplacementFamily(runtime: RuntimeApiHandle) {
518
600
  ...(input.actionId ? { actionId: input.actionId } : {}),
519
601
  actorId: input.actorId ?? "agent",
520
602
  origin: input.origin ?? "agent",
521
- emittedAtUtc: new Date(0).toISOString(),
603
+ emittedAtUtc: currentAuditTimestamp(runtime),
522
604
  });
523
605
 
524
606
  emitUxResponse(runtime, {
@@ -535,6 +617,7 @@ export function createReplacementFamily(runtime: RuntimeApiHandle) {
535
617
  return {
536
618
  proposalId,
537
619
  applied: result.applied,
620
+ ...(result.validation.posture ? { posture: result.validation.posture } : {}),
538
621
  ...(result.reason ? { reason: result.reason } : {}),
539
622
  ...(result.validation.blockedReasons.length > 0
540
623
  ? { blockers: Object.freeze([...result.validation.blockedReasons]) }
@@ -117,7 +117,7 @@ export const queryScopeAtPositionMetadata: ApiV3FnMetadata = {
117
117
  sourceLayer: "semantic-scope-compiler",
118
118
  liveEvidence: {
119
119
  runnerTest: "test/api/v3/ai/query-scope-position.test.ts",
120
- commit: "pending",
120
+ commit: "a7518fb46",
121
121
  },
122
122
  uxIntent: { uiVisible: false, expectsUxResponse: "none" },
123
123
  agentMetadata: {
@@ -139,7 +139,7 @@ export const queryScopeInRangeMetadata: ApiV3FnMetadata = {
139
139
  sourceLayer: "semantic-scope-compiler",
140
140
  liveEvidence: {
141
141
  runnerTest: "test/api/v3/ai/query-scope-position.test.ts",
142
- commit: "pending",
142
+ commit: "a7518fb46",
143
143
  },
144
144
  uxIntent: { uiVisible: false, expectsUxResponse: "none" },
145
145
  agentMetadata: {
@@ -32,11 +32,17 @@
32
32
  import type { RuntimeApiHandle } from "../_runtime-handle.ts";
33
33
  import type { ApiV3FnMetadata } from "../_layer-metadata.ts";
34
34
  import { emitUxResponse } from "../_ux-response.ts";
35
+ import { createScopeCompilerService } from "../../../runtime/scopes/index.ts";
35
36
  import {
36
37
  captureScopeSnapshot,
37
38
  emitScopeMetadataAudit,
38
39
  snapshotDocumentHash,
39
40
  } from "./_metadata-audit.ts";
41
+ import { currentAuditTimestamp } from "./_audit-time.ts";
42
+ import {
43
+ projectAuditReference,
44
+ type AiActionAuditReference,
45
+ } from "./_audit-reference.ts";
40
46
 
41
47
  export interface AcceptRevisionInput {
42
48
  readonly revisionId: string;
@@ -76,6 +82,42 @@ export interface ResolveCommentThreadResult {
76
82
  readonly reason?: string;
77
83
  }
78
84
 
85
+ export interface ResolveIssueInput {
86
+ readonly issueId: string;
87
+ readonly actorId?: string;
88
+ readonly origin?: "ui" | "agent" | "host";
89
+ }
90
+
91
+ export interface ResolveIssueResult {
92
+ readonly issueId: string;
93
+ readonly resolved: boolean;
94
+ readonly reason?: string;
95
+ readonly blockers?: readonly string[];
96
+ readonly entryId?: string;
97
+ readonly scopeId?: string;
98
+ readonly fromStatus?: "open" | "resolved";
99
+ readonly toStatus?: "open" | "resolved";
100
+ readonly auditReference?: AiActionAuditReference;
101
+ }
102
+
103
+ export interface ReopenIssueInput {
104
+ readonly issueId: string;
105
+ readonly actorId?: string;
106
+ readonly origin?: "ui" | "agent" | "host";
107
+ }
108
+
109
+ export interface ReopenIssueResult {
110
+ readonly issueId: string;
111
+ readonly reopened: boolean;
112
+ readonly reason?: string;
113
+ readonly blockers?: readonly string[];
114
+ readonly entryId?: string;
115
+ readonly scopeId?: string;
116
+ readonly fromStatus?: "open" | "resolved";
117
+ readonly toStatus?: "open" | "resolved";
118
+ readonly auditReference?: AiActionAuditReference;
119
+ }
120
+
79
121
  export const acceptRevisionMetadata: ApiV3FnMetadata = {
80
122
  name: "ai.acceptRevision",
81
123
  status: "live-with-adapter",
@@ -157,7 +199,64 @@ export const resolveCommentThreadMetadata: ApiV3FnMetadata = {
157
199
  "§AI API § ai.resolveCommentThread. Adapter over runtime.resolveComment (Layer-06 review-workflow). A4 audit emitted when scopeId hint resolves.",
158
200
  };
159
201
 
202
+ export const resolveIssueMetadata: ApiV3FnMetadata = {
203
+ name: "ai.resolveIssue",
204
+ status: "live-with-adapter",
205
+ sourceLayer: "semantic-scope-compiler",
206
+ liveEvidence: {
207
+ runnerTest:
208
+ "test/runtime/scopes/issue-lifecycle.test.ts,test/api/v3/ai/ai-review-workflow.test.ts",
209
+ commit: "refactor-09-issue-lifecycle-projection",
210
+ },
211
+ uxIntent: {
212
+ uiVisible: true,
213
+ expectsUxResponse: "inline-change",
214
+ expectedDelta: "AI issue status changes to resolved",
215
+ },
216
+ agentMetadata: {
217
+ readOrMutate: "mutate",
218
+ boundedScope: "scope",
219
+ auditCategory: "issue-resolve",
220
+ contextPromptShape:
221
+ "Resolve an AI-created issue by issueId or metadata entry id. Refuses if the issue target scope no longer compiles.",
222
+ },
223
+ stateClass: "A-canonical",
224
+ persistsTo: "customXml",
225
+ broadcastsVia: "crdt",
226
+ rwdReference:
227
+ "§AI API § ai.resolveIssue. Adapter over the Layer-08 issue lifecycle binding and Layer-06 durable ai.issue status transition. Finds the issue metadata entry, recompiles its target scope, transitions open -> resolved, and returns a compact auditReference on success.",
228
+ };
229
+
230
+ export const reopenIssueMetadata: ApiV3FnMetadata = {
231
+ name: "ai.reopenIssue",
232
+ status: "live-with-adapter",
233
+ sourceLayer: "semantic-scope-compiler",
234
+ liveEvidence: {
235
+ runnerTest:
236
+ "test/runtime/scopes/issue-lifecycle.test.ts,test/api/v3/ai/ai-review-workflow.test.ts",
237
+ commit: "refactor-09-issue-lifecycle-projection",
238
+ },
239
+ uxIntent: {
240
+ uiVisible: true,
241
+ expectsUxResponse: "inline-change",
242
+ expectedDelta: "AI issue status changes back to open",
243
+ },
244
+ agentMetadata: {
245
+ readOrMutate: "mutate",
246
+ boundedScope: "scope",
247
+ auditCategory: "issue-reopen",
248
+ contextPromptShape:
249
+ "Reopen a resolved AI-created issue by issueId or metadata entry id. Refuses if the issue target scope no longer compiles.",
250
+ },
251
+ stateClass: "A-canonical",
252
+ persistsTo: "customXml",
253
+ broadcastsVia: "crdt",
254
+ rwdReference:
255
+ "§AI API § ai.reopenIssue. Adapter over the Layer-08 issue lifecycle binding and Layer-06 durable ai.issue status transition. Finds the issue metadata entry, recompiles its target scope, transitions resolved -> open, and returns a compact auditReference on success.",
256
+ };
257
+
160
258
  export function createReviewFamily(runtime: RuntimeApiHandle) {
259
+ const compiler = createScopeCompilerService(runtime);
161
260
  return {
162
261
  acceptRevision(input: AcceptRevisionInput): AcceptRevisionResult {
163
262
  // @endStateApi — live-with-adapter. Routes through Layer-06's
@@ -199,7 +298,7 @@ export function createReviewFamily(runtime: RuntimeApiHandle) {
199
298
  },
200
299
  compiledOperationKind: "metadata-attach-explanation",
201
300
  compiledOperationSummary: `accept revision ${revisionId}`,
202
- emittedAtUtc: new Date(0).toISOString(),
301
+ emittedAtUtc: currentAuditTimestamp(runtime),
203
302
  documentHashBefore,
204
303
  });
205
304
  }
@@ -258,7 +357,7 @@ export function createReviewFamily(runtime: RuntimeApiHandle) {
258
357
  },
259
358
  compiledOperationKind: "metadata-attach-explanation",
260
359
  compiledOperationSummary: `reject revision ${revisionId}`,
261
- emittedAtUtc: new Date(0).toISOString(),
360
+ emittedAtUtc: currentAuditTimestamp(runtime),
262
361
  documentHashBefore,
263
362
  });
264
363
  }
@@ -319,7 +418,7 @@ export function createReviewFamily(runtime: RuntimeApiHandle) {
319
418
  },
320
419
  compiledOperationKind: "metadata-attach-explanation",
321
420
  compiledOperationSummary: `resolve comment thread ${threadId}`,
322
- emittedAtUtc: new Date(0).toISOString(),
421
+ emittedAtUtc: currentAuditTimestamp(runtime),
323
422
  documentHashBefore,
324
423
  });
325
424
  }
@@ -338,5 +437,80 @@ export function createReviewFamily(runtime: RuntimeApiHandle) {
338
437
  reason: `thread-not-resolvable:${threadId}`,
339
438
  };
340
439
  },
440
+
441
+ resolveIssue(input: ResolveIssueInput): ResolveIssueResult {
442
+ // @endStateApi — live-with-adapter. L06 owns durable ai.issue
443
+ // transitions; L08 owns the scope/evidence join. This adapter keeps the
444
+ // public AI surface flat and projects a compact audit reference.
445
+ const result = compiler.transitionIssueLifecycle({
446
+ issueId: input.issueId,
447
+ action: "resolve",
448
+ actorId: input.actorId ?? "agent",
449
+ origin: input.origin ?? "agent",
450
+ emittedAtUtc: currentAuditTimestamp(runtime),
451
+ });
452
+ emitUxResponse(runtime, {
453
+ apiFn: resolveIssueMetadata.name,
454
+ intent: resolveIssueMetadata.uxIntent.expectedDelta ?? "",
455
+ mockOrLive: "live",
456
+ uiVisible: true,
457
+ expectedDelta: resolveIssueMetadata.uxIntent.expectedDelta,
458
+ });
459
+ if (!result.transitioned) {
460
+ return {
461
+ issueId: result.issueId,
462
+ resolved: false,
463
+ reason: result.reason,
464
+ blockers: Object.freeze([...result.blockers]),
465
+ ...(result.scopeId ? { scopeId: result.scopeId } : {}),
466
+ };
467
+ }
468
+ return {
469
+ issueId: result.issueId,
470
+ resolved: true,
471
+ entryId: result.entryId,
472
+ scopeId: result.scopeId,
473
+ fromStatus: result.fromStatus,
474
+ toStatus: result.toStatus,
475
+ auditReference: projectAuditReference(result.audit),
476
+ };
477
+ },
478
+
479
+ reopenIssue(input: ReopenIssueInput): ReopenIssueResult {
480
+ // @endStateApi — live-with-adapter. Symmetric with resolveIssue, but
481
+ // transitions resolved ai.issue metadata back to open.
482
+ const result = compiler.transitionIssueLifecycle({
483
+ issueId: input.issueId,
484
+ action: "reopen",
485
+ actorId: input.actorId ?? "agent",
486
+ origin: input.origin ?? "agent",
487
+ emittedAtUtc: currentAuditTimestamp(runtime),
488
+ });
489
+ emitUxResponse(runtime, {
490
+ apiFn: reopenIssueMetadata.name,
491
+ intent: reopenIssueMetadata.uxIntent.expectedDelta ?? "",
492
+ mockOrLive: "live",
493
+ uiVisible: true,
494
+ expectedDelta: reopenIssueMetadata.uxIntent.expectedDelta,
495
+ });
496
+ if (!result.transitioned) {
497
+ return {
498
+ issueId: result.issueId,
499
+ reopened: false,
500
+ reason: result.reason,
501
+ blockers: Object.freeze([...result.blockers]),
502
+ ...(result.scopeId ? { scopeId: result.scopeId } : {}),
503
+ };
504
+ }
505
+ return {
506
+ issueId: result.issueId,
507
+ reopened: true,
508
+ entryId: result.entryId,
509
+ scopeId: result.scopeId,
510
+ fromStatus: result.fromStatus,
511
+ toStatus: result.toStatus,
512
+ auditReference: projectAuditReference(result.audit),
513
+ };
514
+ },
341
515
  };
342
516
  }
@@ -16,6 +16,7 @@ export { createApiV3 } from "./_create.ts";
16
16
  export type { ApiV3, CreateApiV3Opts } from "./_create.ts";
17
17
  export type {
18
18
  AiPe2EvidenceOptions,
19
+ AiPe2GraphOracleReference,
19
20
  AiPe2OracleEvidence,
20
21
  AiPe2OracleEvidenceProvider,
21
22
  AiPe2OracleEvidenceProviderInput,