@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
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pixel-point/toolcraft",
3
- "version": "0.0.16",
3
+ "version": "0.0.18",
4
4
  "license": "SEE LICENSE IN LICENSE.md",
5
5
  "type": "module",
6
6
  "bin": {
@@ -99,7 +99,7 @@ test("generated standalone app preserves authority-bound performance iteration l
99
99
  }
100
100
  const output = `${stdout}\n${stderr}`;
101
101
  assert.doesNotMatch(output, /ERR_MODULE_NOT_FOUND/u);
102
- assert.match(output, /^# tests 9$/mu);
103
- assert.match(output, /^# pass 9$/mu);
102
+ assert.match(output, /^# tests 11$/mu);
103
+ assert.match(output, /^# pass 11$/mu);
104
104
  assert.match(output, /^# fail 0$/mu);
105
105
  });
@@ -31,10 +31,20 @@ const materialBrowserTestNames = Object.freeze([
31
31
  "browser: frosting color changes material output",
32
32
  "browser: gloss changes material output",
33
33
  "browser: layer mode changes material output",
34
- "browser: material appearance reaches exported pixels",
34
+ "browser: material appearance reaches decoded exported pixels",
35
35
  "browser: sprinkle color changes material output",
36
36
  "browser: sprinkles visibility changes material output",
37
37
  ]);
38
+ const materialDeliveryBrowserTestNames = Object.freeze([
39
+ "browser: ambient speed changes product output",
40
+ ...materialBrowserTestNames,
41
+ "browser: timeline playback drives product output",
42
+ ]);
43
+ const materialDeliveryAcceptanceIds = Object.freeze([
44
+ "animation.speed",
45
+ ...materialAcceptanceIds,
46
+ "runtime.timeline",
47
+ ]);
38
48
  const materialProductTestFiles = Object.freeze([
39
49
  "src/app/acceptance/validation-pipeline.test.ts",
40
50
  "src/app/app-acceptance.base-coverage.test.ts",
@@ -44,6 +54,7 @@ const materialProductTestFiles = Object.freeze([
44
54
  "src/app/app-automated-runtime-evidence.test.ts",
45
55
  "src/app/app-performance.fixture-helper.test.ts",
46
56
  "src/app/app-performance.gates.test.ts",
57
+ "src/app/app-performance.lifecycle.test.ts",
47
58
  "src/app/app-schema.test.ts",
48
59
  "src/app/material-output.test.tsx",
49
60
  ]);
@@ -294,8 +305,8 @@ export async function assertGeneratedFreshDemandOnlyLifecycle({
294
305
  browserEnv,
295
306
  checkpointPath,
296
307
  execFileAsync,
297
- expectedAcceptanceIds: materialAcceptanceIds,
298
- expectedBrowserTestNames: materialBrowserTestNames,
308
+ expectedAcceptanceIds: materialDeliveryAcceptanceIds,
309
+ expectedBrowserTestNames: materialDeliveryBrowserTestNames,
299
310
  expectedChangedFiles: generatedProductMaterialIterationFixturePaths,
300
311
  expectedProductTestFiles: materialProductTestFiles,
301
312
  targetDir,
@@ -115,15 +115,13 @@ function createGeneratedCommandEnvironment(packageManager, overrides = {}) {
115
115
  return childEnv;
116
116
  }
117
117
 
118
- export async function runGeneratedCommand(
118
+ async function executeGeneratedPackageManager(
119
119
  execFileAsync,
120
120
  targetDir,
121
- args,
121
+ packageManager,
122
+ packageManagerArgs,
122
123
  { env = {} } = {},
123
124
  ) {
124
- const { packageManager } = await readGeneratedPackageManager(targetDir);
125
- const packageManagerArgs =
126
- packageManager === "npm" ? ["run", ...args] : args;
127
125
  try {
128
126
  return await execFileAsync(packageManager, packageManagerArgs, {
129
127
  cwd: targetDir,
@@ -143,6 +141,45 @@ export async function runGeneratedCommand(
143
141
  }
144
142
  }
145
143
 
144
+ export async function runGeneratedCommand(
145
+ execFileAsync,
146
+ targetDir,
147
+ args,
148
+ options,
149
+ ) {
150
+ const { packageManager } = await readGeneratedPackageManager(targetDir);
151
+ const packageManagerArgs =
152
+ packageManager === "npm" ? ["run", ...args] : args;
153
+ return executeGeneratedPackageManager(
154
+ execFileAsync,
155
+ targetDir,
156
+ packageManager,
157
+ packageManagerArgs,
158
+ options,
159
+ );
160
+ }
161
+
162
+ export async function runGeneratedExecutable(
163
+ execFileAsync,
164
+ targetDir,
165
+ executableName,
166
+ args,
167
+ options,
168
+ ) {
169
+ const { packageManager } = await readGeneratedPackageManager(targetDir);
170
+ const packageManagerArgs =
171
+ packageManager === "npm"
172
+ ? ["exec", "--", executableName, ...args]
173
+ : ["exec", executableName, ...args];
174
+ return executeGeneratedPackageManager(
175
+ execFileAsync,
176
+ targetDir,
177
+ packageManager,
178
+ packageManagerArgs,
179
+ options,
180
+ );
181
+ }
182
+
146
183
  export function runGeneratedScript(
147
184
  execFileAsync,
148
185
  targetDir,
@@ -2,6 +2,21 @@ import assert from "node:assert/strict";
2
2
  import fs from "node:fs/promises";
3
3
  import path from "node:path";
4
4
 
5
+ import {
6
+ assertGeneratedExportIntentDocsDoNotInferVideo,
7
+ collectGeneratedAgentFacingDocs,
8
+ getMissingGeneratedPerformanceLifecycleMarkers,
9
+ getStaleAgentPerformanceWorkflowFailures,
10
+ } from "./generate-test-doc-policy-assertions.mjs";
11
+
12
+ export {
13
+ assertGeneratedExportIntentDocsDoNotInferVideo,
14
+ collectGeneratedAgentFacingDocs,
15
+ getMissingGeneratedPerformanceLifecycleMarkers,
16
+ getStaleAgentPerformanceWorkflowFailures,
17
+ staleAnimationDerivedVideoPattern,
18
+ } from "./generate-test-doc-policy-assertions.mjs";
19
+
5
20
  const generatedDocPaths = [
6
21
  "README.md",
7
22
  "workflow.md",
@@ -23,53 +38,6 @@ const generatedDocPaths = [
23
38
  "core/reference-study.md",
24
39
  ];
25
40
 
26
- const staleAgentPerformanceWorkflowPattern =
27
- /app-performance-impact|explicit-performance-work|test:browser:perf|verify:(?:final|quick|ui)|verify:perf:(?:playwright|record-iteration|refresh)|--reason=performance-iteration|--performance-test/iu;
28
-
29
- async function collectMarkdownPaths(rootDir, relativeDir = "") {
30
- const currentDir = path.join(rootDir, relativeDir);
31
- const entries = await fs.readdir(currentDir, { withFileTypes: true });
32
- const markdownPaths = [];
33
-
34
- for (const entry of entries.sort((left, right) => left.name.localeCompare(right.name))) {
35
- const relativePath = path.join(relativeDir, entry.name);
36
- if (entry.isDirectory()) {
37
- markdownPaths.push(...(await collectMarkdownPaths(rootDir, relativePath)));
38
- } else if (entry.isFile() && entry.name.endsWith(".md")) {
39
- markdownPaths.push(relativePath);
40
- }
41
- }
42
-
43
- return markdownPaths;
44
- }
45
-
46
- export async function collectGeneratedAgentFacingDocs(targetDir) {
47
- const docsRoot = path.join(targetDir, "docs/toolcraft");
48
- const docPaths = await collectMarkdownPaths(docsRoot);
49
- const docs = await Promise.all(
50
- docPaths.map(async (docPath) => ({
51
- docPath: path.posix.join("docs/toolcraft", docPath.split(path.sep).join("/")),
52
- source: await fs.readFile(path.join(docsRoot, docPath), "utf8"),
53
- })),
54
- );
55
-
56
- return [
57
- {
58
- docPath: "AGENTS.md",
59
- source: await fs.readFile(path.join(targetDir, "AGENTS.md"), "utf8"),
60
- },
61
- ...docs,
62
- ];
63
- }
64
-
65
- export function getStaleAgentPerformanceWorkflowFailures(sources) {
66
- return sources.flatMap(({ docPath, source }) =>
67
- staleAgentPerformanceWorkflowPattern.test(source)
68
- ? [`${docPath} contains removed performance workflow guidance`]
69
- : [],
70
- );
71
- }
72
-
73
41
  export async function assertGeneratedDocs(targetDir) {
74
42
  await Promise.all(
75
43
  generatedDocPaths.map((docPath) => fs.stat(path.join(targetDir, "docs/toolcraft", docPath))),
@@ -79,15 +47,21 @@ export async function assertGeneratedDocs(targetDir) {
79
47
  const licenseSource = await fs.readFile(path.join(targetDir, "LICENSE.md"), "utf8");
80
48
  const noticeSource = await fs.readFile(path.join(targetDir, "NOTICE.md"), "utf8");
81
49
  const localDocPaths = generatedDocPaths.filter((docPath) => docPath !== "README.md");
50
+ const generatedAgentFacingDocs = await collectGeneratedAgentFacingDocs(targetDir);
82
51
  const staleWorkflowFailures = getStaleAgentPerformanceWorkflowFailures(
83
- await collectGeneratedAgentFacingDocs(targetDir),
52
+ generatedAgentFacingDocs,
84
53
  );
85
54
 
55
+ assert.match(noticeSource, /Mediabunny/u);
56
+ assert.match(noticeSource, /MPL-2\.0/u);
57
+ assert.match(noticeSource, /https:\/\/github\.com\/Vanilagy\/mediabunny/u);
58
+
86
59
  assert.deepEqual(
87
60
  staleWorkflowFailures,
88
61
  [],
89
62
  `Generated agent-facing docs contain stale performance workflow guidance:\n${staleWorkflowFailures.join("\n")}`,
90
63
  );
64
+ assertGeneratedExportIntentDocsDoNotInferVideo(generatedAgentFacingDocs);
91
65
 
92
66
  assert.match(agentsSource, /Quick Entry Contract/);
93
67
  assert.match(agentsSource, /Local Reference Docs/);
@@ -139,6 +113,7 @@ export async function assertGeneratedDocs(targetDir) {
139
113
  assert.match(agentsSource, /defineToolcraft/);
140
114
  assert.match(agentsSource, /ToolcraftApp/);
141
115
  assert.match(agentsSource, /canvasContent/);
116
+ assert.match(agentsSource, /exportRenderer/);
142
117
  assert.match(agentsSource, /renderDefaultCanvasMedia=\{false\}/);
143
118
  assert.match(agentsSource, /onPanelAction/);
144
119
  assert.match(agentsSource, /MIT License/);
@@ -213,18 +188,63 @@ export async function assertGeneratedDocs(targetDir) {
213
188
  assert.match(coreRuntimeBoundarySource, /immutable signed copy of the shared runtime/);
214
189
  assert.match(coreRuntimeBoundarySource, /copied runtime tree or its signed integrity manifest is missing/);
215
190
  assert.match(coreRuntimeBoundarySource, /modelPresentation/);
191
+ assert.match(coreRuntimeBoundarySource, /exportRenderer/);
192
+ assert.match(coreRuntimeBoundarySource, /runtime owns typed image\/video actions end to end/i);
216
193
  assert.match(coreRuntimeBoundarySource, /does not suppress runtime model layers/);
217
194
  assert.match(componentRulesSource, /Do not show Layers for a single-layer app/);
218
195
  assert.match(coreTimelineSource, /Any app with `Export Video` must enable the top Toolcraft timeline/);
219
- assert.match(coreSetupExportSource, /Every product exposes `Export PNG`/);
196
+ assert.match(coreSetupExportSource, /productReadiness\.exportIntent/);
197
+ assert.match(
198
+ coreSetupExportSource,
199
+ /image[^\n]*`toolcraft-default`[^\n]*`user-requested`[^\n]*`user-removed`/i,
200
+ );
201
+ assert.match(
202
+ coreSetupExportSource,
203
+ /video[^\n]*`not-requested`[^\n]*`user-requested`/i,
204
+ );
205
+ assert.match(
206
+ coreSetupExportSource,
207
+ /image[^\n]*`user-requested`[^\n]*non-empty[^\n]*evidence/i,
208
+ );
209
+ assert.match(
210
+ coreSetupExportSource,
211
+ /image[^\n]*`user-removed`[^\n]*non-empty[^\n]*evidence/i,
212
+ );
213
+ assert.match(
214
+ coreSetupExportSource,
215
+ /video[^\n]*`user-requested`[^\n]*non-empty[^\n]*evidence/i,
216
+ );
217
+ for (const decisionStep of [
218
+ "Start every product with image export.",
219
+ "Add video export only when the user explicitly requests video delivery.",
220
+ "Do not infer video from animation, playback, keyframes, or timeline.",
221
+ "Keep image with video unless the user explicitly requests removal.",
222
+ "Record in `productReadiness.exportIntent`.",
223
+ ]) {
224
+ assert.equal(
225
+ coreSetupExportSource.split(decisionStep).length - 1,
226
+ 1,
227
+ `Canonical export-intent step must appear exactly once: ${decisionStep}`,
228
+ );
229
+ }
220
230
  assert.match(
221
231
  coreSetupExportSource,
222
- /Products whose requested delivery includes animation also expose `Export Video`/,
232
+ /Clipboard copy may be an additional product action, but it never changes or substitutes for the recorded artifact intent/,
223
233
  );
224
- assert.match(coreSetupExportSource, /Clipboard copy is optional and never replaces export/);
225
234
  assert.match(coreSetupExportSource, /renderScaleCoverage/);
226
235
  assert.match(coreSetupExportSource, /selected-backing-pixels/);
227
236
  assert.match(coreSetupExportSource, /canvas-render-scale-backing/);
237
+ assert.match(coreSetupExportSource, /Mediabunny/);
238
+ assert.match(coreSetupExportSource, /fixed 30 FPS offline schedule/);
239
+ assert.match(coreSetupExportSource, /Product code must not instantiate `MediaRecorder` or `VideoEncoder`/);
240
+ assert.doesNotMatch(
241
+ coreSetupExportSource,
242
+ /createToolcraftPngExportCanvas|renderRuntimeSceneToCanvas|renderModelsToCanvas/,
243
+ );
244
+ assert.doesNotMatch(
245
+ coreMediaUploadSource,
246
+ /createToolcraftPngExportCanvas|renderRuntimeSceneToCanvas|renderModelsToCanvas/,
247
+ );
228
248
  assert.match(
229
249
  coreSetupExportSource,
230
250
  /interaction[\s\S]*steady[\s\S]*playback[\s\S]*timeline/i,
@@ -236,6 +256,10 @@ export async function assertGeneratedDocs(targetDir) {
236
256
  );
237
257
  assert.match(assemblyDocsSource, /@\/toolcraft\/runtime/);
238
258
  assert.match(assemblyDocsSource, /ToolcraftAppComposition/);
259
+ assert.match(
260
+ assemblyDocsSource,
261
+ /Every product declaration requires `productReadiness\.exportIntent`/,
262
+ );
239
263
  assert.match(assemblyDocsSource, /protected route/);
240
264
  assert.match(assemblyDocsSource, /transferMode: "reference-runtime-clone"/);
241
265
  assert.match(assemblyDocsSource, /`canvasContent` must not contain app UI/);
@@ -249,6 +273,7 @@ export async function assertGeneratedDocs(targetDir) {
249
273
  assert.match(acceptanceDocsSource, /automatedTestName/);
250
274
  assert.match(acceptanceDocsSource, /browserTestName/);
251
275
  assert.match(acceptanceDocsSource, /app-acceptance-data\.ts/);
276
+ assert.match(acceptanceDocsSource, /productReadiness\.exportIntent/);
252
277
  assert.match(
253
278
  acceptanceDocsSource,
254
279
  /Contract docs are signed except product-owned `agent-worklog\.md` and optional `workflow-observation\.md`; only the observation stays outside `sourceHash`/,
@@ -269,6 +294,8 @@ export async function assertGeneratedDocs(targetDir) {
269
294
  acceptanceDocsSource,
270
295
  /export-content proof is distinct from export mechanics/i,
271
296
  );
297
+ assert.match(acceptanceDocsSource, /actual encoded packet metadata/i);
298
+ assert.match(acceptanceDocsSource, /distinct decoded frame hashes/i);
272
299
  assert.match(
273
300
  acceptanceDocsSource,
274
301
  /Each browser file owns one acceptance domain/i,
@@ -312,24 +339,14 @@ export async function assertGeneratedDocs(targetDir) {
312
339
  path.join(targetDir, "docs/toolcraft/core/performance.md"),
313
340
  "utf8",
314
341
  );
315
- const performanceLifecycleMarkers = [
316
- "<!-- toolcraft-performance-lifecycle: first-delivery=functional; later-delivery=functional-targeted; complaint=one-authority-targeted-performance-iteration; full-audit=explicit-only -->",
317
- "<!-- toolcraft-performance-iteration: authority=exact-request-evidence+canonical-path-ids; fixture=reachable-development; after-pass=return-app-to-user+stop -->",
318
- "<!-- toolcraft-performance-full-authority: automatic=forbidden; recommendation=two-compatible-iterations-or-broad-unlocalizable-problem; command=pnpm verify:perf; authority=explicit-user-request-or-accepted-offer -->",
319
- ];
320
- for (const [docPath, source] of [
321
- ["workflow.md", workflowDocsSource],
322
- ["performance.md", performanceDocsSource],
323
- ["core/performance.md", corePerformanceDocsSource],
324
- ]) {
325
- for (const marker of performanceLifecycleMarkers) {
326
- assert.equal(
327
- source.includes(marker),
328
- true,
329
- `docs/toolcraft/${docPath} must include canonical marker: ${marker}`,
330
- );
331
- }
332
- }
342
+ assert.deepEqual(
343
+ getMissingGeneratedPerformanceLifecycleMarkers([
344
+ { docPath: "workflow.md", source: workflowDocsSource },
345
+ { docPath: "performance.md", source: performanceDocsSource },
346
+ { docPath: "core/performance.md", source: corePerformanceDocsSource },
347
+ ]),
348
+ [],
349
+ );
333
350
  assert.match(rendererDocsSource, /rendererTechnique/);
334
351
  assert.match(rendererDocsSource, /sourceRepresentation/);
335
352
  assert.match(rendererDocsSource, /whyNotAlternativeStrategies/);
@@ -339,6 +356,7 @@ export async function assertGeneratedDocs(targetDir) {
339
356
  assert.match(rendererDocsSource, /canonical paths/i);
340
357
  assert.match(rendererDocsSource, /combined fixtures/i);
341
358
  assert.match(rendererDocsSource, /modelPresentation/);
359
+ assert.match(rendererDocsSource, /ToolcraftAppComposition\.exportRenderer/);
342
360
  assert.match(rendererDocsSource, /useToolcraftModelPresentationConsumer/);
343
361
  assert.match(rendererDocsSource, /acquirePresentation/);
344
362
  assert.match(rendererDocsSource, /lease\.release/);
@@ -367,12 +385,34 @@ export async function assertGeneratedDocs(targetDir) {
367
385
  corePerformanceDocsSource,
368
386
  /functional failure[\s\S]*without measured performance/i,
369
387
  );
388
+ assert.match(corePerformanceDocsSource, /exact 30 FPS packet cadence[\s\S]*functional correctness/i);
389
+ assert.match(performanceDocsSource, /exact 30 FPS packet cadence[\s\S]*functional proof/i);
370
390
  assert.match(performanceDocsSource, /coherent (?:user-visible )?delivery batch/i);
371
391
  assert.match(performanceDocsSource, /verify:delivery/);
372
392
  assert.match(performanceDocsSource, /immediately previous successful delivery/i);
373
393
  assert.match(performanceDocsSource, /performance-iteration/);
374
394
  assert.match(performanceDocsSource, /renderScaleCoverage/);
375
395
  assert.match(performanceDocsSource, /canvas-render-scale-backing/);
396
+ assert.match(
397
+ performanceDocsSource,
398
+ /localized complaint[^\n]*without (?:a |asking the user)/iu,
399
+ );
400
+ assert.match(
401
+ performanceDocsSource,
402
+ /ambiguous complaint[\s\S]*one user-facing/iu,
403
+ );
404
+ assert.match(
405
+ performanceDocsSource,
406
+ /targeted diagnosis[\s\S]*complete (?:performance )?review/iu,
407
+ );
408
+ assert.match(
409
+ performanceDocsSource,
410
+ /every measured path[\s\S]*devicePixelRatio × 2/iu,
411
+ );
412
+ assert.match(
413
+ coreSetupExportSource,
414
+ /product code may customize only the slider step/iu,
415
+ );
376
416
  assert.match(performanceDocsSource, /Use bare `npm run verify:delivery` once/i);
377
417
  assert.match(
378
418
  workflowDocsSource,
@@ -5,10 +5,54 @@ import path from "node:path";
5
5
  import test from "node:test";
6
6
 
7
7
  import {
8
+ assertGeneratedExportIntentDocsDoNotInferVideo,
8
9
  collectGeneratedAgentFacingDocs,
10
+ getMissingGeneratedPerformanceLifecycleMarkers,
9
11
  getStaleAgentPerformanceWorkflowFailures,
10
12
  } from "./generate-test-doc-assertions.mjs";
11
13
 
14
+ test("rejects animation-derived video rules without rejecting explicit authority", () => {
15
+ for (const source of [
16
+ [
17
+ "Products whose requested delivery includes",
18
+ "animation also expose",
19
+ "`Export Video`.",
20
+ ].join(" "),
21
+ ["Animated apps automatically show", "Export Video."].join(" "),
22
+ ["Animation also", "requires", "Export Video."].join(" "),
23
+ "Timeline playback enables Export Video.",
24
+ "Animated apps automatically expose video export.",
25
+ "Timeline playback enables video export.",
26
+ "Animation implies video delivery.",
27
+ ]) {
28
+ assert.throws(
29
+ () =>
30
+ assertGeneratedExportIntentDocsDoNotInferVideo([
31
+ { docPath: "docs/toolcraft/stale.md", source },
32
+ ]),
33
+ /docs\/toolcraft\/stale\.md contains animation-derived video export guidance/,
34
+ );
35
+ }
36
+
37
+ assert.doesNotThrow(() =>
38
+ assertGeneratedExportIntentDocsDoNotInferVideo([
39
+ {
40
+ docPath: "docs/toolcraft/allowed.md",
41
+ source: [
42
+ "Do not infer video from animation, playback, keyframes, or timeline.",
43
+ "Animation, playback, keyframes, and timeline presence never add Export Video.",
44
+ "Animated apps do not automatically expose video export.",
45
+ "Timeline playback never enables video export.",
46
+ "Animation does not imply video delivery.",
47
+ "An animated app includes Export Video because the user explicitly requested video delivery.",
48
+ "An animated app exposes video export because the user explicitly requested video delivery.",
49
+ "Any app with Export Video must enable the top Toolcraft timeline.",
50
+ ].join("\n"),
51
+ },
52
+ ]),
53
+ );
54
+ });
55
+
12
56
  test("scans every generated agent-facing Markdown source independently", async (t) => {
13
57
  const targetDir = await fs.mkdtemp(path.join(os.tmpdir(), "toolcraft-doc-scan-"));
14
58
  t.after(() => fs.rm(targetDir, { force: true, recursive: true }));
@@ -41,3 +85,24 @@ test("scans every generated agent-facing Markdown source independently", async (
41
85
  "docs/toolcraft/core/performance.md contains removed performance workflow guidance",
42
86
  ]);
43
87
  });
88
+
89
+ test("requires adaptive routing in every generated performance lifecycle document", () => {
90
+ const routingMarker =
91
+ "<!-- toolcraft-performance-routing: localized=agent-targeted; ambiguous=one-user-facing-choice; broad=offer-targeted-or-full; full=explicit-only -->";
92
+ const existingMarkers = [
93
+ "<!-- toolcraft-performance-lifecycle: first-delivery=functional; later-delivery=functional-targeted; complaint=one-authority-targeted-performance-iteration; full-audit=explicit-only -->",
94
+ "<!-- toolcraft-performance-iteration: authority=exact-request-evidence+canonical-path-ids; fixture=reachable-development; after-pass=return-app-to-user+stop -->",
95
+ "<!-- toolcraft-performance-full-authority: automatic=forbidden; recommendation=two-compatible-iterations-or-broad-unlocalizable-problem; command=pnpm verify:perf; authority=explicit-user-request-or-accepted-offer -->",
96
+ ].join("\n");
97
+
98
+ assert.deepEqual(
99
+ getMissingGeneratedPerformanceLifecycleMarkers([
100
+ { docPath: "workflow.md", source: `${existingMarkers}\n${routingMarker}` },
101
+ { docPath: "performance.md", source: existingMarkers },
102
+ { docPath: "core/performance.md", source: `${existingMarkers}\n${routingMarker}` },
103
+ ]),
104
+ [
105
+ `docs/toolcraft/performance.md must include canonical marker: ${routingMarker}`,
106
+ ],
107
+ );
108
+ });
@@ -0,0 +1,92 @@
1
+ import assert from "node:assert/strict";
2
+ import fs from "node:fs/promises";
3
+ import path from "node:path";
4
+
5
+ const staleAgentPerformanceWorkflowPattern =
6
+ /app-performance-impact|explicit-performance-work|test:browser:perf|verify:(?:final|quick|ui)|verify:perf:(?:playwright|record-iteration|refresh)|--reason=performance-iteration|--performance-test/iu;
7
+
8
+ const exportIntentSentencePatternSource = String.raw`[^\n.!?]`;
9
+ const videoArtifactTargetPatternSource = String.raw`(?:Export Video|video\s+(?:export|delivery))`;
10
+ const explicitVideoAuthorityPatternSource = String.raw`(?:explicit(?:ly)?\s+(?:(?:user[- ]?)?request(?:ed|s)?\s+)?${videoArtifactTargetPatternSource}|user\s+explicitly\s+request(?:ed|s)?\s+${videoArtifactTargetPatternSource}|user[- ]request(?:ed|s)?\s+${videoArtifactTargetPatternSource})`;
11
+ const animationInferenceVerbPatternSource = String.raw`(?:exposes?|includes?|shows?|adds?|enables?|implies?|has|needs?|requires?|produces?|proves?)`;
12
+ const staleAnimationInferencePatternSource = [
13
+ String.raw`products?\s+whose\s+requested\s+delivery\s+includes\s+animation`,
14
+ String.raw`animated\s+apps?\b(?!${exportIntentSentencePatternSource}{0,100}\b(?:do(?:es)?\s+not|never)\b)${exportIntentSentencePatternSource}{0,100}\b${animationInferenceVerbPatternSource}\b${exportIntentSentencePatternSource}{0,60}\b${videoArtifactTargetPatternSource}\b`,
15
+ String.raw`(?:animation|playback|keyframes?|timeline(?:\s+playback)?)\b\s+(?:also\s+)?${animationInferenceVerbPatternSource}\b${exportIntentSentencePatternSource}{0,60}\b${videoArtifactTargetPatternSource}\b`,
16
+ ].join("|");
17
+
18
+ export const staleAnimationDerivedVideoPattern = new RegExp(
19
+ String.raw`(?:^|[\n.!?]\s*)(?!${exportIntentSentencePatternSource}{0,240}\b${explicitVideoAuthorityPatternSource}\b)${exportIntentSentencePatternSource}{0,240}(?:${staleAnimationInferencePatternSource})`,
20
+ "iu",
21
+ );
22
+
23
+ const generatedPerformanceLifecycleMarkers = [
24
+ "<!-- toolcraft-performance-lifecycle: first-delivery=functional; later-delivery=functional-targeted; complaint=one-authority-targeted-performance-iteration; full-audit=explicit-only -->",
25
+ "<!-- toolcraft-performance-iteration: authority=exact-request-evidence+canonical-path-ids; fixture=reachable-development; after-pass=return-app-to-user+stop -->",
26
+ "<!-- toolcraft-performance-full-authority: automatic=forbidden; recommendation=two-compatible-iterations-or-broad-unlocalizable-problem; command=pnpm verify:perf; authority=explicit-user-request-or-accepted-offer -->",
27
+ "<!-- toolcraft-performance-routing: localized=agent-targeted; ambiguous=one-user-facing-choice; broad=offer-targeted-or-full; full=explicit-only -->",
28
+ ];
29
+
30
+ export function getMissingGeneratedPerformanceLifecycleMarkers(sources) {
31
+ return sources.flatMap(({ docPath, source }) =>
32
+ generatedPerformanceLifecycleMarkers.flatMap((marker) =>
33
+ source.includes(marker)
34
+ ? []
35
+ : [`docs/toolcraft/${docPath} must include canonical marker: ${marker}`],
36
+ ),
37
+ );
38
+ }
39
+
40
+ async function collectMarkdownPaths(rootDir, relativeDir = "") {
41
+ const currentDir = path.join(rootDir, relativeDir);
42
+ const entries = await fs.readdir(currentDir, { withFileTypes: true });
43
+ const markdownPaths = [];
44
+
45
+ for (const entry of entries.sort((left, right) => left.name.localeCompare(right.name))) {
46
+ const relativePath = path.join(relativeDir, entry.name);
47
+ if (entry.isDirectory()) {
48
+ markdownPaths.push(...(await collectMarkdownPaths(rootDir, relativePath)));
49
+ } else if (entry.isFile() && entry.name.endsWith(".md")) {
50
+ markdownPaths.push(relativePath);
51
+ }
52
+ }
53
+
54
+ return markdownPaths;
55
+ }
56
+
57
+ export async function collectGeneratedAgentFacingDocs(targetDir) {
58
+ const docsRoot = path.join(targetDir, "docs/toolcraft");
59
+ const docPaths = await collectMarkdownPaths(docsRoot);
60
+ const docs = await Promise.all(
61
+ docPaths.map(async (docPath) => ({
62
+ docPath: path.posix.join("docs/toolcraft", docPath.split(path.sep).join("/")),
63
+ source: await fs.readFile(path.join(docsRoot, docPath), "utf8"),
64
+ })),
65
+ );
66
+
67
+ return [
68
+ {
69
+ docPath: "AGENTS.md",
70
+ source: await fs.readFile(path.join(targetDir, "AGENTS.md"), "utf8"),
71
+ },
72
+ ...docs,
73
+ ];
74
+ }
75
+
76
+ export function getStaleAgentPerformanceWorkflowFailures(sources) {
77
+ return sources.flatMap(({ docPath, source }) =>
78
+ staleAgentPerformanceWorkflowPattern.test(source)
79
+ ? [`${docPath} contains removed performance workflow guidance`]
80
+ : [],
81
+ );
82
+ }
83
+
84
+ export function assertGeneratedExportIntentDocsDoNotInferVideo(sources) {
85
+ for (const { docPath, source } of sources) {
86
+ assert.doesNotMatch(
87
+ source,
88
+ staleAnimationDerivedVideoPattern,
89
+ `${docPath} contains animation-derived video export guidance`,
90
+ );
91
+ }
92
+ }