@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
@@ -64,6 +64,20 @@ describe("generateToolcraft", () => {
64
64
  );
65
65
  assert.match(performanceSource, /workloadEnvelope:\s*\{\s*dimensions:\s*\[\]\s*\}/u);
66
66
  assert.doesNotMatch(performanceSource, /\bworkloadTargets\s*:/u);
67
+
68
+ assert.ok(
69
+ await fs.stat(
70
+ path.join(
71
+ targetDir,
72
+ "src/toolcraft/runtime/schema/canvas-render-scale.ts",
73
+ ),
74
+ ),
75
+ );
76
+ const gitignoreSource = await fs.readFile(
77
+ path.join(targetDir, ".gitignore"),
78
+ "utf8",
79
+ );
80
+ assert.match(gitignoreSource, /^\.playwright-mcp$/mu);
67
81
  });
68
82
 
69
83
  it("tests, typechecks, and builds a non-neutral generated product in isolation", async () => {
@@ -152,6 +152,38 @@ describe("Toolcraft template component contracts: choices and actions", () => {
152
152
  expect(contract.aiUsageRules).toContain(
153
153
  "Use FontPicker as the collection item control when each repeated item is a typography/text-style entity; do not split its font, color, opacity, size, case, letter-spacing, or line-height into sibling collection fields.",
154
154
  );
155
+ expect(contract.aiUsageRules).toContain(
156
+ "Use itemControl when each repeated entity is one built-in value. Use itemControls only when two or more built-in fields describe one logical repeated product entity and are added or removed atomically.",
157
+ );
158
+ expect(contract.decisionCatalog?.layoutConstraints).toContain(
159
+ "Compound itemControls records render one line only between adjacent logical items, with no generated Item N headings; standalone color itemControl collections keep the compact two-column grid without item dividers.",
160
+ );
161
+ expect(contract.decisionCatalog?.requiredAcceptance).toContain(
162
+ "For itemControls, prove plus creates every declared default field in one runtime record, editing preserves sibling fields, and minus removes the whole final record from preview and export.",
163
+ );
164
+ });
165
+
166
+ it("documents SourceCollection as source-cardinality built-in item controls", () => {
167
+ const contract = getToolcraftComponentContract("sourceCollection");
168
+
169
+ expect(contract.stateMode).toBe("controlled");
170
+ expect(contract.visualComponent).toBe("ControlsPanelCollectionItems");
171
+ expect(contract.decisionCatalog?.strictness).toBe("exact-owner");
172
+ expect(contract.decisionCatalog?.ownsValueModel).toContain(
173
+ "source-owned collection cardinality",
174
+ );
175
+ expect(contract.decisionCatalog?.useWhen.join(" ")).toMatch(
176
+ /loaded|derived|detected/i,
177
+ );
178
+ expect(contract.decisionCatalog?.doNotReplaceWith?.join(" ")).toMatch(
179
+ /custom control|copied|recreated/i,
180
+ );
181
+ expect(contract.decisionCatalog?.requiredAcceptance.join(" ")).toMatch(
182
+ /source item count.*canvas preview.*export/i,
183
+ );
184
+ expect(contract.aiUsageRules.join(" ")).toMatch(
185
+ /built-in itemControl.*must not render add or remove/i,
186
+ );
155
187
  });
156
188
 
157
189
  it("documents segmented controls as compact selector-only choices", () => {
@@ -216,10 +248,10 @@ describe("Toolcraft template component contracts: choices and actions", () => {
216
248
  "Do not use panelActions for resetting controls; the controls panel header owns Reset controls.",
217
249
  );
218
250
  expect(contract.aiUsageRules).toContain(
219
- "Handle product-specific panelActions through ToolcraftApp onPanelAction.",
251
+ "Handle product-specific non-export panelActions through ToolcraftApp onPanelAction. Typed export-image and export-video actions are runtime-owned and consume ToolcraftAppComposition.exportRenderer.",
220
252
  );
221
253
  expect(contract.aiUsageRules).toContain(
222
- "Async product actions such as Export, Download, Copy, Generate, or Apply must return the real Promise from onPanelAction and report progress through the onPanelAction reportProgress callback.",
254
+ "Async non-export product actions such as Download, Copy, Generate, or Apply must return the real Promise from onPanelAction and report progress through the onPanelAction reportProgress callback.",
223
255
  );
224
256
  expect(contract.aiUsageRules).toContain(
225
257
  "The sticky footer top accent indicator is determinate when reportProgress receives 0..1 values and falls back to pending state only when progress is unavailable.",
@@ -228,13 +260,13 @@ describe("Toolcraft template component contracts: choices and actions", () => {
228
260
  "defineToolcraft hoists panelActions into the controls panel sticky footer automatically.",
229
261
  );
230
262
  expect(contract.aiUsageRules).toContain(
231
- "Product-output apps must always include export in panelActions.",
263
+ "Every product must declare productReadiness.exportIntent before authoring export panelActions or settings sections.",
232
264
  );
233
265
  expect(contract.aiUsageRules).toContain(
234
266
  'Export-labeled panelActions use icon "upload-simple", matching the Setup "Export Settings" action; do not use "download", "download-simple", or "export" icons for Export PNG or Export Video.',
235
267
  );
236
268
  expect(contract.aiUsageRules).toContain(
237
- "Static or still-output apps include Export PNG as the primary footer action.",
269
+ 'Image export is the Toolcraft product default. Keep Export PNG unless productReadiness.exportIntent.image is "user-removed" with non-empty explicit user-removal evidence.',
238
270
  );
239
271
  expect(contract.aiUsageRules).toContain(
240
272
  'Every app with Export PNG must expose a separate "Image Export" controls section.',
@@ -249,10 +281,13 @@ describe("Toolcraft template component contracts: choices and actions", () => {
249
281
  "Image Export format and resolution render as one compact two-column inline Select pair, matching the Video Export settings structure.",
250
282
  );
251
283
  expect(contract.aiUsageRules).toContain(
252
- "Image Export resolution controls the actual exported image long edge: 2K = 2048px, 4K = 4096px, 8K = 8192px. Pass the selected runtime value to createToolcraftPngExportCanvas resolution and prove decoded image width/height in browser acceptance.",
284
+ "Image Export resolution controls the actual exported image long edge: 2K = 2048px, 4K = 4096px, 8K = 8192px. Runtime resolves the selected value and protected browser acceptance proves decoded image width and height.",
253
285
  );
254
286
  expect(contract.aiUsageRules).toContain(
255
- "Apps that deliver animation include Export Video as the primary footer action and Export PNG as a secondary footer action; timeline playback alone does not invent video delivery.",
287
+ 'Add Export Video only when productReadiness.exportIntent.video is "user-requested" with non-empty explicit user-request evidence.',
288
+ );
289
+ expect(contract.aiUsageRules).toContain(
290
+ 'Animation, playback, keyframes, and timeline presence never add Export Video when productReadiness.exportIntent.video is "not-requested".',
256
291
  );
257
292
  expect(contract.aiUsageRules).toContain(
258
293
  "Any app with Export Video must enable the top Toolcraft timeline; video duration, loop, and rendered timestamps come from runtime timeline state.",
@@ -263,6 +298,15 @@ describe("Toolcraft template component contracts: choices and actions", () => {
263
298
  expect(contract.aiUsageRules).toContain(
264
299
  'Apps with both Export PNG and Export Video must expose both "Image Export" and "Video Export"; Image Export sits immediately before Video Export.',
265
300
  );
301
+ expect(contract.aiUsageRules).toContain(
302
+ 'Image-only apps place "Image Export" directly above sticky footer panelActions.',
303
+ );
304
+ expect(contract.aiUsageRules).toContain(
305
+ 'Video-only apps require explicit image user-removal evidence and place "Video Export" directly above sticky footer panelActions.',
306
+ );
307
+ expect(contract.aiUsageRules).toContain(
308
+ 'Explicit no-export apps require image user-removal evidence with video "not-requested" and omit export actions and settings sections.',
309
+ );
266
310
  expect(contract.aiUsageRules).toContain(
267
311
  'The Video Export section must include format and resolution controls such as targets "export.video.format" and "export.video.resolution".',
268
312
  );
@@ -285,16 +329,16 @@ describe("Toolcraft template component contracts: choices and actions", () => {
285
329
  'Baseline browser video formats are "mp4" and "webm"; MOV or ProRes require an explicit custom encoder/transcoder and dedicated acceptance plus performance coverage.',
286
330
  );
287
331
  expect(contract.aiUsageRules).toContain(
288
- "Video export code must choose the actual MIME/container through MediaRecorder.isTypeSupported or an equivalent encoder capability check, then fall back safely.",
332
+ "Runtime video export chooses the actual MIME, container, and codec through the timestamped Mediabunny encoder capability check, then returns the real supported format or a typed visible failure.",
289
333
  );
290
334
  expect(contract.aiUsageRules).toContain(
291
335
  "Video export must use getToolcraftVideoExportSize for current and 4K dimensions. Current video export uses the current canvas/output size with even encoder-safe rounding; 4K video fits inside an encoder-safe 3840x2160 box, preserves canvas aspect ratio, and uses even pixel dimensions. Do not hand-roll 4096px long-edge video sizing.",
292
336
  );
293
337
  expect(contract.aiUsageRules).toContain(
294
- "Video export must set recording canvas dimensions before captureStream, MediaRecorder, VideoEncoder, or equivalent encoder setup, and must reject recorder/encoder errors instead of returning corrupt blobs.",
338
+ "Runtime video export allocates the selected recording canvas dimensions before timestamped encoder setup and rejects renderer, encoder, muxer, and output-limit errors instead of returning corrupt blobs.",
295
339
  );
296
340
  expect(contract.aiUsageRules).toContain(
297
- "Offline video export duration must be encoded from runtime timeline timestamps. Do not rely on canvas.captureStream plus MediaRecorder wall-clock recording time as the only duration mechanism for rendered-frame export.",
341
+ "Offline video export duration is encoded from the fixed runtime 30 FPS timeline schedule. Product code must not use canvas.captureStream, MediaRecorder, or wall-clock time as a fallback.",
298
342
  );
299
343
  expect(contract.aiUsageRules).toContain(
300
344
  'Video resolution must control exported dimensions. Use "current" output size by default; "4K" is an export resolution target, not a hardcoded 3840x2160 canvas lock.',
@@ -309,13 +353,16 @@ describe("Toolcraft template component contracts: choices and actions", () => {
309
353
  'Product-output apps declare the standard background pair in an authored "Background" source section. Runtime removes that visible section and places Background plus Infinity canvas, then Background color, in Setup; Timeline is the final Setup control.',
310
354
  );
311
355
  expect(contract.aiUsageRules).toContain(
312
- "Product-output apps pass the includeBackground runtime value to createToolcraftPngExportCanvas for PNG alpha and call shouldIncludeToolcraftPreviewBackground(state) for bounded live preview background. In Infinity mode CanvasShell owns the selected full-viewport background color, and Background off restores finite mode and disables Infinity.",
356
+ "Product-output apps call shouldIncludeToolcraftPreviewBackground(state) for bounded live preview background. Runtime artifact export owns Background semantics: PNG may be transparent, JPEG and video remain opaque, Infinity uses the full-viewport color, and Background off restores finite mode and disables Infinity.",
357
+ );
358
+ expect(contract.aiUsageRules).toContain(
359
+ "ToolcraftAppComposition.exportRenderer draws one deterministic product frame in scene coordinates for both image and video. Runtime owns canvas allocation, background, runtime media/model compositing, selected dimensions, encoding, download, progress, and typed failures.",
313
360
  );
314
361
  expect(contract.aiUsageRules).toContain(
315
- "PNG export must use createToolcraftPngExportCanvas so background transparency and selected image dimensions or retina fallback are applied consistently; turning Background off makes preview product background and PNG alpha transparent without hiding the Toolcraft canvas backing or video background.",
362
+ "Product modules must not encode or download export canvases directly and must not instantiate MediaRecorder or VideoEncoder; the generated-source boundary rejects those duplicate export paths.",
316
363
  );
317
364
  expect(contract.aiUsageRules).toContain(
318
- "Video export must keep product background and use getToolcraftVideoExportSize for current/4K output dimensions.",
365
+ "Runtime video export keeps the selected background and uses getToolcraftVideoExportSize for current/4K output dimensions.",
319
366
  );
320
367
  expect(contract.aiUsageRules).toContain(
321
368
  "Copy PNG can be a secondary action when clipboard output is useful, but copy does not replace export.",
@@ -264,6 +264,7 @@ export const TOOLCRAFT_CHOICE_COMPONENT_CONTRACTS = {
264
264
  "CollectionActions sits at the start of its section, renders the collection label on the left, and keeps remove/add icon buttons together on the right.",
265
265
  "Homogeneous repeated item controls do not render visible per-item labels when the collection label already names the group.",
266
266
  "Collection item controls follow normal density rules: plain color items use equal 50% columns when they fit, while color+opacity items stay stacked.",
267
+ "Compound itemControls records render one line only between adjacent logical items, with no generated Item N headings; standalone color itemControl collections keep the compact two-column grid without item dividers.",
267
268
  "CollectionActions is a compound control and follows content-width compound divider rules when sharing a section with sibling controls.",
268
269
  "recommendedMaxItems is an agent/layout/performance hint, not a hard add limit; hardMaxItems is allowed only for real algorithm, format, API, export, or proven performance limits.",
269
270
  ],
@@ -271,6 +272,7 @@ export const TOOLCRAFT_CHOICE_COMPONENT_CONTRACTS = {
271
272
  "Prove plus adds a runtime item and that the new item appears in or affects canvas preview and export.",
272
273
  "Prove minus removes a runtime item and that the removed item disappears from or stops affecting canvas preview and export.",
273
274
  "Prove minItems prevents deleting below the minimum and recommendedMaxItems does not silently block adding more items.",
275
+ "For itemControls, prove plus creates every declared default field in one runtime record, editing preserves sibling fields, and minus removes the whole final record from preview and export.",
274
276
  ],
275
277
  }),
276
278
  stateMode: "controlled",
@@ -280,12 +282,51 @@ export const TOOLCRAFT_CHOICE_COMPONENT_CONTRACTS = {
280
282
  "Do not model add/remove item behavior with a Slider count when users need to edit the actual items.",
281
283
  "recommendedMaxItems is advisory only and must not disable the plus button. Use hardMaxItems only when a real product, algorithm, API, export, or measured performance limit requires it.",
282
284
  "CollectionActions item controls use built-in controls whenever possible, such as Color, ColorOpacity, TextInput, Select, Segmented, Slider, Switch, Checkbox, RangeInput, or FontPicker.",
285
+ "Use itemControl when each repeated entity is one built-in value. Use itemControls only when two or more built-in fields describe one logical repeated product entity and are added or removed atomically.",
286
+ "Every itemControls field must belong to the same repeated product entity, share one parent array record, and affect that entity's product outcome; never group unrelated settings because they happen to use add/remove UI.",
283
287
  "Use FontPicker as the collection item control when each repeated item is a typography/text-style entity; do not split its font, color, opacity, size, case, letter-spacing, or line-height into sibling collection fields.",
284
288
  "Do not add visible labels like Color 1, Color 2, Item 1, or Item 2 for homogeneous collection items when the collection label already explains the group.",
285
289
  "Use compact half-width item layout whenever the child control is allowed to fit in a half row; color items without opacity are the default two-column case.",
286
290
  "Acceptance must add and remove items through the browser UI and prove canvas/export output follows the changed collection.",
287
291
  ],
288
292
  },
293
+ sourceCollection: {
294
+ ...control(
295
+ "sourceCollection",
296
+ "ControlsPanelCollectionItems",
297
+ "standalone",
298
+ "component-owned",
299
+ ),
300
+ decisionCatalog: decisionCatalog({
301
+ strictness: "exact-owner",
302
+ ownsValueModel: [
303
+ "source-owned collection cardinality",
304
+ "source-defined repeatable product entity collection",
305
+ "source-defined dynamic list of visible controls",
306
+ ],
307
+ useWhen: [
308
+ "Use SourceCollection when a loaded, derived, or detected source owns the item array length and the user edits only existing item values.",
309
+ ],
310
+ doNotReplaceWith: [
311
+ "Do not use CollectionActions when the user must not add or remove source-defined items.",
312
+ "Do not rebuild, copy, or recreate the repeated built-in item control in a custom control renderer.",
313
+ ],
314
+ acceptableAlternatives: [
315
+ "Use CollectionActions when the user owns add/remove cardinality.",
316
+ ],
317
+ layoutConstraints: [
318
+ "SourceCollection uses the same built-in item layout as CollectionActions and renders no add/remove header.",
319
+ ],
320
+ requiredAcceptance: [
321
+ "Prove the source item count, item editing, canvas preview, export output, and absence of add/remove commands.",
322
+ ],
323
+ }),
324
+ aiUsageRules: [
325
+ "Use SourceCollection for externally sized arrays of built-in item controls.",
326
+ "Declare a built-in itemControl; SourceCollection must not render add or remove commands.",
327
+ "The source workflow writes the complete target array; the panel edits item values only.",
328
+ ],
329
+ },
289
330
  panelActions: {
290
331
  ...control("panelActions", "PanelActions", "standalone", "component-owned"),
291
332
  decisionCatalog: decisionCatalog({
@@ -319,22 +360,26 @@ export const TOOLCRAFT_CHOICE_COMPONENT_CONTRACTS = {
319
360
  aiUsageRules: [
320
361
  "Use panelActions only for sticky footer product actions such as Generate, Export, Copy, or Download.",
321
362
  "Do not use panelActions for resetting controls; the controls panel header owns Reset controls.",
322
- "Handle product-specific panelActions through ToolcraftApp onPanelAction.",
323
- "Async product actions such as Export, Download, Copy, Generate, or Apply must return the real Promise from onPanelAction and report progress through the onPanelAction reportProgress callback.",
363
+ "Handle product-specific non-export panelActions through ToolcraftApp onPanelAction. Typed export-image and export-video actions are runtime-owned and consume ToolcraftAppComposition.exportRenderer.",
364
+ "Async non-export product actions such as Download, Copy, Generate, or Apply must return the real Promise from onPanelAction and report progress through the onPanelAction reportProgress callback.",
324
365
  "The sticky footer top accent indicator is determinate when reportProgress receives 0..1 values and falls back to pending state only when progress is unavailable.",
325
366
  "defineToolcraft hoists panelActions into the controls panel sticky footer automatically.",
326
- "Product-output apps must always include export in panelActions.",
367
+ "Every product must declare productReadiness.exportIntent before authoring export panelActions or settings sections.",
327
368
  'Export-labeled panelActions use icon "upload-simple", matching the Setup "Export Settings" action; do not use "download", "download-simple", or "export" icons for Export PNG or Export Video.',
328
- "Static or still-output apps include Export PNG as the primary footer action.",
369
+ 'Image export is the Toolcraft product default. Keep Export PNG unless productReadiness.exportIntent.image is "user-removed" with non-empty explicit user-removal evidence.',
329
370
  'Every app with Export PNG must expose a separate "Image Export" controls section.',
330
371
  'The Image Export section must include "export.image.format" as a Select control with PNG and JPG choices, defaulting to "png".',
331
372
  'The Image Export section must include "export.image.resolution" as a Select control with 2K, 4K, and 8K choices, defaulting to "4k".',
332
373
  "Image Export format and resolution render as one compact two-column inline Select pair, matching the Video Export settings structure.",
333
- "Image Export resolution controls the actual exported image long edge: 2K = 2048px, 4K = 4096px, 8K = 8192px. Pass the selected runtime value to createToolcraftPngExportCanvas resolution and prove decoded image width/height in browser acceptance.",
334
- "Apps that deliver animation include Export Video as the primary footer action and Export PNG as a secondary footer action; timeline playback alone does not invent video delivery.",
374
+ "Image Export resolution controls the actual exported image long edge: 2K = 2048px, 4K = 4096px, 8K = 8192px. Runtime resolves the selected value and protected browser acceptance proves decoded image width and height.",
375
+ 'Add Export Video only when productReadiness.exportIntent.video is "user-requested" with non-empty explicit user-request evidence.',
376
+ 'Animation, playback, keyframes, and timeline presence never add Export Video when productReadiness.exportIntent.video is "not-requested".',
335
377
  "Any app with Export Video must enable the top Toolcraft timeline; video duration, loop, and rendered timestamps come from runtime timeline state.",
336
378
  'Apps with Export Video must expose a separate "Video Export" controls section.',
337
379
  'Apps with both Export PNG and Export Video must expose both "Image Export" and "Video Export"; Image Export sits immediately before Video Export.',
380
+ 'Image-only apps place "Image Export" directly above sticky footer panelActions.',
381
+ 'Video-only apps require explicit image user-removal evidence and place "Video Export" directly above sticky footer panelActions.',
382
+ 'Explicit no-export apps require image user-removal evidence with video "not-requested" and omit export actions and settings sections.',
338
383
  'The Video Export section must include format and resolution controls such as targets "export.video.format" and "export.video.resolution".',
339
384
  "Use Select controls for Video Export format and resolution; do not use Segmented unless the product has a deliberately tiny fixed output menu and browser tests prove every cell keeps padding.",
340
385
  'Place the Video Export section as the final controls section directly above sticky footer panelActions.',
@@ -342,17 +387,18 @@ export const TOOLCRAFT_CHOICE_COMPONENT_CONTRACTS = {
342
387
  'Video Export resolution defaults to "current"; keep "4k" available as the high-resolution alternate.',
343
388
  "Video Export format and resolution are a compact semantic pair and should use a two-column inline layout by default; use stacked rows only when labels or selected values do not fit without clipping.",
344
389
  'Baseline browser video formats are "mp4" and "webm"; MOV or ProRes require an explicit custom encoder/transcoder and dedicated acceptance plus performance coverage.',
345
- "Video export code must choose the actual MIME/container through MediaRecorder.isTypeSupported or an equivalent encoder capability check, then fall back safely.",
390
+ "Runtime video export chooses the actual MIME, container, and codec through the timestamped Mediabunny encoder capability check, then returns the real supported format or a typed visible failure.",
346
391
  "Video export must use getToolcraftVideoExportSize for current and 4K dimensions. Current video export uses the current canvas/output size with even encoder-safe rounding; 4K video fits inside an encoder-safe 3840x2160 box, preserves canvas aspect ratio, and uses even pixel dimensions. Do not hand-roll 4096px long-edge video sizing.",
347
- "Video export must set recording canvas dimensions before captureStream, MediaRecorder, VideoEncoder, or equivalent encoder setup, and must reject recorder/encoder errors instead of returning corrupt blobs.",
348
- "Offline video export duration must be encoded from runtime timeline timestamps. Do not rely on canvas.captureStream plus MediaRecorder wall-clock recording time as the only duration mechanism for rendered-frame export.",
392
+ "Runtime video export allocates the selected recording canvas dimensions before timestamped encoder setup and rejects renderer, encoder, muxer, and output-limit errors instead of returning corrupt blobs.",
393
+ "Offline video export duration is encoded from the fixed runtime 30 FPS timeline schedule. Product code must not use canvas.captureStream, MediaRecorder, or wall-clock time as a fallback.",
349
394
  'Video resolution must control exported dimensions. Use "current" output size by default; "4K" is an export resolution target, not a hardcoded 3840x2160 canvas lock.',
350
395
  "Video export browser coverage must load the exported blob metadata and prove video.duration matches the edited runtime timeline duration; blobSize/blobType checks alone are not enough.",
351
396
  "Video export must report frame-based progress through reportProgress during render/encode steps. PNG export should report phase progress for render, blob, and handoff when those phases are asynchronous.",
352
397
  'Product-output apps declare the standard background pair in an authored "Background" source section. Runtime removes that visible section and places Background plus Infinity canvas, then Background color, in Setup; Timeline is the final Setup control.',
353
- "Product-output apps pass the includeBackground runtime value to createToolcraftPngExportCanvas for PNG alpha and call shouldIncludeToolcraftPreviewBackground(state) for bounded live preview background. In Infinity mode CanvasShell owns the selected full-viewport background color, and Background off restores finite mode and disables Infinity.",
354
- "PNG export must use createToolcraftPngExportCanvas so background transparency and selected image dimensions or retina fallback are applied consistently; turning Background off makes preview product background and PNG alpha transparent without hiding the Toolcraft canvas backing or video background.",
355
- "Video export must keep product background and use getToolcraftVideoExportSize for current/4K output dimensions.",
398
+ "Product-output apps call shouldIncludeToolcraftPreviewBackground(state) for bounded live preview background. Runtime artifact export owns Background semantics: PNG may be transparent, JPEG and video remain opaque, Infinity uses the full-viewport color, and Background off restores finite mode and disables Infinity.",
399
+ "ToolcraftAppComposition.exportRenderer draws one deterministic product frame in scene coordinates for both image and video. Runtime owns canvas allocation, background, runtime media/model compositing, selected dimensions, encoding, download, progress, and typed failures.",
400
+ "Product modules must not encode or download export canvases directly and must not instantiate MediaRecorder or VideoEncoder; the generated-source boundary rejects those duplicate export paths.",
401
+ "Runtime video export keeps the selected background and uses getToolcraftVideoExportSize for current/4K output dimensions.",
356
402
  "Copy PNG can be a secondary action when clipboard output is useful, but copy does not replace export.",
357
403
  "Add Copy PNG as a secondary action only when the prompt/reference includes clipboard output or the product clearly benefits from paste/share workflows.",
358
404
  "Footer actions must be one compact horizontal group; do not split them into stacked full-width sections.",
@@ -77,13 +77,13 @@ describe("Toolcraft template component contracts: inputs", () => {
77
77
  "Visual discrete sliders must still drag smoothly; their canonical performance path adapter should use dragToolcraftSliderByTarget for real pointer drag and let the central path profile own the budget.",
78
78
  );
79
79
  expect(slider.aiUsageRules).toContain(
80
- "Use visibleWhen for sliders that are meaningful only in some mode/type/source/include/count states; inactive branches should disappear so the panel shows only controls usable in the current state.",
80
+ 'Every product slider declares applicability as mode: "always" or mode: "conditional"; omitted applicability and legacy visibleWhen are runtime compatibility only.',
81
81
  );
82
82
  expect(slider.aiUsageRules).toContain(
83
- "Do not use schema disabled: true or disabledWhen for product sliders; product panels should show only controls usable in the current state. Use visibleWhen for unavailable product states instead of rendering disabled controls.",
83
+ "Use conditional applicability for sliders that are meaningful only in some mode/type/source/include/count states; every predicate must match and inactive branches disappear.",
84
84
  );
85
85
  expect(slider.aiUsageRules).toContain(
86
- "Do not leave an inactive conditional slider visible while making the renderer ignore it; hide it with visibleWhen.",
86
+ "Do not use schema disabled: true or disabledWhen for product sliders, and do not leave a visible slider in a branch where its value has no product effect.",
87
87
  );
88
88
  expect(rangeSlider.decisionCatalog.requiredAcceptance).toContain(
89
89
  "Prove dragging rangeSlider.lower and rangeSlider.upper both affect product output while the drag is in progress, not only after pointer release, blur, Apply, or a final commit.",
@@ -140,13 +140,13 @@ describe("Toolcraft template component contracts: inputs", () => {
140
140
  "Visual discrete sliders must still drag smoothly; their canonical performance path adapter should use dragToolcraftSliderByTarget for real pointer drag and let the central path profile own the budget.",
141
141
  );
142
142
  expect(rangeSlider.aiUsageRules).toContain(
143
- "Use visibleWhen for range sliders that are meaningful only in some mode/type/source/include/count states; inactive branches should disappear so the panel shows only controls usable in the current state.",
143
+ 'Every product range slider declares applicability as mode: "always" or mode: "conditional"; omitted applicability and legacy visibleWhen are runtime compatibility only.',
144
144
  );
145
145
  expect(rangeSlider.aiUsageRules).toContain(
146
- "Do not use schema disabled: true or disabledWhen for product range sliders; product panels should show only controls usable in the current state. Use visibleWhen for unavailable product states instead of rendering disabled controls.",
146
+ "Use conditional applicability for range sliders that are meaningful only in some mode/type/source/include/count states; every predicate must match and inactive branches disappear.",
147
147
  );
148
148
  expect(rangeSlider.aiUsageRules).toContain(
149
- "Do not leave an inactive conditional range slider visible while making the renderer ignore it; hide it with visibleWhen.",
149
+ "Do not use schema disabled: true or disabledWhen for product range sliders, and do not leave a visible range slider in a branch where its value has no product effect.",
150
150
  );
151
151
  expect(rangeSlider.aiUsageRules).toContain(
152
152
  "Acceptance must prove both rangeSlider.lower and rangeSlider.upper change the product output; testing one handle is not enough.",
@@ -85,9 +85,9 @@ export const TOOLCRAFT_INPUT_COMPONENT_CONTRACTS = {
85
85
  'Specs, plans, and app-schema tests must assert explicit discrete sliders render as variant: "discrete" with markers derived from min, max, and step.',
86
86
  'Browser verification can inspect [data-slot="slider"][data-variant="discrete"] plus slider markers to prove the Toolcraft component variant rendered.',
87
87
  "Visual discrete sliders must still drag smoothly; their canonical performance path adapter should use dragToolcraftSliderByTarget for real pointer drag and let the central path profile own the budget.",
88
- "Use visibleWhen for sliders that are meaningful only in some mode/type/source/include/count states; inactive branches should disappear so the panel shows only controls usable in the current state.",
89
- "Do not use schema disabled: true or disabledWhen for product sliders; product panels should show only controls usable in the current state. Use visibleWhen for unavailable product states instead of rendering disabled controls.",
90
- "Do not leave an inactive conditional slider visible while making the renderer ignore it; hide it with visibleWhen.",
88
+ 'Every product slider declares applicability as mode: "always" or mode: "conditional"; omitted applicability and legacy visibleWhen are runtime compatibility only.',
89
+ "Use conditional applicability for sliders that are meaningful only in some mode/type/source/include/count states; every predicate must match and inactive branches disappear.",
90
+ "Do not use schema disabled: true or disabledWhen for product sliders, and do not leave a visible slider in a branch where its value has no product effect.",
91
91
  ],
92
92
  },
93
93
  rangeSlider: {
@@ -137,9 +137,9 @@ export const TOOLCRAFT_INPUT_COMPONENT_CONTRACTS = {
137
137
  "Manual range value editing accepts common separators such as slash, hyphen, spaces, and dashes, including when values include unit suffixes such as 30%-150% or 30% - 90%; do not create custom parsers for RangeSlider labels.",
138
138
  'Specs, plans, and app-schema tests must assert explicit discrete range sliders render as variant: "discrete" with markers derived from min, max, and step.',
139
139
  "Visual discrete sliders must still drag smoothly; their canonical performance path adapter should use dragToolcraftSliderByTarget for real pointer drag and let the central path profile own the budget.",
140
- "Use visibleWhen for range sliders that are meaningful only in some mode/type/source/include/count states; inactive branches should disappear so the panel shows only controls usable in the current state.",
141
- "Do not use schema disabled: true or disabledWhen for product range sliders; product panels should show only controls usable in the current state. Use visibleWhen for unavailable product states instead of rendering disabled controls.",
142
- "Do not leave an inactive conditional range slider visible while making the renderer ignore it; hide it with visibleWhen.",
140
+ 'Every product range slider declares applicability as mode: "always" or mode: "conditional"; omitted applicability and legacy visibleWhen are runtime compatibility only.',
141
+ "Use conditional applicability for range sliders that are meaningful only in some mode/type/source/include/count states; every predicate must match and inactive branches disappear.",
142
+ "Do not use schema disabled: true or disabledWhen for product range sliders, and do not leave a visible range slider in a branch where its value has no product effect.",
143
143
  "Acceptance must prove both rangeSlider.lower and rangeSlider.upper change the product output; testing one handle is not enough.",
144
144
  ],
145
145
  },
@@ -14,13 +14,13 @@ describe("Toolcraft template component contracts: media and custom controls", ()
14
14
  "Every visible custom-control element must justify its space by enabling selection, ordering, preview, removal, upload, editing, or status that affects the product.",
15
15
  );
16
16
  expect(contract.aiUsageRules).toContain(
17
- "Do not use a custom control to recreate a built-in Slider, RangeSlider, Select, Segmented, Switch, Checkbox, Color, ColorOpacity, Gradient, FontPicker, ImagePicker, FileDrop, TextInput, CodeTextarea, RangeInput, Palette, Actions, CollectionActions, Curves, AnchorGrid, ChannelMixer, Vector, or PanelActions control.",
17
+ "Do not use a custom control to recreate a built-in Slider, RangeSlider, Select, Segmented, Switch, Checkbox, Color, ColorOpacity, Gradient, FontPicker, ImagePicker, FileDrop, TextInput, CodeTextarea, RangeInput, Palette, Actions, CollectionActions, SourceCollection, Curves, AnchorGrid, ChannelMixer, Vector, or PanelActions control.",
18
18
  );
19
19
  expect(contract.aiUsageRules).toContain(
20
20
  "Every custom-control builtInFitCheck declares typed capabilities. Use collection, reorder, selection, commands, custom-interaction, custom-value-model, and custom-visualization to describe behavior without relying on product nouns.",
21
21
  );
22
22
  expect(contract.aiUsageRules).toContain(
23
- "When a custom control owns a growable, removable, selectable, or reorderable runtime item set, its builtInFitCheck must explicitly check collectionActions and actions before choosing custom; this is based on the value model and user workflow, not on entity names such as masks or glyphs.",
23
+ "When a custom control owns a repeated runtime item set, its builtInFitCheck must explicitly check sourceCollection, collectionActions, and actions before choosing custom; this is based on source versus user cardinality and the value workflow, not on entity names such as masks or glyphs.",
24
24
  );
25
25
  expect(contract.aiUsageRules).toContain(
26
26
  "Do not justify custom controls with icons, layout, styling, compactness, or custom buttons alone. The fit check must name the product interaction or value model that built-ins cannot express.",
@@ -107,6 +107,12 @@ describe("Toolcraft template component contracts: media and custom controls", ()
107
107
  expect(contract.aiUsageRules).toContain(
108
108
  "Use fileDrop with multiple: true when the app needs several uploaded images as one source set; do not build a custom thumbnail uploader for this.",
109
109
  );
110
+ expect(contract.aiUsageRules).toContain(
111
+ 'Use fileDrop variant: "collection-actions" only with assetKind: "file" and multiple: true when the user explicitly wants collectionActions-style compact − / + cardinality. The variant keeps runtime media order, uses + to add an empty upload slot, and uses − to remove the final slot or attached file.',
112
+ );
113
+ expect(contract.aiUsageRules).toContain(
114
+ "Each collection-actions FileDrop slot and its itemControls settings form one logical item group; runtime renders one line only between adjacent groups.",
115
+ );
110
116
  expect(contract.aiUsageRules).toContain(
111
117
  "fileDrop recommendedMaxItems is advisory and never rejects an import. A finite nonnegative safe-integer hardMaxItems is an actual admission limit: additive imports count existing assets for the same source target plus the incoming logical batch, replacement imports count only the replacement batch, and overflow is rejected before decoding or repository allocation.",
112
118
  );
@@ -29,6 +29,9 @@ export const TOOLCRAFT_MEDIA_CUSTOM_COMPONENT_CONTRACTS = {
29
29
  "When fileDrop has multiple: true and more than one image is present, the runtime renders a sortable four-column thumbnail grid with the add-more tile last.",
30
30
  "When fileDrop has multiple: true and more than one image is present, image transform actions render only after the user selects a thumbnail, and they target only that selected image.",
31
31
  "When fileDrop has assetKind: file, the runtime renders a sortable file list with a paperclip icon, filename, remove button, and row separators using --border/5.",
32
+ 'A multiple file-kind fileDrop may use variant: "collection-actions" when collection cardinality should use the same compact − / + header as collectionActions. In that variant + adds one empty FileDrop slot and − removes the final slot or its attached file.',
33
+ "A collection-actions fileDrop may declare itemControls so every attached file renders its own built-in settings immediately below its upload row. The parent target stores records keyed by mediaId while binary lifecycle remains in runtime media state.",
34
+ "Each collection-actions FileDrop slot and its itemControls settings form one logical item group; runtime renders one line only between adjacent groups.",
32
35
  "When fileDrop has assetKind: model, standard model preview remains runtime-owned even when renderDefaultCanvasMedia is false; custom presentation is selected only through ToolcraftAppComposition modelPresentation.",
33
36
  ],
34
37
  requiredAcceptance: [
@@ -56,6 +59,9 @@ export const TOOLCRAFT_MEDIA_CUSTOM_COMPONENT_CONTRACTS = {
56
59
  "In file mode, the runtime shows uploaded files as a sortable list with paperclip icons, file names, remove buttons, and --border/5 separators.",
57
60
  "In single-layer apps, global Reset controls and section reset must restore fileDrop source media to schema media.defaultAssets for that target; when no default asset exists, Reset removes uploaded media and returns the fileDrop target to defaultValue.",
58
61
  "Use fileDrop with multiple: true when the app needs several uploaded images as one source set; do not build a custom thumbnail uploader for this.",
62
+ 'Use fileDrop variant: "collection-actions" only with assetKind: "file" and multiple: true when the user explicitly wants collectionActions-style compact − / + cardinality. The variant keeps runtime media order, uses + to add an empty upload slot, and uses − to remove the final slot or attached file.',
63
+ "Use fileDrop itemControls when settings belong to individual attached files. Each nested control declares a defaultValue; only attached slots render settings, and the runtime commits a parent-target array keyed by mediaId for reset, undo, and persistence.",
64
+ "Each collection-actions FileDrop slot and its itemControls settings form one logical item group; runtime renders one line only between adjacent groups.",
59
65
  "fileDrop recommendedMaxItems is advisory and never rejects an import. A finite nonnegative safe-integer hardMaxItems is an actual admission limit: additive imports count existing assets for the same source target plus the incoming logical batch, replacement imports count only the replacement batch, and overflow is rejected before decoding or repository allocation.",
60
66
  "When multiple uploaded images are present, the runtime appends media, shows a sortable four-column preview grid, puts the add-more tile last, and exposes per-image removal.",
61
67
  "Canvas drops route to the first visible matching fileDrop target by asset kind: image files prefer image uploaders, non-image files prefer file uploaders, and file uploaders accept images only when no image uploader matches.",
@@ -134,9 +140,9 @@ export const TOOLCRAFT_MEDIA_CUSTOM_COMPONENT_CONTRACTS = {
134
140
  }),
135
141
  aiUsageRules: [
136
142
  "Use custom controls only for product interactions that built-in controls cannot express.",
137
- "Do not use a custom control to recreate a built-in Slider, RangeSlider, Select, Segmented, Switch, Checkbox, Color, ColorOpacity, Gradient, FontPicker, ImagePicker, FileDrop, TextInput, CodeTextarea, RangeInput, Palette, Actions, CollectionActions, Curves, AnchorGrid, ChannelMixer, Vector, or PanelActions control.",
143
+ "Do not use a custom control to recreate a built-in Slider, RangeSlider, Select, Segmented, Switch, Checkbox, Color, ColorOpacity, Gradient, FontPicker, ImagePicker, FileDrop, TextInput, CodeTextarea, RangeInput, Palette, Actions, CollectionActions, SourceCollection, Curves, AnchorGrid, ChannelMixer, Vector, or PanelActions control.",
138
144
  "Every custom-control builtInFitCheck declares typed capabilities. Use collection, reorder, selection, commands, custom-interaction, custom-value-model, and custom-visualization to describe behavior without relying on product nouns.",
139
- "When a custom control owns a growable, removable, selectable, or reorderable runtime item set, its builtInFitCheck must explicitly check collectionActions and actions before choosing custom; this is based on the value model and user workflow, not on entity names such as masks or glyphs.",
145
+ "When a custom control owns a repeated runtime item set, its builtInFitCheck must explicitly check sourceCollection, collectionActions, and actions before choosing custom; this is based on source versus user cardinality and the value workflow, not on entity names such as masks or glyphs.",
140
146
  "Do not justify custom controls with icons, layout, styling, compactness, or custom buttons alone. The fit check must name the product interaction or value model that built-ins cannot express.",
141
147
  "Custom controls may use Toolcraft primitives for small app-specific chrome, but must not import or render low-level runtime surfaces or duplicate toolbar, timeline, layers, canvas, panel, or built-in control mechanics.",
142
148
  "Custom controls must render the minimum UI needed to understand the value, context, and available actions; avoid decorative metadata and text that repeats what the section, label, or visible item already explains.",
@@ -38,16 +38,16 @@ describe("Toolcraft template component contracts: runtime and composition", () =
38
38
  "Conditional entities require fixtures that make the condition observable.",
39
39
  );
40
40
  expect(contract.aiUsageRules).toContain(
41
- "Use visibleWhen for mode-, type-, source-, include-, variant-, or count-exclusive sections or controls that do not belong to the current selected state.",
41
+ 'Every generated product control declares applicability as mode: "always" or mode: "conditional"; omitted applicability and legacy visibleWhen are runtime compatibility only and fail starter acceptance.',
42
42
  );
43
43
  expect(contract.aiUsageRules).toContain(
44
- "When a count/quantity control determines how many sibling controls are available, hide unavailable siblings with visibleWhen; do not render all possible controls while the renderer reads only the first N.",
44
+ "Conditional applicability combines every predicate with AND, hides controls outside mode/type/source/include/variant/count branches, and preserves their runtime values while hidden.",
45
45
  );
46
46
  expect(contract.aiUsageRules).toContain(
47
- "If a switch/select/segmented/tabs/imagePicker/checkbox chooses a branch for the same product entity, controls outside the current branch use visibleWhen, not disabledWhen.",
47
+ "A visible product control must prove its existing product outcome in every supported finite sibling-selector branch from its Control Section Inventory, including always controls and selectors omitted from its predicates.",
48
48
  );
49
49
  expect(contract.aiUsageRules).toContain(
50
- "Do not use schema disabled: true or disabledWhen for generated product controls; the panel should show only controls usable in the current state. Runtime primitives may still have disabled styling internally, but app schemas should model product availability with visibleWhen.",
50
+ "Do not use schema disabled: true or disabledWhen for generated product availability; runtime primitives may use disabled styling internally, but product controls outside the active branch are absent.",
51
51
  );
52
52
  expect(contract.aiUsageRules).toContain(
53
53
  "Do not leave inactive conditional controls visible while making the renderer ignore them.",
@@ -82,10 +82,22 @@ describe("Toolcraft template component contracts: runtime and composition", () =
82
82
  );
83
83
  expect(rules).toContain("performance-iteration");
84
84
  expect(rules).toContain(
85
- "one exact authority-selected targeted performance iteration",
85
+ "Only a localized complaint or a post-clarification targeted choice creates performance-iteration intent",
86
86
  );
87
87
  expect(rules).toContain(
88
- "Complaint wording and repetition never select the complete performance matrix",
88
+ "Classifier output establishes complaint authority only and never path localization",
89
+ );
90
+ expect(rules).toContain(
91
+ "Regardless of whether the classifier returned high-confidence performance-iteration or needs-agent-judgment, unresolved localization creates neither performance-iteration intent nor canonical path authority",
92
+ );
93
+ expect(rules).toContain(
94
+ "repetition alone never expands scope or selects the complete performance matrix",
95
+ );
96
+ expect(rules).not.toContain(
97
+ "One complaint maps to one exact authority-selected targeted performance iteration",
98
+ );
99
+ expect(rules).not.toContain(
100
+ "Direct and repeated performance complaints each start one bounded targeted iteration",
89
101
  );
90
102
  expect(rules).toContain(
91
103
  "After two consecutive compatible protected performance iterations, the agent must offer a slower complete audit",
@@ -135,6 +147,9 @@ describe("Toolcraft template component contracts: runtime and composition", () =
135
147
  expect(rules).toContain(
136
148
  TOOLCRAFT_PERFORMANCE_VERIFICATION_POLICY.renderScaleFidelity,
137
149
  );
150
+ expect(rules).toContain(
151
+ TOOLCRAFT_PERFORMANCE_VERIFICATION_POLICY.complaintRouting,
152
+ );
138
153
  expect(contract.aiUsageRules).toContain(
139
154
  "Every export performance scenario must declare the exact panelActions actionValue, visible controlLabel, and completionEvidence it exercises; selectors, unrelated export actions, and injectable completion callbacks are not output evidence. The named browser scenario must resolve to test or it imported from @playwright/test, be statically registered at module scope or inside an executable test.describe, test.describe.serial, or test.describe.parallel suite, and directly await the matching unshadowed protected output-action helper in its root callback block without focus, skip, condition, or nesting; local test runners, uncalled or conditional test declarations, skipped/fixme suites, and runtime skip/fixme/fail annotations are invalid, and generated integrity protects the focused-test Playwright config.",
140
155
  );
@@ -24,7 +24,13 @@ describe("Toolcraft template component contracts: runtime and composition", () =
24
24
  "Acceptance validators suggest semantic replacement labels for weak generic labels; fix the schema label instead of relying on runtime fallback rewriting.",
25
25
  );
26
26
  expect(contract.aiUsageRules).toContain(
27
- "Controls-panel sections should stay discrete: two to seven product controls is the normal size, and larger sections must split by product sub-entity or workflow stage.",
27
+ "One to seven controls is the normal section size. Eight to ten controls are allowed only for one cohesive entity and require semanticGroup on every control; ten is the hard maximum.",
28
+ );
29
+ expect(contract.aiUsageRules).toContain(
30
+ "Every Control Section Inventory entry declares required entityId, entity, exact targets, and groupingReason. One entity with ten or fewer controls stays in one section regardless of control type, visual height, or target namespace.",
31
+ );
32
+ expect(contract.aiUsageRules).toContain(
33
+ "An entity above ten controls splits into balanced workflow sections of two to ten controls. Every split section keeps the same entityId and entity and declares a unique workflowStage plus splitReason.",
28
34
  );
29
35
  expect(contract.aiUsageRules).toContain(
30
36
  "Every app-authored controls-panel body section must have a short meaningful visible title. Runtime-created Setup renders as the first visible headerless controls block with no title, reset action, collapse button, or collapsed state; sticky footer action sections use the technical title Export but render without a visible heading.",
@@ -62,7 +62,10 @@ describe("Toolcraft template component contracts: runtime and composition", () =
62
62
  'Any product app with Export Video must enable the top Toolcraft timeline: use panels.timeline mode "playback" for product animation transport, or mode "keyframes" when exported animation is driven by keyframes.',
63
63
  );
64
64
  expect(TOOLCRAFT_COMPONENT_CONTRACTS.timelinePanel.aiUsageRules).toContain(
65
- "Video export renderers must render deterministic frames from runtime timeline timestamps and duration; do not treat autonomous wall-clock time or captureStream recording time as the source of product duration.",
65
+ 'Export Video is valid only when productReadiness.exportIntent.video is "user-requested" with non-empty explicit user-request evidence; animation, playback, keyframes, and timeline presence never authorize video export.',
66
+ );
67
+ expect(TOOLCRAFT_COMPONENT_CONTRACTS.timelinePanel.aiUsageRules).toContain(
68
+ "Video export frames are rendered deterministically from the runtime-owned 30 FPS timeline schedule; product code must not use autonomous wall-clock time, captureStream, or MediaRecorder as the source or fallback for product duration.",
66
69
  );
67
70
  expect(TOOLCRAFT_COMPONENT_CONTRACTS.timelinePanel.aiUsageRules).toContain(
68
71
  "Playback renderers must consume runtime timeline state; pause freezes output, scrubbing renders a deterministic frame, and the full animation cycle maps to state.timeline.durationSeconds instead of a local fixed duration.",
@@ -58,7 +58,10 @@ describe("Toolcraft template component contracts: runtime and composition", () =
58
58
  "When the standard Background pair exists, disabling Background atomically restores finite mode and disables Infinity canvas; re-enabling Background restores switch availability without enabling Infinity automatically.",
59
59
  );
60
60
  expect(contract.aiUsageRules).toContain(
61
- "Infinite exports use canonical world-space scene frames: ToolcraftAppComposition.sceneBoundsProvider contributes product bounds, runtime image and model bounds are unioned once, hidden or suppressed content is excluded, PNG crops tightly, and video uses one time-range envelope.",
61
+ "Infinite exports use canonical world-space scene frames: ToolcraftAppComposition.sceneBoundsProvider contributes exact-state product bounds, runtime image and model bounds are unioned, hidden or suppressed content is excluded, PNG crops tightly, and runtime video unions every scheduled frame state into one envelope.",
62
+ );
63
+ expect(contract.aiUsageRules).toContain(
64
+ "Infinite live preview uses that same exact-state product frame: runtime positions one product scene surface from sceneBoundsProvider, and custom raster/WebGL output consumes useToolcraftProductSceneFrame for backing size and world-to-local translation instead of dormant canvas.size.",
62
65
  );
63
66
  expect(contract.aiUsageRules).toContain(
64
67
  "Do not let canvas.upload choose intrinsic-media by default. Upload without explicit sizing resolves to editable-output so source/background images do not own product output size.",