@beyondwork/docx-react-component 1.0.66 → 1.0.69

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 (384) hide show
  1. package/README.md +75 -931
  2. package/package.json +26 -27
  3. package/src/api/anchor-conversion.ts +43 -0
  4. package/src/api/editor-state-types.ts +2 -1
  5. package/src/api/public-types.ts +504 -101
  6. package/src/api/session-state.ts +4 -0
  7. package/src/api/v3/README.md +91 -0
  8. package/src/api/v3/_create.ts +146 -0
  9. package/src/api/v3/_layer-metadata.ts +362 -0
  10. package/src/api/v3/_mocks.ts +84 -0
  11. package/src/api/v3/_runtime-handle.ts +162 -0
  12. package/src/api/v3/_ux-response.ts +73 -0
  13. package/src/api/v3/ai/_metadata-audit.ts +225 -0
  14. package/src/api/v3/ai/attach.ts +235 -0
  15. package/src/api/v3/ai/bundle.ts +132 -0
  16. package/src/api/v3/ai/explain.ts +144 -0
  17. package/src/api/v3/ai/export.ts +54 -0
  18. package/src/api/v3/ai/inspect.ts +118 -0
  19. package/src/api/v3/ai/policy.ts +77 -0
  20. package/src/api/v3/ai/replacement.ts +341 -0
  21. package/src/api/v3/ai/resolve.ts +133 -0
  22. package/src/api/v3/index.ts +79 -0
  23. package/src/api/v3/runtime/chart.ts +310 -0
  24. package/src/api/v3/runtime/clipboard.ts +81 -0
  25. package/src/api/v3/runtime/collab.ts +331 -0
  26. package/src/api/v3/runtime/content.ts +236 -0
  27. package/src/api/v3/runtime/document.ts +282 -0
  28. package/src/api/v3/runtime/formatting.ts +186 -0
  29. package/src/api/v3/runtime/geometry.ts +349 -0
  30. package/src/api/v3/runtime/layout.ts +108 -0
  31. package/src/api/v3/runtime/review.ts +129 -0
  32. package/src/api/v3/runtime/search.ts +74 -0
  33. package/src/api/v3/runtime/table.ts +63 -0
  34. package/src/api/v3/runtime/workflow.ts +434 -0
  35. package/src/api/v3/ui/_context.ts +86 -0
  36. package/src/api/v3/ui/_create.ts +65 -0
  37. package/src/api/v3/ui/_types.ts +520 -0
  38. package/src/api/v3/ui/chrome-composition.ts +342 -0
  39. package/src/{ui-tailwind/chrome → api/v3/ui}/chrome-preset-model.ts +11 -1
  40. package/src/api/v3/ui/chrome.ts +476 -0
  41. package/src/api/v3/ui/debug.ts +124 -0
  42. package/src/api/v3/ui/index.ts +64 -0
  43. package/src/api/v3/ui/overlays-visibility.ts +170 -0
  44. package/src/api/v3/ui/overlays.ts +427 -0
  45. package/src/api/v3/ui/scope.ts +71 -0
  46. package/src/api/v3/ui/session.ts +100 -0
  47. package/src/api/v3/ui/surface.ts +170 -0
  48. package/src/api/v3/ui/viewport.ts +303 -0
  49. package/src/core/commands/index.ts +28 -6
  50. package/src/core/commands/list-commands.ts +3 -2
  51. package/src/core/commands/section-layout-commands.ts +9 -8
  52. package/src/core/schema/text-schema.ts +16 -0
  53. package/src/core/selection/mapping.ts +33 -72
  54. package/src/core/state/editor-state.ts +96 -189
  55. package/src/index.ts +23 -4
  56. package/src/io/chart-preview-resolver.ts +1 -1
  57. package/src/io/docx-session.ts +36 -4795
  58. package/src/io/export/build-app-properties-xml.ts +1 -1
  59. package/src/io/export/serialize-comments.ts +1 -1
  60. package/src/io/export/serialize-headers-footers.ts +6 -1
  61. package/src/io/export/serialize-main-document.ts +45 -0
  62. package/src/io/export/serialize-run-formatting.ts +17 -2
  63. package/src/io/export/twip.ts +1 -1
  64. package/src/io/normalize/normalize-text.ts +27 -20
  65. package/src/io/ooxml/chart/parse-series.ts +1 -1
  66. package/src/io/ooxml/chart/resolve-color.ts +2 -2
  67. package/src/io/ooxml/chart/types.ts +1 -1
  68. package/src/io/ooxml/classify-embedding.ts +83 -33
  69. package/src/io/ooxml/parse-fill.ts +1 -1
  70. package/src/io/ooxml/parse-main-document.ts +71 -1
  71. package/src/io/ooxml/parse-object.ts +14 -10
  72. package/src/io/ooxml/parse-run-formatting.ts +47 -1
  73. package/src/io/ooxml/property-grab-bag.ts +2 -2
  74. package/src/io/ooxml/units.ts +11 -0
  75. package/src/io/ooxml/workflow-payload.ts +282 -7
  76. package/src/model/anchor.ts +85 -0
  77. package/src/model/canonical-document.ts +351 -15
  78. package/src/model/chart-types.ts +1 -1
  79. package/src/model/layout/index.ts +83 -0
  80. package/src/model/layout/page-graph-types.ts +181 -0
  81. package/src/model/layout/page-layout-snapshot.ts +105 -0
  82. package/src/model/layout/resolved-layout-types.ts +47 -0
  83. package/src/model/layout/runtime-page-graph-types.ts +102 -0
  84. package/src/model/paragraph-scope-ids.ts +72 -0
  85. package/src/model/review/comment-types.ts +112 -0
  86. package/src/model/review/index.ts +2 -0
  87. package/src/model/review/revision-types.ts +215 -0
  88. package/src/model/snapshot.ts +32 -0
  89. package/src/review/store/comment-store.ts +21 -47
  90. package/src/review/store/revision-types.ts +40 -198
  91. package/src/runtime/collab/base-doc-fingerprint.ts +6 -1
  92. package/src/runtime/collab/runtime-collab-sync.ts +13 -3
  93. package/src/runtime/collab-session.ts +1 -1
  94. package/src/runtime/debug/build-debug-inspector-snapshot.ts +686 -0
  95. package/src/runtime/debug/event-ring-buffer.ts +64 -0
  96. package/src/runtime/debug/probability-sampler.ts +18 -0
  97. package/src/runtime/debug/runtime-debug-facet.ts +67 -0
  98. package/src/runtime/debug/stage-tokens.ts +31 -0
  99. package/src/runtime/debug/telemetry-bus.ts +271 -0
  100. package/src/runtime/debug/types.ts +275 -0
  101. package/src/runtime/debug/wrap-ref-for-telemetry.ts +118 -0
  102. package/src/runtime/document-layout.ts +8 -6
  103. package/src/runtime/document-runtime.ts +843 -1141
  104. package/src/runtime/document-search.ts +1 -1
  105. package/src/runtime/edit-ops/index.ts +1 -1
  106. package/src/runtime/external-send-runtime.ts +1 -1
  107. package/src/runtime/formatting/document-lookup.ts +235 -0
  108. package/src/runtime/formatting/field/registry.ts +41 -0
  109. package/src/runtime/{field-resolver.ts → formatting/field/resolver.ts} +27 -2
  110. package/src/runtime/formatting/font-resolution.ts +83 -0
  111. package/src/runtime/formatting/formatting-context.ts +903 -0
  112. package/src/runtime/formatting/formatting-types.ts +157 -0
  113. package/src/runtime/{hyperlink-color-resolver.ts → formatting/hyperlink-color.ts} +2 -2
  114. package/src/runtime/formatting/index.ts +125 -0
  115. package/src/runtime/{resolved-numbering-geometry.ts → formatting/numbering/geometry.ts} +1 -1
  116. package/src/runtime/{numbering-prefix.ts → formatting/numbering/prefix.ts} +170 -3
  117. package/src/runtime/formatting/paragraph-style-resolver.ts +92 -0
  118. package/src/runtime/formatting/projector.ts +75 -0
  119. package/src/runtime/formatting/resolve-effective.ts +407 -0
  120. package/src/runtime/formatting/revision-display.ts +105 -0
  121. package/src/runtime/{paragraph-style-resolver.ts → formatting/style-cascade.ts} +84 -141
  122. package/src/runtime/{table-style-resolver.ts → formatting/table-style-resolver.ts} +1 -1
  123. package/src/runtime/formatting/telemetry-bridge.ts +106 -0
  124. package/src/runtime/{theme-color-resolver.ts → formatting/theme-color.ts} +2 -30
  125. package/src/runtime/geometry/caret-geometry.ts +164 -0
  126. package/src/runtime/geometry/geometry-facet.ts +364 -0
  127. package/src/runtime/geometry/geometry-types.ts +256 -0
  128. package/src/runtime/geometry/hit-test.ts +125 -0
  129. package/src/runtime/geometry/index.ts +71 -0
  130. package/src/runtime/geometry/inert-geometry-facet.ts +43 -0
  131. package/src/runtime/geometry/invalidation.ts +35 -0
  132. package/src/runtime/geometry/object-handles.ts +77 -0
  133. package/src/runtime/geometry/overlay-rects.ts +85 -0
  134. package/src/runtime/geometry/project-anchors.ts +100 -0
  135. package/src/runtime/geometry/project-fragments.ts +216 -0
  136. package/src/runtime/geometry/projector.ts +129 -0
  137. package/src/runtime/geometry/replacement-envelope.ts +130 -0
  138. package/src/runtime/geometry/viewport.ts +218 -0
  139. package/src/runtime/layout/compat-input-ledger.ts +211 -0
  140. package/src/runtime/layout/index.ts +6 -1
  141. package/src/runtime/layout/inert-layout-facet.ts +12 -7
  142. package/src/runtime/layout/layout-engine-instance.ts +189 -11
  143. package/src/runtime/layout/layout-engine-version.ts +450 -1
  144. package/src/runtime/layout/layout-facet-types.ts +60 -0
  145. package/src/runtime/layout/layout-measurement-provider.ts +13 -0
  146. package/src/runtime/layout/measurement-backend-canvas.ts +14 -2
  147. package/src/runtime/layout/measurement-backend-empirical.ts +23 -4
  148. package/src/runtime/layout/page-graph.ts +62 -209
  149. package/src/runtime/layout/page-story-resolver.ts +7 -12
  150. package/src/runtime/layout/paginated-layout-engine.ts +186 -11
  151. package/src/runtime/layout/project-block-fragments.ts +11 -0
  152. package/src/runtime/layout/projector.ts +90 -0
  153. package/src/runtime/layout/public-facet.ts +187 -442
  154. package/src/runtime/layout/resolved-formatting-state.ts +158 -26
  155. package/src/runtime/layout/table-render-plan.ts +1 -1
  156. package/src/runtime/prerender/cache-envelope.ts +6 -1
  157. package/src/runtime/prerender/prerender-document.ts +18 -23
  158. package/src/runtime/render/decoration-resolver.ts +1 -1
  159. package/src/runtime/render/render-frame-types.ts +20 -0
  160. package/src/runtime/render/render-kernel.ts +94 -25
  161. package/src/runtime/scopes/_formatting-seam.ts +262 -0
  162. package/src/runtime/scopes/_scope-dependencies.ts +49 -0
  163. package/src/runtime/scopes/action-validation.ts +356 -0
  164. package/src/runtime/scopes/attach-explanation.ts +102 -0
  165. package/src/runtime/scopes/audit-bundle.ts +71 -0
  166. package/src/runtime/scopes/compile-scope-bundle.ts +163 -0
  167. package/src/runtime/scopes/compile-scope.ts +262 -0
  168. package/src/runtime/scopes/compiler-service.ts +431 -0
  169. package/src/runtime/scopes/create-issue.ts +107 -0
  170. package/src/runtime/scopes/enumerate-scopes.ts +543 -0
  171. package/src/runtime/scopes/evidence.ts +233 -0
  172. package/src/runtime/scopes/index.ts +150 -0
  173. package/src/runtime/scopes/position-map.ts +214 -0
  174. package/src/runtime/scopes/preservation-boundary.ts +91 -0
  175. package/src/runtime/scopes/projector.ts +49 -0
  176. package/src/runtime/scopes/replaceability.ts +87 -0
  177. package/src/runtime/scopes/replacement/apply.ts +228 -0
  178. package/src/runtime/scopes/replacement/compile.ts +59 -0
  179. package/src/runtime/scopes/replacement/propose.ts +42 -0
  180. package/src/runtime/scopes/resolve-reference.ts +347 -0
  181. package/src/runtime/scopes/review-bundle.ts +141 -0
  182. package/src/runtime/scopes/scope-kinds/_paragraph-text.ts +57 -0
  183. package/src/runtime/scopes/scope-kinds/_table-text.ts +42 -0
  184. package/src/runtime/scopes/scope-kinds/comment-thread.ts +59 -0
  185. package/src/runtime/scopes/scope-kinds/field.ts +65 -0
  186. package/src/runtime/scopes/scope-kinds/heading.ts +84 -0
  187. package/src/runtime/scopes/scope-kinds/list-item.ts +77 -0
  188. package/src/runtime/scopes/scope-kinds/paragraph.ts +182 -0
  189. package/src/runtime/scopes/scope-kinds/revision.ts +62 -0
  190. package/src/runtime/scopes/scope-kinds/table-cell.ts +57 -0
  191. package/src/runtime/scopes/scope-kinds/table-row.ts +61 -0
  192. package/src/runtime/scopes/scope-kinds/table.ts +55 -0
  193. package/src/runtime/scopes/scope-range.ts +208 -0
  194. package/src/runtime/scopes/semantic-scope-types.ts +454 -0
  195. package/src/runtime/scopes/workflow-overlap.ts +92 -0
  196. package/src/runtime/selection/index.ts +1 -1
  197. package/src/runtime/structure-ops/fragment-insert.ts +1 -1
  198. package/src/runtime/structure-ops/index.ts +1 -1
  199. package/src/runtime/surface-projection.ts +232 -262
  200. package/src/runtime/units.ts +4 -2
  201. package/src/runtime/workflow/coordinator.ts +1348 -0
  202. package/src/runtime/workflow/derived-scope-resolver.ts +125 -0
  203. package/src/runtime/workflow/index.ts +25 -0
  204. package/src/runtime/workflow/markup-mode-policy.ts +98 -0
  205. package/src/runtime/{workflow-markup.ts → workflow/markup.ts} +6 -6
  206. package/src/runtime/workflow/metadata-persistence.ts +306 -0
  207. package/src/runtime/workflow/metadata-writer.ts +123 -0
  208. package/src/runtime/workflow/overlay-store.ts +690 -0
  209. package/src/runtime/workflow/projector.ts +127 -0
  210. package/src/runtime/{query-scopes.ts → workflow/query-scopes.ts} +3 -3
  211. package/src/runtime/{workflow-rail-segments.ts → workflow/rail/compose.ts} +60 -165
  212. package/src/runtime/workflow/rail/types.ts +198 -0
  213. package/src/runtime/workflow/scope-rail-composer.ts +39 -0
  214. package/src/runtime/{scope-resolver.ts → workflow/scope-resolver.ts} +3 -3
  215. package/src/runtime/workflow/scope-writer.ts +188 -0
  216. package/src/runtime/{tamper-gate.ts → workflow/tamper-gate.ts} +1 -1
  217. package/src/runtime/workflow/visibility-policy.ts +129 -0
  218. package/src/session/_sync-legacy.ts +66 -0
  219. package/src/session/export/embedded-reconstitute.ts +104 -0
  220. package/src/session/export/export-diagnostics.ts +85 -0
  221. package/src/session/export/export-validation.ts +110 -0
  222. package/src/session/export/index.ts +34 -0
  223. package/src/session/export/preservation-reattach.ts +30 -0
  224. package/src/session/export/serialize-dispatch.ts +165 -0
  225. package/src/session/export/stateful-export-pipeline.ts +432 -0
  226. package/src/session/export/stateful-export.ts +684 -0
  227. package/src/session/import/canonical-assembly.ts +227 -0
  228. package/src/session/import/diagnostics-session.ts +54 -0
  229. package/src/session/import/embedded-discovery.ts +225 -0
  230. package/src/session/import/embedded-offload.ts +337 -0
  231. package/src/session/import/import-diagnostics.ts +69 -0
  232. package/src/session/import/loader-types.ts +313 -0
  233. package/src/session/import/loader.ts +1834 -0
  234. package/src/session/import/normalize.ts +195 -0
  235. package/src/session/import/package-parts.ts +217 -0
  236. package/src/session/import/package-read.ts +195 -0
  237. package/src/session/import/parse-orchestration.ts +105 -0
  238. package/src/session/import/part-constants.ts +70 -0
  239. package/src/session/import/part-discovery.ts +94 -0
  240. package/src/session/import/preservation-index.ts +46 -0
  241. package/src/{runtime/read-only-diagnostics-runtime.ts → session/import/read-only-diagnostics.ts} +24 -3
  242. package/src/session/import/review-import.ts +508 -0
  243. package/src/session/import/styles-consolidation.ts +281 -0
  244. package/src/session/import/workflow-scope-import.ts +256 -0
  245. package/src/session/index.ts +37 -0
  246. package/src/session/session-state.ts +69 -0
  247. package/src/session/session.ts +532 -0
  248. package/src/session/shared/protection.ts +228 -0
  249. package/src/session/shared/session-utils.ts +82 -0
  250. package/src/session/types.ts +499 -0
  251. package/src/shell/chart-snapshots.ts +96 -0
  252. package/src/shell/media-previews.ts +85 -0
  253. package/src/shell/overlay-anchor-bridge.ts +53 -0
  254. package/src/shell/paste-adapter.ts +23 -0
  255. package/src/shell/ref-commands.ts +1697 -0
  256. package/src/shell/ref-utilities.ts +48 -0
  257. package/src/shell/search.ts +51 -0
  258. package/src/{ui/editor-runtime-boundary.ts → shell/session-bootstrap.ts} +243 -67
  259. package/src/shell/ui-subscriber-channels.ts +81 -0
  260. package/src/shell/use-collab-sync.ts +116 -0
  261. package/src/ui/WordReviewEditor.tsx +496 -2051
  262. package/src/ui/editor-shell-view.tsx +30 -1
  263. package/src/ui/editor-surface-controller.tsx +49 -1
  264. package/src/ui/headless/revision-decoration-model.ts +83 -0
  265. package/src/{ui-tailwind/chrome → ui/headless}/role-action-sets.ts +1 -1
  266. package/src/ui/headless/scoped-chrome-policy.ts +2 -2
  267. package/src/ui/headless/selection-tool-context.ts +1 -1
  268. package/src/ui/headless/selection-tool-resolver.ts +1 -1
  269. package/src/ui/runtime-shortcut-dispatch.ts +46 -1
  270. package/src/ui/ui-controller-factory.ts +221 -0
  271. package/src/ui-tailwind/chart/ChartSurface.tsx +2 -2
  272. package/src/ui-tailwind/chart/layout/legend-layout.ts +1 -1
  273. package/src/ui-tailwind/chart/layout/plot-area.ts +2 -2
  274. package/src/ui-tailwind/chart/layout/title-layout.ts +1 -1
  275. package/src/ui-tailwind/chart/render/area.tsx +3 -3
  276. package/src/ui-tailwind/chart/render/bar-column.tsx +3 -3
  277. package/src/ui-tailwind/chart/render/bubble.tsx +3 -3
  278. package/src/ui-tailwind/chart/render/combo.tsx +2 -2
  279. package/src/ui-tailwind/chart/render/data-labels.tsx +2 -2
  280. package/src/ui-tailwind/chart/render/font-metrics.ts +2 -2
  281. package/src/ui-tailwind/chart/render/line.tsx +3 -3
  282. package/src/ui-tailwind/chart/render/pie.tsx +6 -6
  283. package/src/ui-tailwind/chart/render/scatter.tsx +3 -3
  284. package/src/ui-tailwind/chart/render/svg-primitives.ts +3 -3
  285. package/src/ui-tailwind/chart/render/unsupported.tsx +2 -2
  286. package/src/ui-tailwind/chrome/build-context-menu-entries.ts +88 -0
  287. package/src/ui-tailwind/chrome/chrome-preset-toolbar.tsx +1 -1
  288. package/src/ui-tailwind/chrome/collab-send-to-supplier-button.tsx +1 -1
  289. package/src/ui-tailwind/chrome/collab-tamper-banner.tsx +1 -1
  290. package/src/ui-tailwind/chrome/collab-top-nav-container.tsx +1 -1
  291. package/src/ui-tailwind/chrome/editor-action-registry.ts +553 -0
  292. package/src/ui-tailwind/chrome/editor-actions-to-palette.ts +182 -0
  293. package/src/ui-tailwind/chrome/local-surface-arbiter.ts +534 -0
  294. package/src/ui-tailwind/chrome/resolve-target-kind.ts +226 -0
  295. package/src/ui-tailwind/chrome/tw-alert-banner.tsx +38 -4
  296. package/src/ui-tailwind/chrome/tw-context-band.tsx +125 -0
  297. package/src/ui-tailwind/chrome/tw-context-menu-portal.tsx +248 -0
  298. package/src/ui-tailwind/chrome/tw-image-context-toolbar.tsx +42 -1
  299. package/src/ui-tailwind/chrome/tw-selection-anchor-resolver.ts +8 -7
  300. package/src/ui-tailwind/chrome/tw-selection-tool-blocked.tsx +38 -4
  301. package/src/ui-tailwind/chrome/tw-selection-tool-comment.tsx +104 -6
  302. package/src/ui-tailwind/chrome/tw-selection-tool-host.tsx +66 -7
  303. package/src/ui-tailwind/chrome/tw-selection-tool-workflow.tsx +54 -8
  304. package/src/ui-tailwind/chrome/tw-shortcut-hint.tsx +7 -1
  305. package/src/ui-tailwind/chrome/tw-suggestion-card.tsx +33 -0
  306. package/src/ui-tailwind/chrome/tw-table-context-toolbar.tsx +78 -1
  307. package/src/ui-tailwind/chrome/tw-table-grip-layer.tsx +16 -8
  308. package/src/ui-tailwind/chrome/tw-workspace-chrome-host.tsx +276 -0
  309. package/src/ui-tailwind/chrome/use-context-menu-controller.ts +201 -0
  310. package/src/ui-tailwind/chrome-overlay/chrome-overlay-projector.ts +1 -1
  311. package/src/ui-tailwind/chrome-overlay/tw-chrome-overlay.tsx +22 -4
  312. package/src/ui-tailwind/chrome-overlay/tw-comment-balloon-layer.tsx +1 -1
  313. package/src/ui-tailwind/chrome-overlay/tw-locked-block-layer.tsx +1 -1
  314. package/src/ui-tailwind/chrome-overlay/tw-object-selection-overlay.tsx +11 -5
  315. package/src/ui-tailwind/chrome-overlay/tw-page-stack-overlay-layer.tsx +197 -3
  316. package/src/ui-tailwind/chrome-overlay/tw-revision-margin-bar-layer.tsx +1 -1
  317. package/src/ui-tailwind/chrome-overlay/tw-scope-card-layer.tsx +35 -6
  318. package/src/ui-tailwind/chrome-overlay/tw-scope-rail-layer.tsx +24 -16
  319. package/src/ui-tailwind/chrome-overlay/tw-table-continuation-header.tsx +1 -1
  320. package/src/ui-tailwind/debug/README.md +57 -0
  321. package/src/ui-tailwind/debug/index.ts +3 -0
  322. package/src/ui-tailwind/debug/tw-debug-overlay.tsx +186 -0
  323. package/src/ui-tailwind/debug/tw-debug-presentation.tsx +80 -0
  324. package/src/ui-tailwind/debug/tw-debug-top-bar.tsx +83 -0
  325. package/src/ui-tailwind/editor-surface/chart-node-view.tsx +2 -2
  326. package/src/ui-tailwind/editor-surface/float-wrap-resolver.ts +1 -1
  327. package/src/ui-tailwind/editor-surface/pm-command-bridge.ts +135 -10
  328. package/src/ui-tailwind/editor-surface/pm-decorations.ts +40 -13
  329. package/src/ui-tailwind/editor-surface/pm-page-break-decorations.ts +1 -1
  330. package/src/ui-tailwind/editor-surface/pm-schema.ts +1 -1
  331. package/src/ui-tailwind/editor-surface/pm-state-from-snapshot.ts +3 -3
  332. package/src/ui-tailwind/editor-surface/predicted-tag-preflight.ts +1 -1
  333. package/src/ui-tailwind/editor-surface/remote-cursor-plugin.ts +2 -2
  334. package/src/ui-tailwind/editor-surface/scroll-anchor.ts +91 -9
  335. package/src/ui-tailwind/editor-surface/shape-renderer.ts +1 -1
  336. package/src/ui-tailwind/editor-surface/surface-layer.ts +1 -1
  337. package/src/ui-tailwind/editor-surface/tw-opaque-block.tsx +1 -1
  338. package/src/ui-tailwind/editor-surface/tw-page-block-view.helpers.ts +23 -6
  339. package/src/ui-tailwind/editor-surface/tw-prosemirror-surface.tsx +132 -22
  340. package/src/ui-tailwind/editor-surface/tw-table-node-view.tsx +1 -1
  341. package/src/ui-tailwind/index.ts +0 -5
  342. package/src/ui-tailwind/overlay-anchor-bridge-context.tsx +33 -0
  343. package/src/ui-tailwind/page-stack/floating-image-overlay-model.ts +66 -29
  344. package/src/ui-tailwind/page-stack/tw-floating-image-layer.tsx +25 -2
  345. package/src/ui-tailwind/review/comment-markdown-renderer.tsx +15 -0
  346. package/src/ui-tailwind/review/tw-review-rail.tsx +92 -4
  347. package/src/ui-tailwind/review/tw-workflow-tab.tsx +1 -1
  348. package/src/ui-tailwind/review-workspace/page-chrome.ts +210 -0
  349. package/src/ui-tailwind/review-workspace/page-shell-metrics.ts +101 -0
  350. package/src/ui-tailwind/review-workspace/paragraph-layout.ts +115 -0
  351. package/src/ui-tailwind/review-workspace/selection-toolbar-placement.ts +97 -0
  352. package/src/ui-tailwind/review-workspace/tw-review-workspace-navigator.tsx +130 -0
  353. package/src/ui-tailwind/review-workspace/tw-review-workspace-page-toolbar.tsx +240 -0
  354. package/src/ui-tailwind/review-workspace/tw-review-workspace-rail.tsx +59 -0
  355. package/src/ui-tailwind/review-workspace/types.ts +408 -0
  356. package/src/ui-tailwind/review-workspace/use-chrome-policy.ts +104 -0
  357. package/src/ui-tailwind/review-workspace/use-derived-view-state.ts +151 -0
  358. package/src/ui-tailwind/review-workspace/use-diagnostics-signal.ts +70 -0
  359. package/src/ui-tailwind/review-workspace/use-grabbed-segment-offsets.ts +40 -0
  360. package/src/ui-tailwind/review-workspace/use-layout-facet-render-signal.ts +55 -0
  361. package/src/ui-tailwind/review-workspace/use-page-markers.ts +130 -0
  362. package/src/ui-tailwind/review-workspace/use-pm-surface-capture.ts +60 -0
  363. package/src/ui-tailwind/review-workspace/use-review-rail-state.ts +63 -0
  364. package/src/ui-tailwind/review-workspace/use-scope-card-state.ts +170 -0
  365. package/src/ui-tailwind/review-workspace/use-scroll-root-capture.ts +28 -0
  366. package/src/ui-tailwind/review-workspace/use-selection-toolbar-placement.ts +113 -0
  367. package/src/ui-tailwind/review-workspace/use-shell-selection-anchor-bridge.ts +120 -0
  368. package/src/ui-tailwind/review-workspace/use-status-bar-page-facts.ts +55 -0
  369. package/src/ui-tailwind/review-workspace/use-viewport-dimensions.ts +43 -0
  370. package/src/ui-tailwind/review-workspace/use-workspace-arbiter.ts +25 -0
  371. package/src/ui-tailwind/review-workspace/use-workspace-composition.ts +86 -0
  372. package/src/ui-tailwind/review-workspace/use-workspace-side-effects.ts +150 -0
  373. package/src/ui-tailwind/theme/editor-theme.css +25 -0
  374. package/src/ui-tailwind/toolbar/tw-role-action-region.tsx +2 -2
  375. package/src/ui-tailwind/toolbar/tw-toolbar.tsx +61 -98
  376. package/src/ui-tailwind/tw-review-workspace.tsx +521 -1802
  377. package/src/ui-tailwind/ui-api-context.tsx +43 -0
  378. package/src/ui-tailwind/ui-shell-channels-context.tsx +49 -0
  379. package/src/validation/compatibility-engine.ts +6 -6
  380. package/src/runtime/styles-cascade.ts +0 -33
  381. package/src/ui-tailwind/chrome/tw-mode-dock.tsx +0 -85
  382. /package/src/runtime/{page-number-format.ts → formatting/field/page-number-format.ts} +0 -0
  383. /package/src/runtime/{ai-action-policy.ts → workflow/ai-action-policy.ts} +0 -0
  384. /package/src/runtime/{scope-tag-registry.ts → workflow/scope-tag-registry.ts} +0 -0
@@ -0,0 +1,84 @@
1
+ /**
2
+ * @endStateApi v3 — deterministic mock factories.
3
+ *
4
+ * Every mock payload carries `__mock: true`. Mocks are seeded from stable
5
+ * inputs (documentId, sha256, or a fixed counter) so BW consumer tests
6
+ * snapshot against deterministic shapes. Nothing here uses randomness.
7
+ */
8
+
9
+ import type { MockPayload } from "./_layer-metadata.ts";
10
+
11
+ /**
12
+ * @endStateApi
13
+ * Tag a payload as a mock. Called by every mock-returning v3 function.
14
+ *
15
+ * Body is spread FIRST, then the markers — this guarantees that even if a
16
+ * caller accidentally passes a body with an `__mock`/`reason`/`shape` key,
17
+ * the markers win and the mock flag is never lost.
18
+ */
19
+ export function mockPayload<T extends object>(
20
+ reason: string,
21
+ shape: string,
22
+ body: T,
23
+ ): T & MockPayload {
24
+ return {
25
+ ...body,
26
+ __mock: true,
27
+ reason,
28
+ shape,
29
+ };
30
+ }
31
+
32
+ /**
33
+ * @endStateApi
34
+ * Tag an ARRAY payload as a mock. `mockPayload` uses object spread which
35
+ * collapses arrays to plain objects (loses Array-ness). Use this for
36
+ * functions whose return type is `ReadonlyArray<T>` — it attaches the
37
+ * markers as non-indexed properties so `Array.isArray` still returns true
38
+ * AND `isMock` narrows correctly.
39
+ */
40
+ export function mockArray<T>(
41
+ reason: string,
42
+ shape: string,
43
+ items: readonly T[],
44
+ ): readonly T[] & MockPayload {
45
+ const arr = items.slice() as T[];
46
+ return Object.assign(arr, {
47
+ __mock: true as const,
48
+ reason,
49
+ shape,
50
+ }) as readonly T[] & MockPayload;
51
+ }
52
+
53
+ /**
54
+ * @endStateApi
55
+ * Derive a stable string from a seed + name. Used where a mock needs an
56
+ * ID-shaped value (e.g. scopeId for a mock createScope call).
57
+ */
58
+ export function mockId(seed: string, name: string): string {
59
+ // Simple deterministic mix — not cryptographic; just stable across runs.
60
+ let h = 0;
61
+ const input = `${seed}::${name}`;
62
+ for (let i = 0; i < input.length; i += 1) {
63
+ h = (h * 31 + input.charCodeAt(i)) | 0;
64
+ }
65
+ // `h >>> 0` coerces the 32-bit signed int to unsigned so base36 never
66
+ // produces a negative-prefixed string (edge case: h === -2^31 where
67
+ // Math.abs would still return negative).
68
+ const hex = (h >>> 0).toString(36).padStart(8, "0");
69
+ return `mock-${name}-${hex}`;
70
+ }
71
+
72
+ /**
73
+ * @endStateApi
74
+ * Return `true` iff the argument carries the mock flag. Consumer tests
75
+ * can narrow via this guard: `if (isMock(result)) { ... }`.
76
+ */
77
+ export function isMock(value: unknown): value is MockPayload {
78
+ return (
79
+ typeof value === "object" &&
80
+ value !== null &&
81
+ (value as { __mock?: unknown }).__mock === true
82
+ );
83
+ }
84
+
@@ -0,0 +1,162 @@
1
+ /**
2
+ * @endStateApi v3 — `RuntimeApiHandle`.
3
+ *
4
+ * The narrow interface v3 family modules type against. It enumerates the
5
+ * exact runtime surface v3 reads — nothing more. Family modules accept a
6
+ * `RuntimeApiHandle` instead of a full `DocumentRuntime`, which:
7
+ *
8
+ * 1. Removes the `(runtime as unknown as { ... })` casts that previously
9
+ * asserted facet shape from inside each family module.
10
+ * 2. Enables headless callers (tests, debug harness, stateless services)
11
+ * to satisfy the v3 API with a minimal stub — no full runtime needed.
12
+ * 3. Makes the v3 dependency surface auditable: any v3 code that reaches
13
+ * a method not on this interface is a compile error.
14
+ *
15
+ * The interface is defined as `Pick<DocumentRuntime, ...>` so the member
16
+ * types stay in lockstep with the authoritative runtime interface. If a
17
+ * runtime method signature changes, v3 breaks at the consumption site
18
+ * rather than silently drifting.
19
+ *
20
+ * Geometry note: `handle.geometry` is the `GeometryFacet` constructed by
21
+ * `document-runtime.ts` against the shared render kernel. v3's
22
+ * `runtime.geometry` family reads through `handle.geometry` — the prior
23
+ * "reach geometry through `handle.layout`" shim was removed in the
24
+ * refactor/05 adversarial-closure pass once the runtime exposed the facet
25
+ * directly.
26
+ */
27
+
28
+ import type { DocumentRuntime } from "../../runtime/document-runtime.ts";
29
+ import type { CollabSession } from "../../runtime/collab-session.ts";
30
+
31
+ /**
32
+ * @endStateApi
33
+ * Narrow runtime surface consumed by v3 family modules. Structurally a
34
+ * subset of `DocumentRuntime`; any `DocumentRuntime` satisfies it.
35
+ *
36
+ * Optional members (declared via intersection after the Pick) expose
37
+ * surfaces the runtime does not own directly but hosts can wire in —
38
+ * `collabSession` is the first such member, letting v3's
39
+ * `runtime.collab.*` family graduate its live path when hosts supply it
40
+ * and fall back to deterministic mocks when they don't.
41
+ */
42
+ export type RuntimeApiHandle = Pick<
43
+ DocumentRuntime,
44
+ // Session + export (runtime.document family)
45
+ | "getSessionState"
46
+ | "exportDocx"
47
+ | "getCompatibilityReport"
48
+ | "getWarnings"
49
+ | "getRenderSnapshot"
50
+ // Canonical document read (ai.inspect + ai.bundle + ai.resolve families,
51
+ // added in refactor/08 Slice 2/3 graduations)
52
+ | "getCanonicalDocument"
53
+ // Content search + selection (runtime.content + ai.bundle families)
54
+ | "findAllText"
55
+ // Review (runtime.review family)
56
+ | "getReviewWorkSnapshot"
57
+ | "acceptChange"
58
+ | "resolveComment"
59
+ // Workflow (runtime.workflow + ai.inspect families)
60
+ | "queryScopes"
61
+ | "getWorkflowMarkupSnapshot"
62
+ | "getInteractionGuardSnapshot"
63
+ | "getWorkflowOverlay"
64
+ // Workflow writers (runtime.workflow.createScope + attachMetadata
65
+ // graduations, Layer-06 Slice 3). The handle must expose the
66
+ // pipeline each adapter dispatches through, or v3 cannot be a real
67
+ // live seam. `getWorkflowMetadataSnapshot` is the read side the
68
+ // metadata writer inspects before merging its entry.
69
+ | "addScope"
70
+ | "setWorkflowMetadataEntries"
71
+ | "getWorkflowMetadataSnapshot"
72
+ // W10 overlay-visibility policy (state-classes X1). Class-A canonical
73
+ // state; L06 owns read + write, L10 composes with local preference.
74
+ | "getVisibilityPolicy"
75
+ | "getVisibilityPolicies"
76
+ | "setVisibilityPolicy"
77
+ | "clearVisibilityPolicy"
78
+ | "subscribeVisibilityPolicy"
79
+ // X5 class-A markup-mode policy (state-classes X5).
80
+ | "getMarkupModePolicy"
81
+ | "setMarkupModePolicy"
82
+ | "subscribeMarkupModePolicy"
83
+ // Scope lookup (runtime.content.getLocation — replaced the debug-facet
84
+ // escape-hatch path with a typed scope + anchor read)
85
+ | "getScope"
86
+ | "getLocationForAnchor"
87
+ // Layer-10 `ui.scope.*` enabler (coord-07 §2.7 / coord-10 §α). L10's
88
+ // layer-purity guard blocks `src/runtime/scopes/**` + `src/api/v3/ai/**`,
89
+ // so L10 cannot construct its own scope compiler or call ai.getScopeBundle.
90
+ // Exposing this scopeId-keyed compile primitive on the handle lets
91
+ // `ui/scope.ts` deliver `SemanticScope` / `ScopeBundle` reads directly.
92
+ | "compileScopeBundleById"
93
+ // Layer-08 Slice-5 — scope-scoped replacement dispatch. `ai.apply
94
+ // ReplacementScope` + `runtime.content.replaceText(scopeId, …)`
95
+ // route compiled plans through this seam.
96
+ | "applyScopeReplacement"
97
+ // Nested facets (runtime.layout + runtime.geometry families,
98
+ // plus debug telemetry bus for UxResponse emission)
99
+ | "debug"
100
+ | "layout"
101
+ | "geometry"
102
+ > & {
103
+ /**
104
+ * Optional collab session — hosts that wire Yjs/Awareness pass the
105
+ * session here so `api.runtime.collab.getPresence` / `.getPosture`
106
+ * can delegate live. When absent, those functions return
107
+ * deterministic mock snapshots (peers: [], posture: unattached).
108
+ */
109
+ readonly collabSession?: CollabSession;
110
+ };
111
+
112
+ /**
113
+ * @endStateApi
114
+ * Compile-time drift guard. This object's type is
115
+ * `Record<keyof RuntimeApiHandle, true>`, so adding a member to the
116
+ * interface without adding a key here — or removing a member without
117
+ * dropping the key — fails `pnpm run lint:tsgo` before it can land.
118
+ *
119
+ * `test/api/v3/runtime-handle-shape.test.ts` imports this constant and
120
+ * asserts at runtime that its key set equals the explicit audit roster
121
+ * maintained there. The two checks together catch both interface drift
122
+ * (this file, via tsgo) and audit-list drift (the test, via node:test).
123
+ */
124
+ export const RUNTIME_API_HANDLE_SHAPE_CHECK: Record<keyof RuntimeApiHandle, true> = {
125
+ getSessionState: true,
126
+ exportDocx: true,
127
+ getCompatibilityReport: true,
128
+ getWarnings: true,
129
+ getRenderSnapshot: true,
130
+ getCanonicalDocument: true,
131
+ findAllText: true,
132
+ getReviewWorkSnapshot: true,
133
+ acceptChange: true,
134
+ resolveComment: true,
135
+ queryScopes: true,
136
+ getWorkflowMarkupSnapshot: true,
137
+ getInteractionGuardSnapshot: true,
138
+ getWorkflowOverlay: true,
139
+ addScope: true,
140
+ setWorkflowMetadataEntries: true,
141
+ getWorkflowMetadataSnapshot: true,
142
+ getVisibilityPolicy: true,
143
+ getVisibilityPolicies: true,
144
+ setVisibilityPolicy: true,
145
+ clearVisibilityPolicy: true,
146
+ subscribeVisibilityPolicy: true,
147
+ getMarkupModePolicy: true,
148
+ setMarkupModePolicy: true,
149
+ subscribeMarkupModePolicy: true,
150
+ getScope: true,
151
+ getLocationForAnchor: true,
152
+ compileScopeBundleById: true,
153
+ applyScopeReplacement: true,
154
+ debug: true,
155
+ layout: true,
156
+ geometry: true,
157
+ // Optional members participate in the drift guard too — they're keys
158
+ // of the full RuntimeApiHandle type, so omitting them here would not
159
+ // fail `Record<keyof ...>`. Included explicitly so additions are
160
+ // visible in this single audit roster.
161
+ collabSession: true,
162
+ };
@@ -0,0 +1,73 @@
1
+ /**
2
+ * @endStateApi v3 — UX-response event contract.
3
+ *
4
+ * Every v3 function whose metadata declares `uxIntent.uiVisible === true`
5
+ * MUST call `emitUxResponse(runtime, res)` exactly once per invocation.
6
+ * The response is published on the existing `api` telemetry channel with
7
+ * `type: "ux.response.<apiFn>"`. Phase Q's debug UX subscribes to these to
8
+ * render mock-or-live visual traces identically.
9
+ *
10
+ * Unit tests assert the one-emit-per-call invariant. The
11
+ * `ci-check-api-v3-metadata` guard cross-checks that every function with
12
+ * `uiVisible: true` in its metadata has a corresponding `emitUxResponse`
13
+ * call in its implementation body.
14
+ */
15
+
16
+ import type { RuntimeApiHandle } from "./_runtime-handle.ts";
17
+ import type { ApiStatus, UxIntent } from "./_layer-metadata.ts";
18
+
19
+ /**
20
+ * @endStateApi
21
+ * UX event envelope. `intent`/`expectedDelta` come from the function's
22
+ * metadata and describe what the UI should render if the operation is
23
+ * live. `actualDelta` is populated only on the live path.
24
+ */
25
+ export interface UxResponse {
26
+ readonly apiFn: string;
27
+ readonly intent: string;
28
+ readonly mockOrLive: ApiStatus;
29
+ readonly uiVisible: boolean;
30
+ readonly expectedDelta?: string;
31
+ readonly actualDelta?: {
32
+ readonly kind: NonNullable<UxIntent["expectsUxResponse"]>;
33
+ readonly payload: unknown;
34
+ };
35
+ readonly auditHints?: readonly string[];
36
+ readonly ts: number;
37
+ }
38
+
39
+ /**
40
+ * @endStateApi
41
+ * Publish a UxResponse on the runtime's `api` telemetry channel. Safe to
42
+ * call when the channel is off — the bus bitmap gate short-circuits
43
+ * before any allocation. Returns nothing; fire-and-forget.
44
+ *
45
+ * Defensive posture: when the handle does not carry a telemetry bus
46
+ * (headless stubs, test doubles that exercise UI dispatch logic without
47
+ * wiring telemetry), this function silently no-ops instead of throwing.
48
+ * Production `DocumentRuntime` always supplies `runtime.debug.bus`;
49
+ * test handles may choose not to. A missing bus is NOT a contract
50
+ * violation — it's an opt-out of telemetry observation, not of the
51
+ * dispatch/subscribe behaviour itself.
52
+ */
53
+ export function emitUxResponse(
54
+ runtime: RuntimeApiHandle,
55
+ res: Omit<UxResponse, "ts">,
56
+ ): void {
57
+ const bus = runtime.debug?.bus;
58
+ if (!bus || typeof bus.emit !== "function") return;
59
+ const stamped: UxResponse = { ...res, ts: performanceNow() };
60
+ bus.emit({
61
+ channel: "api",
62
+ type: `ux.response.${res.apiFn}`,
63
+ t: 0,
64
+ payload: stamped as unknown as Record<string, unknown>,
65
+ });
66
+ }
67
+
68
+ function performanceNow(): number {
69
+ if (typeof performance !== "undefined" && typeof performance.now === "function") {
70
+ return performance.now();
71
+ }
72
+ return Date.now();
73
+ }
@@ -0,0 +1,225 @@
1
+ /**
2
+ * @endStateApi v3 — metadata-write audit emitter.
3
+ *
4
+ * Architecture 09 §A4: every mutating function emits exactly one
5
+ * `ScopeActionAudit` record. `applyReplacementScope` emits via the
6
+ * /08 scope-compiler's `emitScopeActionAudit` on the replacement path.
7
+ * `attachExplanation` and `createIssue` are mutating (scope-metadata
8
+ * writes) but do not route through the replacement lifecycle — they
9
+ * need their own audit emission that reuses the `ScopeActionAudit`
10
+ * shape with `operation: "annotate"`.
11
+ *
12
+ * This module is the single place that constructs those audit records
13
+ * for /09's metadata writes, so the shape + emission site stay honest
14
+ * and discoverable from one file.
15
+ *
16
+ * Design notes:
17
+ *
18
+ * 1. We reuse the `ScopeActionAudit` shape exactly — no new type.
19
+ * The `ReplacementScope.operation: "annotate"` variant already
20
+ * exists in the taxonomy for this case; metadata writes fill it
21
+ * with a synthesized `ReplacementScope` carrying structured
22
+ * payload describing the attached explanation / created issue.
23
+ *
24
+ * 2. The helper compiles the target scope fresh before emission —
25
+ * the snapshot captures the scope state AFTER the primitive
26
+ * returned, which is the post-mutation canonical truth.
27
+ *
28
+ * 3. `validation` is synthesized as a `safe: true, blockedReasons:[],
29
+ * warnings: []` record. Metadata writes currently bypass the
30
+ * unified action-validation contract (Layer 08 Slice 4 shape);
31
+ * when /06 ships the workflow-metadata policy-gating pass, this
32
+ * emitter upgrades to route through composeScopeValidation too.
33
+ *
34
+ * 4. `documentHash` is a local copy of the same block-count + text-
35
+ * length structural hash used by the replacement path so
36
+ * before/after signatures stay comparable across audit types.
37
+ * When /08 promotes `documentHash` to a public export the two
38
+ * copies collapse to one.
39
+ *
40
+ * 5. Errors in the emitter MUST NOT propagate. The primitive's
41
+ * write has already committed; the audit is a telemetry side-
42
+ * effect. Failure here is logged on the `commit` channel (per
43
+ * CLAUDE.md's "never break the edit path" discipline) and
44
+ * swallowed.
45
+ */
46
+
47
+ import type { RuntimeApiHandle } from "../_runtime-handle.ts";
48
+ import type { CanonicalDocumentEnvelope } from "../../../core/state/editor-state.ts";
49
+ import {
50
+ buildParagraphIndexMap,
51
+ compileScope,
52
+ enumerateScopes,
53
+ type ReplacementOperationKind,
54
+ type ReplacementScope,
55
+ type ScopeActionAudit,
56
+ type SemanticScope,
57
+ type ValidationResult,
58
+ } from "../../../runtime/scopes/index.ts";
59
+
60
+ export interface EmitMetadataAuditInputs {
61
+ readonly runtime: RuntimeApiHandle;
62
+ /**
63
+ * API function name in dotted form (e.g. `"ai.attachExplanation"`).
64
+ * Used as the audit's `actionId`.
65
+ */
66
+ readonly actionId: string;
67
+ /** `"ui" | "agent" | "host"` — default `"agent"` when omitted. */
68
+ readonly origin?: "ui" | "agent" | "host";
69
+ /** Actor id — default `"v3-ai-api"` when omitted. */
70
+ readonly actorId?: string;
71
+ /** ScopeId of the write target. */
72
+ readonly scopeId: string;
73
+ /**
74
+ * Pre-mutation scope snapshot. The caller captures this BEFORE running
75
+ * the primitive — post-mutation the scope's text / formatting may have
76
+ * shifted (common when a same-session replacement just ran against the
77
+ * same target) and a fresh compile can miss. When the caller cannot
78
+ * produce a snapshot (e.g. the scope did not enumerate before the
79
+ * write), the audit is skipped silently.
80
+ */
81
+ readonly targetScopeSnapshot: SemanticScope;
82
+ /**
83
+ * Structured payload describing what was attached. Surfaced in the
84
+ * audit's `proposed.proposedContent.structured` field.
85
+ */
86
+ readonly proposedContent: {
87
+ readonly kind: "explanation" | "issue";
88
+ readonly payload: Record<string, unknown>;
89
+ };
90
+ /**
91
+ * Single-step `compiledOperations` entry kind. Distinct from the
92
+ * replacement-path kinds (`text-replace`, etc.) so audit consumers
93
+ * can narrow by `step.kind`.
94
+ */
95
+ readonly compiledOperationKind: "metadata-attach-explanation" | "metadata-attach-issue";
96
+ /** Short description of the write — surfaced on `compiledOperations[0].summary`. */
97
+ readonly compiledOperationSummary: string;
98
+ /** Pin for determinism — callers pass a fixed value in tests. */
99
+ readonly emittedAtUtc: string;
100
+ /** Document hash snapshot BEFORE the primitive ran. Captured by the caller. */
101
+ readonly documentHashBefore: string;
102
+ }
103
+
104
+ function structuralDocumentHash(doc: CanonicalDocumentEnvelope): string {
105
+ // Same shape as /08's `documentHash` in replacement/apply.ts — local
106
+ // copy until /08 promotes that helper to a public export.
107
+ const root = doc.content;
108
+ let textLength = 0;
109
+ for (const block of root.children) {
110
+ if (block.type === "paragraph") {
111
+ for (const child of block.children) {
112
+ if (child.type === "text") textLength += child.text.length;
113
+ }
114
+ }
115
+ }
116
+ return `blocks:${root.children.length}|text:${textLength}`;
117
+ }
118
+
119
+ function compileScopeSnapshot(
120
+ runtime: RuntimeApiHandle,
121
+ scopeId: string,
122
+ ): SemanticScope | null {
123
+ const doc = runtime.getCanonicalDocument();
124
+ const overlay = runtime.getWorkflowOverlay();
125
+ const paragraphIndexByBlockIndex = buildParagraphIndexMap(doc);
126
+ for (const entry of enumerateScopes(doc, overlay ? { overlay } : {})) {
127
+ if (entry.handle.scopeId !== scopeId) continue;
128
+ const compiled = compileScope(entry, {
129
+ document: doc,
130
+ ...(overlay ? { overlay } : {}),
131
+ paragraphIndexByBlockIndex,
132
+ });
133
+ if (compiled) return compiled;
134
+ }
135
+ return null;
136
+ }
137
+
138
+ export function snapshotDocumentHash(runtime: RuntimeApiHandle): string {
139
+ return structuralDocumentHash(
140
+ runtime.getCanonicalDocument() as CanonicalDocumentEnvelope,
141
+ );
142
+ }
143
+
144
+ /**
145
+ * Build + emit one `ScopeActionAudit` on the `scope` telemetry channel
146
+ * for a metadata-write (attachExplanation / createIssue). Returns the
147
+ * emitted audit so callers can surface it on their result.
148
+ *
149
+ * Never throws — telemetry failures are swallowed.
150
+ */
151
+ /**
152
+ * Pre-mutation snapshot helper. Caller composes `captureScopeSnapshot`
153
+ * + primitive-write + `emitScopeMetadataAudit({targetScopeSnapshot})`
154
+ * so the snapshot reflects the state the write targeted, not the
155
+ * possibly-shifted post-mutation state.
156
+ */
157
+ export function captureScopeSnapshot(
158
+ runtime: RuntimeApiHandle,
159
+ scopeId: string,
160
+ ): SemanticScope | null {
161
+ return compileScopeSnapshot(runtime, scopeId);
162
+ }
163
+
164
+ export function emitScopeMetadataAudit(
165
+ inputs: EmitMetadataAuditInputs,
166
+ ): ScopeActionAudit | null {
167
+ try {
168
+ const snapshot = inputs.targetScopeSnapshot;
169
+ const operation: ReplacementOperationKind = "annotate";
170
+ const proposed: ReplacementScope = {
171
+ targetHandle: snapshot.handle,
172
+ operation,
173
+ proposedContent: {
174
+ kind: "structured",
175
+ structured: {
176
+ metadataKind: inputs.proposedContent.kind,
177
+ ...inputs.proposedContent.payload,
178
+ },
179
+ },
180
+ proposedAtUtc: inputs.emittedAtUtc,
181
+ };
182
+
183
+ const validation: ValidationResult = {
184
+ safe: true,
185
+ blockedReasons: [],
186
+ warnings: [],
187
+ };
188
+
189
+ // We build + emit the audit inline rather than going through
190
+ // `/08`'s `emitScopeActionAudit(plan)` helper because the helper
191
+ // reads `plan.steps[].kind` as a narrow `RuntimeOperationStepKind`
192
+ // union (`text-replace | … | fragment-replace`). Metadata writes
193
+ // need distinct op kinds (`metadata-attach-explanation`, etc.) that
194
+ // aren't in the replacement-path taxonomy and shouldn't be — they
195
+ // are not runtime operations in the replacement sense. The audit's
196
+ // downstream `compiledOperations[].kind` field is typed `string`,
197
+ // so emitting a metadata-specific value here is shape-compatible.
198
+ const audit: ScopeActionAudit = {
199
+ actionId: inputs.actionId,
200
+ actorId: inputs.actorId ?? "v3-ai-api",
201
+ origin: inputs.origin ?? "agent",
202
+ documentHashBefore: inputs.documentHashBefore,
203
+ documentHashAfter: snapshotDocumentHash(inputs.runtime),
204
+ targetScopeSnapshot: snapshot,
205
+ proposed,
206
+ compiledOperations: Object.freeze([
207
+ {
208
+ kind: inputs.compiledOperationKind,
209
+ summary: inputs.compiledOperationSummary,
210
+ },
211
+ ]),
212
+ validation,
213
+ emittedAtUtc: inputs.emittedAtUtc,
214
+ };
215
+
216
+ inputs.runtime.debug?.bus.emitLazy("scope", () => ({
217
+ type: "scope.action_audit",
218
+ payload: audit,
219
+ }));
220
+ return audit;
221
+ } catch {
222
+ // Audit is a side-effect; never break the edit path.
223
+ return null;
224
+ }
225
+ }