@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
@@ -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,17 +24,23 @@ 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";
34
33
  import type { AIAction } from "../../../runtime/workflow/ai-action-policy.ts";
35
34
  import type { TextFormattingDirective } from "../../public-types.ts";
35
+ import {
36
+ projectAuditReference,
37
+ type AiActionAuditReference,
38
+ } from "./_audit-reference.ts";
39
+ import { currentAuditTimestamp } from "./_audit-time.ts";
36
40
 
37
41
  export interface ReplacementProposalInput {
38
42
  readonly targetScopeId: string;
39
- readonly operation: ReplacementOperationKind;
43
+ readonly operation: ScopeReplacementOperationKind;
40
44
  readonly proposedText?: string;
41
45
  readonly formatting?: TextFormattingDirective;
42
46
  readonly preserve?: ReplacementPreservePolicy;
@@ -90,6 +94,7 @@ export const proposeReplacementScopeMetadata: ApiV3FnMetadata = {
90
94
  export interface ValidationResult {
91
95
  readonly proposalId: string;
92
96
  readonly safe: boolean;
97
+ readonly posture?: ScopeActionPosture;
93
98
  readonly blockers?: readonly string[];
94
99
  readonly warnings?: readonly string[];
95
100
  readonly approvalRequired?: boolean;
@@ -121,10 +126,12 @@ export const validateReplacementScopeMetadata: ApiV3FnMetadata = {
121
126
  export interface ApplyResult {
122
127
  readonly proposalId: string;
123
128
  readonly applied: boolean;
129
+ readonly posture?: ScopeActionPosture;
124
130
  readonly reason?: string;
125
131
  readonly blockers?: readonly string[];
126
132
  readonly blockerDetails?: readonly ActionBlockerDetail[];
127
133
  readonly auditHint?: string;
134
+ readonly auditReference?: AiActionAuditReference;
128
135
  /**
129
136
  * Gap A (post-Slice-7 integration) — revision IDs authored during
130
137
  * the apply. Populated for suggest-mode (tracked insert + delete);
@@ -140,16 +147,20 @@ export interface ActionBlockerDetail {
140
147
  | "unsupported-scope-kind"
141
148
  | "unsupported-operation"
142
149
  | "unresolved-scope"
143
- | "policy-or-guard";
150
+ | "preservation-boundary"
151
+ | "policy-or-guard"
152
+ | "capability-prerequisite";
144
153
  readonly message: string;
145
154
  readonly nextStep: string;
146
155
  readonly scopeKind?: string;
147
156
  readonly operation?: string;
157
+ readonly fragmentId?: string;
158
+ readonly payloadKind?: string;
148
159
  }
149
160
 
150
161
  export interface ApplyReplacementScopeInput {
151
162
  readonly targetScopeId: string;
152
- readonly operation?: ReplacementOperationKind;
163
+ readonly operation?: ScopeReplacementOperationKind;
153
164
  /**
154
165
  * Flat text payload. Shorthand for
155
166
  * `proposedContent: {kind: "text", text}`. Ignored when
@@ -249,7 +260,7 @@ export const applyScopeActionMetadata: ApiV3FnMetadata = {
249
260
  export interface ValidateReplacementScopeInput {
250
261
  readonly proposalId?: string;
251
262
  readonly targetScopeId?: string;
252
- readonly operation?: ReplacementOperationKind;
263
+ readonly operation?: ScopeReplacementOperationKind;
253
264
  readonly proposedText?: string;
254
265
  /** Optional action id override; composer derives a default otherwise. */
255
266
  readonly actionId?: AIAction;
@@ -270,6 +281,7 @@ function projectValidationResult(
270
281
  return {
271
282
  proposalId,
272
283
  safe: verdict.safe,
284
+ ...(verdict.posture ? { posture: verdict.posture } : {}),
273
285
  ...(blockers ? { blockers } : {}),
274
286
  ...(warnings ? { warnings } : {}),
275
287
  ...(verdict.approval?.required ? { approvalRequired: true } : {}),
@@ -288,6 +300,22 @@ function blockerDetailFor(code: string): ActionBlockerDetail | null {
288
300
  };
289
301
  }
290
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
+
291
319
  if (!code.startsWith("compile-refused:")) return null;
292
320
 
293
321
  const [, scopeKind = "unknown", ...rest] = code.split(":");
@@ -305,12 +333,45 @@ function blockerDetailFor(code: string): ActionBlockerDetail | null {
305
333
  };
306
334
  }
307
335
 
308
- 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";
309
366
  return {
310
367
  code,
311
368
  category: "unsupported-scope-kind",
312
369
  scopeKind,
313
- 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.`,
314
375
  nextStep:
315
376
  "Split the request into paragraph-scoped replacements, or attach an explanation/issue until the multi-paragraph planner ships.",
316
377
  };
@@ -323,7 +384,7 @@ function blockerDetailFor(code: string): ActionBlockerDetail | null {
323
384
  scopeKind,
324
385
  message: `Flat text replacement is not implemented for ${scopeKind} scopes because it can break table structure.`,
325
386
  nextStep:
326
- "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.",
327
388
  };
328
389
  }
329
390
 
@@ -359,11 +420,34 @@ function blockerDetailFor(code: string): ActionBlockerDetail | null {
359
420
  };
360
421
  }
361
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
+
362
446
  function projectBlockerDetails(
363
447
  blockers: readonly string[],
364
448
  ): readonly ActionBlockerDetail[] | undefined {
365
449
  const details = blockers
366
- .map((code) => blockerDetailFor(code))
450
+ .map((code) => capabilityBlockerDetailFor(code) ?? blockerDetailFor(code))
367
451
  .filter((detail): detail is ActionBlockerDetail => detail !== null);
368
452
  return details.length > 0 ? Object.freeze(details) : undefined;
369
453
  }
@@ -401,6 +485,7 @@ export function createReplacementFamily(runtime: RuntimeApiHandle) {
401
485
  return {
402
486
  proposalId,
403
487
  safe: false,
488
+ posture: "hard-refusal",
404
489
  blockers: Object.freeze([
405
490
  "legacy-proposalId-only:pass-targetScopeId-to-validate-now",
406
491
  ]),
@@ -412,13 +497,14 @@ export function createReplacementFamily(runtime: RuntimeApiHandle) {
412
497
  return {
413
498
  proposalId,
414
499
  safe: false,
500
+ posture: "hard-refusal",
415
501
  blockers: Object.freeze([
416
502
  `scope-not-resolvable:${input.targetScopeId}`,
417
503
  ]),
418
504
  };
419
505
  }
420
506
 
421
- const operation: ReplacementOperationKind = input.operation ?? "replace";
507
+ const operation: ScopeReplacementOperationKind = input.operation ?? "replace";
422
508
  const proposedContent =
423
509
  typeof input.proposedText === "string"
424
510
  ? ({ kind: "text", text: input.proposedText } as const)
@@ -465,7 +551,7 @@ export function createReplacementFamily(runtime: RuntimeApiHandle) {
465
551
  ...(input.actionId ? { actionId: input.actionId } : {}),
466
552
  actorId: input.actorId ?? "agent",
467
553
  origin: input.origin ?? "agent",
468
- emittedAtUtc: new Date(0).toISOString(),
554
+ emittedAtUtc: currentAuditTimestamp(runtime),
469
555
  });
470
556
 
471
557
  emitUxResponse(runtime, {
@@ -482,12 +568,16 @@ export function createReplacementFamily(runtime: RuntimeApiHandle) {
482
568
  return {
483
569
  proposalId,
484
570
  applied: result.applied,
571
+ ...(result.validation.posture ? { posture: result.validation.posture } : {}),
485
572
  ...(result.reason ? { reason: result.reason } : {}),
486
573
  ...(result.validation.blockedReasons.length > 0
487
574
  ? { blockers: Object.freeze([...result.validation.blockedReasons]) }
488
575
  : {}),
489
576
  ...(blockerDetails ? { blockerDetails } : {}),
490
577
  ...(result.audit ? { auditHint: result.audit.actionId } : {}),
578
+ ...(result.audit
579
+ ? { auditReference: projectAuditReference(result.audit) }
580
+ : {}),
491
581
  authoredRevisionIds: result.authoredRevisionIds,
492
582
  };
493
583
  },
@@ -510,7 +600,7 @@ export function createReplacementFamily(runtime: RuntimeApiHandle) {
510
600
  ...(input.actionId ? { actionId: input.actionId } : {}),
511
601
  actorId: input.actorId ?? "agent",
512
602
  origin: input.origin ?? "agent",
513
- emittedAtUtc: new Date(0).toISOString(),
603
+ emittedAtUtc: currentAuditTimestamp(runtime),
514
604
  });
515
605
 
516
606
  emitUxResponse(runtime, {
@@ -527,12 +617,16 @@ export function createReplacementFamily(runtime: RuntimeApiHandle) {
527
617
  return {
528
618
  proposalId,
529
619
  applied: result.applied,
620
+ ...(result.validation.posture ? { posture: result.validation.posture } : {}),
530
621
  ...(result.reason ? { reason: result.reason } : {}),
531
622
  ...(result.validation.blockedReasons.length > 0
532
623
  ? { blockers: Object.freeze([...result.validation.blockedReasons]) }
533
624
  : {}),
534
625
  ...(blockerDetails ? { blockerDetails } : {}),
535
626
  ...(result.audit ? { auditHint: result.audit.actionId } : {}),
627
+ ...(result.audit
628
+ ? { auditReference: projectAuditReference(result.audit) }
629
+ : {}),
536
630
  authoredRevisionIds: result.authoredRevisionIds,
537
631
  };
538
632
  },
@@ -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
  }
@@ -14,6 +14,14 @@
14
14
 
15
15
  export { createApiV3 } from "./_create.ts";
16
16
  export type { ApiV3, CreateApiV3Opts } from "./_create.ts";
17
+ export type {
18
+ AiPe2EvidenceOptions,
19
+ AiPe2GraphOracleReference,
20
+ AiPe2OracleEvidence,
21
+ AiPe2OracleEvidenceProvider,
22
+ AiPe2OracleEvidenceProviderInput,
23
+ AiPe2OracleVerdict,
24
+ } from "./ai/_pe2-evidence.ts";
17
25
 
18
26
  export type {
19
27
  ApiStatus,