@pixel-point/toolcraft 0.0.16 → 0.0.18

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 (313) hide show
  1. package/package.json +1 -1
  2. package/src/generate-performance-iteration-execution.test.mjs +2 -2
  3. package/src/generate-test-demand-only-lifecycle.mjs +14 -3
  4. package/src/generate-test-dependency-sandbox.mjs +42 -5
  5. package/src/generate-test-doc-assertions.mjs +109 -69
  6. package/src/generate-test-doc-assertions.test.mjs +65 -0
  7. package/src/generate-test-doc-policy-assertions.mjs +92 -0
  8. package/src/generate-test-file-assertions.mjs +36 -118
  9. package/src/generate-test-output-assertions.mjs +95 -0
  10. package/src/generate-test-package-assertions.mjs +6 -0
  11. package/src/generate-test-performance-boundary-assertions.mjs +45 -0
  12. package/src/generate-test-product-fixture.mjs +1 -0
  13. package/src/generate-test-product-fixture.test.mjs +18 -0
  14. package/src/generate-test-product-iteration.mjs +1 -0
  15. package/src/generate-test-product-iteration.test.mjs +18 -0
  16. package/src/generate-test-runtime-boundary-assertions.mjs +158 -0
  17. package/src/generate-test-standalone-assertions.mjs +10 -0
  18. package/src/generate-test-standalone-delivery-stages.mjs +14 -6
  19. package/src/generate.test.mjs +14 -0
  20. package/templates/runtime/contracts/component-contracts.choices.test.ts +59 -12
  21. package/templates/runtime/contracts/component-contracts.choices.ts +58 -12
  22. package/templates/runtime/contracts/component-contracts.inputs.test.ts +6 -6
  23. package/templates/runtime/contracts/component-contracts.inputs.ts +6 -6
  24. package/templates/runtime/contracts/component-contracts.media-custom.test.ts +8 -2
  25. package/templates/runtime/contracts/component-contracts.media-custom.ts +8 -2
  26. package/templates/runtime/contracts/component-contracts.runtime-acceptance.test.ts +21 -6
  27. package/templates/runtime/contracts/component-contracts.runtime-labels.test.ts +7 -1
  28. package/templates/runtime/contracts/component-contracts.runtime-panels.test.ts +4 -1
  29. package/templates/runtime/contracts/component-contracts.runtime-shell.test.ts +4 -1
  30. package/templates/runtime/contracts/component-contracts.runtime.ts +15 -10
  31. package/templates/runtime/contracts/component-contracts.test.ts +2 -0
  32. package/templates/runtime/contracts/decision-contracts.test.ts +43 -4
  33. package/templates/runtime/contracts/decision-contracts.ts +10 -10
  34. package/templates/runtime/contracts/performance-verification-policy.test.ts +59 -6
  35. package/templates/runtime/contracts/performance-verification-policy.ts +5 -2
  36. package/templates/runtime/export/artifact-download.test.ts +71 -0
  37. package/templates/runtime/export/artifact-download.ts +80 -0
  38. package/templates/runtime/export/artifact-export-request.ts +23 -0
  39. package/templates/runtime/export/artifact-export-settings.test.ts +75 -0
  40. package/templates/runtime/export/artifact-export-settings.ts +96 -0
  41. package/templates/runtime/export/artifact-frame-renderer.test.ts +107 -0
  42. package/templates/runtime/export/artifact-frame-renderer.ts +96 -0
  43. package/templates/runtime/export/artifact-frame-state.test.ts +54 -0
  44. package/templates/runtime/export/artifact-frame-state.ts +44 -0
  45. package/templates/runtime/export/artifact-scene-frame.test.ts +84 -0
  46. package/templates/runtime/export/artifact-scene-frame.ts +118 -0
  47. package/templates/runtime/export/export-background.ts +46 -0
  48. package/templates/runtime/export/export-error.ts +43 -0
  49. package/templates/runtime/export/export.test.ts +49 -226
  50. package/templates/runtime/export/image-artifact-export.test.ts +111 -0
  51. package/templates/runtime/export/image-artifact-export.ts +145 -0
  52. package/templates/runtime/export/index.ts +44 -3
  53. package/templates/runtime/export/product-export-renderer.test.ts +77 -0
  54. package/templates/runtime/export/product-export-renderer.ts +63 -0
  55. package/templates/runtime/export/video-artifact-export.test.ts +166 -0
  56. package/templates/runtime/export/video-artifact-export.ts +164 -0
  57. package/templates/runtime/export/video-encoding-backend.ts +124 -0
  58. package/templates/runtime/export/video-encoding-policy.test.ts +68 -0
  59. package/templates/runtime/export/video-encoding-policy.ts +86 -0
  60. package/templates/runtime/export/video-frame-schedule.test.ts +58 -0
  61. package/templates/runtime/export/video-frame-schedule.ts +42 -0
  62. package/templates/runtime/index.ts +3 -0
  63. package/templates/runtime/model-import/model-import-limits.ts +15 -4
  64. package/templates/runtime/react/app-shell/runtime-public-api-runtime.test.tsx +291 -0
  65. package/templates/runtime/react/app-shell/runtime-public-api-test-support.ts +21 -0
  66. package/templates/runtime/react/app-shell/runtime-public-api.test.tsx +15 -304
  67. package/templates/runtime/react/app-shell/toolcraft-app.scene-export.test.tsx +41 -27
  68. package/templates/runtime/react/app-shell/toolcraft-app.tsx +39 -11
  69. package/templates/runtime/react/app-shell/toolcraft-source-asset-context.test.tsx +16 -57
  70. package/templates/runtime/react/app-shell/toolcraft-source-asset-context.tsx +75 -434
  71. package/templates/runtime/react/app-shell/toolcraft-source-asset-owner.test.ts +194 -0
  72. package/templates/runtime/react/app-shell/toolcraft-source-asset-owner.ts +227 -0
  73. package/templates/runtime/react/app-shell/toolcraft-source-asset-provider-ownership.test.tsx +243 -0
  74. package/templates/runtime/react/app-shell/toolcraft-source-asset-test-support.ts +51 -0
  75. package/templates/runtime/react/app-shell/toolcraft-unavailable-resource-proof.test.ts +138 -0
  76. package/templates/runtime/react/app-shell/toolcraft-unavailable-resource-proof.ts +378 -0
  77. package/templates/runtime/react/canvas/canvas-shell-rendering.test.tsx +76 -0
  78. package/templates/runtime/react/canvas/canvas-shell-test-utils.tsx +13 -1
  79. package/templates/runtime/react/canvas/canvas-shell.tsx +16 -1
  80. package/templates/runtime/react/canvas/product-scene-surface.test.tsx +195 -0
  81. package/templates/runtime/react/canvas/product-scene-surface.tsx +155 -0
  82. package/templates/runtime/react/canvas/runtime-scene-export.ts +1 -1
  83. package/templates/runtime/react/controls-panel/__tests__/control-conditions.test.ts +61 -0
  84. package/templates/runtime/react/controls-panel/__tests__/controls-panel.collection-actions.test.tsx +89 -0
  85. package/templates/runtime/react/controls-panel/__tests__/controls-panel.control-visibility.test.tsx +40 -19
  86. package/templates/runtime/react/controls-panel/__tests__/controls-panel.footer-actions.test.tsx +7 -12
  87. package/templates/runtime/react/controls-panel/__tests__/controls-panel.scene-export-actions.test.tsx +18 -84
  88. package/templates/runtime/react/controls-panel/__tests__/controls-panel.source-collection.test.tsx +62 -0
  89. package/templates/runtime/react/controls-panel/__tests__/controls-panel.source-color-bank.test.tsx +67 -0
  90. package/templates/runtime/react/controls-panel/actions/controls-panel-actions.ts +43 -87
  91. package/templates/runtime/react/controls-panel/actions/export-action-runner.ts +68 -0
  92. package/templates/runtime/react/controls-panel/conditions/control-conditions.ts +32 -119
  93. package/templates/runtime/react/controls-panel/layout/controls-panel-control-group.tsx +5 -2
  94. package/templates/runtime/react/controls-panel/layout/controls-panel-layout.tsx +10 -8
  95. package/templates/runtime/react/controls-panel/layout/controls-panel-section.tsx +3 -3
  96. package/templates/runtime/react/controls-panel/renderers/controls-panel-collection-item-fields.test.tsx +63 -0
  97. package/templates/runtime/react/controls-panel/renderers/controls-panel-collection-item-fields.tsx +138 -0
  98. package/templates/runtime/react/controls-panel/renderers/controls-panel-collection-items.tsx +104 -0
  99. package/templates/runtime/react/controls-panel/renderers/controls-panel-collection-renderer.tsx +115 -236
  100. package/templates/runtime/react/controls-panel/renderers/controls-panel-file-drop-item-controls.test.tsx +95 -0
  101. package/templates/runtime/react/controls-panel/renderers/controls-panel-file-drop-item-controls.tsx +82 -0
  102. package/templates/runtime/react/controls-panel/renderers/controls-panel-media-renderer.test.tsx +73 -1
  103. package/templates/runtime/react/controls-panel/renderers/controls-panel-media-renderer.tsx +50 -1
  104. package/templates/runtime/react/controls-panel/renderers/controls-panel-renderer-registry.test.ts +1 -1
  105. package/templates/runtime/react/controls-panel/renderers/controls-panel-renderer-registry.ts +2 -1
  106. package/templates/runtime/react/controls-panel/values/controls-panel-collection-values.ts +38 -6
  107. package/templates/runtime/react/controls-panel/values/controls-panel-value-labels.ts +1 -0
  108. package/templates/runtime/react/index.ts +4 -0
  109. package/templates/runtime/react/model-rendering/model-export.ts +1 -1
  110. package/templates/runtime/react/orientation-gizmo/orientation-gizmo-selection.ts +5 -5
  111. package/templates/runtime/react/orientation-gizmo/use-toolcraft-orientation-control-selection.ts +2 -2
  112. package/templates/runtime/react/panel-host/panel-host-runtime-placement.test.tsx +30 -0
  113. package/templates/runtime/react/panel-host/panel-host.tsx +4 -0
  114. package/templates/runtime/scene/scene-bounds.test.ts +23 -0
  115. package/templates/runtime/scene/scene-bounds.ts +32 -19
  116. package/templates/runtime/schema/app-capabilities.test.ts +4 -0
  117. package/templates/runtime/schema/canvas-render-scale.ts +62 -0
  118. package/templates/runtime/schema/collection-item-controls.ts +29 -0
  119. package/templates/runtime/schema/control-applicability.test.ts +127 -0
  120. package/templates/runtime/schema/control-applicability.ts +433 -0
  121. package/templates/runtime/schema/control-schema-normalization.ts +177 -11
  122. package/templates/runtime/schema/controls-panel-actions.ts +2 -2
  123. package/templates/runtime/schema/controls-panel-section-id.test.ts +1 -1
  124. package/templates/runtime/schema/controls-panel-section-layout.ts +14 -9
  125. package/templates/runtime/schema/controls-panel-section-roundtrip.test.ts +62 -3
  126. package/templates/runtime/schema/define-toolcraft.collection-actions-item-controls.test.ts +127 -0
  127. package/templates/runtime/schema/define-toolcraft.control-sections.test.ts +10 -4
  128. package/templates/runtime/schema/define-toolcraft.file-drop-item-controls.test.ts +98 -0
  129. package/templates/runtime/schema/define-toolcraft.file-drop-normalization.test.ts +27 -0
  130. package/templates/runtime/schema/define-toolcraft.segmented-control-fit.test.ts +20 -2
  131. package/templates/runtime/schema/define-toolcraft.setup-background.test.ts +1 -1
  132. package/templates/runtime/schema/define-toolcraft.setup-canvas.test.ts +16 -3
  133. package/templates/runtime/schema/define-toolcraft.setup-render-scale-timeline.test.ts +71 -3
  134. package/templates/runtime/schema/define-toolcraft.source-collection.test.ts +79 -0
  135. package/templates/runtime/schema/define-toolcraft.ts +1 -1
  136. package/templates/runtime/schema/runtime-setup-section.ts +24 -5
  137. package/templates/runtime/schema/schema-resolvers.ts +0 -56
  138. package/templates/runtime/schema/types.ts +72 -17
  139. package/templates/runtime/state/canvas-background-state.ts +6 -0
  140. package/templates/runtime/testing/performance-render-plan-integration.test.ts +64 -0
  141. package/templates/runtime/testing/performance-renderer-pipeline-validation.ts +18 -1
  142. package/templates/starter/AGENTS.md +18 -15
  143. package/templates/starter/NOTICE.md +6 -0
  144. package/templates/starter/docs/toolcraft/README.md +2 -1
  145. package/templates/starter/docs/toolcraft/acceptance-testing.md +23 -35
  146. package/templates/starter/docs/toolcraft/agent-worklog.md +18 -3
  147. package/templates/starter/docs/toolcraft/assembly-workflow.md +13 -5
  148. package/templates/starter/docs/toolcraft/component-rules.md +8 -6
  149. package/templates/starter/docs/toolcraft/core/control-selection.md +9 -3
  150. package/templates/starter/docs/toolcraft/core/layout.md +7 -7
  151. package/templates/starter/docs/toolcraft/core/media-upload.md +4 -2
  152. package/templates/starter/docs/toolcraft/core/performance.md +14 -3
  153. package/templates/starter/docs/toolcraft/core/runtime-boundary.md +16 -3
  154. package/templates/starter/docs/toolcraft/core/setup-export.md +49 -19
  155. package/templates/starter/docs/toolcraft/core/timeline-animation.md +4 -4
  156. package/templates/starter/docs/toolcraft/custom-controls.md +6 -4
  157. package/templates/starter/docs/toolcraft/decision-contract.md +3 -1
  158. package/templates/starter/docs/toolcraft/performance.md +20 -6
  159. package/templates/starter/docs/toolcraft/renderer-technique.md +5 -1
  160. package/templates/starter/docs/toolcraft/schema-reference.md +23 -25
  161. package/templates/starter/docs/toolcraft/workflow.md +7 -2
  162. package/templates/starter/e2e/app-browser-control-applicability-requirements.spec.ts +163 -0
  163. package/templates/starter/e2e/app-browser-control-coverage.spec.ts +33 -0
  164. package/templates/starter/e2e/app-browser-infinity-canvas-evidence.spec.ts +50 -3
  165. package/templates/starter/e2e/app-browser-orientation-evidence.spec.ts +174 -21
  166. package/templates/starter/e2e/app-browser-render-scale-evidence.spec.ts +60 -6
  167. package/templates/starter/e2e/app-browser-render-scale-requirements.spec.ts +77 -1
  168. package/templates/starter/e2e/app-browser-runtime-provenance.spec.ts +8 -0
  169. package/templates/starter/e2e/app-browser-runtime-reporter.spec.ts +35 -0
  170. package/templates/starter/e2e/app-browser-runtime-requirements.spec.ts +5 -34
  171. package/templates/starter/e2e/app-browser-semantic-evidence.spec.ts +61 -16
  172. package/templates/starter/e2e/app-browser-shell.spec.ts +13 -5
  173. package/templates/starter/e2e/app-performance-path-adapters.ts +8 -1
  174. package/templates/starter/e2e/app-performance.spec.ts +16 -9
  175. package/templates/starter/e2e/{browser-conditional-output-evidence-helpers.ts → browser-background-output-evidence.ts} +0 -68
  176. package/templates/starter/e2e/browser-control-applicability-evidence.ts +114 -0
  177. package/templates/starter/e2e/browser-infinity-canvas-evidence.ts +29 -341
  178. package/templates/starter/e2e/browser-infinity-canvas-unavailable-image-evidence.ts +370 -0
  179. package/templates/starter/e2e/browser-media-export-evidence.spec.ts +77 -0
  180. package/templates/starter/e2e/browser-media-export-evidence.ts +208 -0
  181. package/templates/starter/e2e/browser-orientation-gizmo-evidence-helpers.ts +4 -0
  182. package/templates/starter/e2e/browser-orientation-gizmo-live-preconditions.ts +86 -0
  183. package/templates/starter/e2e/browser-render-scale-evidence.ts +37 -29
  184. package/templates/starter/e2e/browser-runtime-applicability-requirements.ts +84 -0
  185. package/templates/starter/e2e/browser-runtime-evidence-reporter.ts +13 -3
  186. package/templates/starter/e2e/browser-runtime-evidence-requirements.ts +72 -35
  187. package/templates/starter/e2e/browser-semantic-evidence-test-helpers.ts +25 -0
  188. package/templates/starter/e2e/browser-standard-timeline-evidence.ts +106 -0
  189. package/templates/starter/e2e/decoded-pixel-observation.spec.ts +74 -0
  190. package/templates/starter/e2e/decoded-pixel-observation.ts +227 -0
  191. package/templates/starter/e2e/export-artifact-helpers.ts +206 -25
  192. package/templates/starter/e2e/image-artifact-inspection.spec.ts +41 -0
  193. package/templates/starter/e2e/image-artifact-inspection.ts +99 -0
  194. package/templates/starter/e2e/performance-canvas-helpers.ts +22 -21
  195. package/templates/starter/e2e/performance-canvas-quality-guard.spec.ts +312 -0
  196. package/templates/starter/e2e/performance-canvas-quality-guard.ts +196 -0
  197. package/templates/starter/e2e/performance-canvas-quality-observer.ts +351 -0
  198. package/templates/starter/e2e/performance-control-layout-helpers.ts +2 -2
  199. package/templates/starter/e2e/performance-path-adapter-contract.ts +4 -4
  200. package/templates/starter/e2e/performance-path-helpers.spec.ts +203 -70
  201. package/templates/starter/e2e/performance-path-helpers.ts +210 -91
  202. package/templates/starter/e2e/performance-pipeline-evidence-continuity.spec.ts +76 -0
  203. package/templates/starter/e2e/performance-pipeline-evidence-test-fixtures.ts +11 -2
  204. package/templates/starter/e2e/performance-pipeline-invariants.ts +47 -4
  205. package/templates/starter/e2e/performance-render-scale-runner.spec.ts +140 -38
  206. package/templates/starter/e2e/video-artifact-inspection.spec.ts +64 -0
  207. package/templates/starter/e2e/video-artifact-inspection.ts +281 -0
  208. package/templates/starter/gitignore +1 -0
  209. package/templates/starter/package.json +1 -0
  210. package/templates/starter/scripts/check-toolcraft-docs.mjs +17 -0
  211. package/templates/starter/scripts/toolcraft-contract-manifest.json +1 -0
  212. package/templates/starter/scripts/toolcraft-delivery-plan-authority.test.mjs +60 -0
  213. package/templates/starter/scripts/toolcraft-delivery-plan-errors.test.mjs +7 -2
  214. package/templates/starter/scripts/toolcraft-delivery-plan.mjs +7 -3
  215. package/templates/starter/scripts/toolcraft-product-boundary-ast.mjs +95 -103
  216. package/templates/starter/scripts/toolcraft-product-boundary-module-policy.mjs +101 -0
  217. package/templates/starter/scripts/toolcraft-product-boundary.mjs +12 -0
  218. package/templates/starter/scripts/toolcraft-product-boundary.test.mjs +0 -26
  219. package/templates/starter/scripts/toolcraft-product-control-bindings.mjs +182 -0
  220. package/templates/starter/scripts/toolcraft-product-control-boundary.mjs +146 -0
  221. package/templates/starter/scripts/toolcraft-product-control-boundary.test.mjs +186 -0
  222. package/templates/starter/scripts/toolcraft-product-evidence-boundary.test.mjs +85 -20
  223. package/templates/starter/scripts/toolcraft-product-evidence-import-policy.mjs +43 -0
  224. package/templates/starter/scripts/toolcraft-product-export-boundary.mjs +157 -0
  225. package/templates/starter/scripts/toolcraft-product-export-boundary.test.mjs +196 -0
  226. package/templates/starter/scripts/toolcraft-typescript-analysis.d.mts +73 -0
  227. package/templates/starter/scripts/toolcraft-typescript-source-evidence-structure.test.mjs +20 -0
  228. package/templates/starter/scripts/toolcraft-typescript-source-evidence.mjs +1 -0
  229. package/templates/starter/scripts/toolcraft-unavailable-resource-evidence-facade-test-helpers.mjs +318 -0
  230. package/templates/starter/scripts/toolcraft-unavailable-resource-evidence-facade.test.mjs +157 -206
  231. package/templates/starter/scripts/toolcraft-verification-inventory.mjs +1 -0
  232. package/templates/starter/scripts/toolcraft-verification-receipt.test.mjs +51 -0
  233. package/templates/starter/scripts/toolcraft-vite-env-activation.test.mjs +128 -59
  234. package/templates/starter/src/app/acceptance/artifact-export-intent.ts +130 -0
  235. package/templates/starter/src/app/acceptance/conditions.ts +1 -1
  236. package/templates/starter/src/app/acceptance/control-acceptance-context.ts +2 -2
  237. package/templates/starter/src/app/acceptance/control-acceptance-coverage.ts +6 -20
  238. package/templates/starter/src/app/acceptance/control-acceptance-kind-rules.ts +24 -5
  239. package/templates/starter/src/app/acceptance/control-acceptance-policy.ts +2 -13
  240. package/templates/starter/src/app/acceptance/control-applicability-cases.test.ts +321 -0
  241. package/templates/starter/src/app/acceptance/control-applicability-cases.ts +290 -0
  242. package/templates/starter/src/app/acceptance/control-applicability.ts +332 -0
  243. package/templates/starter/src/app/acceptance/control-layout-dependency-rules.ts +20 -9
  244. package/templates/starter/src/app/acceptance/control-layout-entity-rules.ts +4 -6
  245. package/templates/starter/src/app/acceptance/control-layout-model.ts +4 -4
  246. package/templates/starter/src/app/acceptance/control-layout-section-rules.ts +3 -10
  247. package/templates/starter/src/app/acceptance/control-parts.ts +8 -0
  248. package/templates/starter/src/app/acceptance/control-section-entity-cohesion.test.ts +215 -0
  249. package/templates/starter/src/app/acceptance/control-section-entity-cohesion.ts +132 -0
  250. package/templates/starter/src/app/acceptance/control-section-inventory.test.ts +87 -0
  251. package/templates/starter/src/app/acceptance/control-section-inventory.ts +74 -44
  252. package/templates/starter/src/app/acceptance/custom-controls.ts +11 -3
  253. package/templates/starter/src/app/acceptance/export-artifact-coverage.ts +113 -0
  254. package/templates/starter/src/app/acceptance/inline-layout.ts +64 -9
  255. package/templates/starter/src/app/acceptance/media-upload.ts +5 -1
  256. package/templates/starter/src/app/acceptance/orientation-gizmo.ts +18 -11
  257. package/templates/starter/src/app/acceptance/output-background-rules.ts +2 -2
  258. package/templates/starter/src/app/acceptance/output-export-layout-rules.ts +18 -10
  259. package/templates/starter/src/app/acceptance/output-export-model.ts +54 -11
  260. package/templates/starter/src/app/acceptance/output-export.ts +35 -12
  261. package/templates/starter/src/app/acceptance/output-video-export-rules.ts +99 -0
  262. package/templates/starter/src/app/acceptance/types.ts +30 -8
  263. package/templates/starter/src/app/acceptance/validate-coverage.ts +20 -2
  264. package/templates/starter/src/app/starter-acceptance.artifact-export-intent.test.ts +239 -0
  265. package/templates/starter/src/app/starter-acceptance.background-export.test.ts +33 -0
  266. package/templates/starter/src/app/starter-acceptance.canvas-sizing.test.ts +16 -1
  267. package/templates/starter/src/app/starter-acceptance.compound-part-coverage.test.ts +65 -0
  268. package/templates/starter/src/app/starter-acceptance.contract-fixtures.test.ts +41 -0
  269. package/templates/starter/src/app/starter-acceptance.contract-fixtures.ts +112 -16
  270. package/templates/starter/src/app/starter-acceptance.control-applicability.test.ts +224 -0
  271. package/templates/starter/src/app/starter-acceptance.control-order.test.ts +58 -3
  272. package/templates/starter/src/app/starter-acceptance.control-state.test.ts +0 -111
  273. package/templates/starter/src/app/starter-acceptance.custom-control-collections.test.ts +3 -1
  274. package/templates/starter/src/app/starter-acceptance.custom-control-valid.test.ts +1 -1
  275. package/templates/starter/src/app/starter-acceptance.export-artifact.test.ts +131 -0
  276. package/templates/starter/src/app/starter-acceptance.export-test-utils.ts +22 -0
  277. package/templates/starter/src/app/{starter-acceptance.image-video-export-settings.test.ts → starter-acceptance.image-export-settings.test.ts} +37 -9
  278. package/templates/starter/src/app/starter-acceptance.interaction-ownership.test.ts +12 -0
  279. package/templates/starter/src/app/starter-acceptance.keyframes.test.ts +14 -4
  280. package/templates/starter/src/app/starter-acceptance.media-upload.fixtures.ts +11 -1
  281. package/templates/starter/src/app/starter-acceptance.media-upload.test.ts +28 -0
  282. package/templates/starter/src/app/starter-acceptance.output-export-intent.test.ts +384 -0
  283. package/templates/starter/src/app/starter-acceptance.output-export-rules.test.ts +46 -1
  284. package/templates/starter/src/app/starter-acceptance.product-output-export.test.ts +112 -105
  285. package/templates/starter/src/app/starter-acceptance.product-readiness.test.ts +12 -0
  286. package/templates/starter/src/app/starter-acceptance.section-cohesion.test.ts +1 -1
  287. package/templates/starter/src/app/starter-acceptance.section-dependencies.test.ts +35 -24
  288. package/templates/starter/src/app/starter-acceptance.section-size.test.ts +72 -0
  289. package/templates/starter/src/app/starter-acceptance.ts +13 -2
  290. package/templates/starter/src/app/starter-acceptance.video-export-settings.test.ts +336 -0
  291. package/templates/starter/src/app/starter-acceptance.view-interaction.test.ts +8 -0
  292. package/templates/starter/src/app/starter-performance-test-utils.ts +103 -0
  293. package/templates/starter/src/app/starter-performance.gates.test.ts +231 -278
  294. package/templates/starter/src/app/starter-performance.lifecycle.test.ts +261 -0
  295. package/templates/starter/src/app/test-evidence/browser-runtime-contract.ts +4 -2
  296. package/templates/starter/src/routes/index.tsx +2 -0
  297. package/templates/ui/components/composites/tabs.tsx +2 -2
  298. package/templates/ui/components/controls/collection-actions/collection-item-groups.test.tsx +38 -0
  299. package/templates/ui/components/controls/collection-actions/collection-item-groups.tsx +36 -0
  300. package/templates/ui/components/controls/collection-actions/index.ts +2 -0
  301. package/templates/ui/components/controls/file-drop/file-drop-collection-control.tsx +137 -0
  302. package/templates/ui/components/controls/file-drop/file-drop-control.test.tsx +189 -0
  303. package/templates/ui/components/controls/file-drop/file-drop-control.tsx +81 -88
  304. package/templates/ui/components/controls/file-drop/file-drop-presentation.tsx +131 -9
  305. package/templates/ui/components/controls/file-drop/file-drop-types.ts +36 -22
  306. package/templates/ui/components/controls/file-drop/index.ts +1 -0
  307. package/templates/ui/components/controls/index.ts +3 -0
  308. package/templates/ui/components/controls/range-slider/range-slider-control.tsx +1 -1
  309. package/templates/ui/components/controls/range-slider/range-slider-value.test.ts +14 -0
  310. package/templates/ui/components/controls/range-slider/range-slider-value.ts +3 -2
  311. package/templates/runtime/export/export.ts +0 -166
  312. package/templates/starter/src/app/acceptance/condition-exclusivity.test.ts +0 -60
  313. package/templates/starter/src/app/acceptance/condition-exclusivity.ts +0 -221
@@ -0,0 +1,86 @@
1
+ import { expect, type Locator, type Page } from "@playwright/test";
2
+
3
+ import {
4
+ countToolcraftControlOwnersByTarget,
5
+ getToolcraftControlFieldByTarget,
6
+ } from "./browser-control-target-helpers";
7
+ import {
8
+ getToolcraftBrowserProofPage,
9
+ type ToolcraftBrowserProofSession,
10
+ } from "./browser-proof-session";
11
+
12
+ const TOOLCRAFT_APP_ROOT_SELECTOR = '[data-slot="toolcraft-runtime-app"]';
13
+ const RENDER_SCALE_TARGET = "canvas.renderScale";
14
+
15
+ async function expectPausedPlaybackWhenPresent(page: Page): Promise<void> {
16
+ const root = page.locator(TOOLCRAFT_APP_ROOT_SELECTOR);
17
+ const play = root.getByRole("button", { name: "Play playback" });
18
+ const pause = root.getByRole("button", { name: "Pause playback" });
19
+ const playCount = await play.count();
20
+ const actionCount = playCount + (await pause.count());
21
+
22
+ if (actionCount === 0) return;
23
+
24
+ expect(
25
+ actionCount,
26
+ "Orientation axis-drag proof requires exactly one standard playback action.",
27
+ ).toBe(1);
28
+ expect(
29
+ playCount,
30
+ "Orientation axis-drag proof must be paused; Play playback must be visible before its baseline.",
31
+ ).toBe(1);
32
+ await expect(play).toBeVisible();
33
+ }
34
+
35
+ async function readFiniteAriaNumber(
36
+ slider: Locator,
37
+ name: "aria-valuemax" | "aria-valuenow",
38
+ ): Promise<number> {
39
+ const raw = await slider.getAttribute(name);
40
+ const value = raw === null || raw.trim() === "" ? Number.NaN : Number(raw);
41
+
42
+ expect(
43
+ Number.isFinite(value),
44
+ `Orientation axis-drag proof requires a finite ${name} on canvas.renderScale.`,
45
+ ).toBe(true);
46
+ return value;
47
+ }
48
+
49
+ async function expectMaximumRenderScaleWhenPresent(page: Page): Promise<void> {
50
+ const ownerCount = await countToolcraftControlOwnersByTarget(
51
+ page,
52
+ RENDER_SCALE_TARGET,
53
+ );
54
+
55
+ if (ownerCount === 0) return;
56
+
57
+ expect(
58
+ ownerCount,
59
+ "Orientation axis-drag proof requires exactly one canvas.renderScale owner.",
60
+ ).toBe(1);
61
+ const field = await getToolcraftControlFieldByTarget(
62
+ page,
63
+ RENDER_SCALE_TARGET,
64
+ );
65
+ const slider = field.getByRole("slider");
66
+ await expect(
67
+ slider,
68
+ "Orientation axis-drag proof requires one canvas.renderScale slider.",
69
+ ).toHaveCount(1);
70
+ await expect(slider).toBeVisible();
71
+
72
+ const value = await readFiniteAriaNumber(slider, "aria-valuenow");
73
+ const maximum = await readFiniteAriaNumber(slider, "aria-valuemax");
74
+ expect(
75
+ value,
76
+ "Orientation axis-drag proof requires canvas.renderScale at maximum quality before its baseline.",
77
+ ).toBe(maximum);
78
+ }
79
+
80
+ export async function expectToolcraftOrientationLiveDragPreconditions(
81
+ session: ToolcraftBrowserProofSession,
82
+ ): Promise<void> {
83
+ const page = await getToolcraftBrowserProofPage(session);
84
+ await expectPausedPlaybackWhenPresent(page);
85
+ await expectMaximumRenderScaleWhenPresent(page);
86
+ }
@@ -1,10 +1,11 @@
1
1
  import { expect, type Page } from "@playwright/test";
2
- import type { ToolcraftPerformancePath } from "@repo/toolcraft-runtime";
2
+ import { TOOLCRAFT_CANVAS_RENDER_SCALE } from "@repo/toolcraft-runtime";
3
3
 
4
+ import type { ToolcraftPerformancePipelinePhase } from "../src/app/test-evidence/browser-performance-contract";
4
5
  import type { ToolcraftRenderScaleState } from "../src/app/starter-acceptance";
5
6
  import { attachToolcraftBrowserRuntimeEvidence } from "./browser-runtime-evidence";
6
7
 
7
- type CanvasMetrics = Readonly<{
8
+ export type ToolcraftCanvasBackingMetrics = Readonly<{
8
9
  backingHeight: number;
9
10
  backingWidth: number;
10
11
  cssHeight: number;
@@ -12,6 +13,8 @@ type CanvasMetrics = Readonly<{
12
13
  devicePixelRatio: number;
13
14
  }>;
14
15
 
16
+ export type ToolcraftCanvasVisibleSizePolicy = "fixed" | "viewport-driven";
17
+
15
18
  export type ToolcraftRenderScaleStateTransition = Readonly<{
16
19
  run: () => Promise<void>;
17
20
  state: ToolcraftRenderScaleState;
@@ -19,20 +22,12 @@ export type ToolcraftRenderScaleStateTransition = Readonly<{
19
22
 
20
23
  export type ToolcraftRenderScaleBackingOptions = Readonly<{
21
24
  baselineCssSize?: Readonly<{ height: number; width: number }>;
22
- state?: ToolcraftRenderScaleState | "performance";
25
+ state?:
26
+ | ToolcraftRenderScaleState
27
+ | "performance-baseline"
28
+ | `performance-${ToolcraftPerformancePipelinePhase}`;
23
29
  }>;
24
30
 
25
- export function isToolcraftRenderScalePerformancePath(
26
- path: Pick<ToolcraftPerformancePath, "targets" | "workloadDimensions">,
27
- ): boolean {
28
- return (
29
- path.targets.includes("canvas.renderScale") ||
30
- path.workloadDimensions.some((dimension) =>
31
- /render[-_.]?scale|resolution[-_.]?scale/iu.test(dimension),
32
- )
33
- );
34
- }
35
-
36
31
  function assertPositiveFiniteScale(selectedScale: number): void {
37
32
  if (!Number.isFinite(selectedScale) || selectedScale <= 0) {
38
33
  throw new Error(
@@ -64,7 +59,7 @@ export function assertToolcraftRenderScaleBackingProofConfig(
64
59
  async function readCanvasMetrics(
65
60
  page: Page,
66
61
  canvasSelector: string,
67
- ): Promise<CanvasMetrics> {
62
+ ): Promise<ToolcraftCanvasBackingMetrics> {
68
63
  if (!canvasSelector.trim()) {
69
64
  throw new Error(
70
65
  "Toolcraft canvas backing-pixel checks require a non-empty canvas selector.",
@@ -95,8 +90,8 @@ async function readCanvasMetrics(
95
90
  });
96
91
  }
97
92
 
98
- function assertBackingMetrics(
99
- metrics: CanvasMetrics,
93
+ export function assertToolcraftCanvasBackingMetricsForRenderScale(
94
+ metrics: ToolcraftCanvasBackingMetrics,
100
95
  selectedScale: number,
101
96
  options: ToolcraftRenderScaleBackingOptions,
102
97
  ): void {
@@ -128,14 +123,16 @@ function assertBackingMetrics(
128
123
  metrics.cssWidth * metrics.devicePixelRatio * selectedScale;
129
124
  const expectedHeight =
130
125
  metrics.cssHeight * metrics.devicePixelRatio * selectedScale;
126
+ const widthError = Math.abs(metrics.backingWidth - expectedWidth);
127
+ const heightError = Math.abs(metrics.backingHeight - expectedHeight);
131
128
  expect(
132
- metrics.backingWidth,
133
- `${stateLabel} expected canvas backing width to honor Resolution scale ${selectedScale}.`,
134
- ).toBeGreaterThanOrEqual(Math.floor(expectedWidth - 1));
129
+ widthError,
130
+ `${stateLabel} expected exact canvas backing width within one physical pixel for Resolution scale ${selectedScale}.`,
131
+ ).toBeLessThanOrEqual(1);
135
132
  expect(
136
- metrics.backingHeight,
137
- `${stateLabel} expected canvas backing height to honor Resolution scale ${selectedScale}.`,
138
- ).toBeGreaterThanOrEqual(Math.floor(expectedHeight - 1));
133
+ heightError,
134
+ `${stateLabel} expected exact canvas backing height within one physical pixel for Resolution scale ${selectedScale}.`,
135
+ ).toBeLessThanOrEqual(1);
139
136
  }
140
137
 
141
138
  export async function expectToolcraftCanvasBackingPixelsForRenderScale(
@@ -146,7 +143,11 @@ export async function expectToolcraftCanvasBackingPixelsForRenderScale(
146
143
  ): Promise<Readonly<{ height: number; width: number }>> {
147
144
  assertPositiveFiniteScale(selectedScale);
148
145
  const metrics = await readCanvasMetrics(page, canvasSelector);
149
- assertBackingMetrics(metrics, selectedScale, options);
146
+ assertToolcraftCanvasBackingMetricsForRenderScale(
147
+ metrics,
148
+ selectedScale,
149
+ options,
150
+ );
150
151
  return { height: metrics.cssHeight, width: metrics.cssWidth };
151
152
  }
152
153
 
@@ -216,13 +217,17 @@ export async function expectToolcraftPerformanceRenderScaleBackingEvidence(
216
217
  page: Page,
217
218
  {
218
219
  canvasSelector,
220
+ baselineCssSize,
221
+ cssSizePolicy,
219
222
  pathId,
220
- selectedScale,
223
+ phase,
221
224
  target,
222
225
  }: Readonly<{
226
+ baselineCssSize: Readonly<{ height: number; width: number }>;
223
227
  canvasSelector: string;
228
+ cssSizePolicy: ToolcraftCanvasVisibleSizePolicy;
224
229
  pathId: string;
225
- selectedScale: number;
230
+ phase: ToolcraftPerformancePipelinePhase;
226
231
  target?: string;
227
232
  }>,
228
233
  ): Promise<void> {
@@ -234,12 +239,15 @@ export async function expectToolcraftPerformanceRenderScaleBackingEvidence(
234
239
  await expectToolcraftCanvasBackingPixelsForRenderScale(
235
240
  page,
236
241
  canvasSelector,
237
- selectedScale,
238
- { state: "performance" },
242
+ TOOLCRAFT_CANVAS_RENDER_SCALE.defaultValue,
243
+ {
244
+ ...(cssSizePolicy === "fixed" ? { baselineCssSize } : {}),
245
+ state: `performance-${phase}`,
246
+ },
239
247
  );
240
248
  await attachToolcraftBrowserRuntimeEvidence({
241
249
  evidenceType: "performance-render-scale",
242
- requirementId: pathId,
250
+ requirementId: `${pathId}#${phase}`,
243
251
  target,
244
252
  });
245
253
  }
@@ -0,0 +1,84 @@
1
+ import type { ResolvedToolcraftAppSchema } from "@repo/toolcraft-runtime";
2
+
3
+ import type { ToolcraftBrowserRuntimeRequirement } from "../src/app/test-evidence/browser-runtime-contract";
4
+ import {
5
+ getToolcraftApplicabilityRequirementId,
6
+ getToolcraftControlApplicabilityCases,
7
+ type ToolcraftComponentAcceptance,
8
+ type ToolcraftControlSectionInventoryEntry,
9
+ } from "../src/app/starter-acceptance";
10
+
11
+ type ApplicabilityAcceptanceSource = Pick<
12
+ ToolcraftComponentAcceptance,
13
+ "browserTestName" | "id" | "kind" | "target"
14
+ >;
15
+
16
+ const layoutOnlyEvidenceTypes = new Set<
17
+ ToolcraftBrowserRuntimeRequirement["evidenceType"]
18
+ >(["discrete-slider-layout", "segmented-control-layout"]);
19
+
20
+ export function expandToolcraftControlApplicabilityRequirements({
21
+ entry,
22
+ schema,
23
+ sectionInventory,
24
+ templates,
25
+ }: Readonly<{
26
+ entry: ApplicabilityAcceptanceSource;
27
+ schema?: Pick<ResolvedToolcraftAppSchema, "panels">;
28
+ sectionInventory: readonly ToolcraftControlSectionInventoryEntry[];
29
+ templates: readonly ToolcraftBrowserRuntimeRequirement[];
30
+ }>): ToolcraftBrowserRuntimeRequirement[] {
31
+ if (!schema || entry.kind !== "control" || !entry.target) {
32
+ return [...templates];
33
+ }
34
+
35
+ const cases = getToolcraftControlApplicabilityCases({
36
+ schema,
37
+ sectionInventory,
38
+ target: entry.target,
39
+ });
40
+
41
+ if (cases.length === 0) {
42
+ return [...templates];
43
+ }
44
+
45
+ const hasOutcomeRequirement = templates.some(
46
+ (requirement) => !layoutOnlyEvidenceTypes.has(requirement.evidenceType),
47
+ );
48
+
49
+ return cases.flatMap((applicabilityCase) => {
50
+ const visibilityRequirement = {
51
+ evidenceType:
52
+ applicabilityCase.expectation === "hidden"
53
+ ? ("control-applicability-hidden" as const)
54
+ : ("control-applicability-visible" as const),
55
+ requirementId: getToolcraftApplicabilityRequirementId(
56
+ entry.id,
57
+ applicabilityCase,
58
+ ),
59
+ target: entry.target,
60
+ testName: entry.browserTestName,
61
+ };
62
+
63
+ if (applicabilityCase.expectation === "hidden") {
64
+ return [visibilityRequirement];
65
+ }
66
+
67
+ if (!hasOutcomeRequirement) {
68
+ throw new Error(
69
+ `Visible applicability case for ${entry.id} has no product outcome requirement.`,
70
+ );
71
+ }
72
+
73
+ return [
74
+ visibilityRequirement,
75
+ ...templates.map((requirement) => ({
76
+ ...requirement,
77
+ requirementId: getToolcraftApplicabilityRequirementId(
78
+ requirement.requirementId,
79
+ applicabilityCase,
80
+ ),
81
+ })),
82
+ ];
83
+ });
84
+ }
@@ -19,7 +19,10 @@ import {
19
19
  type ToolcraftBrowserRuntimeRequirement,
20
20
  type ToolcraftBrowserRuntimeTest,
21
21
  } from "../src/app/test-evidence/browser-runtime-contract";
22
- import { starterAcceptance } from "../src/app/starter-acceptance";
22
+ import {
23
+ starterAcceptance,
24
+ starterControlSectionInventory,
25
+ } from "../src/app/starter-acceptance";
23
26
  import { starterPerformance } from "../src/app/starter-performance";
24
27
  import { starterSchema } from "../src/app/starter-schema";
25
28
  import {
@@ -166,14 +169,21 @@ export default class ToolcraftBrowserRuntimeEvidenceReporter implements Reporter
166
169
  this.performanceSchema = this.performanceSchemaOverride ?? starterSchema;
167
170
  this.acceptanceRequirements =
168
171
  this.acceptanceRequirementsOverride ??
169
- deriveToolcraftBrowserRuntimeRequirements(starterAcceptance, starterSchema);
172
+ deriveToolcraftBrowserRuntimeRequirements(
173
+ starterAcceptance,
174
+ starterSchema,
175
+ starterControlSectionInventory,
176
+ );
170
177
  this.canonicalPaths = deriveToolcraftPerformancePaths(
171
178
  this.performanceSchema,
172
179
  this.performanceConfig,
173
180
  );
174
181
  this.performanceRequirements =
175
182
  this.performanceRequirementsOverride ??
176
- deriveToolcraftPerformancePathRuntimeRequirements(this.canonicalPaths);
183
+ deriveToolcraftPerformancePathRuntimeRequirements(
184
+ this.canonicalPaths,
185
+ this.performanceSchema,
186
+ );
177
187
  }
178
188
 
179
189
  onEnd(_result: FullResult): { status: "failed" } | undefined {
@@ -3,20 +3,23 @@ import type {
3
3
  ToolcraftPerformancePath,
4
4
  } from "@repo/toolcraft-runtime";
5
5
 
6
+ import {
7
+ TOOLCRAFT_PERFORMANCE_PIPELINE_PHASES,
8
+ } from "../src/app/test-evidence/browser-performance-contract";
6
9
  import type { ToolcraftBrowserRuntimeRequirement } from "../src/app/test-evidence/browser-runtime-contract";
7
10
  import { schemaHasVideoExportPanelAction } from "../src/app/acceptance/output-export";
8
11
  import {
9
12
  getRequiredToolcraftControlPartCoverage,
10
13
  type ToolcraftBackgroundOutputCoverage,
11
14
  type ToolcraftComponentAcceptance,
12
- type ToolcraftConditionalVisibilityCoverage,
15
+ type ToolcraftControlSectionInventoryEntry,
13
16
  type ToolcraftInfinityCanvasCoverage,
14
17
  type ToolcraftModelImportCoverage,
15
18
  type ToolcraftOrientationGizmoCoverage,
16
19
  type ToolcraftTimelinePlaybackCoverage,
17
20
  TOOLCRAFT_REQUIRED_MODEL_IMPORT_COVERAGE,
18
21
  } from "../src/app/starter-acceptance";
19
- import { isToolcraftRenderScalePerformancePath } from "./browser-render-scale-evidence";
22
+ import { expandToolcraftControlApplicabilityRequirements } from "./browser-runtime-applicability-requirements";
20
23
  import { getToolcraftPerformancePathTestName } from "./performance-path-helpers";
21
24
 
22
25
  type BrowserAcceptanceRequirementSource = Pick<
@@ -27,6 +30,7 @@ type BrowserAcceptanceRequirementSource = Pick<
27
30
  | "canvasHandle"
28
31
  | "controlPartCoverage"
29
32
  | "evidence"
33
+ | "exportArtifactCoverage"
30
34
  | "id"
31
35
  | "infinityCanvasCoverage"
32
36
  | "layerCoverage"
@@ -38,10 +42,14 @@ type BrowserAcceptanceRequirementSource = Pick<
38
42
  | "target"
39
43
  | "timelineCoverage"
40
44
  | "timelinePlaybackCoverage"
41
- | "visibilityCoverage"
42
- >;
45
+ > & {
46
+ kind?: ToolcraftComponentAcceptance["kind"];
47
+ };
43
48
 
44
- type BrowserSchemaRequirementSource = Pick<ResolvedToolcraftAppSchema, "panels">;
49
+ type BrowserSchemaRequirementSource = Pick<
50
+ ResolvedToolcraftAppSchema,
51
+ "canvas" | "panels"
52
+ >;
45
53
 
46
54
  function getAcceptanceEvidenceType(
47
55
  evidence: ToolcraftComponentAcceptance["evidence"],
@@ -90,18 +98,6 @@ const timelinePlaybackCoverage = Object.keys(
90
98
  timelineEvidenceTypeByCoverage,
91
99
  ) as Array<keyof typeof timelineEvidenceTypeByCoverage>;
92
100
 
93
- const conditionalVisibilityEvidenceTypeByCoverage = {
94
- hidden: "conditional-control-hidden",
95
- visible: "conditional-control-visible",
96
- } as const satisfies Record<
97
- ToolcraftConditionalVisibilityCoverage,
98
- ToolcraftBrowserRuntimeRequirement["evidenceType"]
99
- >;
100
-
101
- const conditionalVisibilityCoverage = Object.keys(
102
- conditionalVisibilityEvidenceTypeByCoverage,
103
- ) as ToolcraftConditionalVisibilityCoverage[];
104
-
105
101
  const backgroundOutputEvidenceTypeByCoverage = {
106
102
  "image-transparent-when-excluded": "background-image-transparency",
107
103
  "infinity-viewport-color-and-dependency": "background-infinity-viewport",
@@ -167,7 +163,7 @@ const modelImportEvidenceTypeByCoverage = {
167
163
  ToolcraftBrowserRuntimeRequirement["evidenceType"]
168
164
  >;
169
165
 
170
- export function deriveToolcraftBrowserRuntimeRequirements(
166
+ function deriveUnqualifiedToolcraftBrowserRuntimeRequirements(
171
167
  acceptance: readonly BrowserAcceptanceRequirementSource[],
172
168
  schema?: BrowserSchemaRequirementSource,
173
169
  ): ToolcraftBrowserRuntimeRequirement[] {
@@ -188,7 +184,25 @@ export function deriveToolcraftBrowserRuntimeRequirements(
188
184
  entry.orientationGizmoCoverage !== undefined;
189
185
  const evidenceTypes: ToolcraftBrowserRuntimeRequirement["evidenceType"][] = [];
190
186
  const baseEvidenceType = getAcceptanceEvidenceType(entry.evidence);
191
- if (baseEvidenceType) evidenceTypes.push(baseEvidenceType);
187
+ const exportArtifactCoverage = entry.exportArtifactCoverage
188
+ ? typeof entry.exportArtifactCoverage === "string"
189
+ ? [entry.exportArtifactCoverage]
190
+ : entry.exportArtifactCoverage
191
+ : [];
192
+ if (
193
+ baseEvidenceType &&
194
+ entry.renderScaleCoverage === undefined &&
195
+ exportArtifactCoverage.length === 0
196
+ ) {
197
+ evidenceTypes.push(baseEvidenceType);
198
+ }
199
+ for (const coverage of exportArtifactCoverage) {
200
+ evidenceTypes.push(
201
+ coverage === "all-required-image-export-behavior"
202
+ ? "image-export-artifact"
203
+ : "video-export-artifact",
204
+ );
205
+ }
192
206
 
193
207
  if (entry.canvasHandle && !hasOrientationCoverage) {
194
208
  evidenceTypes.push("canvas-handle-interaction");
@@ -216,15 +230,6 @@ export function deriveToolcraftBrowserRuntimeRequirements(
216
230
  if (entry.referenceCoverage || entry.referenceTimelineCoverage) {
217
231
  evidenceTypes.push("reference-parity");
218
232
  }
219
- const visibilityCoverage =
220
- entry.visibilityCoverage === "all-conditional-visibility"
221
- ? conditionalVisibilityCoverage
222
- : entry.visibilityCoverage ?? [];
223
- evidenceTypes.push(
224
- ...visibilityCoverage.map(
225
- (item) => conditionalVisibilityEvidenceTypeByCoverage[item],
226
- ),
227
- );
228
233
  const backgroundCoverage =
229
234
  entry.backgroundOutputCoverage === "all-required-background-output"
230
235
  ? backgroundOutputCoverage.filter(
@@ -321,7 +326,28 @@ export function deriveToolcraftBrowserRuntimeRequirements(
321
326
  });
322
327
  }
323
328
 
324
- const pathProductOutcomeInteractions = new Set<ToolcraftPerformancePath["interaction"]>([
329
+ export function deriveToolcraftBrowserRuntimeRequirements(
330
+ acceptance: readonly BrowserAcceptanceRequirementSource[],
331
+ schema?: BrowserSchemaRequirementSource,
332
+ sectionInventory: readonly ToolcraftControlSectionInventoryEntry[] = [],
333
+ ): ToolcraftBrowserRuntimeRequirement[] {
334
+ return acceptance.flatMap((entry) => {
335
+ const templates = deriveUnqualifiedToolcraftBrowserRuntimeRequirements(
336
+ [entry],
337
+ schema,
338
+ );
339
+ return expandToolcraftControlApplicabilityRequirements({
340
+ entry,
341
+ schema,
342
+ sectionInventory,
343
+ templates,
344
+ });
345
+ });
346
+ }
347
+
348
+ const pathProductOutcomeInteractions = new Set<
349
+ ToolcraftPerformancePath["interaction"]
350
+ >([
325
351
  "control-change",
326
352
  "control-drag",
327
353
  "mask-drag",
@@ -332,6 +358,7 @@ const pathProductOutcomeInteractions = new Set<ToolcraftPerformancePath["interac
332
358
 
333
359
  export function deriveToolcraftPerformancePathRuntimeRequirements(
334
360
  paths: readonly ToolcraftPerformancePath[],
361
+ schema: ResolvedToolcraftAppSchema,
335
362
  ): ToolcraftBrowserRuntimeRequirement[] {
336
363
  return paths.flatMap((path) => {
337
364
  const evidenceTypes: ToolcraftBrowserRuntimeRequirement["evidenceType"][] = [
@@ -353,19 +380,29 @@ export function deriveToolcraftPerformancePathRuntimeRequirements(
353
380
  if (path.interaction === "animation-frame") {
354
381
  evidenceTypes.push("performance-animation-frames");
355
382
  }
356
- if (path.interaction === "viewport-drag" || path.interaction === "viewport-zoom") {
383
+ if (
384
+ path.interaction === "viewport-drag" ||
385
+ path.interaction === "viewport-zoom"
386
+ ) {
357
387
  evidenceTypes.push("performance-viewport");
358
388
  }
359
- if (isToolcraftRenderScalePerformancePath(path)) {
360
- evidenceTypes.push("performance-render-scale");
361
- }
362
-
363
389
  const target = path.targets.length === 1 ? path.targets[0] : undefined;
364
- return evidenceTypes.map((evidenceType) => ({
390
+ const requirements = evidenceTypes.map((evidenceType) => ({
365
391
  evidenceType,
366
392
  requirementId: path.id,
367
393
  target,
368
394
  testName: getToolcraftPerformancePathTestName(path),
369
395
  }));
396
+ if (schema.canvas.renderScale.enabled) {
397
+ for (const phase of TOOLCRAFT_PERFORMANCE_PIPELINE_PHASES) {
398
+ requirements.push({
399
+ evidenceType: "performance-render-scale",
400
+ requirementId: `${path.id}#${phase}`,
401
+ target,
402
+ testName: getToolcraftPerformancePathTestName(path),
403
+ });
404
+ }
405
+ }
406
+ return requirements;
370
407
  });
371
408
  }
@@ -55,3 +55,28 @@ export async function setSyntheticControlOwner(
55
55
  { target, visible },
56
56
  );
57
57
  }
58
+
59
+ export async function setSyntheticSwitchControlOwner(
60
+ page: Page,
61
+ target: string,
62
+ checked: boolean,
63
+ ): Promise<void> {
64
+ await setSyntheticControlOwner(page, target, true);
65
+ await page.locator("[data-synthetic-target]").evaluateAll(
66
+ (owners, entry) => {
67
+ const owner = owners.find(
68
+ (candidate) =>
69
+ candidate.getAttribute("data-synthetic-target") === entry.target,
70
+ );
71
+ const button = owner?.querySelector("button");
72
+
73
+ if (!button) {
74
+ throw new Error(`Missing synthetic switch owner ${entry.target}.`);
75
+ }
76
+
77
+ button.setAttribute("aria-checked", String(entry.checked));
78
+ button.setAttribute("role", "switch");
79
+ },
80
+ { checked, target },
81
+ );
82
+ }
@@ -0,0 +1,106 @@
1
+ import { expect } from "@playwright/test";
2
+
3
+ import { attachToolcraftBrowserRuntimeEvidence } from "./browser-runtime-evidence";
4
+ import {
5
+ getToolcraftBrowserProofPage,
6
+ type ToolcraftBrowserProofSession,
7
+ } from "./browser-proof-session";
8
+
9
+ async function readMarkerSignature(
10
+ page: Awaited<ReturnType<typeof getToolcraftBrowserProofPage>>,
11
+ markerSelector: string,
12
+ ): Promise<string> {
13
+ return page.locator(markerSelector).evaluate((element) => {
14
+ const node = element as HTMLElement;
15
+ return `${node.dataset.timelineProgress ?? "missing"}:${node.style.transform || node.style.left}`;
16
+ });
17
+ }
18
+
19
+ export async function expectToolcraftStandardTimelinePlayback(
20
+ session: ToolcraftBrowserProofSession,
21
+ options: Readonly<{
22
+ markerSelector: string;
23
+ requirementId: string;
24
+ }>,
25
+ ): Promise<void> {
26
+ const page = await getToolcraftBrowserProofPage(session);
27
+ const scrubber = page.getByRole("slider", { name: "Playback position" });
28
+ if (!(await scrubber.isVisible())) {
29
+ await page
30
+ .locator('[data-toolcraft-control-target="panels.timeline.extended"]')
31
+ .getByRole("switch")
32
+ .click();
33
+ await expect(scrubber).toBeVisible();
34
+ }
35
+ const pause = page.getByRole("button", { name: "Pause playback" });
36
+ if (await pause.isVisible()) await pause.click();
37
+
38
+ await scrubber.press("Home");
39
+ await expect(scrubber).toHaveAttribute("aria-valuenow", "0");
40
+ const initialStart = await readMarkerSignature(page, options.markerSelector);
41
+ await scrubber.press("End");
42
+ const initialEnd = await readMarkerSignature(page, options.markerSelector);
43
+ expect(initialEnd).toBe(initialStart);
44
+ await scrubber.press("ArrowLeft");
45
+ const scrubbed = await readMarkerSignature(page, options.markerSelector);
46
+ expect(scrubbed).not.toBe(initialStart);
47
+ await attachToolcraftBrowserRuntimeEvidence({
48
+ evidenceType: "timeline-scrub",
49
+ requirementId: options.requirementId,
50
+ target: "timeline.playback",
51
+ });
52
+ await attachToolcraftBrowserRuntimeEvidence({
53
+ evidenceType: "timeline-rendered-frame",
54
+ requirementId: options.requirementId,
55
+ target: "timeline.playback",
56
+ });
57
+ await attachToolcraftBrowserRuntimeEvidence({
58
+ evidenceType: "product-observable-change",
59
+ requirementId: options.requirementId,
60
+ target: "timeline.playback",
61
+ });
62
+
63
+ await page.getByRole("button", { name: "Edit timeline duration" }).click();
64
+ const durationEditor = page.getByRole("textbox", { name: "timeline duration" });
65
+ await durationEditor.fill("2s");
66
+ await durationEditor.press("Enter");
67
+ await expect(scrubber).toHaveAttribute("aria-valuemax", "2");
68
+ await scrubber.press("Home");
69
+ const resizedStart = await readMarkerSignature(page, options.markerSelector);
70
+ await scrubber.press("End");
71
+ const resizedEnd = await readMarkerSignature(page, options.markerSelector);
72
+ expect(resizedEnd).toBe(resizedStart);
73
+ await attachToolcraftBrowserRuntimeEvidence({
74
+ evidenceType: "timeline-duration",
75
+ requirementId: options.requirementId,
76
+ target: "timeline.playback",
77
+ });
78
+ await attachToolcraftBrowserRuntimeEvidence({
79
+ evidenceType: "timeline-loop",
80
+ requirementId: options.requirementId,
81
+ target: "timeline.playback",
82
+ });
83
+
84
+ await scrubber.press("Home");
85
+ await page.getByRole("button", { name: "Play playback" }).click();
86
+ await expect
87
+ .poll(() => scrubber.getAttribute("aria-valuenow"))
88
+ .not.toBe("0");
89
+ await page.getByRole("button", { name: "Pause playback" }).click();
90
+ const pausedTime = await scrubber.getAttribute("aria-valuenow");
91
+ const pausedSignature = await readMarkerSignature(page, options.markerSelector);
92
+ await page.waitForTimeout(100);
93
+ expect(await scrubber.getAttribute("aria-valuenow")).toBe(pausedTime);
94
+ expect(await readMarkerSignature(page, options.markerSelector)).toBe(
95
+ pausedSignature,
96
+ );
97
+ await page.getByRole("button", { name: "Play playback" }).click();
98
+ await expect
99
+ .poll(() => scrubber.getAttribute("aria-valuenow"))
100
+ .not.toBe(pausedTime);
101
+ await attachToolcraftBrowserRuntimeEvidence({
102
+ evidenceType: "timeline-pause-resume",
103
+ requirementId: options.requirementId,
104
+ target: "timeline.playback",
105
+ });
106
+ }