@pixel-point/toolcraft 0.0.17 → 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 (196) hide show
  1. package/package.json +1 -1
  2. package/src/generate-test-doc-assertions.mjs +58 -68
  3. package/src/generate-test-doc-assertions.test.mjs +43 -0
  4. package/src/generate-test-doc-policy-assertions.mjs +92 -0
  5. package/src/generate-test-file-assertions.mjs +26 -75
  6. package/src/generate-test-product-iteration.test.mjs +17 -0
  7. package/src/generate-test-runtime-boundary-assertions.mjs +158 -0
  8. package/src/generate-test-standalone-assertions.mjs +10 -0
  9. package/templates/runtime/contracts/component-contracts.choices.test.ts +47 -3
  10. package/templates/runtime/contracts/component-contracts.choices.ts +48 -3
  11. package/templates/runtime/contracts/component-contracts.inputs.test.ts +6 -6
  12. package/templates/runtime/contracts/component-contracts.inputs.ts +6 -6
  13. package/templates/runtime/contracts/component-contracts.media-custom.test.ts +8 -2
  14. package/templates/runtime/contracts/component-contracts.media-custom.ts +8 -2
  15. package/templates/runtime/contracts/component-contracts.runtime-acceptance.test.ts +4 -4
  16. package/templates/runtime/contracts/component-contracts.runtime-labels.test.ts +7 -1
  17. package/templates/runtime/contracts/component-contracts.runtime-panels.test.ts +3 -0
  18. package/templates/runtime/contracts/component-contracts.runtime-shell.test.ts +3 -0
  19. package/templates/runtime/contracts/component-contracts.runtime.ts +12 -8
  20. package/templates/runtime/contracts/component-contracts.test.ts +2 -0
  21. package/templates/runtime/contracts/decision-contracts.test.ts +18 -0
  22. package/templates/runtime/contracts/decision-contracts.ts +10 -10
  23. package/templates/runtime/export/export-background.ts +0 -19
  24. package/templates/runtime/export/export.test.ts +45 -42
  25. package/templates/runtime/export/index.ts +44 -16
  26. package/templates/runtime/index.ts +2 -0
  27. package/templates/runtime/model-import/model-import-limits.ts +15 -4
  28. package/templates/runtime/react/app-shell/runtime-public-api.test.tsx +11 -1
  29. package/templates/runtime/react/app-shell/toolcraft-app.scene-export.test.tsx +29 -0
  30. package/templates/runtime/react/app-shell/toolcraft-app.tsx +18 -8
  31. package/templates/runtime/react/canvas/canvas-shell-rendering.test.tsx +76 -0
  32. package/templates/runtime/react/canvas/canvas-shell-test-utils.tsx +13 -1
  33. package/templates/runtime/react/canvas/canvas-shell.tsx +16 -1
  34. package/templates/runtime/react/canvas/product-scene-surface.test.tsx +195 -0
  35. package/templates/runtime/react/canvas/product-scene-surface.tsx +155 -0
  36. package/templates/runtime/react/canvas/runtime-scene-export.ts +1 -1
  37. package/templates/runtime/react/controls-panel/__tests__/control-conditions.test.ts +61 -0
  38. package/templates/runtime/react/controls-panel/__tests__/controls-panel.collection-actions.test.tsx +89 -0
  39. package/templates/runtime/react/controls-panel/__tests__/controls-panel.control-visibility.test.tsx +40 -19
  40. package/templates/runtime/react/controls-panel/__tests__/controls-panel.scene-export-actions.test.tsx +1 -1
  41. package/templates/runtime/react/controls-panel/__tests__/controls-panel.source-collection.test.tsx +62 -0
  42. package/templates/runtime/react/controls-panel/__tests__/controls-panel.source-color-bank.test.tsx +67 -0
  43. package/templates/runtime/react/controls-panel/actions/controls-panel-actions.ts +2 -4
  44. package/templates/runtime/react/controls-panel/actions/export-action-runner.ts +10 -4
  45. package/templates/runtime/react/controls-panel/conditions/control-conditions.ts +32 -119
  46. package/templates/runtime/react/controls-panel/layout/controls-panel-control-group.tsx +5 -2
  47. package/templates/runtime/react/controls-panel/layout/controls-panel-layout.tsx +10 -8
  48. package/templates/runtime/react/controls-panel/layout/controls-panel-section.tsx +3 -3
  49. package/templates/runtime/react/controls-panel/renderers/controls-panel-collection-item-fields.test.tsx +63 -0
  50. package/templates/runtime/react/controls-panel/renderers/controls-panel-collection-item-fields.tsx +138 -0
  51. package/templates/runtime/react/controls-panel/renderers/controls-panel-collection-items.tsx +104 -0
  52. package/templates/runtime/react/controls-panel/renderers/controls-panel-collection-renderer.tsx +115 -236
  53. package/templates/runtime/react/controls-panel/renderers/controls-panel-file-drop-item-controls.test.tsx +95 -0
  54. package/templates/runtime/react/controls-panel/renderers/controls-panel-file-drop-item-controls.tsx +82 -0
  55. package/templates/runtime/react/controls-panel/renderers/controls-panel-media-renderer.test.tsx +73 -1
  56. package/templates/runtime/react/controls-panel/renderers/controls-panel-media-renderer.tsx +50 -1
  57. package/templates/runtime/react/controls-panel/renderers/controls-panel-renderer-registry.test.ts +1 -1
  58. package/templates/runtime/react/controls-panel/renderers/controls-panel-renderer-registry.ts +2 -1
  59. package/templates/runtime/react/controls-panel/values/controls-panel-collection-values.ts +38 -6
  60. package/templates/runtime/react/controls-panel/values/controls-panel-value-labels.ts +1 -0
  61. package/templates/runtime/react/index.ts +4 -0
  62. package/templates/runtime/react/model-rendering/model-export.ts +1 -1
  63. package/templates/runtime/react/orientation-gizmo/orientation-gizmo-selection.ts +5 -5
  64. package/templates/runtime/react/orientation-gizmo/use-toolcraft-orientation-control-selection.ts +2 -2
  65. package/templates/runtime/scene/scene-bounds.test.ts +23 -0
  66. package/templates/runtime/scene/scene-bounds.ts +31 -17
  67. package/templates/runtime/schema/app-capabilities.test.ts +4 -0
  68. package/templates/runtime/schema/collection-item-controls.ts +29 -0
  69. package/templates/runtime/schema/control-applicability.test.ts +127 -0
  70. package/templates/runtime/schema/control-applicability.ts +433 -0
  71. package/templates/runtime/schema/control-schema-normalization.ts +177 -11
  72. package/templates/runtime/schema/controls-panel-actions.ts +2 -2
  73. package/templates/runtime/schema/controls-panel-section-layout.ts +14 -9
  74. package/templates/runtime/schema/controls-panel-section-roundtrip.test.ts +59 -0
  75. package/templates/runtime/schema/define-toolcraft.collection-actions-item-controls.test.ts +127 -0
  76. package/templates/runtime/schema/define-toolcraft.control-sections.test.ts +10 -4
  77. package/templates/runtime/schema/define-toolcraft.file-drop-item-controls.test.ts +98 -0
  78. package/templates/runtime/schema/define-toolcraft.file-drop-normalization.test.ts +27 -0
  79. package/templates/runtime/schema/define-toolcraft.segmented-control-fit.test.ts +20 -2
  80. package/templates/runtime/schema/define-toolcraft.setup-canvas.test.ts +16 -3
  81. package/templates/runtime/schema/define-toolcraft.source-collection.test.ts +79 -0
  82. package/templates/runtime/schema/runtime-setup-section.ts +24 -5
  83. package/templates/runtime/schema/types.ts +68 -13
  84. package/templates/starter/AGENTS.md +12 -10
  85. package/templates/starter/docs/toolcraft/acceptance-testing.md +17 -31
  86. package/templates/starter/docs/toolcraft/agent-worklog.md +15 -0
  87. package/templates/starter/docs/toolcraft/assembly-workflow.md +10 -3
  88. package/templates/starter/docs/toolcraft/component-rules.md +8 -6
  89. package/templates/starter/docs/toolcraft/core/control-selection.md +9 -3
  90. package/templates/starter/docs/toolcraft/core/layout.md +7 -7
  91. package/templates/starter/docs/toolcraft/core/media-upload.md +2 -0
  92. package/templates/starter/docs/toolcraft/core/runtime-boundary.md +13 -2
  93. package/templates/starter/docs/toolcraft/core/setup-export.md +33 -7
  94. package/templates/starter/docs/toolcraft/custom-controls.md +6 -4
  95. package/templates/starter/docs/toolcraft/decision-contract.md +3 -1
  96. package/templates/starter/docs/toolcraft/renderer-technique.md +4 -0
  97. package/templates/starter/docs/toolcraft/schema-reference.md +21 -19
  98. package/templates/starter/e2e/app-browser-control-applicability-requirements.spec.ts +163 -0
  99. package/templates/starter/e2e/app-browser-infinity-canvas-evidence.spec.ts +7 -2
  100. package/templates/starter/e2e/app-browser-orientation-evidence.spec.ts +174 -21
  101. package/templates/starter/e2e/app-browser-runtime-requirements.spec.ts +1 -33
  102. package/templates/starter/e2e/app-browser-semantic-evidence.spec.ts +61 -15
  103. package/templates/starter/e2e/{browser-conditional-output-evidence-helpers.ts → browser-background-output-evidence.ts} +0 -68
  104. package/templates/starter/e2e/browser-control-applicability-evidence.ts +114 -0
  105. package/templates/starter/e2e/browser-infinity-canvas-evidence.ts +20 -11
  106. package/templates/starter/e2e/browser-orientation-gizmo-evidence-helpers.ts +4 -0
  107. package/templates/starter/e2e/browser-orientation-gizmo-live-preconditions.ts +86 -0
  108. package/templates/starter/e2e/browser-runtime-applicability-requirements.ts +84 -0
  109. package/templates/starter/e2e/browser-runtime-evidence-reporter.ts +9 -2
  110. package/templates/starter/e2e/browser-runtime-evidence-requirements.ts +29 -26
  111. package/templates/starter/e2e/browser-semantic-evidence-test-helpers.ts +25 -0
  112. package/templates/starter/scripts/toolcraft-contract-manifest.json +1 -0
  113. package/templates/starter/scripts/toolcraft-product-boundary-ast.mjs +85 -115
  114. package/templates/starter/scripts/toolcraft-product-boundary-module-policy.mjs +101 -0
  115. package/templates/starter/scripts/toolcraft-product-boundary.test.mjs +0 -72
  116. package/templates/starter/scripts/toolcraft-product-control-bindings.mjs +182 -0
  117. package/templates/starter/scripts/toolcraft-product-control-boundary.mjs +146 -0
  118. package/templates/starter/scripts/toolcraft-product-control-boundary.test.mjs +186 -0
  119. package/templates/starter/scripts/toolcraft-product-export-boundary.mjs +6 -17
  120. package/templates/starter/scripts/toolcraft-product-export-boundary.test.mjs +196 -0
  121. package/templates/starter/scripts/toolcraft-typescript-source-evidence-structure.test.mjs +20 -0
  122. package/templates/starter/scripts/toolcraft-typescript-source-evidence.mjs +1 -0
  123. package/templates/starter/scripts/toolcraft-unavailable-resource-evidence-facade.test.mjs +2 -0
  124. package/templates/starter/src/app/acceptance/artifact-export-intent.ts +130 -0
  125. package/templates/starter/src/app/acceptance/conditions.ts +1 -1
  126. package/templates/starter/src/app/acceptance/control-acceptance-context.ts +2 -2
  127. package/templates/starter/src/app/acceptance/control-acceptance-coverage.ts +6 -20
  128. package/templates/starter/src/app/acceptance/control-acceptance-kind-rules.ts +24 -5
  129. package/templates/starter/src/app/acceptance/control-acceptance-policy.ts +2 -13
  130. package/templates/starter/src/app/acceptance/control-applicability-cases.test.ts +321 -0
  131. package/templates/starter/src/app/acceptance/control-applicability-cases.ts +290 -0
  132. package/templates/starter/src/app/acceptance/control-applicability.ts +332 -0
  133. package/templates/starter/src/app/acceptance/control-layout-dependency-rules.ts +20 -9
  134. package/templates/starter/src/app/acceptance/control-layout-entity-rules.ts +4 -6
  135. package/templates/starter/src/app/acceptance/control-layout-model.ts +4 -4
  136. package/templates/starter/src/app/acceptance/control-layout-section-rules.ts +3 -10
  137. package/templates/starter/src/app/acceptance/control-parts.ts +8 -0
  138. package/templates/starter/src/app/acceptance/control-section-entity-cohesion.test.ts +215 -0
  139. package/templates/starter/src/app/acceptance/control-section-entity-cohesion.ts +132 -0
  140. package/templates/starter/src/app/acceptance/control-section-inventory.test.ts +87 -0
  141. package/templates/starter/src/app/acceptance/control-section-inventory.ts +74 -44
  142. package/templates/starter/src/app/acceptance/custom-controls.ts +11 -3
  143. package/templates/starter/src/app/acceptance/inline-layout.ts +64 -9
  144. package/templates/starter/src/app/acceptance/media-upload.ts +5 -1
  145. package/templates/starter/src/app/acceptance/orientation-gizmo.ts +18 -11
  146. package/templates/starter/src/app/acceptance/output-background-rules.ts +2 -2
  147. package/templates/starter/src/app/acceptance/output-export-layout-rules.ts +18 -10
  148. package/templates/starter/src/app/acceptance/output-export-model.ts +54 -11
  149. package/templates/starter/src/app/acceptance/output-export.ts +22 -10
  150. package/templates/starter/src/app/acceptance/output-video-export-rules.ts +99 -0
  151. package/templates/starter/src/app/acceptance/types.ts +23 -8
  152. package/templates/starter/src/app/acceptance/validate-coverage.ts +19 -2
  153. package/templates/starter/src/app/starter-acceptance.artifact-export-intent.test.ts +239 -0
  154. package/templates/starter/src/app/starter-acceptance.background-export.test.ts +33 -0
  155. package/templates/starter/src/app/starter-acceptance.canvas-sizing.test.ts +16 -1
  156. package/templates/starter/src/app/starter-acceptance.compound-part-coverage.test.ts +65 -0
  157. package/templates/starter/src/app/starter-acceptance.contract-fixtures.test.ts +41 -0
  158. package/templates/starter/src/app/starter-acceptance.contract-fixtures.ts +112 -16
  159. package/templates/starter/src/app/starter-acceptance.control-applicability.test.ts +224 -0
  160. package/templates/starter/src/app/starter-acceptance.control-order.test.ts +58 -3
  161. package/templates/starter/src/app/starter-acceptance.control-state.test.ts +0 -111
  162. package/templates/starter/src/app/starter-acceptance.custom-control-collections.test.ts +3 -1
  163. package/templates/starter/src/app/starter-acceptance.custom-control-valid.test.ts +1 -1
  164. package/templates/starter/src/app/starter-acceptance.export-test-utils.ts +22 -0
  165. package/templates/starter/src/app/{starter-acceptance.image-video-export-settings.test.ts → starter-acceptance.image-export-settings.test.ts} +37 -9
  166. package/templates/starter/src/app/starter-acceptance.interaction-ownership.test.ts +12 -0
  167. package/templates/starter/src/app/starter-acceptance.media-upload.fixtures.ts +11 -1
  168. package/templates/starter/src/app/starter-acceptance.media-upload.test.ts +28 -0
  169. package/templates/starter/src/app/starter-acceptance.output-export-intent.test.ts +384 -0
  170. package/templates/starter/src/app/starter-acceptance.output-export-rules.test.ts +4 -0
  171. package/templates/starter/src/app/starter-acceptance.product-output-export.test.ts +112 -105
  172. package/templates/starter/src/app/starter-acceptance.product-readiness.test.ts +12 -0
  173. package/templates/starter/src/app/starter-acceptance.section-cohesion.test.ts +1 -1
  174. package/templates/starter/src/app/starter-acceptance.section-dependencies.test.ts +35 -24
  175. package/templates/starter/src/app/starter-acceptance.section-size.test.ts +72 -0
  176. package/templates/starter/src/app/starter-acceptance.ts +11 -2
  177. package/templates/starter/src/app/starter-acceptance.video-export-settings.test.ts +336 -0
  178. package/templates/starter/src/app/starter-acceptance.view-interaction.test.ts +8 -0
  179. package/templates/starter/src/app/test-evidence/browser-runtime-contract.ts +2 -2
  180. package/templates/starter/src/routes/index.tsx +1 -0
  181. package/templates/ui/components/composites/tabs.tsx +2 -2
  182. package/templates/ui/components/controls/collection-actions/collection-item-groups.test.tsx +38 -0
  183. package/templates/ui/components/controls/collection-actions/collection-item-groups.tsx +36 -0
  184. package/templates/ui/components/controls/collection-actions/index.ts +2 -0
  185. package/templates/ui/components/controls/file-drop/file-drop-collection-control.tsx +137 -0
  186. package/templates/ui/components/controls/file-drop/file-drop-control.test.tsx +189 -0
  187. package/templates/ui/components/controls/file-drop/file-drop-control.tsx +81 -88
  188. package/templates/ui/components/controls/file-drop/file-drop-presentation.tsx +131 -9
  189. package/templates/ui/components/controls/file-drop/file-drop-types.ts +36 -22
  190. package/templates/ui/components/controls/file-drop/index.ts +1 -0
  191. package/templates/ui/components/controls/index.ts +3 -0
  192. package/templates/ui/components/controls/range-slider/range-slider-control.tsx +1 -1
  193. package/templates/ui/components/controls/range-slider/range-slider-value.test.ts +14 -0
  194. package/templates/ui/components/controls/range-slider/range-slider-value.ts +3 -2
  195. package/templates/starter/src/app/acceptance/condition-exclusivity.test.ts +0 -60
  196. package/templates/starter/src/app/acceptance/condition-exclusivity.ts +0 -221
@@ -1,5 +1,6 @@
1
1
  import { describe, expect, it } from "vitest";
2
2
 
3
+ import type { ToolcraftCollectionItemControlType } from "./collection-item-controls";
3
4
  import { defineToolcraft } from "./define-toolcraft";
4
5
  import type { ToolcraftControlSchema } from "./types";
5
6
 
@@ -39,8 +40,9 @@ function createModeControl(
39
40
  }
40
41
 
41
42
  function createCollectionModeControl(
42
- type: string,
43
+ type: ToolcraftCollectionItemControlType,
43
44
  labels: readonly string[],
45
+ parentType = "collectionActions",
44
46
  ): ToolcraftControlSchema {
45
47
  return {
46
48
  defaultValue: ["default"],
@@ -57,7 +59,7 @@ function createCollectionModeControl(
57
59
  label: "Modes",
58
60
  minItems: 1,
59
61
  target: "appearance.modes",
60
- type: "collectionActions",
62
+ type: parentType,
61
63
  };
62
64
  }
63
65
 
@@ -138,6 +140,22 @@ describe("defineToolcraft segmented control fit", () => {
138
140
  expect(control).toEqual(inputSnapshot);
139
141
  });
140
142
 
143
+ it("applies segmented item fit validation to sourceCollection", () => {
144
+ expect(() =>
145
+ defineToolcraft(
146
+ createAppSchema(
147
+ createCollectionModeControl(
148
+ "segmented",
149
+ SCREENSHOT_LABELS,
150
+ "sourceCollection",
151
+ ),
152
+ ),
153
+ ),
154
+ ).toThrow(
155
+ /Toolcraft control validation \[segmented-control-fit\] for target "appearance\.modes" itemControl: .*select dropdown/u,
156
+ );
157
+ });
158
+
141
159
  it.each(["type", "target"] as const)(
142
160
  "preserves required %s validation ownership",
143
161
  (fieldName) => {
@@ -32,6 +32,7 @@ describe("defineToolcraft runtime setup canvas controls", () => {
32
32
 
33
33
  expect(canvasSection?.title).toBe("Setup");
34
34
  expect(canvasSection?.controls.infinityCanvas).toMatchObject({
35
+ applicability: { mode: "always", origin: "explicit" },
35
36
  defaultValue: false,
36
37
  label: "Infinity canvas",
37
38
  target: "canvas.infinity",
@@ -52,7 +53,11 @@ describe("defineToolcraft runtime setup canvas controls", () => {
52
53
  performanceRole: "workload",
53
54
  target: "canvas.aspectRatio",
54
55
  type: "aspectRatio",
55
- visibleWhen: { equals: false, target: "canvas.infinity" },
56
+ applicability: {
57
+ all: [{ equals: false, target: "canvas.infinity" }],
58
+ mode: "conditional",
59
+ origin: "explicit",
60
+ },
56
61
  });
57
62
  expect(canvasSection?.controls.canvasWidth).toMatchObject({
58
63
  defaultValue: 1440,
@@ -60,7 +65,11 @@ describe("defineToolcraft runtime setup canvas controls", () => {
60
65
  performanceRole: "workload",
61
66
  target: "canvas.size.width",
62
67
  type: "text",
63
- visibleWhen: { equals: false, target: "canvas.infinity" },
68
+ applicability: {
69
+ all: [{ equals: false, target: "canvas.infinity" }],
70
+ mode: "conditional",
71
+ origin: "explicit",
72
+ },
64
73
  });
65
74
  expect(canvasSection?.controls.canvasHeight).toMatchObject({
66
75
  defaultValue: 900,
@@ -68,7 +77,11 @@ describe("defineToolcraft runtime setup canvas controls", () => {
68
77
  performanceRole: "workload",
69
78
  target: "canvas.size.height",
70
79
  type: "text",
71
- visibleWhen: { equals: false, target: "canvas.infinity" },
80
+ applicability: {
81
+ all: [{ equals: false, target: "canvas.infinity" }],
82
+ mode: "conditional",
83
+ origin: "explicit",
84
+ },
72
85
  });
73
86
  expect(canvasSection?.layoutGroups).toEqual([
74
87
  {
@@ -0,0 +1,79 @@
1
+ import { describe, expect, it } from "vitest";
2
+
3
+ import { defineToolcraft } from "./define-toolcraft";
4
+ import type { ToolcraftControlSchema } from "./types";
5
+
6
+ function createAppSchema(control: ToolcraftControlSchema) {
7
+ return {
8
+ canvas: { enabled: false },
9
+ panels: {
10
+ controls: {
11
+ sections: [
12
+ {
13
+ controls: { sections: control },
14
+ title: "Fill Sections",
15
+ },
16
+ ],
17
+ title: "Controls",
18
+ },
19
+ },
20
+ };
21
+ }
22
+
23
+ function createSourceCollection(
24
+ overrides: Record<string, unknown> = {},
25
+ ): ToolcraftControlSchema {
26
+ return {
27
+ defaultValue: [{ hex: "#DFFF1A" }],
28
+ itemControl: { label: false, type: "color" },
29
+ itemLabel: "Section",
30
+ label: "Sections",
31
+ target: "fills.sections",
32
+ type: "sourceCollection",
33
+ ...overrides,
34
+ } as ToolcraftControlSchema;
35
+ }
36
+
37
+ describe("defineToolcraft source collection", () => {
38
+ it("accepts a source-owned collection of built-in item controls", () => {
39
+ const app = defineToolcraft(createAppSchema(createSourceCollection()));
40
+ const control = app.panels.controls?.sections.find(
41
+ (section) => section.title === "Fill Sections",
42
+ )?.controls.sections;
43
+
44
+ expect(control?.type).toBe("sourceCollection");
45
+ expect(control?.itemControl?.type).toBe("color");
46
+ });
47
+
48
+ it("requires source collections to declare their built-in item control", () => {
49
+ expect(() =>
50
+ defineToolcraft(
51
+ createAppSchema(createSourceCollection({ itemControl: undefined })),
52
+ ),
53
+ ).toThrow(
54
+ /Toolcraft control validation \[source-collection-item-control\] for target "fills\.sections": itemControl is required/u,
55
+ );
56
+ });
57
+
58
+ it("rejects unsupported collection item controls", () => {
59
+ const control = createSourceCollection({
60
+ itemControl: { type: "imaginary" },
61
+ });
62
+
63
+ expect(() => defineToolcraft(createAppSchema(control))).toThrow(
64
+ /Toolcraft control validation \[collection-item-control\] for target "fills\.sections": unsupported collection item control "imaginary"/u,
65
+ );
66
+ });
67
+
68
+ it("rejects unsupported collectionActions item controls instead of falling back to text", () => {
69
+ const control = {
70
+ ...createSourceCollection(),
71
+ itemControl: { type: "imaginary" },
72
+ type: "collectionActions",
73
+ } as unknown as ToolcraftControlSchema;
74
+
75
+ expect(() => defineToolcraft(createAppSchema(control))).toThrow(
76
+ /Toolcraft control validation \[collection-item-control\] for target "fills\.sections": unsupported collection item control "imaginary"/u,
77
+ );
78
+ });
79
+ });
@@ -38,6 +38,11 @@ const finiteCanvasCondition = Object.freeze({
38
38
  equals: false,
39
39
  target: toolcraftCanvasInfinityTarget,
40
40
  });
41
+ const alwaysApplicable = Object.freeze({ mode: "always" as const });
42
+ const finiteCanvasApplicability = Object.freeze({
43
+ all: [finiteCanvasCondition],
44
+ mode: "conditional" as const,
45
+ });
41
46
 
42
47
  type RuntimeSetupControlGroup = {
43
48
  controls: ToolcraftControlSectionSchema["controls"];
@@ -146,6 +151,7 @@ function createRenderScaleControl(
146
151
  }
147
152
 
148
153
  return {
154
+ applicability: alwaysApplicable,
149
155
  defaultValue: canvas.renderScale.defaultValue,
150
156
  description:
151
157
  "Increases raster canvas backing resolution without changing the visible output size.",
@@ -175,6 +181,7 @@ function createInfinityCanvasControl(
175
181
  }
176
182
 
177
183
  return {
184
+ applicability: alwaysApplicable,
178
185
  defaultValue: getToolcraftDefaultCanvasMode(canvas) === "infinite",
179
186
  ...(background
180
187
  ? {
@@ -199,6 +206,7 @@ function createTimelineExtendedControl(
199
206
  }
200
207
 
201
208
  return {
209
+ applicability: alwaysApplicable,
202
210
  defaultValue: false,
203
211
  label: "Timeline",
204
212
  target: toolcraftTimelinePanelExtendedTarget,
@@ -230,13 +238,23 @@ function createRuntimeSetupBackgroundControls({
230
238
  return {
231
239
  controls: {
232
240
  ...(includeBackgroundControl
233
- ? { includeBackground: includeBackgroundControl }
241
+ ? {
242
+ includeBackground: {
243
+ ...includeBackgroundControl,
244
+ applicability: alwaysApplicable,
245
+ },
246
+ }
234
247
  : {}),
235
248
  ...(infinityCanvasControl
236
249
  ? { infinityCanvas: infinityCanvasControl }
237
250
  : {}),
238
251
  ...(backgroundColorControl
239
- ? { background: backgroundColorControl }
252
+ ? {
253
+ background: {
254
+ ...backgroundColorControl,
255
+ applicability: alwaysApplicable,
256
+ },
257
+ }
240
258
  : {}),
241
259
  },
242
260
  layoutGroups:
@@ -262,6 +280,7 @@ function createFiniteCanvasControls(
262
280
  return {
263
281
  controls: {
264
282
  canvasAspectRatio: {
283
+ applicability: finiteCanvasApplicability,
265
284
  defaultValue: getCanvasAspectRatioDefaultValue(canvas.size),
266
285
  label: "Aspect ratio",
267
286
  orderRole: "input",
@@ -269,9 +288,9 @@ function createFiniteCanvasControls(
269
288
  performanceRole: "workload",
270
289
  target: canvasAspectRatioTarget,
271
290
  type: "aspectRatio",
272
- visibleWhen: finiteCanvasCondition,
273
291
  },
274
292
  canvasWidth: {
293
+ applicability: finiteCanvasApplicability,
275
294
  defaultValue: canvas.size.width,
276
295
  label: "Canvas width",
277
296
  orderRole: "input",
@@ -279,9 +298,9 @@ function createFiniteCanvasControls(
279
298
  performanceRole: "workload",
280
299
  target: canvasSizeControlTargets.width,
281
300
  type: "text",
282
- visibleWhen: finiteCanvasCondition,
283
301
  },
284
302
  canvasHeight: {
303
+ applicability: finiteCanvasApplicability,
285
304
  defaultValue: canvas.size.height,
286
305
  label: "Canvas height",
287
306
  orderRole: "input",
@@ -289,7 +308,6 @@ function createFiniteCanvasControls(
289
308
  performanceRole: "workload",
290
309
  target: canvasSizeControlTargets.height,
291
310
  type: "text",
292
- visibleWhen: finiteCanvasCondition,
293
311
  },
294
312
  },
295
313
  layoutGroups: [
@@ -325,6 +343,7 @@ export function createToolcraftRuntimeSetupSection({
325
343
  const section: ToolcraftControlSectionSchema & { id: string } = {
326
344
  controls: {
327
345
  settingsTransfer: {
346
+ applicability: alwaysApplicable,
328
347
  label: false,
329
348
  target: settingsTransferTarget,
330
349
  type: "settingsTransfer",
@@ -3,6 +3,7 @@ import type {
3
3
  ToolcraftControlLayoutGroupLayout,
4
4
  ToolcraftSectionLayout,
5
5
  } from "../contracts/types";
6
+ import type { ToolcraftCollectionItemControlType } from "./collection-item-controls";
6
7
 
7
8
  export type ToolcraftCanvasSize = {
8
9
  height: number;
@@ -412,7 +413,7 @@ export type ResolvedToolcraftMediaSchema = {
412
413
  defaultAssets: readonly ToolcraftDefaultMediaAssetSchema[];
413
414
  };
414
415
 
415
- export type ToolcraftControlConditionSchema = {
416
+ export type ToolcraftControlPredicateSchema = Readonly<{
416
417
  equals?: unknown;
417
418
  greaterThan?: number;
418
419
  greaterThanOrEqual?: number;
@@ -422,7 +423,28 @@ export type ToolcraftControlConditionSchema = {
422
423
  notEquals?: unknown;
423
424
  oneOf?: readonly unknown[];
424
425
  target: string;
425
- };
426
+ }>;
427
+
428
+ export type ToolcraftControlConditionSchema =
429
+ ToolcraftControlPredicateSchema;
430
+
431
+ export type ToolcraftControlApplicabilitySchema =
432
+ | Readonly<{ mode: "always" }>
433
+ | Readonly<{
434
+ all: readonly ToolcraftControlPredicateSchema[];
435
+ mode: "conditional";
436
+ }>;
437
+
438
+ export type ToolcraftResolvedControlApplicabilitySchema =
439
+ | Readonly<{
440
+ mode: "always";
441
+ origin: "explicit" | "implicit";
442
+ }>
443
+ | Readonly<{
444
+ all: readonly ToolcraftControlPredicateSchema[];
445
+ mode: "conditional";
446
+ origin: "explicit" | "legacy";
447
+ }>;
426
448
 
427
449
  export type ToolcraftControlDisabledConditionSchema =
428
450
  ToolcraftControlConditionSchema;
@@ -434,18 +456,29 @@ export type ToolcraftColorOpacityValueSchema = {
434
456
 
435
457
  export type ToolcraftCollectionItemControlSchema = {
436
458
  commitMode?: "content" | "setting";
459
+ coordinateMode?: ToolcraftVectorCoordinateMode;
437
460
  defaultValue?: unknown;
461
+ description?: string;
438
462
  label?: boolean | string;
439
463
  markerCount?: number;
440
464
  max?: number;
441
465
  min?: number;
442
466
  options?: readonly { label: string; value: string }[];
467
+ performanceReason?: string;
468
+ performanceRole?: ToolcraftControlPerformanceRole;
469
+ sliderValueKind?: ToolcraftSliderValueKind;
443
470
  step?: number;
444
- type: string;
471
+ type: ToolcraftCollectionItemControlType;
445
472
  unit?: string;
446
473
  variant?: string;
474
+ xLabel?: string;
475
+ yLabel?: string;
447
476
  };
448
477
 
478
+ export type ToolcraftCollectionItemControlsSchema = Readonly<
479
+ Record<string, ToolcraftCollectionItemControlSchema>
480
+ >;
481
+
449
482
  export type ToolcraftFontPickerValueSchema = {
450
483
  color?: string;
451
484
  fontId: string;
@@ -461,7 +494,7 @@ export type ToolcraftCurveInterpolation = "monotone" | "smooth";
461
494
 
462
495
  export type ToolcraftVectorCoordinateMode = "cartesian" | "screen";
463
496
 
464
- export type ToolcraftControlSchemaBase = {
497
+ type ToolcraftControlSchemaFields = {
465
498
  accept?: string;
466
499
  actions?: readonly (ToolcraftActionSchema | string)[];
467
500
  addLabel?: string;
@@ -476,6 +509,7 @@ export type ToolcraftControlSchemaBase = {
476
509
  interpolation?: ToolcraftCurveInterpolation;
477
510
  items?: readonly ToolcraftImagePickerItemSchema[];
478
511
  itemControl?: ToolcraftCollectionItemControlSchema;
512
+ itemControls?: ToolcraftCollectionItemControlsSchema;
479
513
  itemDefaultValue?: unknown;
480
514
  itemLabel?: string;
481
515
  keyframeable?: boolean;
@@ -498,11 +532,16 @@ export type ToolcraftControlSchemaBase = {
498
532
  unit?: string;
499
533
  valueLabel?: string;
500
534
  variant?: string;
501
- visibleWhen?: ToolcraftControlConditionSchema;
502
535
  xLabel?: string;
503
536
  yLabel?: string;
504
537
  };
505
538
 
539
+ export type ToolcraftControlSchemaBase = ToolcraftControlSchemaFields & {
540
+ applicability?: ToolcraftControlApplicabilitySchema;
541
+ /** @deprecated Product controls use conditional applicability. */
542
+ visibleWhen?: ToolcraftControlPredicateSchema;
543
+ };
544
+
506
545
  export type ToolcraftNonModelControlSchema = ToolcraftControlSchemaBase & {
507
546
  assetKind?: ToolcraftMediaAssetKind;
508
547
  modelFormats?: never;
@@ -525,18 +564,34 @@ export type ToolcraftControlSchema =
525
564
  | ToolcraftNonModelControlSchema
526
565
  | ToolcraftModelFileDropSchema;
527
566
 
567
+ type ToolcraftResolvedControlApplicabilityFields = Readonly<{
568
+ applicability: ToolcraftResolvedControlApplicabilitySchema;
569
+ }>;
570
+
571
+ export type ResolvedToolcraftNonModelControlSchema = Omit<
572
+ ToolcraftNonModelControlSchema,
573
+ "applicability" | "visibleWhen"
574
+ > &
575
+ ToolcraftResolvedControlApplicabilityFields;
576
+
528
577
  export type ResolvedToolcraftModelFileDropSchema = Omit<
529
578
  ToolcraftModelFileDropSchema,
530
- "modelFormats" | "modelLimits" | "multiple" | "topologyProfile"
531
- > & {
532
- modelFormats: readonly ToolcraftModelFormat[];
533
- modelLimits: ToolcraftModelImportLimits;
534
- multiple: false;
535
- topologyProfile: ToolcraftModelTopologyProfile;
536
- };
579
+ | "applicability"
580
+ | "modelFormats"
581
+ | "modelLimits"
582
+ | "multiple"
583
+ | "topologyProfile"
584
+ | "visibleWhen"
585
+ > &
586
+ ToolcraftResolvedControlApplicabilityFields & {
587
+ modelFormats: readonly ToolcraftModelFormat[];
588
+ modelLimits: ToolcraftModelImportLimits;
589
+ multiple: false;
590
+ topologyProfile: ToolcraftModelTopologyProfile;
591
+ };
537
592
 
538
593
  export type ResolvedToolcraftControlSchema =
539
- | ToolcraftNonModelControlSchema
594
+ | ResolvedToolcraftNonModelControlSchema
540
595
  | ResolvedToolcraftModelFileDropSchema;
541
596
 
542
597
  export type ToolcraftControlLayoutGroupSchema = {
@@ -20,10 +20,10 @@ Process matching routes sequentially within the current phase and skip repeated
20
20
 
21
21
  1. Build through `defineToolcraft` and `ToolcraftApp`.
22
22
  2. Keep app state in Toolcraft runtime schema and commands.
23
- 3. Keep product output in `canvasContent`; never render app UI there. The signed host owns `ToolcraftApp`, bootstrap, routes, and global runtime styles; product code supplies only `ToolcraftAppComposition`. Model upload preserves supported authored appearance from standalone files, folders, and bounded ZIP packages; declare typed `modelPresentation`, using runtime presentation by default and checked consumers for custom presentation. `renderDefaultCanvasMedia={false}` never hides runtime model layers. If upload/import is part of the source-material flow, do not invent canvas placeholder artwork, CTA copy, helper text, fake sample output, or preset source designs before real content exists.
24
- 4. Use built-in Toolcraft controls before custom controls. Before controls or canvas interactions, declare typed `interactionOwnership`: user request, inspected reference, or product usability selects one primary surface for each operation; complementary operations may share related state, but canvas and panel must not mirror the same operation. Every product also declares typed `viewInteraction` before renderer code; a visible editable spatial scene defaults to `orbit` and uses schema `orientationGizmo` plus runtime model-orbit interaction. Fixed or timeline-owned cameras require an explicit user request or inspected-reference evidence.
25
- 5. Do not hand-compose runtime surfaces or render built-in control components directly in app code; use `ToolcraftApp`, schema controls, `canvasContent`, `controlRenderers`, `onPanelAction`, and runtime commands.
26
- 6. Before writing controls, make and export `starterControlSectionInventory`: each product controls section declares its title, product entity or workflow stage, targets, and grouping reason. Group by product meaning, not UI component type.
23
+ 3. Keep product output in `canvasContent`; never render app UI there. The signed host owns `ToolcraftApp`, bootstrap, routes, global runtime styles, and the finite/infinite product scene surface; product code supplies only `ToolcraftAppComposition`. Infinite custom raster/WebGL output declares `sceneBoundsProvider` and consumes `useToolcraftProductSceneFrame` instead of dormant finite `canvas.size`. A preview-only environment that must fill the complete Infinity viewport uses `infiniteCanvasContent`; it remains pointer-transparent and outside world transforms, scene bounds, and export. Model upload preserves supported authored appearance from standalone files, folders, and bounded ZIP packages; declare typed `modelPresentation`, using runtime presentation by default and checked consumers for custom presentation. `renderDefaultCanvasMedia={false}` never hides runtime model layers. If upload/import is part of the source-material flow, do not invent canvas placeholder artwork, CTA copy, helper text, fake sample output, or preset source designs before real content exists.
24
+ 4. Use built-in Toolcraft controls before custom controls. Use `sourceCollection` when a source/runtime workflow owns array cardinality and users edit existing built-in item values; use `collectionActions` when users own add/remove cardinality. Before controls or canvas interactions, declare typed `interactionOwnership`: user request, inspected reference, or product usability selects one primary surface for each operation; complementary operations may share related state, but canvas and panel must not mirror the same operation. Every product also declares typed `viewInteraction` before renderer code; a visible editable spatial scene defaults to `orbit` and uses schema `orientationGizmo` plus runtime model-orbit interaction. Fixed or timeline-owned cameras require an explicit user request or inspected-reference evidence.
25
+ 5. Do not hand-compose runtime surfaces or render built-in control components directly in app code; use `ToolcraftApp`, schema controls, `canvasContent`, `controlRenderers`, `onPanelAction`, and runtime commands. Product code never imports modules below `src/toolcraft/ui/components/controls/**` or substitutes native `color`, `range`, `file`, `checkbox`, `radio`, `select`, or `textarea` value models for schema controls. Product artifact surfaces must correspond exactly to required `productReadiness.exportIntent`; use `docs/toolcraft/core/setup-export.md` for the canonical export decision sequence.
26
+ 6. Before writing controls, export `starterControlSectionInventory`. Every product section declares stable `entityId`, human-readable `entity`, exact targets, and `groupingReason`. Every product control explicitly declares `applicability` as `always` or `conditional`; inactive branches are absent, and every visible finite sibling branch must prove the control's accepted product outcome. Keep one entity in one section through ten controls; sections with eight to ten controls declare `semanticGroup` on every control. Entities above ten controls split into balanced two-to-ten-control workflow stages that keep the same entity identity and declare unique `workflowStage` plus `splitReason`. Group by product meaning, never by UI component type or target namespace.
27
27
  7. Keep control `label` short but semantically sufficient with the nearest visible section/group context, and put product-specific behavior help in schema `description`; runtime renders the label help tooltip only when that description adds meaning beyond the label.
28
28
  8. Enable layers and timeline only when product behavior requires them, then test the real UI. Product animation loops are seamless forward-only by default: first and last frames stitch, direction does not reverse, and mirror/yoyo/ping-pong behavior requires explicit user intent.
29
29
  9. Animated preview renderers suspend or coalesce non-essential animation work during canvas drag, pan, pinch, zoom, and radar/center interactions, then resume without changing user playback state.
@@ -33,7 +33,7 @@ Process matching routes sequentially within the current phase and skip repeated
33
33
  13. Generated apps follow the mandatory runtime Setup, Infinity canvas/finite canvas sizing, render scale, Timeline switch, Background, Image Export, Video Export, and sticky action rules in `docs/toolcraft/core/setup-export.md`. Do not duplicate or reinterpret those controls in app-authored sections.
34
34
  14. Media uploads, image/file mode, source images, multi-upload sorting, default assets, and image transform actions follow `docs/toolcraft/core/media-upload.md`.
35
35
  15. Keep `docs/toolcraft/agent-worklog.md` current with a decision trail, product decisions, explicit reference inputs, evidence, verification, and risks. Reference-runtime-clone apps also declare `referenceStudy` plus `referenceFeatureInventory` so every inspected reference feature has feature-level behavior evidence and maps to Toolcraft implementation and acceptance coverage.
36
- 16. Prove every visible entity through acceptance, browser, and performance coverage. Browser acceptance and performance pass only when protected helpers emit matching runtime evidence after successful assertions; source-code spelling and acceptance prose are not outcome authority. Declare typed conditional-visibility and background-output coverage, then use their fixed protected browser recipes. Raster products with `canvas.renderScale` declare `renderScaleCoverage` for interaction and steady state, plus playback when timeline is enabled, and prove real backing pixels with `canvas-render-scale-backing`; clamping quality is a functional failure without measured performance. Every measured path in a render-scale-enabled raster product proves actual CSS × devicePixelRatio × 2 backing after each measured phase. Generic acceptance outcomes prove command side effects only; use the fixed media, persistence, viewport, compound-control, layer, and timeline semantic recipes for specialized evidence.
36
+ 16. Prove every visible entity through acceptance, browser, and performance coverage. Browser acceptance and performance pass only when protected helpers emit matching runtime evidence after successful assertions; source-code spelling and acceptance prose are not outcome authority. Control applicability derives case-scoped presence/absence and product-outcome evidence from semantic section peers; background output keeps its fixed typed recipe. Raster products with `canvas.renderScale` declare `renderScaleCoverage` for interaction and steady state, plus playback when timeline is enabled, and prove real backing pixels with `canvas-render-scale-backing`; clamping quality is a functional failure without measured performance. Every measured path in a render-scale-enabled raster product proves actual CSS × devicePixelRatio × 2 backing after each measured phase. Generic acceptance outcomes prove command side effects only; use the fixed media, persistence, viewport, compound-control, layer, and timeline semantic recipes for specialized evidence.
37
37
  17. Performance planning follows one sequence: reachable controls and inputs; workload dimensions and enforced boundaries; pass cost, frequency, lifecycle, and invalidation; render-plan assessment and protected kernel benchmark when required; derived paths and combined fixtures; targeted functional/browser development checks; lifecycle-appropriate delivery proof. Keep `src/app/app-verification-impact.json` complete so later functional changes derive exact verification scope. Only exact request authority may create a measured targeted performance iteration. Details live in `docs/toolcraft/core/performance.md` and `docs/toolcraft/performance.md`.
38
38
  18. Custom renderer apps compile one canonical `rendererPipelineRegistration`, supply it through `ToolcraftAppComposition`, reuse that registration as `rendererPipeline` in performance assessment, and derive paths and fixtures from it. The neutral starter has no registration, runtime provider, or fixture adapters.
39
39
  19. Classify each coherent user-visible delivery batch with a verification tier before editing. Steering and fixes inside the same request stay in that batch. Use targeted checks for development feedback, then run the protected delivery gate once when the batch is ready.
@@ -42,7 +42,7 @@ Process matching routes sequentially within the current phase and skip repeated
42
42
 
43
43
  The generated folder starts as a neutral Toolcraft shell: canvas upload plus toolbar. It intentionally does not include demo controls, prompt fields, layers, or timeline. Do not treat test fixtures or documentation examples as product requirements. Add controls, timeline, layers, sticky actions, and custom renderers only after the requested product or reference app requires them; base workspace persistence already exists.
44
44
 
45
- When the folder becomes a real product, update `src/app/app-acceptance-data.ts` from `appProductReadiness.mode: "starter"` to `mode: "product"` and fill `productName`, `productSummary`, `requestedBehavior`, typed `interactionOwnership`, and typed `viewInteraction`. Compare canvas and panel for each operation that could plausibly live on either surface. A visible editable 3D scene uses `orbit`; `fixed-camera` and `timeline-camera` require explicit request/reference evidence. Renamed product folders are not allowed to keep neutral starter readiness.
45
+ When the folder becomes a real product, update `src/app/app-acceptance-data.ts` from `appProductReadiness.mode: "starter"` to `mode: "product"` and fill `productName`, `productSummary`, `requestedBehavior`, required typed `exportIntent`, typed `interactionOwnership`, and typed `viewInteraction`. Compare canvas and panel for each operation that could plausibly live on either surface. A visible editable 3D scene uses `orbit`; `fixed-camera` and `timeline-camera` require explicit request/reference evidence. Renamed product folders are not allowed to keep neutral starter readiness.
46
46
 
47
47
  ## License
48
48
 
@@ -119,9 +119,10 @@ These ids mirror `TOOLCRAFT_DECISION_CONTRACT` in `@/toolcraft/runtime`. Keep th
119
119
 
120
120
  - Use `defineToolcraft` from `@/toolcraft/runtime`.
121
121
  - Export an `appComposition` satisfying `ToolcraftAppComposition` from `src/app/app-composition.tsx`.
122
- - Keep product composition limited to `schema`, `canvasContent`, `controlRenderers`, `exportRenderer`, `modelPresentation`, `onPanelAction`, `renderDefaultCanvasMedia`, and optional `rendererPipelineRegistration`; host `className` and `style` are not product extension points.
122
+ - Keep product composition limited to `schema`, `canvasContent`, `infiniteCanvasContent`, `controlRenderers`, `exportRenderer`, `sceneBoundsProvider`, `modelPresentation`, `onPanelAction`, `renderDefaultCanvasMedia`, and optional `rendererPipelineRegistration`; host `className` and `style` are not product extension points.
123
+ - Infinite product output fills the runtime-owned scene surface derived from `sceneBoundsProvider`. Raster/WebGL renderers call `useToolcraftProductSceneFrame` for backing size and coordinate translation; they never position a second bounds wrapper or use dormant finite `canvas.size` while infinite.
123
124
  - Custom renderers use one compiled `rendererPipelineRegistration` as the canonical declaration for runtime execution and new-envelope performance assessment. Product code receives only the disposal-free pipeline client through hooks and panel action context.
124
- - Do not import `ToolcraftApp`, low-level runtime surfaces, or built-in controls into product modules. The signed host renders the shell; product modules use schema controls and supported composition fields.
125
+ - Do not import `ToolcraftApp`, low-level runtime surfaces, built-in controls, or modules below `src/toolcraft/ui/components/controls/**` into product modules. Do not recreate schema control value models with native form elements. The signed host renders the shell; product modules use schema controls and supported composition fields.
125
126
  - Use `renderDefaultCanvasMedia={false}` when a custom renderer replaces generic image/file preview. This flag does not suppress runtime model layers; use typed `modelPresentation` custom mode with checked consumers for declared model targets.
126
127
  - Use `ToolcraftApp onPanelAction` for non-export sticky footer product actions such as Generate, Apply, Copy, or Download. Typed `export-image` and `export-video` actions are executed by the runtime from the shared `ToolcraftAppComposition.exportRenderer`.
127
128
  - Product export renderers draw one deterministic scene-coordinate frame and return/await their real work. Product modules never allocate export canvases, choose encoders, encode blobs, create download URLs, or duplicate image/video composition.
@@ -203,10 +204,11 @@ The app is complete only when:
203
204
  - the Toolcraft runtime shell is present;
204
205
  - `canvasContent` contains product output only;
205
206
  - the runtime canvas backing remains visible behind product output;
206
- - every visible control affects runtime state and product output or a command side effect;
207
+ - infinite product output uses the runtime-owned product scene surface, exact provider bounds, and real edge-pixel proof without clipping to dormant finite `canvas.size`;
208
+ - every product control declares explicit applicability, every non-matching finite branch hides it, and every visible finite branch proves product output or the accepted command side effect;
207
209
  - reset returns schema controls to `defaultValue`;
208
210
  - sticky footer export actions operate on final product output at `state.canvas.size`;
209
- - every product exposes Export PNG; products whose requested delivery includes animation also expose Export Video;
211
+ - artifact actions and settings correspond exactly to `productReadiness.exportIntent`; image export is the product default, video requires explicit user-request evidence, and image removal requires explicit user-removal evidence;
210
212
  - PNG export uses the background controls normalized into runtime Setup: `Background` beside `Infinity canvas`, then `Background color`; live preview hides product background when Background is off, and video keeps background;
211
213
  - every PNG export includes `Image Export` format/resolution `select` controls; runtime resolves the selected settings and exact output size;
212
214
  - products with both PNG and video export place `Image Export` immediately before `Video Export`;