@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
@@ -14,6 +14,81 @@ import {
14
14
  setProofState,
15
15
  } from "./browser-semantic-evidence-test-helpers";
16
16
 
17
+ const orientationBaseline: ToolcraftOrientationBrowserObservation = {
18
+ outputSignature: "model-front",
19
+ pixelSignature: "pixels-front",
20
+ pose: { position: [0, 0, 5], up: [0, 1, 0] },
21
+ poseTarget: "view.orbit",
22
+ presentationCacheKey: "appearance:document-model:v2",
23
+ presentationDocumentId: "document-model",
24
+ viewportOffsetX: 0,
25
+ viewportOffsetY: 0,
26
+ };
27
+
28
+ const orientationDragged: ToolcraftOrientationBrowserObservation = {
29
+ ...orientationBaseline,
30
+ outputSignature: "model-dragged",
31
+ pixelSignature: "pixels-dragged",
32
+ pose: { position: [1, 1, 4.8], up: [0, 1, 0] },
33
+ };
34
+
35
+ const orientationOptions = {
36
+ requirementId: "model.orientation",
37
+ stabilityIntervalMs: 0,
38
+ target: "view.orbit",
39
+ } as const;
40
+
41
+ type OrientationProofUiState = Readonly<{
42
+ playbackActions?: readonly ("pause" | "play")[];
43
+ renderScales?: readonly Readonly<{
44
+ maximum: string;
45
+ value: string;
46
+ }>[];
47
+ }>;
48
+
49
+ async function installOrientationProofUiState(
50
+ page: Page,
51
+ state: OrientationProofUiState,
52
+ ): Promise<void> {
53
+ await page.locator('[data-slot="toolcraft-runtime-app"]').evaluate(
54
+ (root, proofState) => {
55
+ root.querySelector('[data-orientation-proof-state]')?.remove();
56
+ const fixture = document.createElement("div");
57
+ fixture.dataset.orientationProofState = "";
58
+
59
+ for (const action of proofState.playbackActions ?? []) {
60
+ const button = document.createElement("button");
61
+ const label = action === "play" ? "Play playback" : "Pause playback";
62
+ button.type = "button";
63
+ button.textContent = label;
64
+ button.setAttribute("aria-label", label);
65
+ fixture.append(button);
66
+ }
67
+
68
+ for (const [index, renderScale] of (
69
+ proofState.renderScales ?? []
70
+ ).entries()) {
71
+ const field = document.createElement("div");
72
+ field.dataset.toolcraftControlTarget = "canvas.renderScale";
73
+ field.dataset.orientationProofRenderScale = String(index);
74
+ const slider = document.createElement("div");
75
+ slider.dataset.slot = "slider";
76
+ slider.setAttribute("aria-valuemax", renderScale.maximum);
77
+ slider.setAttribute("aria-valuemin", "1");
78
+ slider.setAttribute("aria-valuenow", renderScale.value);
79
+ slider.setAttribute("role", "slider");
80
+ Object.assign(slider.style, { height: "20px", width: "120px" });
81
+ slider.textContent = "Render scale";
82
+ field.append(slider);
83
+ fixture.append(field);
84
+ }
85
+
86
+ root.append(fixture);
87
+ },
88
+ state,
89
+ );
90
+ }
91
+
17
92
  async function installOrientationGizmoPointerProbe(
18
93
  page: Page,
19
94
  initial: ToolcraftOrientationBrowserObservation,
@@ -123,33 +198,107 @@ async function installModelOrbitPointerProbe(
123
198
  );
124
199
  }
125
200
 
201
+ test("orientation axis drag validates paused maximum-quality proof state", async ({
202
+ page,
203
+ }, testInfo) => {
204
+ await page.goto("/");
205
+ const session = await createToolcraftBrowserProofSession(page);
206
+ const attachmentCount = testInfo.attachments.length;
207
+ const observation = session.observe((root) =>
208
+ JSON.parse(root.getAttribute("data-proof-orientation") ?? "null"),
209
+ );
210
+ const invalidStateCases: readonly Readonly<{
211
+ expectedError: RegExp;
212
+ state: OrientationProofUiState;
213
+ }>[] = [
214
+ {
215
+ expectedError: /must be paused.*Play playback/iu,
216
+ state: {
217
+ playbackActions: ["pause"],
218
+ renderScales: [{ maximum: "2", value: "2" }],
219
+ },
220
+ },
221
+ {
222
+ expectedError: /exactly one standard playback action/iu,
223
+ state: { playbackActions: ["play", "pause"] },
224
+ },
225
+ {
226
+ expectedError: /canvas\.renderScale.*maximum/iu,
227
+ state: {
228
+ playbackActions: ["play"],
229
+ renderScales: [{ maximum: "2", value: "1" }],
230
+ },
231
+ },
232
+ {
233
+ expectedError: /exactly one canvas\.renderScale owner/iu,
234
+ state: {
235
+ playbackActions: ["play"],
236
+ renderScales: [
237
+ { maximum: "2", value: "2" },
238
+ { maximum: "2", value: "2" },
239
+ ],
240
+ },
241
+ },
242
+ {
243
+ expectedError: /finite aria-valuenow/iu,
244
+ state: {
245
+ playbackActions: ["play"],
246
+ renderScales: [{ maximum: "2", value: "" }],
247
+ },
248
+ },
249
+ {
250
+ expectedError: /finite aria-valuemax/iu,
251
+ state: {
252
+ playbackActions: ["play"],
253
+ renderScales: [{ maximum: "invalid", value: "2" }],
254
+ },
255
+ },
256
+ ];
257
+
258
+ for (const { expectedError, state } of invalidStateCases) {
259
+ await setProofState(page, "orientation", orientationBaseline);
260
+ await installOrientationGizmoPointerProbe(page, orientationBaseline, {
261
+ outcomes: { drag: orientationDragged },
262
+ });
263
+ await installOrientationProofUiState(page, state);
264
+ await expect(
265
+ expectToolcraftOrientationAxisDrag(observation, session, {
266
+ ...orientationOptions,
267
+ dragDelta: { x: 18, y: 12 },
268
+ }),
269
+ ).rejects.toThrow(expectedError);
270
+ expect(testInfo.attachments).toHaveLength(attachmentCount);
271
+ }
272
+ });
273
+
274
+ test("orientation axis drag retains proof for products without optional playback or render scale", async ({
275
+ page,
276
+ }) => {
277
+ await page.goto("/");
278
+ const session = await createToolcraftBrowserProofSession(page);
279
+ await setProofState(page, "orientation", orientationBaseline);
280
+ const observation = session.observe((root) =>
281
+ JSON.parse(root.getAttribute("data-proof-orientation") ?? "null"),
282
+ );
283
+ await installOrientationGizmoPointerProbe(page, orientationBaseline, {
284
+ outcomes: { drag: orientationDragged },
285
+ });
286
+
287
+ await expectToolcraftOrientationAxisDrag(observation, session, {
288
+ ...orientationOptions,
289
+ dragDelta: { x: 18, y: 12 },
290
+ });
291
+ });
292
+
126
293
  test("orientation recipes require shared pose/output changes and correct canvas ownership", async ({
127
294
  page,
128
295
  }, testInfo) => {
129
296
  await page.goto("/");
130
297
  const session = await createToolcraftBrowserProofSession(page);
131
298
  const attachmentCount = testInfo.attachments.length;
132
- const baseline: ToolcraftOrientationBrowserObservation = {
133
- outputSignature: "model-front",
134
- pixelSignature: "pixels-front",
135
- pose: { position: [0, 0, 5], up: [0, 1, 0] },
136
- poseTarget: "view.orbit",
137
- presentationCacheKey: "appearance:document-model:v2",
138
- presentationDocumentId: "document-model",
139
- viewportOffsetX: 0,
140
- viewportOffsetY: 0,
141
- };
142
- const dragged: ToolcraftOrientationBrowserObservation = {
143
- ...baseline,
144
- outputSignature: "model-dragged",
145
- pixelSignature: "pixels-dragged",
146
- pose: { position: [1, 1, 4.8], up: [0, 1, 0] },
147
- };
148
- const options = {
149
- requirementId: "model.orientation",
150
- stabilityIntervalMs: 0,
151
- target: "view.orbit",
152
- } as const;
299
+ const baseline = orientationBaseline;
300
+ const dragged = orientationDragged;
301
+ const options = orientationOptions;
153
302
 
154
303
  await setProofState(page, "orientation", baseline);
155
304
  const observation = session.observe((root) =>
@@ -175,6 +324,10 @@ test("orientation recipes require shared pose/output changes and correct canvas
175
324
  expect(testInfo.attachments).toHaveLength(attachmentCount);
176
325
 
177
326
  await setProofState(page, "orientation", baseline);
327
+ await installOrientationProofUiState(page, {
328
+ playbackActions: ["play"],
329
+ renderScales: [{ maximum: "2", value: "2" }],
330
+ });
178
331
  await installOrientationGizmoPointerProbe(page, baseline, {
179
332
  dragCommit: "pointerup",
180
333
  outcomes: { drag: dragged },
@@ -1,5 +1,8 @@
1
1
  import { expect, test, type Page, type TestInfo } from "@playwright/test";
2
2
 
3
+ import {
4
+ parseToolcraftBrowserRuntimeEvidence,
5
+ } from "../src/app/test-evidence/browser-runtime-contract";
3
6
  import { attachedEvidenceTypes } from "./browser-semantic-evidence-test-helpers";
4
7
  import {
5
8
  expectToolcraftCanvasRenderScaleEvidence,
@@ -89,6 +92,24 @@ test("selected 2x rejects a 1x backing store and attaches no evidence", async ({
89
92
  expect(testInfo.attachments).toHaveLength(attachmentCount);
90
93
  });
91
94
 
95
+ test("selected 2x rejects a 3x backing store and attaches no evidence", async ({
96
+ page,
97
+ }, testInfo: TestInfo) => {
98
+ await installCanvas(page, 3);
99
+ const attachmentCount = testInfo.attachments.length;
100
+
101
+ await expect(
102
+ expectToolcraftCanvasRenderScaleEvidence(page, {
103
+ canvasSelector,
104
+ requirementId: "canvas.render-scale",
105
+ selectedScale: 2,
106
+ stateTransitions: [transition("steady", () => setBackingScale(page, 3))],
107
+ target: "canvas.renderScale",
108
+ }),
109
+ ).rejects.toThrow(/backing width.*one physical pixel/iu);
110
+ expect(testInfo.attachments).toHaveLength(attachmentCount);
111
+ });
112
+
92
113
  test("a backing clamp introduced by a state transition fails the whole recipe", async ({
93
114
  page,
94
115
  }, testInfo: TestInfo) => {
@@ -164,15 +185,23 @@ test("performance render-scale evidence attaches only after real backing proof",
164
185
  const attachmentCount = testInfo.attachments.length;
165
186
 
166
187
  await expectToolcraftPerformanceRenderScaleBackingEvidence(page, {
188
+ baselineCssSize: { height: 80, width: 120 },
167
189
  canvasSelector,
190
+ cssSizePolicy: "fixed",
168
191
  pathId: "performance-path:render-scale",
169
- selectedScale: 2,
192
+ phase: "cold",
170
193
  target: "canvas.renderScale",
171
194
  });
172
195
 
173
- expect(attachedEvidenceTypes(testInfo, attachmentCount)).toEqual([
174
- "performance-render-scale",
175
- ]);
196
+ const [qualityEvidence] = testInfo.attachments
197
+ .slice(attachmentCount)
198
+ .map(parseToolcraftBrowserRuntimeEvidence)
199
+ .filter(
200
+ (evidence) => evidence?.evidenceType === "performance-render-scale",
201
+ );
202
+ expect(qualityEvidence?.requirementId).toBe(
203
+ "performance-path:render-scale#cold",
204
+ );
176
205
  });
177
206
 
178
207
  test("performance render-scale evidence rejects a quality clamp without attaching", async ({
@@ -183,11 +212,36 @@ test("performance render-scale evidence rejects a quality clamp without attachin
183
212
 
184
213
  await expect(
185
214
  expectToolcraftPerformanceRenderScaleBackingEvidence(page, {
215
+ baselineCssSize: { height: 80, width: 120 },
186
216
  canvasSelector,
217
+ cssSizePolicy: "fixed",
187
218
  pathId: "performance-path:render-scale",
188
- selectedScale: 2,
219
+ phase: "warm",
189
220
  target: "canvas.renderScale",
190
221
  }),
191
- ).rejects.toThrow(/backing width.*Resolution scale 2/iu);
222
+ ).rejects.toThrow(/warm.*backing width/iu);
223
+ expect(testInfo.attachments).toHaveLength(attachmentCount);
224
+ });
225
+
226
+ test("performance render-scale evidence rejects a changed CSS baseline", async ({
227
+ page,
228
+ }, testInfo: TestInfo) => {
229
+ await installCanvas(page, 2);
230
+ const attachmentCount = testInfo.attachments.length;
231
+ await page.locator(canvasSelector).evaluate((canvas) => {
232
+ canvas.setAttribute("style", "display:block;width:60px;height:40px");
233
+ });
234
+ await setBackingScale(page, 2);
235
+
236
+ await expect(
237
+ expectToolcraftPerformanceRenderScaleBackingEvidence(page, {
238
+ baselineCssSize: { height: 80, width: 120 },
239
+ canvasSelector,
240
+ cssSizePolicy: "fixed",
241
+ pathId: "performance-path:render-scale",
242
+ phase: "sustained",
243
+ target: "canvas.renderScale",
244
+ }),
245
+ ).rejects.toThrow(/sustained.*preserve.*CSS width/iu);
192
246
  expect(testInfo.attachments).toHaveLength(attachmentCount);
193
247
  });
@@ -1,7 +1,14 @@
1
1
  import { expect, test } from "@playwright/test";
2
+ import {
3
+ defineToolcraft,
4
+ type ToolcraftPerformancePath,
5
+ } from "@repo/toolcraft-runtime";
2
6
 
3
7
  import type { ToolcraftComponentAcceptance } from "../src/app/starter-acceptance";
4
- import { deriveToolcraftBrowserRuntimeRequirements } from "./browser-runtime-evidence-requirements";
8
+ import {
9
+ deriveToolcraftBrowserRuntimeRequirements,
10
+ deriveToolcraftPerformancePathRuntimeRequirements,
11
+ } from "./browser-runtime-evidence-requirements";
5
12
 
6
13
  function renderScaleAcceptance(
7
14
  states: readonly ("interaction" | "playback" | "steady")[],
@@ -57,6 +64,20 @@ test("render-scale coverage derives one exact functional requirement per state",
57
64
  ]);
58
65
  });
59
66
 
67
+ test("render-scale backing replaces generic visual-change evidence", () => {
68
+ const requirements = deriveToolcraftBrowserRuntimeRequirements([
69
+ renderScaleAcceptance(["interaction", "steady"]),
70
+ ]);
71
+
72
+ expect(requirements.map(({ evidenceType }) => evidenceType)).not.toContain(
73
+ "product-observable-change",
74
+ );
75
+ expect(requirements.map(({ evidenceType }) => evidenceType)).toEqual([
76
+ "canvas-render-scale-backing",
77
+ "canvas-render-scale-backing",
78
+ ]);
79
+ });
80
+
60
81
  test("render-scale functional requirements remain separate from performance evidence", () => {
61
82
  const requirements = deriveToolcraftBrowserRuntimeRequirements([
62
83
  renderScaleAcceptance(["interaction", "steady"]),
@@ -69,3 +90,58 @@ test("render-scale functional requirements remain separate from performance evid
69
90
  "performance-render-scale",
70
91
  );
71
92
  });
93
+
94
+ const unrelatedPerformancePaths = [
95
+ {
96
+ id: "performance-path:color",
97
+ interaction: "control-drag",
98
+ invalidates: ["composite"],
99
+ profile: "live-interaction",
100
+ runsOn: ["main"],
101
+ targets: ["appearance.color"],
102
+ workloadDimensions: [],
103
+ },
104
+ {
105
+ id: "performance-path:viewport",
106
+ interaction: "viewport-drag",
107
+ invalidates: ["composite"],
108
+ profile: "frame-budget",
109
+ runsOn: ["main"],
110
+ targets: [],
111
+ workloadDimensions: [],
112
+ },
113
+ ] as const satisfies readonly ToolcraftPerformancePath[];
114
+
115
+ test("every raster performance path derives cold, warm, and sustained 2x requirements", () => {
116
+ const requirements = deriveToolcraftPerformancePathRuntimeRequirements(
117
+ unrelatedPerformancePaths,
118
+ defineToolcraft({
119
+ canvas: { enabled: true, renderScale: true },
120
+ panels: {},
121
+ }),
122
+ ).filter(({ evidenceType }) => evidenceType === "performance-render-scale");
123
+
124
+ expect(requirements).toEqual([
125
+ expect.objectContaining({ requirementId: "performance-path:color#cold" }),
126
+ expect.objectContaining({ requirementId: "performance-path:color#warm" }),
127
+ expect.objectContaining({
128
+ requirementId: "performance-path:color#sustained",
129
+ }),
130
+ expect.objectContaining({ requirementId: "performance-path:viewport#cold" }),
131
+ expect.objectContaining({ requirementId: "performance-path:viewport#warm" }),
132
+ expect.objectContaining({
133
+ requirementId: "performance-path:viewport#sustained",
134
+ }),
135
+ ]);
136
+ });
137
+
138
+ test("vector performance paths derive no raster quality requirements", () => {
139
+ const requirements = deriveToolcraftPerformancePathRuntimeRequirements(
140
+ unrelatedPerformancePaths,
141
+ defineToolcraft({ canvas: { enabled: true }, panels: {} }),
142
+ );
143
+
144
+ expect(requirements.map(({ evidenceType }) => evidenceType)).not.toContain(
145
+ "performance-render-scale",
146
+ );
147
+ });
@@ -23,6 +23,13 @@ test("protected observable evidence is attached only after its assertion succeed
23
23
  const output = document.createElement("div");
24
24
  output.dataset.toolcraftProductOutput = "";
25
25
  output.dataset.toolcraftProofProductOutput = "";
26
+ output.style.backgroundColor = "#000000";
27
+ output.style.height = "24px";
28
+ output.style.left = "0";
29
+ output.style.position = "fixed";
30
+ output.style.top = "0";
31
+ output.style.width = "24px";
32
+ output.style.zIndex = "100";
26
33
  output.textContent = "Before";
27
34
  const button = document.createElement("button");
28
35
  button.id = "change-output";
@@ -46,6 +53,7 @@ test("protected observable evidence is attached only after its assertion succeed
46
53
  button.dataset.activated = "true";
47
54
  });
48
55
  await currentPage.locator('[data-toolcraft-proof-product-output]').evaluate((output) => {
56
+ (output as HTMLElement).style.backgroundColor = "#FFFFFF";
49
57
  output.textContent = "After";
50
58
  });
51
59
  }),
@@ -135,6 +135,41 @@ test("performance marker requires every derived scenario evidence type", () => {
135
135
  expect(errors).toContainEqual(expect.stringContaining("performance-budget"));
136
136
  });
137
137
 
138
+ test("performance reporter rejects missing warm and sustained raster quality evidence", () => {
139
+ const testName = "browser perf: toolcraft path performance-path:color";
140
+ const requirements: ToolcraftBrowserRuntimeRequirement[] = [
141
+ "cold",
142
+ "warm",
143
+ "sustained",
144
+ ].map((phase) => ({
145
+ evidenceType: "performance-render-scale",
146
+ requirementId: `performance-path:color#${phase}`,
147
+ target: "appearance.color",
148
+ testName,
149
+ }));
150
+ const marker = fakeTestCase({
151
+ file: "/product/e2e/app-browser-runtime-evidence.spec.ts",
152
+ title: TOOLCRAFT_BROWSER_PERFORMANCE_MARKER_TEST_NAME,
153
+ });
154
+ const scenarioTest = fakeTestCase({
155
+ results: [passedResultWithEvidence(requirements[0]!)],
156
+ title: testName,
157
+ });
158
+ const errors: string[] = [];
159
+ const reporter = new ToolcraftBrowserRuntimeEvidenceReporter({
160
+ acceptanceRequirements: [],
161
+ performanceRequirements: requirements,
162
+ reportError: (error) => errors.push(error),
163
+ });
164
+
165
+ reporter.onBegin?.({} as never, fakeSuite([marker, scenarioTest]));
166
+ expect(reporter.onEnd?.({ status: "passed" } as never)).toEqual({
167
+ status: "failed",
168
+ });
169
+ expect(errors).toContainEqual(expect.stringContaining("#warm"));
170
+ expect(errors).toContainEqual(expect.stringContaining("#sustained"));
171
+ });
172
+
138
173
  test("runtime evidence reporter requires render-scale proof for every covered state", () => {
139
174
  const testName = "browser: canvas render scale backing pixels";
140
175
  const requirements: ToolcraftBrowserRuntimeRequirement[] = [
@@ -35,7 +35,10 @@ test("derived paths own one canonical browser evidence requirement set", () => {
35
35
  },
36
36
  ] as const;
37
37
 
38
- const requirements = deriveToolcraftPerformancePathRuntimeRequirements([path]);
38
+ const requirements = deriveToolcraftPerformancePathRuntimeRequirements(
39
+ [path],
40
+ defineToolcraft({ canvas: { enabled: true }, panels: {} }),
41
+ );
39
42
 
40
43
  expect(requirements.map(({ evidenceType }) => evidenceType)).toEqual([
41
44
  "performance-measurement",
@@ -130,7 +133,7 @@ test("control schema derives segmented and discrete runtime evidence", () => {
130
133
  );
131
134
  });
132
135
 
133
- test("typed visibility and background coverage derive dedicated semantic evidence", () => {
136
+ test("typed background coverage derives dedicated semantic evidence", () => {
134
137
  const schema = defineToolcraft({
135
138
  canvas: { enabled: true },
136
139
  panels: {
@@ -138,19 +141,6 @@ test("typed visibility and background coverage derive dedicated semantic evidenc
138
141
  sections: [
139
142
  {
140
143
  controls: {
141
- conditional: {
142
- defaultValue: 1,
143
- label: "Conditional",
144
- target: "feature.conditional",
145
- type: "slider",
146
- visibleWhen: { equals: true, target: "feature.enabled" },
147
- },
148
- enabled: {
149
- defaultValue: false,
150
- label: "Enabled",
151
- target: "feature.enabled",
152
- type: "switch",
153
- },
154
144
  includeBackground: {
155
145
  defaultValue: true,
156
146
  label: "Include",
@@ -178,14 +168,6 @@ test("typed visibility and background coverage derive dedicated semantic evidenc
178
168
  });
179
169
  const requirements = deriveToolcraftBrowserRuntimeRequirements(
180
170
  [
181
- {
182
- browser: true,
183
- browserTestName: "browser: conditional visibility",
184
- evidence: "product-output",
185
- id: "feature.conditional",
186
- target: "feature.conditional",
187
- visibilityCoverage: ["hidden", "visible"],
188
- },
189
171
  {
190
172
  backgroundOutputCoverage: [
191
173
  "preview-hidden-when-excluded",
@@ -202,17 +184,6 @@ test("typed visibility and background coverage derive dedicated semantic evidenc
202
184
  ],
203
185
  schema,
204
186
  );
205
-
206
- expect(
207
- requirements
208
- .filter((requirement) => requirement.requirementId === "feature.conditional")
209
- .map((requirement) => requirement.evidenceType),
210
- ).toEqual(
211
- expect.arrayContaining([
212
- "conditional-control-hidden",
213
- "conditional-control-visible",
214
- ]),
215
- );
216
187
  expect(
217
188
  requirements
218
189
  .filter((requirement) => requirement.requirementId === "export.includeBackground")
@@ -1,8 +1,6 @@
1
1
  import { expect, test } from "@playwright/test";
2
- import {
3
- expectToolcraftBackgroundOutputSemantics,
4
- expectToolcraftConditionalControlVisibility,
5
- } from "./browser-conditional-output-evidence-helpers";
2
+ import { expectToolcraftBackgroundOutputSemantics } from "./browser-background-output-evidence";
3
+ import { expectToolcraftControlApplicabilityState } from "./browser-control-applicability-evidence";
6
4
  import {
7
5
  expectToolcraftLayerReorder,
8
6
  expectToolcraftLayerSelection,
@@ -12,6 +10,7 @@ import {
12
10
  attachedEvidenceTypes,
13
11
  setProofState,
14
12
  setSyntheticControlOwner,
13
+ setSyntheticSwitchControlOwner,
15
14
  } from "./browser-semantic-evidence-test-helpers";
16
15
  import {
17
16
  expectToolcraftMediaLifecycle,
@@ -169,7 +168,6 @@ test("layer recipes reject collection substitution and prove exact reorder", asy
169
168
  expect(attachedEvidenceTypes(testInfo, attachmentCount)).toEqual([
170
169
  "product-observable-change",
171
170
  "layer-reorder",
172
- "product-observable-change",
173
171
  "layer-selection",
174
172
  ]);
175
173
  });
@@ -316,34 +314,81 @@ test("timeline recipes bind duration to renderer, keyframes to output, and loops
316
314
  ]);
317
315
  });
318
316
 
319
- test("conditional visibility recipe proves target absence and restoration through the gating control", async ({
317
+ test("control applicability recipe proves exact target absence and presence", async ({
320
318
  page,
321
319
  }, testInfo) => {
322
320
  await page.goto("/");
323
321
  const session = await createToolcraftBrowserProofSession(page);
324
322
  const attachmentCount = testInfo.attachments.length;
325
- await setSyntheticControlOwner(page, "feature.enabled", true);
323
+ await setSyntheticSwitchControlOwner(page, "feature.enabled", false);
326
324
  await setSyntheticControlOwner(page, "feature.conditional", true);
327
325
 
328
- await expectToolcraftConditionalControlVisibility(
326
+ const hiddenCase = {
327
+ expectation: "hidden" as const,
328
+ selectorControlType: "switch" as const,
329
+ selectorLabel: "feature.enabled",
330
+ selectorTarget: "feature.enabled",
331
+ selectorValue: false,
332
+ target: "feature.conditional",
333
+ };
334
+ const visibleCase = {
335
+ ...hiddenCase,
336
+ expectation: "visible" as const,
337
+ selectorValue: true,
338
+ };
339
+ const mismatchedAction = session.controlAction(
340
+ "feature.other",
341
+ () => undefined,
342
+ );
343
+
344
+ await expect(
345
+ expectToolcraftControlApplicabilityState(
346
+ session,
347
+ mismatchedAction,
348
+ hiddenCase,
349
+ { baseRequirementId: "feature.conditional", timeoutMs: 50 },
350
+ ),
351
+ ).rejects.toThrow(/target-scoped selector action/);
352
+ expect(testInfo.attachments).toHaveLength(attachmentCount);
353
+
354
+ await expect(
355
+ expectToolcraftControlApplicabilityState(
356
+ session,
357
+ session.controlAction("feature.enabled", () => undefined),
358
+ hiddenCase,
359
+ { baseRequirementId: "feature.conditional", timeoutMs: 50 },
360
+ ),
361
+ ).rejects.toThrow(/should be hidden/);
362
+ expect(testInfo.attachments).toHaveLength(attachmentCount);
363
+
364
+ await expectToolcraftControlApplicabilityState(
329
365
  session,
330
366
  session.controlAction("feature.enabled", (control, currentPage) => {
331
367
  expect(control).toBeTruthy();
332
- return setSyntheticControlOwner(currentPage, "feature.conditional", false);
368
+ return Promise.all([
369
+ setSyntheticSwitchControlOwner(currentPage, "feature.enabled", false),
370
+ setSyntheticControlOwner(currentPage, "feature.conditional", false),
371
+ ]).then(() => undefined);
333
372
  }),
373
+ hiddenCase,
374
+ { baseRequirementId: "feature.conditional" },
375
+ );
376
+ await expectToolcraftControlApplicabilityState(
377
+ session,
334
378
  session.controlAction("feature.enabled", (control, currentPage) => {
335
379
  expect(control).toBeTruthy();
336
- return setSyntheticControlOwner(currentPage, "feature.conditional", true);
380
+ return Promise.all([
381
+ setSyntheticSwitchControlOwner(currentPage, "feature.enabled", true),
382
+ setSyntheticControlOwner(currentPage, "feature.conditional", true),
383
+ ]).then(() => undefined);
337
384
  }),
338
- {
339
- requirementId: "feature.conditional",
340
- target: "feature.conditional",
341
- },
385
+ visibleCase,
386
+ { baseRequirementId: "feature.conditional" },
342
387
  );
343
388
 
344
389
  expect(attachedEvidenceTypes(testInfo, attachmentCount)).toEqual([
345
- "conditional-control-hidden",
346
- "conditional-control-visible",
390
+ "control-applicability-hidden",
391
+ "control-applicability-visible",
347
392
  ]);
348
393
  });
349
394