@pixel-point/toolcraft 0.0.18 → 0.0.19

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/package.json +1 -1
  2. package/src/generate-demand-only-lifecycle.test.mjs +9 -1
  3. package/src/generate-performance-iteration-execution.test.mjs +3 -3
  4. package/src/generate-test-delivery-architecture-assertions.mjs +11 -73
  5. package/src/generate-test-demand-only-lifecycle.mjs +34 -36
  6. package/src/generate-test-dependency-sandbox.mjs +4 -1
  7. package/src/generate-test-doc-assertions.mjs +55 -53
  8. package/src/generate-test-doc-assertions.test.mjs +1 -1
  9. package/src/generate-test-doc-policy-assertions.mjs +1 -1
  10. package/src/generate-test-export-doc-assertions.mjs +53 -0
  11. package/src/generate-test-file-assertions.mjs +51 -8
  12. package/src/generate-test-integrity-assertions.mjs +5 -2
  13. package/src/generate-test-integrity-manifest-assertions.mjs +16 -5
  14. package/src/generate-test-integrity-ownership-assertions.mjs +36 -4
  15. package/src/generate-test-package-assertions.mjs +4 -0
  16. package/src/generate-test-product-fixture.mjs +1 -1
  17. package/src/generate-test-product-fixture.test.mjs +35 -38
  18. package/src/generate-test-product-iteration.mjs +0 -1
  19. package/src/generate-test-product-iteration.test.mjs +0 -1
  20. package/src/generate-test-runtime-boundary-assertions.mjs +20 -0
  21. package/src/generate-test-standalone-assertions.mjs +0 -16
  22. package/src/generate-test-standalone-delivery-stages.mjs +2 -86
  23. package/src/generate.test.mjs +4 -0
  24. package/src/generated-integrity-manifest.test.mjs +5 -2
  25. package/src/generated-source-policy.test.mjs +0 -4
  26. package/src/package-json.mjs +5 -0
  27. package/src/package-json.test.mjs +21 -0
  28. package/src/package-manager.mjs +2 -0
  29. package/src/package-manager.test.mjs +8 -0
  30. package/src/packaged-template-fallback-helpers.mjs +254 -0
  31. package/src/packaged-template-fallback.test.mjs +121 -46
  32. package/src/packaged-template-runtime-assertions.mjs +150 -0
  33. package/templates/runtime/contracts/component-contracts.choices.test.ts +18 -3
  34. package/templates/runtime/contracts/component-contracts.choices.ts +8 -3
  35. package/templates/runtime/contracts/component-contracts.inputs.ts +2 -1
  36. package/templates/runtime/contracts/component-contracts.runtime-acceptance.test.ts +18 -4
  37. package/templates/runtime/contracts/component-contracts.runtime-shell.test.ts +1 -1
  38. package/templates/runtime/contracts/component-contracts.runtime.ts +7 -4
  39. package/templates/runtime/contracts/component-contracts.visual.test.ts +20 -0
  40. package/templates/runtime/contracts/component-contracts.visual.ts +4 -0
  41. package/templates/runtime/contracts/decision-contracts.test.ts +18 -12
  42. package/templates/runtime/contracts/decision-contracts.ts +4 -4
  43. package/templates/runtime/contracts/performance-verification-policy.test.ts +12 -12
  44. package/templates/runtime/contracts/performance-verification-policy.ts +8 -8
  45. package/templates/runtime/export/artifact-download.ts +1 -0
  46. package/templates/runtime/export/artifact-frame-renderer.ts +2 -15
  47. package/templates/runtime/export/artifact-frame-state.ts +16 -0
  48. package/templates/runtime/export/artifact-scene-frame.test.ts +3 -3
  49. package/templates/runtime/export/artifact-scene-frame.ts +3 -3
  50. package/templates/runtime/export/export-error.ts +5 -0
  51. package/templates/runtime/export/export-renderer-coverage.ts +54 -0
  52. package/templates/runtime/export/image-artifact-export.ts +2 -2
  53. package/templates/runtime/export/index.ts +13 -0
  54. package/templates/runtime/export/product-export-renderer.test.ts +38 -4
  55. package/templates/runtime/export/product-export-renderer.ts +0 -41
  56. package/templates/runtime/export/product-svg-export-renderer.ts +21 -0
  57. package/templates/runtime/export/svg-artifact-export.test.ts +98 -0
  58. package/templates/runtime/export/svg-artifact-export.ts +120 -0
  59. package/templates/runtime/export/svg-document.test.ts +221 -0
  60. package/templates/runtime/export/svg-document.ts +400 -0
  61. package/templates/runtime/export/svg-policy.ts +47 -0
  62. package/templates/runtime/index.ts +1 -0
  63. package/templates/runtime/react/app-shell/runtime-public-api.test.tsx +10 -0
  64. package/templates/runtime/react/app-shell/settings-transfer.test.ts +21 -0
  65. package/templates/runtime/react/app-shell/toolcraft-app.scene-export.test.tsx +46 -1
  66. package/templates/runtime/react/app-shell/toolcraft-app.tsx +7 -2
  67. package/templates/runtime/react/canvas/canvas-shell-interactions.test.tsx +30 -0
  68. package/templates/runtime/react/canvas/canvas-shell-pinch-interactions.test.tsx +155 -0
  69. package/templates/runtime/react/canvas/canvas-shell-rendering.test.tsx +1 -1
  70. package/templates/runtime/react/canvas/canvas-shell.tsx +16 -5
  71. package/templates/runtime/react/canvas/canvas-viewport-geometry.ts +133 -0
  72. package/templates/runtime/react/canvas/use-canvas-viewport-interactions.ts +285 -36
  73. package/templates/runtime/react/controls-panel/__tests__/controls-panel.color-applicability-layout.test.tsx +134 -0
  74. package/templates/runtime/react/controls-panel/__tests__/controls-panel.color-layout.test.tsx +16 -1
  75. package/templates/runtime/react/controls-panel/__tests__/controls-panel.render-isolation.test.tsx +2 -0
  76. package/templates/runtime/react/controls-panel/__tests__/controls-panel.scene-export-actions.test.tsx +39 -0
  77. package/templates/runtime/react/controls-panel/__tests__/controls-panel.setup-controls.test.tsx +3 -3
  78. package/templates/runtime/react/controls-panel/__tests__/controls-panel.source-collection.test.tsx +1 -4
  79. package/templates/runtime/react/controls-panel/__tests__/controls-panel.source-color-bank.test.tsx +2 -2
  80. package/templates/runtime/react/controls-panel/__tests__/controls-panel.vector-interaction.test.tsx +6 -6
  81. package/templates/runtime/react/controls-panel/actions/controls-panel-actions.ts +2 -2
  82. package/templates/runtime/react/controls-panel/actions/export-action-runner.ts +40 -11
  83. package/templates/runtime/react/controls-panel/layout/controls-panel-layout.tsx +31 -2
  84. package/templates/runtime/react/controls-panel/layout/controls-panel-section.tsx +4 -1
  85. package/templates/runtime/react/controls-panel/renderers/controls-panel-action-renderer.tsx +2 -1
  86. package/templates/runtime/react/controls-panel/renderers/controls-panel-basic-renderers.tsx +2 -52
  87. package/templates/runtime/react/controls-panel/values/controls-panel-color-values.ts +1 -9
  88. package/templates/runtime/react/controls-panel/values/controls-panel-spatial-values.ts +7 -2
  89. package/templates/runtime/react/orientation-gizmo/orientation-gizmo-math.ts +9 -69
  90. package/templates/runtime/schema/artifact-export-actions.test.ts +47 -0
  91. package/templates/runtime/schema/artifact-export-actions.ts +49 -0
  92. package/templates/runtime/schema/types.ts +1 -0
  93. package/templates/runtime/state/canvas-background-state.ts +1 -16
  94. package/templates/runtime/state/canvas-state.ts +32 -20
  95. package/templates/runtime/state/control-value-codecs.test.ts +98 -0
  96. package/templates/runtime/state/control-value-codecs.ts +700 -0
  97. package/templates/runtime/state/control-value-normalization.test.ts +1 -1
  98. package/templates/runtime/state/control-value-normalization.ts +127 -135
  99. package/templates/runtime/state/controls-reducer.ts +26 -3
  100. package/templates/runtime/state/create-template-state.test.ts +30 -0
  101. package/templates/runtime/state/create-template-state.ts +52 -31
  102. package/templates/runtime/state/orientation-pose.ts +80 -0
  103. package/templates/runtime/state/persistence-control-values.test.ts +59 -0
  104. package/templates/runtime/state/reducer-controls.test.ts +45 -0
  105. package/templates/runtime/state/reducer-media-reset.test.ts +1 -1
  106. package/templates/runtime/state/reducer-timeline.test.ts +70 -0
  107. package/templates/runtime/state/timeline-reducer.ts +40 -2
  108. package/templates/runtime/testing/performance-schema-queries.ts +2 -2
  109. package/templates/starter/AGENTS.md +21 -22
  110. package/templates/starter/docs/toolcraft/README.md +6 -6
  111. package/templates/starter/docs/toolcraft/acceptance-testing.md +20 -20
  112. package/templates/starter/docs/toolcraft/agent-worklog.md +88 -1
  113. package/templates/starter/docs/toolcraft/assembly-workflow.md +7 -6
  114. package/templates/starter/docs/toolcraft/component-rules.md +2 -2
  115. package/templates/starter/docs/toolcraft/core/layout.md +6 -1
  116. package/templates/starter/docs/toolcraft/core/media-upload.md +1 -1
  117. package/templates/starter/docs/toolcraft/core/performance.md +8 -10
  118. package/templates/starter/docs/toolcraft/core/reference-study.md +80 -19
  119. package/templates/starter/docs/toolcraft/core/runtime-boundary.md +2 -1
  120. package/templates/starter/docs/toolcraft/core/setup-export.md +43 -13
  121. package/templates/starter/docs/toolcraft/decision-contract.md +2 -2
  122. package/templates/starter/docs/toolcraft/performance.md +8 -10
  123. package/templates/starter/docs/toolcraft/renderer-technique.md +2 -2
  124. package/templates/starter/docs/toolcraft/schema-reference.md +8 -8
  125. package/templates/starter/docs/toolcraft/workflow.md +14 -14
  126. package/templates/starter/e2e/app-browser-infinity-canvas-evidence.spec.ts +38 -0
  127. package/templates/starter/e2e/app-browser-motion-reference-requirements.spec.ts +411 -0
  128. package/templates/starter/e2e/app-browser-proof-session.spec.ts +85 -0
  129. package/templates/starter/e2e/app-browser-runtime-requirements.spec.ts +62 -0
  130. package/templates/starter/e2e/app-browser-selection-scope-evidence.spec.ts +137 -0
  131. package/templates/starter/e2e/browser-infinity-canvas-svg-evidence.ts +42 -0
  132. package/templates/starter/e2e/browser-layer-evidence-helpers.ts +15 -36
  133. package/templates/starter/e2e/browser-product-raster-snapshot.ts +213 -0
  134. package/templates/starter/e2e/browser-proof-session.ts +43 -15
  135. package/templates/starter/e2e/browser-proof-surface-action.ts +79 -0
  136. package/templates/starter/e2e/browser-proof-types.ts +15 -0
  137. package/templates/starter/e2e/browser-runtime-evidence-requirements.ts +22 -6
  138. package/templates/starter/e2e/browser-selection-scope-evidence.ts +269 -0
  139. package/templates/starter/e2e/browser-semantic-evidence-test-helpers.ts +97 -0
  140. package/templates/starter/e2e/browser-svg-export-evidence.spec.ts +79 -0
  141. package/templates/starter/e2e/browser-svg-export-evidence.ts +152 -0
  142. package/templates/starter/e2e/export-artifact-helpers.ts +70 -0
  143. package/templates/starter/e2e/product-observable-helpers.ts +8 -129
  144. package/templates/starter/e2e/svg-artifact-inspection.spec.ts +136 -0
  145. package/templates/starter/e2e/svg-artifact-inspection.ts +331 -0
  146. package/templates/starter/e2e/svg-download-test-fixtures.ts +34 -0
  147. package/templates/starter/e2e/toolcraft-delivery-catalog-reporter.ts +198 -15
  148. package/templates/starter/e2e/toolcraft-feature-verification-reporter.spec.ts +149 -0
  149. package/templates/starter/e2e/toolcraft-feature-verification-reporter.ts +223 -0
  150. package/templates/starter/package.json +2 -0
  151. package/templates/starter/scripts/check-toolcraft-docs.mjs +1 -0
  152. package/templates/starter/scripts/create-toolcraft-motion-reference-study.mjs +233 -0
  153. package/templates/starter/scripts/create-toolcraft-motion-reference-study.test.mjs +459 -0
  154. package/templates/starter/scripts/playwright-test-title-selection.d.mts +45 -0
  155. package/templates/starter/scripts/run-browser-performance-test-helpers.mjs +0 -27
  156. package/templates/starter/scripts/run-browser-performance.mjs +0 -6
  157. package/templates/starter/scripts/run-browser-performance.test.mjs +5 -13
  158. package/templates/starter/scripts/run-delivery-verification-test-helpers.mjs +0 -34
  159. package/templates/starter/scripts/run-delivery-verification.test.mjs +135 -31
  160. package/templates/starter/scripts/run-feature-verification.mjs +212 -0
  161. package/templates/starter/scripts/run-feature-verification.test.mjs +226 -0
  162. package/templates/starter/scripts/toolcraft-checkpoint-delivery-state.mjs +21 -8
  163. package/templates/starter/scripts/toolcraft-checkpoint-delivery-state.test.mjs +32 -92
  164. package/templates/starter/scripts/toolcraft-checkpoint-transaction.mjs +28 -6
  165. package/templates/starter/scripts/toolcraft-checkpoint-transaction.test.mjs +8 -63
  166. package/templates/starter/scripts/toolcraft-delivery-anchor.mjs +28 -8
  167. package/templates/starter/scripts/toolcraft-delivery-anchor.test.mjs +50 -61
  168. package/templates/starter/scripts/toolcraft-delivery-architecture-inventory.mjs +0 -4
  169. package/templates/starter/scripts/toolcraft-delivery-architecture-policy.mjs +0 -1
  170. package/templates/starter/scripts/toolcraft-delivery-evidence.test.mjs +2 -3
  171. package/templates/starter/scripts/toolcraft-delivery-executor.mjs +5 -1
  172. package/templates/starter/scripts/toolcraft-delivery-executor.test.mjs +5 -12
  173. package/templates/starter/scripts/toolcraft-delivery-functional-context.d.mts +1 -48
  174. package/templates/starter/scripts/toolcraft-delivery-functional-context.mjs +2 -30
  175. package/templates/starter/scripts/toolcraft-delivery-functional-context.test.mjs +4 -166
  176. package/templates/starter/scripts/toolcraft-delivery-functional-initial.test.mjs +4 -20
  177. package/templates/starter/scripts/toolcraft-delivery-lifecycle.mjs +43 -192
  178. package/templates/starter/scripts/toolcraft-delivery-performance-iteration-lifecycle.test.mjs +79 -440
  179. package/templates/starter/scripts/toolcraft-delivery-plan-authority.test.mjs +102 -297
  180. package/templates/starter/scripts/toolcraft-delivery-plan-construction.mjs +46 -97
  181. package/templates/starter/scripts/toolcraft-delivery-plan-test-helpers.mjs +125 -0
  182. package/templates/starter/scripts/toolcraft-delivery-plan-validation.test.mjs +58 -0
  183. package/templates/starter/scripts/toolcraft-delivery-plan.d.mts +19 -31
  184. package/templates/starter/scripts/toolcraft-delivery-plan.mjs +254 -252
  185. package/templates/starter/scripts/toolcraft-delivery-plan.test.mjs +29 -457
  186. package/templates/starter/scripts/toolcraft-delivery-receipt-performance.test.mjs +1 -1
  187. package/templates/starter/scripts/toolcraft-delivery-receipt-strictness.test.mjs +3 -72
  188. package/templates/starter/scripts/toolcraft-delivery-receipt-test-helpers.mjs +7 -36
  189. package/templates/starter/scripts/toolcraft-delivery-receipt.d.mts +3 -5
  190. package/templates/starter/scripts/toolcraft-delivery-receipt.mjs +15 -31
  191. package/templates/starter/scripts/toolcraft-delivery-receipt.test.mjs +28 -20
  192. package/templates/starter/scripts/toolcraft-delivery-receipt.types.test.mts +22 -30
  193. package/templates/starter/scripts/toolcraft-feature-verification-plan.d.mts +20 -0
  194. package/templates/starter/scripts/toolcraft-feature-verification-plan.mjs +146 -0
  195. package/templates/starter/scripts/toolcraft-feature-verification-plan.test.mjs +96 -0
  196. package/templates/starter/scripts/toolcraft-functional-proof-model.d.mts +3 -18
  197. package/templates/starter/scripts/toolcraft-functional-proof-model.mjs +39 -127
  198. package/templates/starter/scripts/toolcraft-functional-proof-model.test.mjs +25 -362
  199. package/templates/starter/scripts/toolcraft-functional-proof-model.types.test.mts +6 -83
  200. package/templates/starter/scripts/toolcraft-functional-proof-primitives.d.mts +11 -0
  201. package/templates/starter/scripts/toolcraft-functional-proof-primitives.mjs +61 -0
  202. package/templates/starter/scripts/toolcraft-functional-proof-primitives.test.mjs +43 -0
  203. package/templates/starter/scripts/toolcraft-integrity-policy.mjs +13 -0
  204. package/templates/starter/scripts/toolcraft-integrity-test-utils.mjs +1 -1
  205. package/templates/starter/scripts/toolcraft-layer-evidence-facade.test.mjs +58 -22
  206. package/templates/starter/scripts/toolcraft-motion-reference-analysis-protocol.mjs +111 -0
  207. package/templates/starter/scripts/toolcraft-motion-reference-analysis-protocol.test.mjs +116 -0
  208. package/templates/starter/scripts/toolcraft-motion-reference-artifacts.d.mts +37 -0
  209. package/templates/starter/scripts/toolcraft-motion-reference-artifacts.mjs +308 -0
  210. package/templates/starter/scripts/toolcraft-motion-reference-artifacts.test.mjs +420 -0
  211. package/templates/starter/scripts/toolcraft-motion-reference-artifacts.types.test.mts +50 -0
  212. package/templates/starter/scripts/toolcraft-motion-reference-command-options.mjs +195 -0
  213. package/templates/starter/scripts/toolcraft-motion-reference-command-options.test.mjs +224 -0
  214. package/templates/starter/scripts/toolcraft-motion-reference-contact-sheet.mjs +122 -0
  215. package/templates/starter/scripts/toolcraft-motion-reference-contact-sheet.test.mjs +153 -0
  216. package/templates/starter/scripts/toolcraft-motion-reference-evidence-assembly.mjs +270 -0
  217. package/templates/starter/scripts/toolcraft-motion-reference-evidence.test.mjs +432 -0
  218. package/templates/starter/scripts/toolcraft-motion-reference-ffmpeg-output.mjs +275 -0
  219. package/templates/starter/scripts/toolcraft-motion-reference-ffmpeg-output.test.mjs +294 -0
  220. package/templates/starter/scripts/toolcraft-motion-reference-group-inventory.mjs +148 -0
  221. package/templates/starter/scripts/toolcraft-motion-reference-image-filter-graph.mjs +101 -0
  222. package/templates/starter/scripts/toolcraft-motion-reference-image-filter-graph.test.mjs +124 -0
  223. package/templates/starter/scripts/toolcraft-motion-reference-image-source.mjs +259 -0
  224. package/templates/starter/scripts/toolcraft-motion-reference-image-source.test.mjs +338 -0
  225. package/templates/starter/scripts/toolcraft-motion-reference-image-timing.mjs +96 -0
  226. package/templates/starter/scripts/toolcraft-motion-reference-image-timing.test.mjs +81 -0
  227. package/templates/starter/scripts/toolcraft-motion-reference-lock-recovery.test.mjs +144 -0
  228. package/templates/starter/scripts/toolcraft-motion-reference-media-source.mjs +165 -0
  229. package/templates/starter/scripts/toolcraft-motion-reference-media-source.test.mjs +299 -0
  230. package/templates/starter/scripts/toolcraft-motion-reference-publication-discovery.test.mjs +142 -0
  231. package/templates/starter/scripts/toolcraft-motion-reference-publication-isolation.test.mjs +234 -0
  232. package/templates/starter/scripts/toolcraft-motion-reference-publication-journal-schema.mjs +122 -0
  233. package/templates/starter/scripts/toolcraft-motion-reference-publication-journal-store.mjs +89 -0
  234. package/templates/starter/scripts/toolcraft-motion-reference-publication-journal-store.test.mjs +74 -0
  235. package/templates/starter/scripts/toolcraft-motion-reference-publication-journal.mjs +264 -0
  236. package/templates/starter/scripts/toolcraft-motion-reference-publication-journal.test.mjs +377 -0
  237. package/templates/starter/scripts/toolcraft-motion-reference-publication-lease.mjs +254 -0
  238. package/templates/starter/scripts/toolcraft-motion-reference-publication-lease.test.mjs +130 -0
  239. package/templates/starter/scripts/toolcraft-motion-reference-publication-staging.mjs +128 -0
  240. package/templates/starter/scripts/toolcraft-motion-reference-publication-staging.test.mjs +209 -0
  241. package/templates/starter/scripts/toolcraft-motion-reference-publication.fixtures.mjs +88 -0
  242. package/templates/starter/scripts/toolcraft-motion-reference-publication.mjs +249 -0
  243. package/templates/starter/scripts/toolcraft-motion-reference-publication.test.mjs +433 -0
  244. package/templates/starter/scripts/toolcraft-motion-reference-review-output.mjs +38 -0
  245. package/templates/starter/scripts/toolcraft-motion-reference-review-output.test.mjs +29 -0
  246. package/templates/starter/scripts/toolcraft-motion-reference-root-safety.test.mjs +84 -0
  247. package/templates/starter/scripts/toolcraft-motion-reference-roots.mjs +83 -0
  248. package/templates/starter/scripts/toolcraft-motion-reference-sampling.mjs +283 -0
  249. package/templates/starter/scripts/toolcraft-motion-reference-sampling.test.mjs +480 -0
  250. package/templates/starter/scripts/toolcraft-motion-reference-stage-registry.mjs +39 -0
  251. package/templates/starter/scripts/toolcraft-motion-reference-stage-registry.test.mjs +59 -0
  252. package/templates/starter/scripts/toolcraft-motion-reference-stage.mjs +71 -0
  253. package/templates/starter/scripts/toolcraft-motion-reference-stage.test.mjs +61 -0
  254. package/templates/starter/scripts/toolcraft-motion-review-numeric.mjs +74 -0
  255. package/templates/starter/scripts/toolcraft-motion-review-numeric.test.mjs +65 -0
  256. package/templates/starter/scripts/toolcraft-motion-review-partitioning.mjs +268 -0
  257. package/templates/starter/scripts/toolcraft-motion-review-partitioning.test.mjs +329 -0
  258. package/templates/starter/scripts/toolcraft-motion-review-protected-units.mjs +120 -0
  259. package/templates/starter/scripts/toolcraft-motion-review-protected-units.test.mjs +111 -0
  260. package/templates/starter/scripts/toolcraft-performance-escalation-policy.test.mjs +77 -345
  261. package/templates/starter/scripts/toolcraft-performance-receipt-validation.test.mjs +19 -21
  262. package/templates/starter/scripts/toolcraft-product-boundary-ast.mjs +1 -1
  263. package/templates/starter/scripts/toolcraft-product-export-boundary.mjs +10 -1
  264. package/templates/starter/scripts/toolcraft-product-export-boundary.test.mjs +5 -1
  265. package/templates/starter/scripts/toolcraft-product-observable-facade.test.mjs +46 -1
  266. package/templates/starter/scripts/toolcraft-receipt-boundary.test.mjs +0 -1
  267. package/templates/starter/scripts/toolcraft-source-inventory.d.mts +5 -0
  268. package/templates/starter/scripts/toolcraft-source-inventory.mjs +25 -14
  269. package/templates/starter/scripts/toolcraft-source-inventory.test.mjs +48 -0
  270. package/templates/starter/scripts/toolcraft-source-ownership.mjs +0 -1
  271. package/templates/starter/scripts/toolcraft-source-ownership.test.mjs +30 -5
  272. package/templates/starter/scripts/toolcraft-verification-receipt-test-helpers.mjs +5 -26
  273. package/templates/starter/scripts/toolcraft-verification-receipt.d.mts +0 -7
  274. package/templates/starter/scripts/toolcraft-verification-receipt.mjs +3 -47
  275. package/templates/starter/scripts/toolcraft-verification-receipt.test.mjs +6 -87
  276. package/templates/starter/scripts/toolcraft-vitest-runtime-contract.d.mts +19 -0
  277. package/templates/starter/scripts/toolcraft-vitest-runtime-contract.mjs +107 -0
  278. package/templates/starter/scripts/toolcraft-vitest-runtime-contract.test.mjs +114 -0
  279. package/templates/starter/scripts/toolcraft-vitest-runtime-evidence-reporter.mjs +13 -2
  280. package/templates/starter/scripts/toolcraft-vitest-runtime-evidence-reporter.test.mjs +110 -0
  281. package/templates/starter/src/app/acceptance/artifact-export-intent.ts +51 -20
  282. package/templates/starter/src/app/acceptance/canvas-coverage.ts +1 -0
  283. package/templates/starter/src/app/acceptance/color-bank-labels.ts +11 -15
  284. package/templates/starter/src/app/acceptance/color-row-grouping.ts +33 -0
  285. package/templates/starter/src/app/acceptance/control-acceptance-coverage.ts +0 -11
  286. package/templates/starter/src/app/acceptance/control-acceptance-policy.ts +2 -3
  287. package/templates/starter/src/app/acceptance/control-component-rules.ts +0 -10
  288. package/templates/starter/src/app/acceptance/control-labels.ts +30 -1
  289. package/templates/starter/src/app/acceptance/control-layout-section-rules.ts +18 -0
  290. package/templates/starter/src/app/acceptance/export-artifact-coverage.ts +25 -17
  291. package/templates/starter/src/app/acceptance/feature-verification-selection.test.ts +295 -0
  292. package/templates/starter/src/app/acceptance/feature-verification-selection.ts +161 -0
  293. package/templates/starter/src/app/acceptance/infinity-canvas.ts +10 -0
  294. package/templates/starter/src/app/acceptance/interaction-ownership-intent.ts +19 -1
  295. package/templates/starter/src/app/acceptance/interaction-ownership.ts +18 -2
  296. package/templates/starter/src/app/acceptance/motion-reference-evidence-validation.mjs +65 -0
  297. package/templates/starter/src/app/acceptance/motion-reference-evidence-windows.mjs +301 -0
  298. package/templates/starter/src/app/acceptance/motion-reference-evidence.d.mts +11 -0
  299. package/templates/starter/src/app/acceptance/motion-reference-evidence.mjs +145 -0
  300. package/templates/starter/src/app/acceptance/motion-reference-study-behaviors.ts +162 -0
  301. package/templates/starter/src/app/acceptance/motion-reference-study-coverage.ts +206 -0
  302. package/templates/starter/src/app/acceptance/motion-reference-study-identity.ts +129 -0
  303. package/templates/starter/src/app/acceptance/motion-reference-study-partitions.ts +186 -0
  304. package/templates/starter/src/app/acceptance/motion-reference-study.ts +90 -0
  305. package/templates/starter/src/app/acceptance/output-export-actions.test.ts +9 -1
  306. package/templates/starter/src/app/acceptance/output-export-actions.ts +14 -22
  307. package/templates/starter/src/app/acceptance/output-export-layout-rules.ts +5 -1
  308. package/templates/starter/src/app/acceptance/output-export-model.ts +4 -0
  309. package/templates/starter/src/app/acceptance/output-export.ts +4 -1
  310. package/templates/starter/src/app/acceptance/reference-study-types.ts +167 -17
  311. package/templates/starter/src/app/acceptance/runtime-coverage.ts +6 -1
  312. package/templates/starter/src/app/acceptance/selection-scope.test.ts +200 -0
  313. package/templates/starter/src/app/acceptance/selection-scope.ts +151 -0
  314. package/templates/starter/src/app/acceptance/transfer-mode-types.ts +114 -0
  315. package/templates/starter/src/app/acceptance/types.ts +43 -118
  316. package/templates/starter/src/app/acceptance/validate-coverage.ts +14 -3
  317. package/templates/starter/src/app/starter-acceptance-data.ts +1 -0
  318. package/templates/starter/src/app/starter-acceptance.animation-intent.test.ts +3 -0
  319. package/templates/starter/src/app/starter-acceptance.artifact-export-intent.test.ts +81 -8
  320. package/templates/starter/src/app/starter-acceptance.background-export.test.ts +2 -0
  321. package/templates/starter/src/app/starter-acceptance.canvas-sizing.test.ts +2 -0
  322. package/templates/starter/src/app/starter-acceptance.color-row-grouping.test.ts +118 -0
  323. package/templates/starter/src/app/starter-acceptance.compound-part-coverage.test.ts +1 -1
  324. package/templates/starter/src/app/starter-acceptance.contract-fixtures.ts +1 -0
  325. package/templates/starter/src/app/starter-acceptance.control-applicability.test.ts +1 -0
  326. package/templates/starter/src/app/starter-acceptance.control-label-context.test.ts +1 -0
  327. package/templates/starter/src/app/starter-acceptance.control-labels.test.ts +78 -3
  328. package/templates/starter/src/app/starter-acceptance.control-naming-rules.test.ts +77 -1
  329. package/templates/starter/src/app/starter-acceptance.custom-control-fit-check.test.ts +1 -1
  330. package/templates/starter/src/app/starter-acceptance.export-artifact.test.ts +74 -3
  331. package/templates/starter/src/app/starter-acceptance.export-test-utils.ts +5 -1
  332. package/templates/starter/src/app/starter-acceptance.image-export-settings.test.ts +2 -0
  333. package/templates/starter/src/app/starter-acceptance.interaction-ownership.test.ts +19 -3
  334. package/templates/starter/src/app/starter-acceptance.layers.test.ts +2 -1
  335. package/templates/starter/src/app/starter-acceptance.media-upload.test.ts +28 -0
  336. package/templates/starter/src/app/starter-acceptance.motion-reference-evidence.test.ts +17 -0
  337. package/templates/starter/src/app/starter-acceptance.motion-reference-identity.test.ts +163 -0
  338. package/templates/starter/src/app/starter-acceptance.motion-reference-mapping.test.ts +133 -0
  339. package/templates/starter/src/app/starter-acceptance.motion-reference-ownership.test.ts +121 -0
  340. package/templates/starter/src/app/starter-acceptance.motion-reference-partitions.test.ts +210 -0
  341. package/templates/starter/src/app/starter-acceptance.motion-reference-test-utils.ts +313 -0
  342. package/templates/starter/src/app/starter-acceptance.output-export-intent.test.ts +78 -4
  343. package/templates/starter/src/app/starter-acceptance.product-output-export.test.ts +6 -1
  344. package/templates/starter/src/app/starter-acceptance.product-readiness.test.ts +18 -1
  345. package/templates/starter/src/app/starter-acceptance.product-worklog.test.ts +123 -2
  346. package/templates/starter/src/app/starter-acceptance.reference-custom-timeline.test.ts +2 -0
  347. package/templates/starter/src/app/starter-acceptance.reference-foundation.test.ts +8 -0
  348. package/templates/starter/src/app/starter-acceptance.reference-timeline.test.ts +7 -0
  349. package/templates/starter/src/app/starter-acceptance.section-cohesion.test.ts +2 -0
  350. package/templates/starter/src/app/starter-acceptance.timeline-playback.test.ts +5 -0
  351. package/templates/starter/src/app/starter-acceptance.ts +34 -8
  352. package/templates/starter/src/app/starter-acceptance.video-export-settings.test.ts +2 -0
  353. package/templates/starter/src/app/starter-acceptance.video-reference-study.test.ts +34 -236
  354. package/templates/starter/src/app/starter-acceptance.view-interaction.test.ts +4 -0
  355. package/templates/starter/src/app/starter-acceptance.worklog-test-utils.ts +122 -6
  356. package/templates/starter/src/app/starter-acceptance.worklog.test.ts +152 -16
  357. package/templates/starter/src/app/starter-performance.gates.test.ts +20 -18
  358. package/templates/starter/src/app/starter-performance.lifecycle.test.ts +22 -6
  359. package/templates/starter/src/app/test-evidence/browser-runtime-contract.ts +3 -0
  360. package/templates/starter/tsconfig.json +1 -0
  361. package/templates/starter/scripts/toolcraft-browser-impact-selection.mjs +0 -26
  362. package/templates/starter/scripts/toolcraft-delivery-framework-refresh.test.mjs +0 -141
  363. package/templates/starter/scripts/toolcraft-delivery-functional-changed.test.mjs +0 -468
  364. package/templates/starter/scripts/toolcraft-delivery-functional-model-lifecycle.test.mjs +0 -254
  365. package/templates/starter/scripts/toolcraft-delivery-functional-semantic-regression.test.mjs +0 -135
  366. package/templates/starter/scripts/toolcraft-delivery-performance-iteration-durability.test.mjs +0 -329
  367. package/templates/starter/scripts/toolcraft-delivery-plan-errors.test.mjs +0 -430
  368. package/templates/starter/scripts/toolcraft-delivery-plan-functional-model.test.mjs +0 -358
  369. package/templates/starter/scripts/toolcraft-functional-proof-regression-fixtures.mjs +0 -298
  370. package/templates/starter/scripts/toolcraft-verification-impact-inventory.d.mts +0 -62
  371. package/templates/starter/scripts/toolcraft-verification-impact-inventory.mjs +0 -344
  372. package/templates/starter/scripts/toolcraft-verification-impact-inventory.test.mjs +0 -204
  373. package/templates/starter/scripts/toolcraft-verification-impact-performance-candidates.test.mjs +0 -210
  374. package/templates/starter/scripts/toolcraft-verification-impact-resolution.mjs +0 -344
  375. package/templates/starter/scripts/toolcraft-verification-impact-resolution.test.mjs +0 -270
  376. package/templates/starter/scripts/toolcraft-verification-impact-semantic-resolution.test.mjs +0 -449
  377. package/templates/starter/scripts/toolcraft-verification-impact-support-resolution.test.mjs +0 -195
  378. package/templates/starter/scripts/toolcraft-verification-impact.d.mts +0 -132
  379. package/templates/starter/scripts/toolcraft-verification-impact.mjs +0 -169
  380. package/templates/starter/scripts/toolcraft-verification-impact.test.mjs +0 -300
  381. package/templates/starter/scripts/toolcraft-verification-input-roles.mjs +0 -197
  382. package/templates/starter/scripts/toolcraft-verification-input-roles.test.mjs +0 -225
  383. package/templates/starter/src/app/acceptance/video-reference-study.ts +0 -217
  384. package/templates/starter/src/app/starter-verification-impact.json +0 -15
@@ -14,6 +14,7 @@ import {
14
14
  type ToolcraftComponentAcceptance,
15
15
  type ToolcraftControlSectionInventoryEntry,
16
16
  type ToolcraftInfinityCanvasCoverage,
17
+ type ToolcraftExportArtifactCoverage,
17
18
  type ToolcraftModelImportCoverage,
18
19
  type ToolcraftOrientationGizmoCoverage,
19
20
  type ToolcraftTimelinePlaybackCoverage,
@@ -35,10 +36,12 @@ type BrowserAcceptanceRequirementSource = Pick<
35
36
  | "infinityCanvasCoverage"
36
37
  | "layerCoverage"
37
38
  | "modelImportCoverage"
39
+ | "motionReferenceCoverage"
38
40
  | "orientationGizmoCoverage"
39
41
  | "referenceCoverage"
40
42
  | "referenceTimelineCoverage"
41
43
  | "renderScaleCoverage"
44
+ | "selectionScopeCoverage"
42
45
  | "target"
43
46
  | "timelineCoverage"
44
47
  | "timelinePlaybackCoverage"
@@ -112,6 +115,15 @@ const backgroundOutputCoverage = Object.keys(
112
115
  backgroundOutputEvidenceTypeByCoverage,
113
116
  ) as ToolcraftBackgroundOutputCoverage[];
114
117
 
118
+ const exportArtifactEvidenceTypeByCoverage = {
119
+ "all-required-image-export-behavior": "image-export-artifact",
120
+ "all-required-svg-export-behavior": "svg-export-artifact",
121
+ "all-required-video-export-behavior": "video-export-artifact",
122
+ } as const satisfies Record<
123
+ ToolcraftExportArtifactCoverage,
124
+ ToolcraftBrowserRuntimeRequirement["evidenceType"]
125
+ >;
126
+
115
127
  const orientationEvidenceTypeByCoverage = {
116
128
  "axis-drag": "orientation-axis-drag",
117
129
  "axis-snap": "orientation-axis-snap",
@@ -132,6 +144,7 @@ const orientationGizmoCoverage = Object.keys(
132
144
  const infinityCanvasEvidenceTypeByCoverage = {
133
145
  "mode-and-restoration": "infinity-mode-restoration",
134
146
  "scene-bounds-image-export": "infinity-scene-bounds-image-export",
147
+ "scene-bounds-svg-export": "infinity-scene-bounds-svg-export",
135
148
  "scene-bounds-video-export": "infinity-scene-bounds-video-export",
136
149
  } as const satisfies Record<
137
150
  ToolcraftInfinityCanvasCoverage,
@@ -197,11 +210,7 @@ function deriveUnqualifiedToolcraftBrowserRuntimeRequirements(
197
210
  evidenceTypes.push(baseEvidenceType);
198
211
  }
199
212
  for (const coverage of exportArtifactCoverage) {
200
- evidenceTypes.push(
201
- coverage === "all-required-image-export-behavior"
202
- ? "image-export-artifact"
203
- : "video-export-artifact",
204
- );
213
+ evidenceTypes.push(exportArtifactEvidenceTypeByCoverage[coverage]);
205
214
  }
206
215
 
207
216
  if (entry.canvasHandle && !hasOrientationCoverage) {
@@ -222,12 +231,19 @@ function deriveUnqualifiedToolcraftBrowserRuntimeRequirements(
222
231
  if (entry.layerCoverage) {
223
232
  evidenceTypes.push(layerEvidenceTypeByCoverage[entry.layerCoverage]);
224
233
  }
234
+ if (entry.selectionScopeCoverage === "two-entity-isolation") {
235
+ evidenceTypes.push("selection-scoped-control");
236
+ }
225
237
  if (entry.infinityCanvasCoverage) {
226
238
  evidenceTypes.push(
227
239
  infinityCanvasEvidenceTypeByCoverage[entry.infinityCanvasCoverage],
228
240
  );
229
241
  }
230
- if (entry.referenceCoverage || entry.referenceTimelineCoverage) {
242
+ if (
243
+ entry.referenceCoverage ||
244
+ entry.referenceTimelineCoverage ||
245
+ (entry.motionReferenceCoverage?.length ?? 0) > 0
246
+ ) {
231
247
  evidenceTypes.push("reference-parity");
232
248
  }
233
249
  const backgroundCoverage =
@@ -0,0 +1,269 @@
1
+ import { expect } from "@playwright/test";
2
+
3
+ import { attachToolcraftBrowserRuntimeEvidence } from "./browser-runtime-evidence";
4
+ import {
5
+ resolveToolcraftProductRasterProbe,
6
+ type ToolcraftProductRasterProbe,
7
+ type ToolcraftResolvedProductRasterProbe,
8
+ } from "./browser-product-raster-snapshot";
9
+ import {
10
+ assertToolcraftBrowserActionForSession,
11
+ assertToolcraftBrowserProofSession,
12
+ getToolcraftBrowserActionSurface,
13
+ getToolcraftBrowserActionTarget,
14
+ getToolcraftBrowserProofPage,
15
+ readToolcraftBrowserObservation,
16
+ runToolcraftBrowserAction,
17
+ type ToolcraftBrowserAction,
18
+ type ToolcraftBrowserActionSurface,
19
+ type ToolcraftBrowserObservation,
20
+ type ToolcraftBrowserProofSession,
21
+ } from "./browser-proof-session";
22
+ import {
23
+ expectToolcraftPersistentOutcomeChange,
24
+ expectToolcraftStableOutcomeBaseline,
25
+ } from "./stable-outcome-helpers";
26
+
27
+ export type ToolcraftSelectionEntityProbe =
28
+ ToolcraftProductRasterProbe & Readonly<{ entityId: string }>;
29
+
30
+ export type ToolcraftSelectionScopedControlOptions = Readonly<{
31
+ editEntityA: ToolcraftBrowserAction;
32
+ editEntityB: ToolcraftBrowserAction;
33
+ entityA: ToolcraftSelectionEntityProbe;
34
+ entityB: ToolcraftSelectionEntityProbe;
35
+ observePropertyValue: ToolcraftBrowserObservation<unknown>;
36
+ observeSelectedEntityId: ToolcraftBrowserObservation<string>;
37
+ propertySurface: ToolcraftBrowserActionSurface;
38
+ propertyTarget: string;
39
+ proofSession: ToolcraftBrowserProofSession;
40
+ requirementId: string;
41
+ selectionSurface: ToolcraftBrowserActionSurface;
42
+ selectionTarget: string;
43
+ selectEntityA: ToolcraftBrowserAction;
44
+ selectEntityB: ToolcraftBrowserAction;
45
+ stabilityIntervalMs?: number;
46
+ stabilitySamples?: number;
47
+ timeoutMs?: number;
48
+ }>;
49
+
50
+ type SelectionSnapshot = Readonly<{
51
+ controlValue: unknown;
52
+ entityA: string;
53
+ entityB: string;
54
+ selectedEntityId: string;
55
+ }>;
56
+
57
+ function regionsOverlap(
58
+ first: ToolcraftResolvedProductRasterProbe["bounds"],
59
+ second: ToolcraftResolvedProductRasterProbe["bounds"],
60
+ ): boolean {
61
+ return (
62
+ first.x < second.x + second.width &&
63
+ first.x + first.width > second.x &&
64
+ first.y < second.y + second.height &&
65
+ first.y + first.height > second.y
66
+ );
67
+ }
68
+
69
+ function requireActionTarget(
70
+ action: ToolcraftBrowserAction,
71
+ expectedTarget: string,
72
+ label: string,
73
+ ): void {
74
+ expect(
75
+ getToolcraftBrowserActionTarget(action),
76
+ `${label} must use the exact protected semantic target.`,
77
+ ).toBe(expectedTarget);
78
+ }
79
+
80
+ async function requireProductSceneProbe(
81
+ probe: ToolcraftResolvedProductRasterProbe,
82
+ entityId: string,
83
+ ): Promise<void> {
84
+ const provenance = await probe.locator.evaluate((element) => ({
85
+ insideProductScene: Boolean(
86
+ element.closest(
87
+ "[data-toolcraft-product-scene], [data-toolcraft-canvas-content], [data-toolcraft-product-output]",
88
+ ),
89
+ ),
90
+ isEditorHandle: Boolean(element.closest("[data-toolcraft-canvas-handle]")),
91
+ }));
92
+ expect(
93
+ provenance.insideProductScene,
94
+ `Selection entity "${entityId}" must probe product-scene pixels, not selection chrome.`,
95
+ ).toBe(true);
96
+ expect(
97
+ provenance.isEditorHandle,
98
+ `Selection entity "${entityId}" must not probe a canvas handle or gizmo.`,
99
+ ).toBe(false);
100
+ }
101
+
102
+ async function waitForSelectedEntity(
103
+ observation: ToolcraftBrowserObservation<string>,
104
+ entityId: string,
105
+ timeoutMs: number,
106
+ ): Promise<void> {
107
+ await expect
108
+ .poll(() => readToolcraftBrowserObservation(observation), {
109
+ message: `Selection must rebind the property control to "${entityId}".`,
110
+ timeout: timeoutMs,
111
+ })
112
+ .toBe(entityId);
113
+ }
114
+
115
+ export async function expectToolcraftSelectionScopedControl(
116
+ options: ToolcraftSelectionScopedControlOptions,
117
+ ): Promise<void> {
118
+ const {
119
+ editEntityA,
120
+ editEntityB,
121
+ entityA,
122
+ entityB,
123
+ observePropertyValue,
124
+ observeSelectedEntityId,
125
+ proofSession,
126
+ selectEntityA,
127
+ selectEntityB,
128
+ } = options;
129
+ const timeoutMs = options.timeoutMs ?? 5_000;
130
+ const stability = {
131
+ message: `Selection-scoped control "${options.requirementId}" must remain deterministic.`,
132
+ stabilityIntervalMs: options.stabilityIntervalMs ?? 50,
133
+ stabilitySamples: options.stabilitySamples ?? 3,
134
+ timeoutMs,
135
+ };
136
+
137
+ expect(entityA.entityId.trim()).not.toBe("");
138
+ expect(entityB.entityId.trim()).not.toBe("");
139
+ expect(entityA.entityId).not.toBe(entityB.entityId);
140
+ expect(options.propertyTarget.trim()).not.toBe("");
141
+ expect(options.selectionTarget.trim()).not.toBe("");
142
+
143
+ assertToolcraftBrowserProofSession(
144
+ observeSelectedEntityId,
145
+ selectEntityA,
146
+ selectEntityB,
147
+ editEntityA,
148
+ editEntityB,
149
+ );
150
+ assertToolcraftBrowserProofSession(
151
+ observePropertyValue,
152
+ selectEntityA,
153
+ selectEntityB,
154
+ editEntityA,
155
+ editEntityB,
156
+ );
157
+ for (const action of [
158
+ selectEntityA,
159
+ selectEntityB,
160
+ editEntityA,
161
+ editEntityB,
162
+ ]) {
163
+ assertToolcraftBrowserActionForSession(proofSession, action);
164
+ }
165
+
166
+ requireActionTarget(
167
+ selectEntityA,
168
+ options.selectionTarget,
169
+ "Entity A selection",
170
+ );
171
+ requireActionTarget(
172
+ selectEntityB,
173
+ options.selectionTarget,
174
+ "Entity B selection",
175
+ );
176
+ requireActionTarget(editEntityA, options.propertyTarget, "Entity A edit");
177
+ requireActionTarget(editEntityB, options.propertyTarget, "Entity B edit");
178
+ expect(getToolcraftBrowserActionSurface(selectEntityA)).toBe(
179
+ options.selectionSurface,
180
+ );
181
+ expect(getToolcraftBrowserActionSurface(selectEntityB)).toBe(
182
+ options.selectionSurface,
183
+ );
184
+ expect(getToolcraftBrowserActionSurface(editEntityA)).toBe(
185
+ options.propertySurface,
186
+ );
187
+ expect(getToolcraftBrowserActionSurface(editEntityB)).toBe(
188
+ options.propertySurface,
189
+ );
190
+
191
+ const page = await getToolcraftBrowserProofPage(proofSession);
192
+ const [probeA, probeB] = await Promise.all([
193
+ resolveToolcraftProductRasterProbe(page, entityA, timeoutMs),
194
+ resolveToolcraftProductRasterProbe(page, entityB, timeoutMs),
195
+ ]);
196
+ await Promise.all([
197
+ requireProductSceneProbe(probeA, entityA.entityId),
198
+ requireProductSceneProbe(probeB, entityB.entityId),
199
+ ]);
200
+ expect(
201
+ regionsOverlap(probeA.bounds, probeB.bounds),
202
+ "Selection entity probes must have disjoint positive-area bounds.",
203
+ ).toBe(false);
204
+
205
+ const capture = async (): Promise<SelectionSnapshot> => ({
206
+ controlValue: await readToolcraftBrowserObservation(observePropertyValue),
207
+ entityA: await probeA.capture(),
208
+ entityB: await probeB.capture(),
209
+ selectedEntityId: await readToolcraftBrowserObservation(
210
+ observeSelectedEntityId,
211
+ ),
212
+ });
213
+ const captureStable = async (): Promise<SelectionSnapshot> => {
214
+ const snapshot = await capture();
215
+ return expectToolcraftStableOutcomeBaseline(capture, snapshot, stability);
216
+ };
217
+
218
+ await runToolcraftBrowserAction(selectEntityB);
219
+ await waitForSelectedEntity(observeSelectedEntityId, entityB.entityId, timeoutMs);
220
+ const baselineB = await captureStable();
221
+
222
+ await runToolcraftBrowserAction(selectEntityA);
223
+ await waitForSelectedEntity(observeSelectedEntityId, entityA.entityId, timeoutMs);
224
+ const beforeA = await captureStable();
225
+ expect(beforeA.controlValue).not.toEqual(baselineB.controlValue);
226
+ expect(beforeA.entityA).toEqual(baselineB.entityA);
227
+ expect(beforeA.entityB).toEqual(baselineB.entityB);
228
+
229
+ await runToolcraftBrowserAction(editEntityA);
230
+ const afterA = await expectToolcraftPersistentOutcomeChange(
231
+ capture,
232
+ beforeA,
233
+ stability,
234
+ );
235
+ expect(afterA.selectedEntityId).toBe(entityA.entityId);
236
+ expect(afterA.controlValue).not.toEqual(beforeA.controlValue);
237
+ expect(afterA.entityA).not.toEqual(beforeA.entityA);
238
+ expect(afterA.entityB).toEqual(beforeA.entityB);
239
+
240
+ const beforeSelectB = await captureStable();
241
+ await runToolcraftBrowserAction(selectEntityB);
242
+ await waitForSelectedEntity(observeSelectedEntityId, entityB.entityId, timeoutMs);
243
+ const beforeB = await captureStable();
244
+ expect(beforeB.entityA).toEqual(beforeSelectB.entityA);
245
+ expect(beforeB.entityB).toEqual(beforeSelectB.entityB);
246
+ expect(beforeB.controlValue).toEqual(baselineB.controlValue);
247
+
248
+ await runToolcraftBrowserAction(editEntityB);
249
+ const afterB = await expectToolcraftPersistentOutcomeChange(
250
+ capture,
251
+ beforeB,
252
+ stability,
253
+ );
254
+ expect(afterB.selectedEntityId).toBe(entityB.entityId);
255
+ expect(afterB.controlValue).not.toEqual(beforeB.controlValue);
256
+ expect(afterB.entityB).not.toEqual(beforeB.entityB);
257
+ expect(afterB.entityA).toEqual(beforeB.entityA);
258
+
259
+ await attachToolcraftBrowserRuntimeEvidence({
260
+ evidenceType: "product-observable-change",
261
+ requirementId: options.requirementId,
262
+ target: options.propertyTarget,
263
+ });
264
+ await attachToolcraftBrowserRuntimeEvidence({
265
+ evidenceType: "selection-scoped-control",
266
+ requirementId: options.requirementId,
267
+ target: options.propertyTarget,
268
+ });
269
+ }
@@ -4,6 +4,12 @@ import { parseToolcraftBrowserRuntimeEvidence } from "../src/app/test-evidence/b
4
4
 
5
5
  const rootSelector = '[data-slot="toolcraft-runtime-app"]';
6
6
 
7
+ export type SyntheticSelectionSceneState = Readonly<{
8
+ controlValue: string;
9
+ fills: Readonly<Record<string, string>>;
10
+ selectedEntityId: string;
11
+ }>;
12
+
7
13
  export function attachedEvidenceTypes(
8
14
  testInfo: TestInfo,
9
15
  startIndex: number,
@@ -80,3 +86,94 @@ export async function setSyntheticSwitchControlOwner(
80
86
  { checked, target },
81
87
  );
82
88
  }
89
+
90
+ export async function mountSyntheticSelectionScene(
91
+ page: Page,
92
+ initialState: SyntheticSelectionSceneState,
93
+ ): Promise<void> {
94
+ await setSyntheticControlOwner(page, "selectedShape.fill", true);
95
+ await page.locator(rootSelector).evaluate((root, state) => {
96
+ const canvas = root.querySelector<HTMLElement>(
97
+ '[data-slot="toolcraft-runtime-canvas"]',
98
+ );
99
+ if (!canvas) {
100
+ throw new Error("Missing runtime canvas content for selection fixture.");
101
+ }
102
+
103
+ root.setAttribute("data-proof-selection-scope", JSON.stringify(state));
104
+ root.querySelector('[data-proof-selection-scene]')?.remove();
105
+ const scene = document.createElement("div");
106
+ scene.dataset.proofSelectionScene = "true";
107
+ scene.dataset.toolcraftProductOutput = "true";
108
+ Object.assign(scene.style, {
109
+ display: "flex",
110
+ gap: "32px",
111
+ padding: "16px",
112
+ pointerEvents: "auto",
113
+ position: "absolute",
114
+ inset: "16px auto auto 16px",
115
+ zIndex: "50",
116
+ });
117
+
118
+ for (const [entityId, fill] of Object.entries(state.fills)) {
119
+ const entity = document.createElement("button");
120
+ entity.dataset.proofEntity = entityId;
121
+ entity.type = "button";
122
+ entity.setAttribute("aria-label", `Select ${entityId}`);
123
+ Object.assign(entity.style, {
124
+ background: fill,
125
+ border: "0",
126
+ display: "block",
127
+ height: "48px",
128
+ padding: "0",
129
+ width: "48px",
130
+ });
131
+ entity.addEventListener("click", () => {
132
+ const current = JSON.parse(
133
+ root.getAttribute("data-proof-selection-scope") ?? "null",
134
+ ) as SyntheticSelectionSceneState;
135
+ root.setAttribute(
136
+ "data-proof-selection-scope",
137
+ JSON.stringify({
138
+ ...current,
139
+ controlValue: current.fills[entityId],
140
+ selectedEntityId: entityId,
141
+ }),
142
+ );
143
+ });
144
+ entity.addEventListener("pointerdown", (event) => {
145
+ event.stopPropagation();
146
+ });
147
+ scene.append(entity);
148
+ }
149
+
150
+ canvas.append(scene);
151
+ }, initialState);
152
+ }
153
+
154
+ export async function editSyntheticSelectedEntity(
155
+ page: Page,
156
+ fill: string,
157
+ ): Promise<void> {
158
+ await page.locator(rootSelector).evaluate((root, nextFill) => {
159
+ const current = JSON.parse(
160
+ root.getAttribute("data-proof-selection-scope") ?? "null",
161
+ ) as SyntheticSelectionSceneState;
162
+ const entity = root.querySelector<HTMLElement>(
163
+ `[data-proof-entity="${CSS.escape(current.selectedEntityId)}"]`,
164
+ );
165
+ if (!entity) {
166
+ throw new Error(`Missing selected entity ${current.selectedEntityId}.`);
167
+ }
168
+ const nextState = {
169
+ ...current,
170
+ controlValue: nextFill,
171
+ fills: { ...current.fills, [current.selectedEntityId]: nextFill },
172
+ };
173
+ entity.style.background = nextFill;
174
+ root.setAttribute(
175
+ "data-proof-selection-scope",
176
+ JSON.stringify(nextState),
177
+ );
178
+ }, fill);
179
+ }
@@ -0,0 +1,79 @@
1
+ import { expect, test } from "@playwright/test";
2
+
3
+ import { expectToolcraftSvgExportArtifact } from "./browser-svg-export-evidence";
4
+ import { createToolcraftSvgFixtureDownload } from "./svg-download-test-fixtures";
5
+
6
+ const validSvg = `<?xml version="1.0" encoding="UTF-8"?>
7
+ <svg xmlns="http://www.w3.org/2000/svg" width="64" height="48" viewBox="0 0 64 48"><g data-toolcraft-product-scene="true"><rect data-column-track="true" x="8" y="8" width="16" height="32" fill="#52AAFF"/></g></svg>`;
8
+
9
+ test("protected SVG evidence attaches only after exact product semantics pass", async ({
10
+ page,
11
+ }) => {
12
+ await page.goto("/");
13
+
14
+ const inspected = await expectToolcraftSvgExportArtifact(
15
+ async () => createToolcraftSvgFixtureDownload(page, validSvg),
16
+ {
17
+ expectedElements: [
18
+ {
19
+ attributes: {
20
+ fill: "#52AAFF",
21
+ height: "32",
22
+ width: "16",
23
+ },
24
+ selector: 'rect[data-column-track="true"]',
25
+ },
26
+ ],
27
+ expectedBackgroundColor: null,
28
+ expectedHeight: 48,
29
+ expectedWidth: 64,
30
+ page,
31
+ requirementId: "fixture.svg",
32
+ },
33
+ );
34
+
35
+ expect(inspected.inspection.contentHash).not.toHaveLength(0);
36
+ expect(
37
+ test.info().attachments.some(
38
+ ({ name }) => name === "toolcraft.browser-runtime-evidence",
39
+ ),
40
+ ).toBe(true);
41
+ });
42
+
43
+ test("protected SVG evidence rejects empty or mismatched product expectations", async ({
44
+ page,
45
+ }) => {
46
+ await page.goto("/");
47
+ await expect(
48
+ expectToolcraftSvgExportArtifact(
49
+ async () => createToolcraftSvgFixtureDownload(page, validSvg),
50
+ {
51
+ expectedElements: [],
52
+ expectedBackgroundColor: null,
53
+ expectedHeight: 48,
54
+ expectedWidth: 64,
55
+ page,
56
+ requirementId: "fixture.svg.empty",
57
+ },
58
+ ),
59
+ ).rejects.toThrow("requires product vector expectations");
60
+
61
+ await expect(
62
+ expectToolcraftSvgExportArtifact(
63
+ async () => createToolcraftSvgFixtureDownload(page, validSvg),
64
+ {
65
+ expectedElements: [
66
+ {
67
+ attributes: { fill: "#FF0000" },
68
+ selector: 'rect[data-column-track="true"]',
69
+ },
70
+ ],
71
+ expectedBackgroundColor: null,
72
+ expectedHeight: 48,
73
+ expectedWidth: 64,
74
+ page,
75
+ requirementId: "fixture.svg.color",
76
+ },
77
+ ),
78
+ ).rejects.toThrow("needs fill=#FF0000");
79
+ });
@@ -0,0 +1,152 @@
1
+ import { expect, type Download, type Page } from "@playwright/test";
2
+ import {
3
+ TOOLCRAFT_SVG_BACKGROUND_ATTRIBUTE,
4
+ TOOLCRAFT_SVG_NON_RENDERING_CONTAINERS,
5
+ TOOLCRAFT_SVG_PRODUCT_SCENE_ATTRIBUTE,
6
+ TOOLCRAFT_SVG_VECTOR_PRIMITIVES,
7
+ } from "@repo/toolcraft-runtime";
8
+
9
+ import { attachToolcraftBrowserRuntimeEvidence } from "./browser-runtime-evidence";
10
+ import {
11
+ getToolcraftBrowserActionTarget,
12
+ isToolcraftBrowserAction,
13
+ runToolcraftBrowserValueAction,
14
+ type ToolcraftBrowserAction,
15
+ } from "./browser-proof-session";
16
+ import {
17
+ inspectToolcraftSvgDownload,
18
+ type ToolcraftInspectedSvgArtifact,
19
+ } from "./svg-artifact-inspection";
20
+
21
+ export type ToolcraftSvgElementExpectation = Readonly<{
22
+ attributes?: Readonly<Record<string, string>>;
23
+ selector: string;
24
+ textContent?: string;
25
+ }>;
26
+
27
+ async function assertProductExpectations(
28
+ page: Page,
29
+ source: string,
30
+ expectations: readonly ToolcraftSvgElementExpectation[],
31
+ ): Promise<void> {
32
+ if (expectations.length === 0) {
33
+ throw new Error("SVG export evidence requires product vector expectations.");
34
+ }
35
+ await page.evaluate(
36
+ ({
37
+ backgroundAttribute,
38
+ expectedElements,
39
+ nonRenderingNames,
40
+ productSceneAttribute,
41
+ svgSource,
42
+ vectorPrimitiveNames,
43
+ }) => {
44
+ const parsed = new DOMParser().parseFromString(
45
+ svgSource,
46
+ "image/svg+xml",
47
+ );
48
+ const vectorNames = new Set(vectorPrimitiveNames);
49
+ const nonRendering = new Set(nonRenderingNames);
50
+ const productScene = parsed.querySelector(
51
+ `[${productSceneAttribute}="true"]`,
52
+ );
53
+ if (!productScene) {
54
+ throw new Error("SVG product expectations need a product scene.");
55
+ }
56
+ for (const expected of expectedElements) {
57
+ if (
58
+ !expected.selector.trim() ||
59
+ (Object.keys(expected.attributes ?? {}).length === 0 &&
60
+ expected.textContent === undefined)
61
+ ) {
62
+ throw new Error(
63
+ "Each SVG product expectation needs a selector and exact attribute or text semantics.",
64
+ );
65
+ }
66
+ const element = productScene.querySelector(expected.selector);
67
+ if (!element || !vectorNames.has(element.localName.toLowerCase())) {
68
+ throw new Error(
69
+ `SVG product expectation did not match a vector primitive: ${expected.selector}`,
70
+ );
71
+ }
72
+ if (element.hasAttribute(backgroundAttribute)) {
73
+ throw new Error("SVG product expectations cannot target runtime background.");
74
+ }
75
+ let ancestor = element.parentElement;
76
+ while (ancestor && !ancestor.isSameNode(productScene)) {
77
+ if (nonRendering.has(ancestor.localName.toLowerCase())) {
78
+ throw new Error(
79
+ `SVG product expectation targets non-rendering content: ${expected.selector}`,
80
+ );
81
+ }
82
+ ancestor = ancestor.parentElement;
83
+ }
84
+ for (const [name, value] of Object.entries(expected.attributes ?? {})) {
85
+ if (element.getAttribute(name) !== value) {
86
+ throw new Error(
87
+ `SVG product expectation ${expected.selector} needs ${name}=${value}.`,
88
+ );
89
+ }
90
+ }
91
+ if (
92
+ expected.textContent !== undefined &&
93
+ element.textContent !== expected.textContent
94
+ ) {
95
+ throw new Error(
96
+ `SVG product expectation ${expected.selector} has unexpected text.`,
97
+ );
98
+ }
99
+ }
100
+ },
101
+ {
102
+ backgroundAttribute: TOOLCRAFT_SVG_BACKGROUND_ATTRIBUTE,
103
+ expectedElements: expectations,
104
+ nonRenderingNames: TOOLCRAFT_SVG_NON_RENDERING_CONTAINERS,
105
+ productSceneAttribute: TOOLCRAFT_SVG_PRODUCT_SCENE_ATTRIBUTE,
106
+ svgSource: source,
107
+ vectorPrimitiveNames: TOOLCRAFT_SVG_VECTOR_PRIMITIVES,
108
+ },
109
+ );
110
+ }
111
+
112
+ export async function expectToolcraftSvgExportArtifact(
113
+ produceDownload:
114
+ | (() => Promise<Download>)
115
+ | ToolcraftBrowserAction<"interaction", Download>,
116
+ options: Readonly<{
117
+ expectedElements: readonly ToolcraftSvgElementExpectation[];
118
+ expectedBackgroundColor: string | null;
119
+ expectedHeight: number;
120
+ expectedWidth: number;
121
+ page: Page;
122
+ requirementId: string;
123
+ }>,
124
+ ): Promise<ToolcraftInspectedSvgArtifact> {
125
+ const target = isToolcraftBrowserAction(produceDownload)
126
+ ? getToolcraftBrowserActionTarget(produceDownload)
127
+ : undefined;
128
+ const download = isToolcraftBrowserAction(produceDownload)
129
+ ? await runToolcraftBrowserValueAction(produceDownload)
130
+ : await produceDownload();
131
+ const inspected = await inspectToolcraftSvgDownload({
132
+ download,
133
+ page: options.page,
134
+ });
135
+ expect(inspected.inspection.width).toBe(options.expectedWidth);
136
+ expect(inspected.inspection.height).toBe(options.expectedHeight);
137
+ expect(inspected.inspection.backgroundColor).toBe(
138
+ options.expectedBackgroundColor,
139
+ );
140
+ expect(inspected.inspection.contentHash).not.toHaveLength(0);
141
+ await assertProductExpectations(
142
+ options.page,
143
+ inspected.source,
144
+ options.expectedElements,
145
+ );
146
+ await attachToolcraftBrowserRuntimeEvidence({
147
+ evidenceType: "svg-export-artifact",
148
+ requirementId: options.requirementId,
149
+ target,
150
+ });
151
+ return inspected;
152
+ }