@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.
- package/package.json +1 -1
- package/src/generate-test-doc-assertions.mjs +58 -68
- package/src/generate-test-doc-assertions.test.mjs +43 -0
- package/src/generate-test-doc-policy-assertions.mjs +92 -0
- package/src/generate-test-file-assertions.mjs +26 -75
- package/src/generate-test-product-iteration.test.mjs +17 -0
- package/src/generate-test-runtime-boundary-assertions.mjs +158 -0
- package/src/generate-test-standalone-assertions.mjs +10 -0
- package/templates/runtime/contracts/component-contracts.choices.test.ts +47 -3
- package/templates/runtime/contracts/component-contracts.choices.ts +48 -3
- package/templates/runtime/contracts/component-contracts.inputs.test.ts +6 -6
- package/templates/runtime/contracts/component-contracts.inputs.ts +6 -6
- package/templates/runtime/contracts/component-contracts.media-custom.test.ts +8 -2
- package/templates/runtime/contracts/component-contracts.media-custom.ts +8 -2
- package/templates/runtime/contracts/component-contracts.runtime-acceptance.test.ts +4 -4
- package/templates/runtime/contracts/component-contracts.runtime-labels.test.ts +7 -1
- package/templates/runtime/contracts/component-contracts.runtime-panels.test.ts +3 -0
- package/templates/runtime/contracts/component-contracts.runtime-shell.test.ts +3 -0
- package/templates/runtime/contracts/component-contracts.runtime.ts +12 -8
- package/templates/runtime/contracts/component-contracts.test.ts +2 -0
- package/templates/runtime/contracts/decision-contracts.test.ts +18 -0
- package/templates/runtime/contracts/decision-contracts.ts +10 -10
- package/templates/runtime/export/export-background.ts +0 -19
- package/templates/runtime/export/export.test.ts +45 -42
- package/templates/runtime/export/index.ts +44 -16
- package/templates/runtime/index.ts +2 -0
- package/templates/runtime/model-import/model-import-limits.ts +15 -4
- package/templates/runtime/react/app-shell/runtime-public-api.test.tsx +11 -1
- package/templates/runtime/react/app-shell/toolcraft-app.scene-export.test.tsx +29 -0
- package/templates/runtime/react/app-shell/toolcraft-app.tsx +18 -8
- package/templates/runtime/react/canvas/canvas-shell-rendering.test.tsx +76 -0
- package/templates/runtime/react/canvas/canvas-shell-test-utils.tsx +13 -1
- package/templates/runtime/react/canvas/canvas-shell.tsx +16 -1
- package/templates/runtime/react/canvas/product-scene-surface.test.tsx +195 -0
- package/templates/runtime/react/canvas/product-scene-surface.tsx +155 -0
- package/templates/runtime/react/canvas/runtime-scene-export.ts +1 -1
- package/templates/runtime/react/controls-panel/__tests__/control-conditions.test.ts +61 -0
- package/templates/runtime/react/controls-panel/__tests__/controls-panel.collection-actions.test.tsx +89 -0
- package/templates/runtime/react/controls-panel/__tests__/controls-panel.control-visibility.test.tsx +40 -19
- package/templates/runtime/react/controls-panel/__tests__/controls-panel.scene-export-actions.test.tsx +1 -1
- package/templates/runtime/react/controls-panel/__tests__/controls-panel.source-collection.test.tsx +62 -0
- package/templates/runtime/react/controls-panel/__tests__/controls-panel.source-color-bank.test.tsx +67 -0
- package/templates/runtime/react/controls-panel/actions/controls-panel-actions.ts +2 -4
- package/templates/runtime/react/controls-panel/actions/export-action-runner.ts +10 -4
- package/templates/runtime/react/controls-panel/conditions/control-conditions.ts +32 -119
- package/templates/runtime/react/controls-panel/layout/controls-panel-control-group.tsx +5 -2
- package/templates/runtime/react/controls-panel/layout/controls-panel-layout.tsx +10 -8
- package/templates/runtime/react/controls-panel/layout/controls-panel-section.tsx +3 -3
- package/templates/runtime/react/controls-panel/renderers/controls-panel-collection-item-fields.test.tsx +63 -0
- package/templates/runtime/react/controls-panel/renderers/controls-panel-collection-item-fields.tsx +138 -0
- package/templates/runtime/react/controls-panel/renderers/controls-panel-collection-items.tsx +104 -0
- package/templates/runtime/react/controls-panel/renderers/controls-panel-collection-renderer.tsx +115 -236
- package/templates/runtime/react/controls-panel/renderers/controls-panel-file-drop-item-controls.test.tsx +95 -0
- package/templates/runtime/react/controls-panel/renderers/controls-panel-file-drop-item-controls.tsx +82 -0
- package/templates/runtime/react/controls-panel/renderers/controls-panel-media-renderer.test.tsx +73 -1
- package/templates/runtime/react/controls-panel/renderers/controls-panel-media-renderer.tsx +50 -1
- package/templates/runtime/react/controls-panel/renderers/controls-panel-renderer-registry.test.ts +1 -1
- package/templates/runtime/react/controls-panel/renderers/controls-panel-renderer-registry.ts +2 -1
- package/templates/runtime/react/controls-panel/values/controls-panel-collection-values.ts +38 -6
- package/templates/runtime/react/controls-panel/values/controls-panel-value-labels.ts +1 -0
- package/templates/runtime/react/index.ts +4 -0
- package/templates/runtime/react/model-rendering/model-export.ts +1 -1
- package/templates/runtime/react/orientation-gizmo/orientation-gizmo-selection.ts +5 -5
- package/templates/runtime/react/orientation-gizmo/use-toolcraft-orientation-control-selection.ts +2 -2
- package/templates/runtime/scene/scene-bounds.test.ts +23 -0
- package/templates/runtime/scene/scene-bounds.ts +31 -17
- package/templates/runtime/schema/app-capabilities.test.ts +4 -0
- package/templates/runtime/schema/collection-item-controls.ts +29 -0
- package/templates/runtime/schema/control-applicability.test.ts +127 -0
- package/templates/runtime/schema/control-applicability.ts +433 -0
- package/templates/runtime/schema/control-schema-normalization.ts +177 -11
- package/templates/runtime/schema/controls-panel-actions.ts +2 -2
- package/templates/runtime/schema/controls-panel-section-layout.ts +14 -9
- package/templates/runtime/schema/controls-panel-section-roundtrip.test.ts +59 -0
- package/templates/runtime/schema/define-toolcraft.collection-actions-item-controls.test.ts +127 -0
- package/templates/runtime/schema/define-toolcraft.control-sections.test.ts +10 -4
- package/templates/runtime/schema/define-toolcraft.file-drop-item-controls.test.ts +98 -0
- package/templates/runtime/schema/define-toolcraft.file-drop-normalization.test.ts +27 -0
- package/templates/runtime/schema/define-toolcraft.segmented-control-fit.test.ts +20 -2
- package/templates/runtime/schema/define-toolcraft.setup-canvas.test.ts +16 -3
- package/templates/runtime/schema/define-toolcraft.source-collection.test.ts +79 -0
- package/templates/runtime/schema/runtime-setup-section.ts +24 -5
- package/templates/runtime/schema/types.ts +68 -13
- package/templates/starter/AGENTS.md +12 -10
- package/templates/starter/docs/toolcraft/acceptance-testing.md +17 -31
- package/templates/starter/docs/toolcraft/agent-worklog.md +15 -0
- package/templates/starter/docs/toolcraft/assembly-workflow.md +10 -3
- package/templates/starter/docs/toolcraft/component-rules.md +8 -6
- package/templates/starter/docs/toolcraft/core/control-selection.md +9 -3
- package/templates/starter/docs/toolcraft/core/layout.md +7 -7
- package/templates/starter/docs/toolcraft/core/media-upload.md +2 -0
- package/templates/starter/docs/toolcraft/core/runtime-boundary.md +13 -2
- package/templates/starter/docs/toolcraft/core/setup-export.md +33 -7
- package/templates/starter/docs/toolcraft/custom-controls.md +6 -4
- package/templates/starter/docs/toolcraft/decision-contract.md +3 -1
- package/templates/starter/docs/toolcraft/renderer-technique.md +4 -0
- package/templates/starter/docs/toolcraft/schema-reference.md +21 -19
- package/templates/starter/e2e/app-browser-control-applicability-requirements.spec.ts +163 -0
- package/templates/starter/e2e/app-browser-infinity-canvas-evidence.spec.ts +7 -2
- package/templates/starter/e2e/app-browser-orientation-evidence.spec.ts +174 -21
- package/templates/starter/e2e/app-browser-runtime-requirements.spec.ts +1 -33
- package/templates/starter/e2e/app-browser-semantic-evidence.spec.ts +61 -15
- package/templates/starter/e2e/{browser-conditional-output-evidence-helpers.ts → browser-background-output-evidence.ts} +0 -68
- package/templates/starter/e2e/browser-control-applicability-evidence.ts +114 -0
- package/templates/starter/e2e/browser-infinity-canvas-evidence.ts +20 -11
- package/templates/starter/e2e/browser-orientation-gizmo-evidence-helpers.ts +4 -0
- package/templates/starter/e2e/browser-orientation-gizmo-live-preconditions.ts +86 -0
- package/templates/starter/e2e/browser-runtime-applicability-requirements.ts +84 -0
- package/templates/starter/e2e/browser-runtime-evidence-reporter.ts +9 -2
- package/templates/starter/e2e/browser-runtime-evidence-requirements.ts +29 -26
- package/templates/starter/e2e/browser-semantic-evidence-test-helpers.ts +25 -0
- package/templates/starter/scripts/toolcraft-contract-manifest.json +1 -0
- package/templates/starter/scripts/toolcraft-product-boundary-ast.mjs +85 -115
- package/templates/starter/scripts/toolcraft-product-boundary-module-policy.mjs +101 -0
- package/templates/starter/scripts/toolcraft-product-boundary.test.mjs +0 -72
- package/templates/starter/scripts/toolcraft-product-control-bindings.mjs +182 -0
- package/templates/starter/scripts/toolcraft-product-control-boundary.mjs +146 -0
- package/templates/starter/scripts/toolcraft-product-control-boundary.test.mjs +186 -0
- package/templates/starter/scripts/toolcraft-product-export-boundary.mjs +6 -17
- package/templates/starter/scripts/toolcraft-product-export-boundary.test.mjs +196 -0
- package/templates/starter/scripts/toolcraft-typescript-source-evidence-structure.test.mjs +20 -0
- package/templates/starter/scripts/toolcraft-typescript-source-evidence.mjs +1 -0
- package/templates/starter/scripts/toolcraft-unavailable-resource-evidence-facade.test.mjs +2 -0
- package/templates/starter/src/app/acceptance/artifact-export-intent.ts +130 -0
- package/templates/starter/src/app/acceptance/conditions.ts +1 -1
- package/templates/starter/src/app/acceptance/control-acceptance-context.ts +2 -2
- package/templates/starter/src/app/acceptance/control-acceptance-coverage.ts +6 -20
- package/templates/starter/src/app/acceptance/control-acceptance-kind-rules.ts +24 -5
- package/templates/starter/src/app/acceptance/control-acceptance-policy.ts +2 -13
- package/templates/starter/src/app/acceptance/control-applicability-cases.test.ts +321 -0
- package/templates/starter/src/app/acceptance/control-applicability-cases.ts +290 -0
- package/templates/starter/src/app/acceptance/control-applicability.ts +332 -0
- package/templates/starter/src/app/acceptance/control-layout-dependency-rules.ts +20 -9
- package/templates/starter/src/app/acceptance/control-layout-entity-rules.ts +4 -6
- package/templates/starter/src/app/acceptance/control-layout-model.ts +4 -4
- package/templates/starter/src/app/acceptance/control-layout-section-rules.ts +3 -10
- package/templates/starter/src/app/acceptance/control-parts.ts +8 -0
- package/templates/starter/src/app/acceptance/control-section-entity-cohesion.test.ts +215 -0
- package/templates/starter/src/app/acceptance/control-section-entity-cohesion.ts +132 -0
- package/templates/starter/src/app/acceptance/control-section-inventory.test.ts +87 -0
- package/templates/starter/src/app/acceptance/control-section-inventory.ts +74 -44
- package/templates/starter/src/app/acceptance/custom-controls.ts +11 -3
- package/templates/starter/src/app/acceptance/inline-layout.ts +64 -9
- package/templates/starter/src/app/acceptance/media-upload.ts +5 -1
- package/templates/starter/src/app/acceptance/orientation-gizmo.ts +18 -11
- package/templates/starter/src/app/acceptance/output-background-rules.ts +2 -2
- package/templates/starter/src/app/acceptance/output-export-layout-rules.ts +18 -10
- package/templates/starter/src/app/acceptance/output-export-model.ts +54 -11
- package/templates/starter/src/app/acceptance/output-export.ts +22 -10
- package/templates/starter/src/app/acceptance/output-video-export-rules.ts +99 -0
- package/templates/starter/src/app/acceptance/types.ts +23 -8
- package/templates/starter/src/app/acceptance/validate-coverage.ts +19 -2
- package/templates/starter/src/app/starter-acceptance.artifact-export-intent.test.ts +239 -0
- package/templates/starter/src/app/starter-acceptance.background-export.test.ts +33 -0
- package/templates/starter/src/app/starter-acceptance.canvas-sizing.test.ts +16 -1
- package/templates/starter/src/app/starter-acceptance.compound-part-coverage.test.ts +65 -0
- package/templates/starter/src/app/starter-acceptance.contract-fixtures.test.ts +41 -0
- package/templates/starter/src/app/starter-acceptance.contract-fixtures.ts +112 -16
- package/templates/starter/src/app/starter-acceptance.control-applicability.test.ts +224 -0
- package/templates/starter/src/app/starter-acceptance.control-order.test.ts +58 -3
- package/templates/starter/src/app/starter-acceptance.control-state.test.ts +0 -111
- package/templates/starter/src/app/starter-acceptance.custom-control-collections.test.ts +3 -1
- package/templates/starter/src/app/starter-acceptance.custom-control-valid.test.ts +1 -1
- package/templates/starter/src/app/starter-acceptance.export-test-utils.ts +22 -0
- package/templates/starter/src/app/{starter-acceptance.image-video-export-settings.test.ts → starter-acceptance.image-export-settings.test.ts} +37 -9
- package/templates/starter/src/app/starter-acceptance.interaction-ownership.test.ts +12 -0
- package/templates/starter/src/app/starter-acceptance.media-upload.fixtures.ts +11 -1
- package/templates/starter/src/app/starter-acceptance.media-upload.test.ts +28 -0
- package/templates/starter/src/app/starter-acceptance.output-export-intent.test.ts +384 -0
- package/templates/starter/src/app/starter-acceptance.output-export-rules.test.ts +4 -0
- package/templates/starter/src/app/starter-acceptance.product-output-export.test.ts +112 -105
- package/templates/starter/src/app/starter-acceptance.product-readiness.test.ts +12 -0
- package/templates/starter/src/app/starter-acceptance.section-cohesion.test.ts +1 -1
- package/templates/starter/src/app/starter-acceptance.section-dependencies.test.ts +35 -24
- package/templates/starter/src/app/starter-acceptance.section-size.test.ts +72 -0
- package/templates/starter/src/app/starter-acceptance.ts +11 -2
- package/templates/starter/src/app/starter-acceptance.video-export-settings.test.ts +336 -0
- package/templates/starter/src/app/starter-acceptance.view-interaction.test.ts +8 -0
- package/templates/starter/src/app/test-evidence/browser-runtime-contract.ts +2 -2
- package/templates/starter/src/routes/index.tsx +1 -0
- package/templates/ui/components/composites/tabs.tsx +2 -2
- package/templates/ui/components/controls/collection-actions/collection-item-groups.test.tsx +38 -0
- package/templates/ui/components/controls/collection-actions/collection-item-groups.tsx +36 -0
- package/templates/ui/components/controls/collection-actions/index.ts +2 -0
- package/templates/ui/components/controls/file-drop/file-drop-collection-control.tsx +137 -0
- package/templates/ui/components/controls/file-drop/file-drop-control.test.tsx +189 -0
- package/templates/ui/components/controls/file-drop/file-drop-control.tsx +81 -88
- package/templates/ui/components/controls/file-drop/file-drop-presentation.tsx +131 -9
- package/templates/ui/components/controls/file-drop/file-drop-types.ts +36 -22
- package/templates/ui/components/controls/file-drop/index.ts +1 -0
- package/templates/ui/components/controls/index.ts +3 -0
- package/templates/ui/components/controls/range-slider/range-slider-control.tsx +1 -1
- package/templates/ui/components/controls/range-slider/range-slider-value.test.ts +14 -0
- package/templates/ui/components/controls/range-slider/range-slider-value.ts +3 -2
- package/templates/starter/src/app/acceptance/condition-exclusivity.test.ts +0 -60
- package/templates/starter/src/app/acceptance/condition-exclusivity.ts +0 -221
package/package.json
CHANGED
|
@@ -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,70 +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
|
-
const generatedPerformanceLifecycleMarkers = [
|
|
30
|
-
"<!-- toolcraft-performance-lifecycle: first-delivery=functional; later-delivery=functional-targeted; complaint=one-authority-targeted-performance-iteration; full-audit=explicit-only -->",
|
|
31
|
-
"<!-- toolcraft-performance-iteration: authority=exact-request-evidence+canonical-path-ids; fixture=reachable-development; after-pass=return-app-to-user+stop -->",
|
|
32
|
-
"<!-- 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 -->",
|
|
33
|
-
"<!-- toolcraft-performance-routing: localized=agent-targeted; ambiguous=one-user-facing-choice; broad=offer-targeted-or-full; full=explicit-only -->",
|
|
34
|
-
];
|
|
35
|
-
|
|
36
|
-
export function getMissingGeneratedPerformanceLifecycleMarkers(sources) {
|
|
37
|
-
return sources.flatMap(({ docPath, source }) =>
|
|
38
|
-
generatedPerformanceLifecycleMarkers.flatMap((marker) =>
|
|
39
|
-
source.includes(marker)
|
|
40
|
-
? []
|
|
41
|
-
: [`docs/toolcraft/${docPath} must include canonical marker: ${marker}`],
|
|
42
|
-
),
|
|
43
|
-
);
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
async function collectMarkdownPaths(rootDir, relativeDir = "") {
|
|
47
|
-
const currentDir = path.join(rootDir, relativeDir);
|
|
48
|
-
const entries = await fs.readdir(currentDir, { withFileTypes: true });
|
|
49
|
-
const markdownPaths = [];
|
|
50
|
-
|
|
51
|
-
for (const entry of entries.sort((left, right) => left.name.localeCompare(right.name))) {
|
|
52
|
-
const relativePath = path.join(relativeDir, entry.name);
|
|
53
|
-
if (entry.isDirectory()) {
|
|
54
|
-
markdownPaths.push(...(await collectMarkdownPaths(rootDir, relativePath)));
|
|
55
|
-
} else if (entry.isFile() && entry.name.endsWith(".md")) {
|
|
56
|
-
markdownPaths.push(relativePath);
|
|
57
|
-
}
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
return markdownPaths;
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
export async function collectGeneratedAgentFacingDocs(targetDir) {
|
|
64
|
-
const docsRoot = path.join(targetDir, "docs/toolcraft");
|
|
65
|
-
const docPaths = await collectMarkdownPaths(docsRoot);
|
|
66
|
-
const docs = await Promise.all(
|
|
67
|
-
docPaths.map(async (docPath) => ({
|
|
68
|
-
docPath: path.posix.join("docs/toolcraft", docPath.split(path.sep).join("/")),
|
|
69
|
-
source: await fs.readFile(path.join(docsRoot, docPath), "utf8"),
|
|
70
|
-
})),
|
|
71
|
-
);
|
|
72
|
-
|
|
73
|
-
return [
|
|
74
|
-
{
|
|
75
|
-
docPath: "AGENTS.md",
|
|
76
|
-
source: await fs.readFile(path.join(targetDir, "AGENTS.md"), "utf8"),
|
|
77
|
-
},
|
|
78
|
-
...docs,
|
|
79
|
-
];
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
export function getStaleAgentPerformanceWorkflowFailures(sources) {
|
|
83
|
-
return sources.flatMap(({ docPath, source }) =>
|
|
84
|
-
staleAgentPerformanceWorkflowPattern.test(source)
|
|
85
|
-
? [`${docPath} contains removed performance workflow guidance`]
|
|
86
|
-
: [],
|
|
87
|
-
);
|
|
88
|
-
}
|
|
89
|
-
|
|
90
41
|
export async function assertGeneratedDocs(targetDir) {
|
|
91
42
|
await Promise.all(
|
|
92
43
|
generatedDocPaths.map((docPath) => fs.stat(path.join(targetDir, "docs/toolcraft", docPath))),
|
|
@@ -96,8 +47,9 @@ export async function assertGeneratedDocs(targetDir) {
|
|
|
96
47
|
const licenseSource = await fs.readFile(path.join(targetDir, "LICENSE.md"), "utf8");
|
|
97
48
|
const noticeSource = await fs.readFile(path.join(targetDir, "NOTICE.md"), "utf8");
|
|
98
49
|
const localDocPaths = generatedDocPaths.filter((docPath) => docPath !== "README.md");
|
|
50
|
+
const generatedAgentFacingDocs = await collectGeneratedAgentFacingDocs(targetDir);
|
|
99
51
|
const staleWorkflowFailures = getStaleAgentPerformanceWorkflowFailures(
|
|
100
|
-
|
|
52
|
+
generatedAgentFacingDocs,
|
|
101
53
|
);
|
|
102
54
|
|
|
103
55
|
assert.match(noticeSource, /Mediabunny/u);
|
|
@@ -109,6 +61,7 @@ export async function assertGeneratedDocs(targetDir) {
|
|
|
109
61
|
[],
|
|
110
62
|
`Generated agent-facing docs contain stale performance workflow guidance:\n${staleWorkflowFailures.join("\n")}`,
|
|
111
63
|
);
|
|
64
|
+
assertGeneratedExportIntentDocsDoNotInferVideo(generatedAgentFacingDocs);
|
|
112
65
|
|
|
113
66
|
assert.match(agentsSource, /Quick Entry Contract/);
|
|
114
67
|
assert.match(agentsSource, /Local Reference Docs/);
|
|
@@ -240,12 +193,44 @@ export async function assertGeneratedDocs(targetDir) {
|
|
|
240
193
|
assert.match(coreRuntimeBoundarySource, /does not suppress runtime model layers/);
|
|
241
194
|
assert.match(componentRulesSource, /Do not show Layers for a single-layer app/);
|
|
242
195
|
assert.match(coreTimelineSource, /Any app with `Export Video` must enable the top Toolcraft timeline/);
|
|
243
|
-
assert.match(coreSetupExportSource, /
|
|
196
|
+
assert.match(coreSetupExportSource, /productReadiness\.exportIntent/);
|
|
244
197
|
assert.match(
|
|
245
198
|
coreSetupExportSource,
|
|
246
|
-
/
|
|
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
|
+
}
|
|
230
|
+
assert.match(
|
|
231
|
+
coreSetupExportSource,
|
|
232
|
+
/Clipboard copy may be an additional product action, but it never changes or substitutes for the recorded artifact intent/,
|
|
247
233
|
);
|
|
248
|
-
assert.match(coreSetupExportSource, /Clipboard copy is optional and never replaces export/);
|
|
249
234
|
assert.match(coreSetupExportSource, /renderScaleCoverage/);
|
|
250
235
|
assert.match(coreSetupExportSource, /selected-backing-pixels/);
|
|
251
236
|
assert.match(coreSetupExportSource, /canvas-render-scale-backing/);
|
|
@@ -271,6 +256,10 @@ export async function assertGeneratedDocs(targetDir) {
|
|
|
271
256
|
);
|
|
272
257
|
assert.match(assemblyDocsSource, /@\/toolcraft\/runtime/);
|
|
273
258
|
assert.match(assemblyDocsSource, /ToolcraftAppComposition/);
|
|
259
|
+
assert.match(
|
|
260
|
+
assemblyDocsSource,
|
|
261
|
+
/Every product declaration requires `productReadiness\.exportIntent`/,
|
|
262
|
+
);
|
|
274
263
|
assert.match(assemblyDocsSource, /protected route/);
|
|
275
264
|
assert.match(assemblyDocsSource, /transferMode: "reference-runtime-clone"/);
|
|
276
265
|
assert.match(assemblyDocsSource, /`canvasContent` must not contain app UI/);
|
|
@@ -284,6 +273,7 @@ export async function assertGeneratedDocs(targetDir) {
|
|
|
284
273
|
assert.match(acceptanceDocsSource, /automatedTestName/);
|
|
285
274
|
assert.match(acceptanceDocsSource, /browserTestName/);
|
|
286
275
|
assert.match(acceptanceDocsSource, /app-acceptance-data\.ts/);
|
|
276
|
+
assert.match(acceptanceDocsSource, /productReadiness\.exportIntent/);
|
|
287
277
|
assert.match(
|
|
288
278
|
acceptanceDocsSource,
|
|
289
279
|
/Contract docs are signed except product-owned `agent-worklog\.md` and optional `workflow-observation\.md`; only the observation stays outside `sourceHash`/,
|
|
@@ -5,11 +5,54 @@ import path from "node:path";
|
|
|
5
5
|
import test from "node:test";
|
|
6
6
|
|
|
7
7
|
import {
|
|
8
|
+
assertGeneratedExportIntentDocsDoNotInferVideo,
|
|
8
9
|
collectGeneratedAgentFacingDocs,
|
|
9
10
|
getMissingGeneratedPerformanceLifecycleMarkers,
|
|
10
11
|
getStaleAgentPerformanceWorkflowFailures,
|
|
11
12
|
} from "./generate-test-doc-assertions.mjs";
|
|
12
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
|
+
|
|
13
56
|
test("scans every generated agent-facing Markdown source independently", async (t) => {
|
|
14
57
|
const targetDir = await fs.mkdtemp(path.join(os.tmpdir(), "toolcraft-doc-scan-"));
|
|
15
58
|
t.after(() => fs.rm(targetDir, { force: true, recursive: true }));
|
|
@@ -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
|
+
}
|
|
@@ -8,7 +8,7 @@ import {
|
|
|
8
8
|
} from "./generate-test-delivery-architecture-assertions.mjs";
|
|
9
9
|
import { listRelativeFiles, repoRoot } from "./generate-test-fs-utils.mjs";
|
|
10
10
|
import { assertGeneratedPerformanceCanvasBoundary } from "./generate-test-performance-boundary-assertions.mjs";
|
|
11
|
-
import {
|
|
11
|
+
import { assertGeneratedRuntimePublicBoundary } from "./generate-test-runtime-boundary-assertions.mjs";
|
|
12
12
|
|
|
13
13
|
export {
|
|
14
14
|
assertGeneratedRoutesAndStyles,
|
|
@@ -44,16 +44,7 @@ export async function assertGeneratedFiles(targetDir) {
|
|
|
44
44
|
"scripts/toolcraft-targeted-verification-selection.mjs",
|
|
45
45
|
];
|
|
46
46
|
|
|
47
|
-
|
|
48
|
-
assert.ok(await fs.stat(path.join(targetDir, "src/toolcraft/runtime/react/index.ts")));
|
|
49
|
-
assert.ok(
|
|
50
|
-
await fs.stat(
|
|
51
|
-
path.join(
|
|
52
|
-
targetDir,
|
|
53
|
-
"src/toolcraft/runtime/model-import/worker/model-import.worker.bundle.js",
|
|
54
|
-
),
|
|
55
|
-
),
|
|
56
|
-
);
|
|
47
|
+
await assertGeneratedRuntimePublicBoundary(targetDir);
|
|
57
48
|
assert.ok(await fs.stat(path.join(targetDir, "playwright.config.ts")));
|
|
58
49
|
await assert.rejects(
|
|
59
50
|
fs.stat(path.join(targetDir, ".toolcraft")),
|
|
@@ -61,70 +52,6 @@ export async function assertGeneratedFiles(targetDir) {
|
|
|
61
52
|
"Fresh generated apps must not inherit starter server ports or verification receipts.",
|
|
62
53
|
);
|
|
63
54
|
|
|
64
|
-
const generatedToolcraftFiles = await listRelativeFiles(
|
|
65
|
-
path.join(targetDir, "src/toolcraft"),
|
|
66
|
-
);
|
|
67
|
-
const expectedRuntimeFiles = (await listRelativeFiles(
|
|
68
|
-
path.join(repoRoot, "packages/toolcraft-runtime/src"),
|
|
69
|
-
))
|
|
70
|
-
.filter((relativePath) => !isGeneratedTestSupportPath(relativePath))
|
|
71
|
-
.map((relativePath) => `runtime/${relativePath}`);
|
|
72
|
-
assert.deepEqual(
|
|
73
|
-
generatedToolcraftFiles.filter((relativePath) => relativePath.startsWith("runtime/")),
|
|
74
|
-
expectedRuntimeFiles,
|
|
75
|
-
"Generated standalone runtime must contain every production runtime module.",
|
|
76
|
-
);
|
|
77
|
-
for (const relativePath of [
|
|
78
|
-
"runtime/export/artifact-export-settings.ts",
|
|
79
|
-
"runtime/export/artifact-export-request.ts",
|
|
80
|
-
"runtime/export/artifact-frame-renderer.ts",
|
|
81
|
-
"runtime/export/artifact-frame-state.ts",
|
|
82
|
-
"runtime/export/artifact-scene-frame.ts",
|
|
83
|
-
"runtime/export/image-artifact-export.ts",
|
|
84
|
-
"runtime/export/product-export-renderer.ts",
|
|
85
|
-
"runtime/export/video-artifact-export.ts",
|
|
86
|
-
"runtime/export/video-encoding-backend.ts",
|
|
87
|
-
"runtime/export/video-frame-schedule.ts",
|
|
88
|
-
]) {
|
|
89
|
-
assert.ok(
|
|
90
|
-
generatedToolcraftFiles.includes(relativePath),
|
|
91
|
-
`Generated runtime must contain ${relativePath}.`,
|
|
92
|
-
);
|
|
93
|
-
}
|
|
94
|
-
assert.equal(
|
|
95
|
-
generatedToolcraftFiles.includes("runtime/export/export.ts"),
|
|
96
|
-
false,
|
|
97
|
-
"Generated runtime must not copy the removed legacy export helper.",
|
|
98
|
-
);
|
|
99
|
-
for (const relativePath of [
|
|
100
|
-
"e2e/browser-media-export-evidence.ts",
|
|
101
|
-
"e2e/decoded-pixel-observation.ts",
|
|
102
|
-
"e2e/image-artifact-inspection.ts",
|
|
103
|
-
"e2e/video-artifact-inspection.ts",
|
|
104
|
-
]) {
|
|
105
|
-
assert.ok(
|
|
106
|
-
await fs.stat(path.join(targetDir, relativePath)),
|
|
107
|
-
`Generated output must contain ${relativePath}.`,
|
|
108
|
-
);
|
|
109
|
-
}
|
|
110
|
-
for (const relativePath of generatedToolcraftFiles) {
|
|
111
|
-
assert.equal(
|
|
112
|
-
isGeneratedTestSupportPath(relativePath),
|
|
113
|
-
false,
|
|
114
|
-
`Generated runtime contains test support: ${relativePath}`,
|
|
115
|
-
);
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
const removedRuntimeTestHelpers = [
|
|
119
|
-
"runtime/react/canvas/canvas-shell-test-utils.tsx",
|
|
120
|
-
"runtime/react/controls-panel/testing/controls-panel-test-utils.tsx",
|
|
121
|
-
"runtime/react/layers/layers-panel-test-utils.tsx",
|
|
122
|
-
"runtime/react/timeline/timeline-panel-test-utils.tsx",
|
|
123
|
-
];
|
|
124
|
-
for (const relativePath of removedRuntimeTestHelpers) {
|
|
125
|
-
assert.equal(generatedToolcraftFiles.includes(relativePath), false);
|
|
126
|
-
}
|
|
127
|
-
|
|
128
55
|
const playwrightConfigSource = await fs.readFile(
|
|
129
56
|
path.join(targetDir, "playwright.config.ts"),
|
|
130
57
|
"utf8",
|
|
@@ -144,11 +71,35 @@ export async function assertGeneratedFiles(targetDir) {
|
|
|
144
71
|
assert.ok(await fs.stat(path.join(targetDir, "src/app/app-acceptance-data.ts")));
|
|
145
72
|
assert.ok(await fs.stat(path.join(targetDir, "src/app/app-composition.tsx")));
|
|
146
73
|
assert.ok(await fs.stat(path.join(targetDir, "src/app/app-acceptance.base-coverage.test.ts")));
|
|
74
|
+
assert.ok(
|
|
75
|
+
await fs.stat(
|
|
76
|
+
path.join(
|
|
77
|
+
targetDir,
|
|
78
|
+
"src/app/acceptance/control-section-entity-cohesion.ts",
|
|
79
|
+
),
|
|
80
|
+
),
|
|
81
|
+
);
|
|
82
|
+
const generatedApplicabilityValidatorSource = await fs.readFile(
|
|
83
|
+
path.join(targetDir, "src/app/acceptance/control-applicability.ts"),
|
|
84
|
+
"utf8",
|
|
85
|
+
);
|
|
86
|
+
assert.match(
|
|
87
|
+
generatedApplicabilityValidatorSource,
|
|
88
|
+
/getToolcraftControlApplicabilityErrors/,
|
|
89
|
+
);
|
|
147
90
|
const appAcceptanceSource = await fs.readFile(
|
|
148
91
|
path.join(targetDir, "src/app/app-acceptance.ts"),
|
|
149
92
|
"utf8",
|
|
150
93
|
);
|
|
151
94
|
assert.match(appAcceptanceSource, /from "\.\/app-acceptance-data"/);
|
|
95
|
+
const generatedValidationPipeline = await fs.readFile(
|
|
96
|
+
path.join(targetDir, "src/app/acceptance/validate-coverage.ts"),
|
|
97
|
+
"utf8",
|
|
98
|
+
);
|
|
99
|
+
assert.match(
|
|
100
|
+
generatedValidationPipeline,
|
|
101
|
+
/validate:\s*\(\{ sectionInventory \}\)\s*=>\s*getToolcraftControlSectionEntityCohesionErrors\(sectionInventory\)/,
|
|
102
|
+
);
|
|
152
103
|
assert.ok(await fs.stat(path.join(targetDir, "src/app/app-performance.ts")));
|
|
153
104
|
assert.ok(
|
|
154
105
|
await fs.stat(path.join(targetDir, "src/app/app-verification-impact.json")),
|
|
@@ -11,6 +11,11 @@ import {
|
|
|
11
11
|
generatedProductMaterialIterationFixturePaths,
|
|
12
12
|
} from "./generate-test-product-iteration.mjs";
|
|
13
13
|
|
|
14
|
+
const materialIterationFixtureRoot = new URL(
|
|
15
|
+
"../fixtures/generated-product-material-iteration/",
|
|
16
|
+
import.meta.url,
|
|
17
|
+
);
|
|
18
|
+
|
|
14
19
|
const expectedPaths = [
|
|
15
20
|
"e2e/product-material.spec.ts",
|
|
16
21
|
"public/material-chocolate.svg",
|
|
@@ -83,6 +88,18 @@ it("keeps the material iteration overlay exact and immutable", () => {
|
|
|
83
88
|
);
|
|
84
89
|
});
|
|
85
90
|
|
|
91
|
+
it("keeps the material iteration video request explicit", async () => {
|
|
92
|
+
const acceptanceSource = await fs.readFile(
|
|
93
|
+
new URL("src/app/app-acceptance-data.ts", materialIterationFixtureRoot),
|
|
94
|
+
"utf8",
|
|
95
|
+
);
|
|
96
|
+
|
|
97
|
+
assert.match(
|
|
98
|
+
acceptanceSource,
|
|
99
|
+
/video:\s*\{\s*evidence:\s*"The generated export validation fixture explicitly requests one-second video export coverage\.",\s*mode: "user-requested",\s*\}/u,
|
|
100
|
+
);
|
|
101
|
+
});
|
|
102
|
+
|
|
86
103
|
it("atomically applies only the material product overlay", async () => {
|
|
87
104
|
const targetDir = await fs.mkdtemp(
|
|
88
105
|
path.join(os.tmpdir(), "toolcraft-material-iteration-target-"),
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
import assert from "node:assert/strict";
|
|
2
|
+
import fs from "node:fs/promises";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
|
|
5
|
+
import { listRelativeFiles, repoRoot } from "./generate-test-fs-utils.mjs";
|
|
6
|
+
import { isGeneratedTestSupportPath } from "./generated-source-policy.mjs";
|
|
7
|
+
|
|
8
|
+
const productSafeRuntimeExportIndex = [
|
|
9
|
+
'export {',
|
|
10
|
+
' toolcraftImageExportFormatTarget,',
|
|
11
|
+
' toolcraftImageExportResolutionTarget,',
|
|
12
|
+
' toolcraftVideoExportFormatTarget,',
|
|
13
|
+
' toolcraftVideoExportResolutionTarget,',
|
|
14
|
+
'} from "./artifact-export-settings";',
|
|
15
|
+
'export type {',
|
|
16
|
+
' ToolcraftImageExportFormat,',
|
|
17
|
+
' ToolcraftImageExportPresetResolution,',
|
|
18
|
+
' ToolcraftResolvedImageExportSettings,',
|
|
19
|
+
' ToolcraftResolvedVideoExportSettings,',
|
|
20
|
+
' ToolcraftVideoExportFormat,',
|
|
21
|
+
' ToolcraftVideoExportPresetResolution,',
|
|
22
|
+
'} from "./artifact-export-settings";',
|
|
23
|
+
'export { shouldIncludeToolcraftPreviewBackground } from "./export-background";',
|
|
24
|
+
'export type { ToolcraftPreviewBackgroundOptions } from "./export-background";',
|
|
25
|
+
'export {',
|
|
26
|
+
' TOOLCRAFT_MAX_EXPORT_EDGE_PX,',
|
|
27
|
+
' TOOLCRAFT_MAX_EXPORT_PIXELS,',
|
|
28
|
+
' validateToolcraftArtifactSize,',
|
|
29
|
+
'} from "./export-frame";',
|
|
30
|
+
'export type {',
|
|
31
|
+
' ToolcraftArtifactSize,',
|
|
32
|
+
' ToolcraftExportFrame,',
|
|
33
|
+
'} from "./export-frame";',
|
|
34
|
+
'export {',
|
|
35
|
+
' getToolcraftImageExportSize,',
|
|
36
|
+
' getToolcraftRetinaExportPixelRatio,',
|
|
37
|
+
' getToolcraftRetinaExportSize,',
|
|
38
|
+
' getToolcraftVideoExportSize,',
|
|
39
|
+
'} from "./export-sizing";',
|
|
40
|
+
'export type {',
|
|
41
|
+
' ToolcraftExportSizeOptions,',
|
|
42
|
+
' ToolcraftImageExportResolution,',
|
|
43
|
+
' ToolcraftImageExportSizeOptions,',
|
|
44
|
+
' ToolcraftRetinaExportSize,',
|
|
45
|
+
' ToolcraftVideoExportResolution,',
|
|
46
|
+
' ToolcraftVideoExportSizeOptions,',
|
|
47
|
+
'} from "./export-sizing";',
|
|
48
|
+
'export type {',
|
|
49
|
+
' ToolcraftProductExportFrameContext,',
|
|
50
|
+
' ToolcraftProductExportFrameRenderer,',
|
|
51
|
+
' ToolcraftProductExportRenderer,',
|
|
52
|
+
'} from "./product-export-renderer";',
|
|
53
|
+
'',
|
|
54
|
+
].join("\n");
|
|
55
|
+
|
|
56
|
+
const requiredRuntimeExportImplementationPaths = [
|
|
57
|
+
"runtime/export/artifact-export-settings.ts",
|
|
58
|
+
"runtime/export/artifact-export-request.ts",
|
|
59
|
+
"runtime/export/artifact-frame-renderer.ts",
|
|
60
|
+
"runtime/export/artifact-frame-state.ts",
|
|
61
|
+
"runtime/export/artifact-scene-frame.ts",
|
|
62
|
+
"runtime/export/image-artifact-export.ts",
|
|
63
|
+
"runtime/export/product-export-renderer.ts",
|
|
64
|
+
"runtime/export/video-artifact-export.ts",
|
|
65
|
+
"runtime/export/video-encoding-backend.ts",
|
|
66
|
+
"runtime/export/video-frame-schedule.ts",
|
|
67
|
+
];
|
|
68
|
+
|
|
69
|
+
const generatedMediaExportEvidencePaths = [
|
|
70
|
+
"e2e/browser-media-export-evidence.ts",
|
|
71
|
+
"e2e/decoded-pixel-observation.ts",
|
|
72
|
+
"e2e/image-artifact-inspection.ts",
|
|
73
|
+
"e2e/video-artifact-inspection.ts",
|
|
74
|
+
];
|
|
75
|
+
|
|
76
|
+
const removedRuntimeTestHelperPaths = [
|
|
77
|
+
"runtime/react/canvas/canvas-shell-test-utils.tsx",
|
|
78
|
+
"runtime/react/controls-panel/testing/controls-panel-test-utils.tsx",
|
|
79
|
+
"runtime/react/layers/layers-panel-test-utils.tsx",
|
|
80
|
+
"runtime/react/timeline/timeline-panel-test-utils.tsx",
|
|
81
|
+
];
|
|
82
|
+
|
|
83
|
+
export async function assertGeneratedRuntimePublicBoundary(targetDir) {
|
|
84
|
+
assert.ok(await fs.stat(path.join(targetDir, "src/toolcraft/ui/index.ts")));
|
|
85
|
+
assert.ok(await fs.stat(path.join(targetDir, "src/toolcraft/runtime/react/index.ts")));
|
|
86
|
+
|
|
87
|
+
const generatedRuntimeRootSource = await fs.readFile(
|
|
88
|
+
path.join(targetDir, "src/toolcraft/runtime/index.ts"),
|
|
89
|
+
"utf8",
|
|
90
|
+
);
|
|
91
|
+
assert.match(generatedRuntimeRootSource, /export \* from "\.\/export";/u);
|
|
92
|
+
assert.doesNotMatch(
|
|
93
|
+
generatedRuntimeRootSource,
|
|
94
|
+
/exportToolcraft(?:Image|Video)Artifact|createToolcraftVideoEncoderBackend/u,
|
|
95
|
+
);
|
|
96
|
+
|
|
97
|
+
const generatedRuntimeExportIndexSource = await fs.readFile(
|
|
98
|
+
path.join(targetDir, "src/toolcraft/runtime/export/index.ts"),
|
|
99
|
+
"utf8",
|
|
100
|
+
);
|
|
101
|
+
assert.equal(
|
|
102
|
+
generatedRuntimeExportIndexSource,
|
|
103
|
+
productSafeRuntimeExportIndex,
|
|
104
|
+
"Generated runtime export index must expose the exact product-safe allowlist.",
|
|
105
|
+
);
|
|
106
|
+
|
|
107
|
+
assert.ok(
|
|
108
|
+
await fs.stat(
|
|
109
|
+
path.join(
|
|
110
|
+
targetDir,
|
|
111
|
+
"src/toolcraft/runtime/model-import/worker/model-import.worker.bundle.js",
|
|
112
|
+
),
|
|
113
|
+
),
|
|
114
|
+
);
|
|
115
|
+
|
|
116
|
+
const generatedToolcraftFiles = await listRelativeFiles(
|
|
117
|
+
path.join(targetDir, "src/toolcraft"),
|
|
118
|
+
);
|
|
119
|
+
const expectedRuntimeFiles = (await listRelativeFiles(
|
|
120
|
+
path.join(repoRoot, "packages/toolcraft-runtime/src"),
|
|
121
|
+
))
|
|
122
|
+
.filter((relativePath) => !isGeneratedTestSupportPath(relativePath))
|
|
123
|
+
.map((relativePath) => `runtime/${relativePath}`);
|
|
124
|
+
assert.deepEqual(
|
|
125
|
+
generatedToolcraftFiles.filter((relativePath) => relativePath.startsWith("runtime/")),
|
|
126
|
+
expectedRuntimeFiles,
|
|
127
|
+
"Generated standalone runtime must contain every production runtime module.",
|
|
128
|
+
);
|
|
129
|
+
|
|
130
|
+
for (const relativePath of requiredRuntimeExportImplementationPaths) {
|
|
131
|
+
assert.ok(
|
|
132
|
+
generatedToolcraftFiles.includes(relativePath),
|
|
133
|
+
`Generated runtime must contain ${relativePath}.`,
|
|
134
|
+
);
|
|
135
|
+
}
|
|
136
|
+
assert.equal(
|
|
137
|
+
generatedToolcraftFiles.includes("runtime/export/export.ts"),
|
|
138
|
+
false,
|
|
139
|
+
"Generated runtime must not copy the removed legacy export helper.",
|
|
140
|
+
);
|
|
141
|
+
|
|
142
|
+
for (const relativePath of generatedMediaExportEvidencePaths) {
|
|
143
|
+
assert.ok(
|
|
144
|
+
await fs.stat(path.join(targetDir, relativePath)),
|
|
145
|
+
`Generated output must contain ${relativePath}.`,
|
|
146
|
+
);
|
|
147
|
+
}
|
|
148
|
+
for (const relativePath of generatedToolcraftFiles) {
|
|
149
|
+
assert.equal(
|
|
150
|
+
isGeneratedTestSupportPath(relativePath),
|
|
151
|
+
false,
|
|
152
|
+
`Generated runtime contains test support: ${relativePath}`,
|
|
153
|
+
);
|
|
154
|
+
}
|
|
155
|
+
for (const relativePath of removedRuntimeTestHelperPaths) {
|
|
156
|
+
assert.equal(generatedToolcraftFiles.includes(relativePath), false);
|
|
157
|
+
}
|
|
158
|
+
}
|
|
@@ -87,6 +87,16 @@ export async function assertGeneratedProductStandalone({ execFileAsync, targetDi
|
|
|
87
87
|
await writeGeneratedProductFixture(targetDir);
|
|
88
88
|
await assertGeneratedProductWorklog(targetDir);
|
|
89
89
|
|
|
90
|
+
const appSchemaSource = await fs.readFile(
|
|
91
|
+
path.join(targetDir, "src/app/app-schema.ts"),
|
|
92
|
+
"utf8",
|
|
93
|
+
);
|
|
94
|
+
assert.match(
|
|
95
|
+
appSchemaSource,
|
|
96
|
+
/applicability:\s*\{\s*mode:\s*"always"\s*\}/u,
|
|
97
|
+
);
|
|
98
|
+
assert.doesNotMatch(appSchemaSource, /visibleWhen\s*:/u);
|
|
99
|
+
|
|
90
100
|
const verificationImpact = JSON.parse(
|
|
91
101
|
await fs.readFile(
|
|
92
102
|
path.join(targetDir, "src/app/app-verification-impact.json"),
|