@pixel-point/toolcraft 0.0.16 → 0.0.17
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-performance-iteration-execution.test.mjs +2 -2
- package/src/generate-test-demand-only-lifecycle.mjs +14 -3
- package/src/generate-test-dependency-sandbox.mjs +42 -5
- package/src/generate-test-doc-assertions.mjs +68 -18
- package/src/generate-test-doc-assertions.test.mjs +22 -0
- package/src/generate-test-file-assertions.mjs +43 -76
- package/src/generate-test-output-assertions.mjs +95 -0
- package/src/generate-test-package-assertions.mjs +6 -0
- package/src/generate-test-performance-boundary-assertions.mjs +45 -0
- package/src/generate-test-product-fixture.mjs +1 -0
- package/src/generate-test-product-fixture.test.mjs +18 -0
- package/src/generate-test-product-iteration.mjs +1 -0
- package/src/generate-test-product-iteration.test.mjs +1 -0
- package/src/generate-test-standalone-delivery-stages.mjs +14 -6
- package/src/generate.test.mjs +14 -0
- package/templates/runtime/contracts/component-contracts.choices.test.ts +12 -9
- package/templates/runtime/contracts/component-contracts.choices.ts +10 -9
- package/templates/runtime/contracts/component-contracts.runtime-acceptance.test.ts +17 -2
- package/templates/runtime/contracts/component-contracts.runtime-panels.test.ts +1 -1
- package/templates/runtime/contracts/component-contracts.runtime-shell.test.ts +1 -1
- package/templates/runtime/contracts/component-contracts.runtime.ts +3 -2
- package/templates/runtime/contracts/decision-contracts.test.ts +25 -4
- package/templates/runtime/contracts/decision-contracts.ts +3 -3
- package/templates/runtime/contracts/performance-verification-policy.test.ts +59 -6
- package/templates/runtime/contracts/performance-verification-policy.ts +5 -2
- package/templates/runtime/export/artifact-download.test.ts +71 -0
- package/templates/runtime/export/artifact-download.ts +80 -0
- package/templates/runtime/export/artifact-export-request.ts +23 -0
- package/templates/runtime/export/artifact-export-settings.test.ts +75 -0
- package/templates/runtime/export/artifact-export-settings.ts +96 -0
- package/templates/runtime/export/artifact-frame-renderer.test.ts +107 -0
- package/templates/runtime/export/artifact-frame-renderer.ts +96 -0
- package/templates/runtime/export/artifact-frame-state.test.ts +54 -0
- package/templates/runtime/export/artifact-frame-state.ts +44 -0
- package/templates/runtime/export/artifact-scene-frame.test.ts +84 -0
- package/templates/runtime/export/artifact-scene-frame.ts +118 -0
- package/templates/runtime/export/export-background.ts +65 -0
- package/templates/runtime/export/export-error.ts +43 -0
- package/templates/runtime/export/export.test.ts +6 -186
- package/templates/runtime/export/image-artifact-export.test.ts +111 -0
- package/templates/runtime/export/image-artifact-export.ts +145 -0
- package/templates/runtime/export/index.ts +14 -1
- package/templates/runtime/export/product-export-renderer.test.ts +77 -0
- package/templates/runtime/export/product-export-renderer.ts +63 -0
- package/templates/runtime/export/video-artifact-export.test.ts +166 -0
- package/templates/runtime/export/video-artifact-export.ts +164 -0
- package/templates/runtime/export/video-encoding-backend.ts +124 -0
- package/templates/runtime/export/video-encoding-policy.test.ts +68 -0
- package/templates/runtime/export/video-encoding-policy.ts +86 -0
- package/templates/runtime/export/video-frame-schedule.test.ts +58 -0
- package/templates/runtime/export/video-frame-schedule.ts +42 -0
- package/templates/runtime/index.ts +1 -0
- package/templates/runtime/react/app-shell/runtime-public-api-runtime.test.tsx +291 -0
- package/templates/runtime/react/app-shell/runtime-public-api-test-support.ts +21 -0
- package/templates/runtime/react/app-shell/runtime-public-api.test.tsx +4 -303
- package/templates/runtime/react/app-shell/toolcraft-app.scene-export.test.tsx +12 -27
- package/templates/runtime/react/app-shell/toolcraft-app.tsx +22 -4
- package/templates/runtime/react/app-shell/toolcraft-source-asset-context.test.tsx +16 -57
- package/templates/runtime/react/app-shell/toolcraft-source-asset-context.tsx +75 -434
- package/templates/runtime/react/app-shell/toolcraft-source-asset-owner.test.ts +194 -0
- package/templates/runtime/react/app-shell/toolcraft-source-asset-owner.ts +227 -0
- package/templates/runtime/react/app-shell/toolcraft-source-asset-provider-ownership.test.tsx +243 -0
- package/templates/runtime/react/app-shell/toolcraft-source-asset-test-support.ts +51 -0
- package/templates/runtime/react/app-shell/toolcraft-unavailable-resource-proof.test.ts +138 -0
- package/templates/runtime/react/app-shell/toolcraft-unavailable-resource-proof.ts +378 -0
- package/templates/runtime/react/controls-panel/__tests__/controls-panel.footer-actions.test.tsx +7 -12
- package/templates/runtime/react/controls-panel/__tests__/controls-panel.scene-export-actions.test.tsx +17 -83
- package/templates/runtime/react/controls-panel/actions/controls-panel-actions.ts +42 -84
- package/templates/runtime/react/controls-panel/actions/export-action-runner.ts +62 -0
- package/templates/runtime/react/panel-host/panel-host-runtime-placement.test.tsx +30 -0
- package/templates/runtime/react/panel-host/panel-host.tsx +4 -0
- package/templates/runtime/scene/scene-bounds.ts +1 -2
- package/templates/runtime/schema/canvas-render-scale.ts +62 -0
- package/templates/runtime/schema/controls-panel-section-id.test.ts +1 -1
- package/templates/runtime/schema/controls-panel-section-roundtrip.test.ts +3 -3
- package/templates/runtime/schema/define-toolcraft.setup-background.test.ts +1 -1
- package/templates/runtime/schema/define-toolcraft.setup-render-scale-timeline.test.ts +71 -3
- package/templates/runtime/schema/define-toolcraft.ts +1 -1
- package/templates/runtime/schema/schema-resolvers.ts +0 -56
- package/templates/runtime/schema/types.ts +4 -4
- package/templates/runtime/state/canvas-background-state.ts +6 -0
- package/templates/runtime/testing/performance-render-plan-integration.test.ts +64 -0
- package/templates/runtime/testing/performance-renderer-pipeline-validation.ts +18 -1
- package/templates/starter/AGENTS.md +8 -7
- package/templates/starter/NOTICE.md +6 -0
- package/templates/starter/docs/toolcraft/README.md +2 -1
- package/templates/starter/docs/toolcraft/acceptance-testing.md +8 -6
- package/templates/starter/docs/toolcraft/agent-worklog.md +3 -3
- package/templates/starter/docs/toolcraft/assembly-workflow.md +4 -3
- package/templates/starter/docs/toolcraft/core/media-upload.md +2 -2
- package/templates/starter/docs/toolcraft/core/performance.md +14 -3
- package/templates/starter/docs/toolcraft/core/runtime-boundary.md +3 -1
- package/templates/starter/docs/toolcraft/core/setup-export.md +16 -12
- package/templates/starter/docs/toolcraft/core/timeline-animation.md +4 -4
- package/templates/starter/docs/toolcraft/performance.md +20 -6
- package/templates/starter/docs/toolcraft/renderer-technique.md +1 -1
- package/templates/starter/docs/toolcraft/schema-reference.md +2 -6
- package/templates/starter/docs/toolcraft/workflow.md +7 -2
- package/templates/starter/e2e/app-browser-control-coverage.spec.ts +33 -0
- package/templates/starter/e2e/app-browser-infinity-canvas-evidence.spec.ts +43 -1
- package/templates/starter/e2e/app-browser-render-scale-evidence.spec.ts +60 -6
- package/templates/starter/e2e/app-browser-render-scale-requirements.spec.ts +77 -1
- package/templates/starter/e2e/app-browser-runtime-provenance.spec.ts +8 -0
- package/templates/starter/e2e/app-browser-runtime-reporter.spec.ts +35 -0
- package/templates/starter/e2e/app-browser-runtime-requirements.spec.ts +4 -1
- package/templates/starter/e2e/app-browser-semantic-evidence.spec.ts +0 -1
- package/templates/starter/e2e/app-browser-shell.spec.ts +13 -5
- package/templates/starter/e2e/app-performance-path-adapters.ts +8 -1
- package/templates/starter/e2e/app-performance.spec.ts +16 -9
- package/templates/starter/e2e/browser-infinity-canvas-evidence.ts +9 -330
- package/templates/starter/e2e/browser-infinity-canvas-unavailable-image-evidence.ts +370 -0
- package/templates/starter/e2e/browser-media-export-evidence.spec.ts +77 -0
- package/templates/starter/e2e/browser-media-export-evidence.ts +208 -0
- package/templates/starter/e2e/browser-render-scale-evidence.ts +37 -29
- package/templates/starter/e2e/browser-runtime-evidence-reporter.ts +4 -1
- package/templates/starter/e2e/browser-runtime-evidence-requirements.ts +43 -9
- package/templates/starter/e2e/browser-standard-timeline-evidence.ts +106 -0
- package/templates/starter/e2e/decoded-pixel-observation.spec.ts +74 -0
- package/templates/starter/e2e/decoded-pixel-observation.ts +227 -0
- package/templates/starter/e2e/export-artifact-helpers.ts +206 -25
- package/templates/starter/e2e/image-artifact-inspection.spec.ts +41 -0
- package/templates/starter/e2e/image-artifact-inspection.ts +99 -0
- package/templates/starter/e2e/performance-canvas-helpers.ts +22 -21
- package/templates/starter/e2e/performance-canvas-quality-guard.spec.ts +312 -0
- package/templates/starter/e2e/performance-canvas-quality-guard.ts +196 -0
- package/templates/starter/e2e/performance-canvas-quality-observer.ts +351 -0
- package/templates/starter/e2e/performance-control-layout-helpers.ts +2 -2
- package/templates/starter/e2e/performance-path-adapter-contract.ts +4 -4
- package/templates/starter/e2e/performance-path-helpers.spec.ts +203 -70
- package/templates/starter/e2e/performance-path-helpers.ts +210 -91
- package/templates/starter/e2e/performance-pipeline-evidence-continuity.spec.ts +76 -0
- package/templates/starter/e2e/performance-pipeline-evidence-test-fixtures.ts +11 -2
- package/templates/starter/e2e/performance-pipeline-invariants.ts +47 -4
- package/templates/starter/e2e/performance-render-scale-runner.spec.ts +140 -38
- package/templates/starter/e2e/video-artifact-inspection.spec.ts +64 -0
- package/templates/starter/e2e/video-artifact-inspection.ts +281 -0
- package/templates/starter/gitignore +1 -0
- package/templates/starter/package.json +1 -0
- package/templates/starter/scripts/check-toolcraft-docs.mjs +17 -0
- package/templates/starter/scripts/toolcraft-delivery-plan-authority.test.mjs +60 -0
- package/templates/starter/scripts/toolcraft-delivery-plan-errors.test.mjs +7 -2
- package/templates/starter/scripts/toolcraft-delivery-plan.mjs +7 -3
- package/templates/starter/scripts/toolcraft-product-boundary-ast.mjs +24 -2
- package/templates/starter/scripts/toolcraft-product-boundary.mjs +12 -0
- package/templates/starter/scripts/toolcraft-product-boundary.test.mjs +46 -0
- package/templates/starter/scripts/toolcraft-product-evidence-boundary.test.mjs +85 -20
- package/templates/starter/scripts/toolcraft-product-evidence-import-policy.mjs +43 -0
- package/templates/starter/scripts/toolcraft-product-export-boundary.mjs +168 -0
- package/templates/starter/scripts/toolcraft-typescript-analysis.d.mts +73 -0
- package/templates/starter/scripts/toolcraft-unavailable-resource-evidence-facade-test-helpers.mjs +318 -0
- package/templates/starter/scripts/toolcraft-unavailable-resource-evidence-facade.test.mjs +155 -206
- package/templates/starter/scripts/toolcraft-verification-inventory.mjs +1 -0
- package/templates/starter/scripts/toolcraft-verification-receipt.test.mjs +51 -0
- package/templates/starter/scripts/toolcraft-vite-env-activation.test.mjs +128 -59
- package/templates/starter/src/app/acceptance/export-artifact-coverage.ts +113 -0
- package/templates/starter/src/app/acceptance/output-background-rules.ts +1 -1
- package/templates/starter/src/app/acceptance/output-export.ts +13 -2
- package/templates/starter/src/app/acceptance/types.ts +7 -0
- package/templates/starter/src/app/acceptance/validate-coverage.ts +2 -1
- package/templates/starter/src/app/starter-acceptance.export-artifact.test.ts +131 -0
- package/templates/starter/src/app/starter-acceptance.keyframes.test.ts +14 -4
- package/templates/starter/src/app/starter-acceptance.output-export-rules.test.ts +42 -1
- package/templates/starter/src/app/starter-acceptance.ts +2 -0
- package/templates/starter/src/app/starter-performance-test-utils.ts +103 -0
- package/templates/starter/src/app/starter-performance.gates.test.ts +231 -278
- package/templates/starter/src/app/starter-performance.lifecycle.test.ts +261 -0
- package/templates/starter/src/app/test-evidence/browser-runtime-contract.ts +2 -0
- package/templates/starter/src/routes/index.tsx +1 -0
- package/templates/runtime/export/export.ts +0 -166
|
@@ -63,7 +63,7 @@ Use no timeline only when the motion is explicitly autonomous decoration with no
|
|
|
63
63
|
|
|
64
64
|
## Video Export Timing
|
|
65
65
|
|
|
66
|
-
-
|
|
67
|
-
-
|
|
68
|
-
-
|
|
69
|
-
-
|
|
66
|
+
- Runtime video export creates a fixed 30 FPS schedule from runtime timeline duration and evaluates one immutable frame state at every scheduled timeline time.
|
|
67
|
+
- The timestamped Mediabunny backend encodes each completed frame with its explicit schedule timestamp and duration; render wall-clock cost does not affect artifact cadence.
|
|
68
|
+
- Product code must not use `canvas.captureStream()`, `MediaRecorder`, `VideoEncoder`, or its own encoder/download fallback.
|
|
69
|
+
- Protected browser acceptance proves edited timeline duration, actual encoded packet count/timings, and distinct decoded product frames.
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
<!-- toolcraft-performance-lifecycle: first-delivery=functional; later-delivery=functional-targeted; complaint=one-authority-targeted-performance-iteration; full-audit=explicit-only -->
|
|
4
4
|
<!-- toolcraft-performance-iteration: authority=exact-request-evidence+canonical-path-ids; fixture=reachable-development; after-pass=return-app-to-user+stop -->
|
|
5
5
|
<!-- 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 -->
|
|
6
|
+
<!-- toolcraft-performance-routing: localized=agent-targeted; ambiguous=one-user-facing-choice; broad=offer-targeted-or-full; full=explicit-only -->
|
|
6
7
|
|
|
7
8
|
Use this document while authoring `src/app/app-performance.ts`, performance acceptance, and browser evidence. Read `core/performance.md` first. Functional selection and proof ownership are canonical in `workflow.md` and `acceptance-testing.md`; performance metadata does not redefine them.
|
|
8
9
|
|
|
@@ -95,6 +96,14 @@ Scenario interaction is the canonical pipeline interaction from its derived path
|
|
|
95
96
|
|
|
96
97
|
`src/app/app-verification-impact.json` maps every current product production module to `presentation`, `functional`, or `performance` impact and names its nearest acceptance ids. A performance entry is reserved for a module that can change a named pass's execution, invalidation, workload, resource lifecycle, or measured output, and names only those exact pass ids. The inventory contains no stale or missing module paths; blanket every-module acceptance or pass ownership is invalid. This mapping is the authority for verification scope after first delivery.
|
|
97
98
|
|
|
99
|
+
Keep frequently edited product defaults and domain behavior in focused product modules whose ownership entries name only their nearest acceptance ids. `app-schema.ts` remains the public assembly boundary, but it should import those domain definitions instead of accumulating every mutable product decision in one file. A change made directly in a blanket schema owner correctly selects every acceptance id owned by that file; module boundaries are therefore part of later-delivery feedback speed, not cosmetic organization.
|
|
100
|
+
|
|
101
|
+
Viewport drag and zoom normally stay in transforms or uniforms and must not invalidate expensive passes. A render-scale-enabled raster product may rerasterize on `viewport-zoom` only through an off-main `rasterize` pass with `quality: "retina"`; this is the narrow exception that preserves exact CSS × DPR × selected-scale backing as the visible zoom size changes. Main-thread, non-retina, drag-time, and unrelated expensive invalidations remain invalid.
|
|
102
|
+
|
|
103
|
+
Each measured interaction phase performs one primary user operation. State-restoring inverse actions belong in a later phase or outside the measurement, never in the same action. Non-animation interaction probes collect at least 20 post-action frames so nearest-rank p95 remains a percentile rather than collapsing to the single maximum frame; animation keeps its dedicated 120-frame sample and export keeps completion-owned timing.
|
|
104
|
+
|
|
105
|
+
A change isolated to product performance adapters or performance test support selects code-health and any directly affected unit proof for ordinary delivery, but it never infers a measured path. Exact request authority may consume the adapter's performance candidates in one targeted iteration, while explicit `pnpm verify:perf` remains the only full-audit route.
|
|
106
|
+
|
|
98
107
|
## 6. Fixture Adapters And Plans
|
|
99
108
|
|
|
100
109
|
`fixtureAdapters.dimensions` contains exactly one adapter per workload dimension. An adapter:
|
|
@@ -125,6 +134,8 @@ Mutating measurements prove a stable baseline, record the first persistent chang
|
|
|
125
134
|
|
|
126
135
|
`export` scenarios declare exact `actionValue`, visible `controlLabel`, and `completionEvidence`. They may declare a `completionDeadlineMs` only when the product needs a stricter deadline than the central batch profile; scenarios do not author responsiveness budgets. The protected helper owns the click and completion event. Export evidence still inspects the delivered artifact and verifies that it is non-empty and matches selected output semantics.
|
|
127
136
|
|
|
137
|
+
Artifact dimensions, decoded quality, video duration, and exact 30 FPS packet cadence are functional proof. Export latency and UI responsiveness become measured performance only through an authorized targeted iteration or explicit full audit.
|
|
138
|
+
|
|
128
139
|
## 8. Demand-Only Performance Lifecycle
|
|
129
140
|
|
|
130
141
|
During implementation, run targeted unit and functional browser checks for feedback without minting delivery evidence. At the coherent delivery boundary, the protected delivery runner diffs current sources against the immediately previous successful delivery and resolves changed modules through `app-verification-impact.json`. Every functional delivery requires only its ownership-derived functional proof, including changes to performance-owned modules. Performance ownership constrains an exact request-authorized iteration; it does not authorize measurements. A durable full-performance baseline, when one exists, remains historical evidence and is not reused as the functional-delivery diff anchor. Reports remain bound to their executed plan, nonce, and current source hash; product-authored JSON is not evidence.
|
|
@@ -139,17 +150,20 @@ The conversational lifecycle is automatic:
|
|
|
139
150
|
|
|
140
151
|
1. **First product delivery.** Bare `pnpm verify:delivery` proves complete product contracts, performs one production build, and runs full functional acceptance with no measured performance. The receipt preserves any independent full-performance baseline and cannot claim targeted or full performance evidence.
|
|
141
152
|
2. **Later functional-targeted delivery.** The same bare command derives exact ownership-required functional checks from the current inputs and immediately previous successful delivery. It preserves a baseline when present and cannot silently run measured performance or create one.
|
|
142
|
-
3. **
|
|
153
|
+
3. **Localized or clarified targeted work.** Only a localized complaint or a post-clarification targeted choice records an exact request quote and canonical affected path IDs in the latest Decision Trail. Classifier output establishes complaint authority only and never path localization. Any unresolved localization creates neither performance-iteration intent nor canonical path authority, whether classification returned high-confidence `performance-iteration` or `needs-agent-judgment`. One bare `pnpm verify:delivery` then runs one targeted iteration against the reachable development fixture. The protected report stores independently validated `cold`, `warm`, and `sustained` measurements; compatible prior evidence produces diagnostic deltas, while absent or incompatible evidence records a non-comparable result. Current absolute budgets remain the pass/fail authority. Deliver the verified app, then stop and wait for user evaluation. Each later localized request may create one new bounded iteration.
|
|
143
154
|
4. **Full audit.** Only an explicit operator request or accepted offer authorizes `pnpm verify:perf`. It performs one fresh production build and the complete maximum-fixture performance matrix, updates only the performance checkpoints, and preserves the delivery anchor.
|
|
144
155
|
|
|
145
|
-
Use the runtime request classifier as a guard, not as a substitute for understanding the user. It returns high-confidence `performance-iteration`, high-confidence ordinary product work, or `needs-agent-judgment`. High-confidence ordinary product instructions stay ordinary. For ambiguous
|
|
156
|
+
Use the runtime request classifier as a guard, not as a substitute for understanding the user. It returns high-confidence `performance-iteration`, high-confidence ordinary product work, or `needs-agent-judgment`. High-confidence ordinary product instructions stay ordinary. Classifier output establishes complaint authority only and never path localization. A localized complaint lets the agent select the affected canonical paths and run one targeted iteration without asking the user. For an ambiguous complaint, ask one user-facing question naming the visible operation and offering targeted diagnosis or a complete performance review; never ask the user to choose internal path IDs, and create neither performance-iteration intent nor canonical path authority before the answer. A broad or honestly unlocalizable problem may lead to that single targeted/full choice with a recommendation for complete performance review, but only the user's choice authorizes it. A direct complete-performance-review request runs `pnpm verify:perf` without another clarification. Iteration mode requires `Performance intent: performance-iteration — Request evidence: "<verbatim exact Request quote>"` in the worklog before the command runs. The quote must be a nontrivial exact raw substring of the current Request, including identical whitespace and Unicode code units; invented, whitespace-collapsed, or NFKC-equivalent evidence is invalid.
|
|
157
|
+
|
|
158
|
+
Store agent-produced browser diagnostics under `.toolcraft/browser-artifacts/`, or leave them in external tool-owned storage when the browser integration owns them. Diagnostics are not product source and must not affect verification inventory.
|
|
146
159
|
|
|
147
160
|
Canonical classification examples:
|
|
148
161
|
|
|
149
|
-
-
|
|
162
|
+
- reports that the app lags, the editor is slow, frames stutter, or CPU/GPU/memory use is excessive may establish high-confidence complaint authority, but without a visible operation they remain unlocalized and do not select an iteration;
|
|
163
|
+
- “dragging has latency” is localized to a visible operation, so the agent selects its affected canonical paths and runs one targeted iteration without clarification;
|
|
150
164
|
- product commands such as increasing animation speed, freezing a camera at a frame, renaming a mode to “Performance”, or stating that the app does not lag are high-confidence ordinary product work;
|
|
151
|
-
- wording such as “the controls feel sticky” or “something feels off” needs agent judgment from the full request;
|
|
152
|
-
- classification is clause-local: an independent complaint
|
|
165
|
+
- wording such as “the controls feel sticky” or “something feels off” needs agent judgment from the full request; if localization remains unresolved, ask the one visible-operation targeted/full question and record no iteration or path authority before the answer;
|
|
166
|
+
- classification is clause-local: an independent complaint may establish complaint authority even when another clause negates a different complaint or requests a product speed change, while localization is still resolved separately.
|
|
153
167
|
|
|
154
168
|
Use bare `pnpm verify:delivery` once for the coherent delivery batch. Complaint wording, repetition, filename, diagnostic classification, and touched subsystem never launch the complete matrix automatically. A targeted failure triggers targeted diagnosis, not an automatic full-suite run, and a passing iteration stops after returning the verified app for user evaluation.
|
|
155
169
|
|
|
@@ -165,7 +179,7 @@ User-facing completion text must distinguish the levels. Functional delivery rep
|
|
|
165
179
|
|
|
166
180
|
## Performance Quality
|
|
167
181
|
|
|
168
|
-
Preserve selected output quality, preview fidelity, backing resolution, source fidelity, product boundaries, and live interaction behavior. Raster products with Resolution scale declare functional `renderScaleCoverage` using `kind: "selected-backing-pixels"` and exact `interaction`/`steady` states, plus `playback` when timeline is enabled. Their product browser scenario uses `expectToolcraftCanvasRenderScaleEvidence`; `canvas-render-scale-backing` is emitted only after real backing dimensions satisfy the selected scale without changing visible CSS size.
|
|
182
|
+
Preserve selected output quality, preview fidelity, backing resolution, source fidelity, product boundaries, and live interaction behavior. Raster products with Resolution scale declare functional `renderScaleCoverage` using `kind: "selected-backing-pixels"` and exact `interaction`/`steady` states, plus `playback` when timeline is enabled. Their product browser scenario uses `expectToolcraftCanvasRenderScaleEvidence`; `canvas-render-scale-backing` is emitted only after real backing dimensions satisfy the selected scale without changing visible CSS size. Every measured path in a render-scale-enabled raster product must prove actual `CSS size × devicePixelRatio × 2` backing after each measured phase.
|
|
169
183
|
|
|
170
184
|
A renderer that caps 2x output during interaction, playback, or steady state fails functional acceptance without measured performance. `performance-render-scale` remains limited to an explicitly authorized performance path and follows the same backing-pixel assertion; path metadata alone is not evidence. When a budget fails, inspect assessed pass cost, invalidation, resource lifecycle, scheduling, cancellation, and execution location before changing product scope.
|
|
171
185
|
|
|
@@ -110,7 +110,7 @@ The `mount*` and error functions above stand for product renderer integration on
|
|
|
110
110
|
|
|
111
111
|
The presentation lease is keyed by canonical document reference and shares immutable geometry/material/texture GPU resources across preview and export while cloning only scene roots. Replacement, delete/reset, failed creation, release, and provider unmount dispose ownership deterministically. The original uploaded folder/ZIP remains immutable durable source; display fit and camera pose live outside the leased root.
|
|
112
112
|
|
|
113
|
-
The runtime presents structurally valid analyzing or repairing geometry at `0.4` opacity and committed clean/fixed geometry at `1`. Export is always `1`.
|
|
113
|
+
The runtime presents structurally valid analyzing or repairing geometry at `0.4` opacity and committed clean/fixed geometry at `1`. Export is always `1`. Runtime image/video export composites visible committed model layers before awaiting the product's shared `ToolcraftAppComposition.exportRenderer` frame. Runtime owns the presentation binding, pose, scene frame, dimensions, pixel ratio, encoding, and download; product code does not call model compositors or construct a parallel export host.
|
|
114
114
|
|
|
115
115
|
When `viewInteraction.mode` is `orbit`, schema `orientationGizmo`, direct model drag, preview rendering, hit testing, reset/history, and export share every declared orientation pose target. The product renderer supplies geometry-aware `hitTest`; Toolcraft owns gesture scheduling and history through `useToolcraftModelOrbitInteraction`.
|
|
116
116
|
|
|
@@ -51,7 +51,7 @@ export const appComposition: ToolcraftAppComposition = {
|
|
|
51
51
|
|
|
52
52
|
The provider returns world-space `{ x, y, width, height }` rectangles. Runtime unions them once with visible image/model frames. It is called only for infinite export; video passes the requested time range so the provider can return one complete animation envelope. Product code does not read DOM bounds or the dormant finite size.
|
|
53
53
|
|
|
54
|
-
Use `canvas.renderScale: true` only for non-vector raster previews such as Canvas 2D, WebGL, or WebGPU. Do not enable it for DOM/SVG/vector-native previews. Enabling it requires one browser runtime acceptance row targeting `canvas.renderScale` with `renderScaleCoverage.kind: "selected-backing-pixels"` and exact sorted states `["interaction", "steady"]`, plus `"playback"` when timeline is enabled. The fixed `canvas-render-scale-backing` recipe proves actual backing pixels rather than timing.
|
|
54
|
+
Use `canvas.renderScale: true` or `canvas.renderScale: { step }` only for non-vector raster previews such as Canvas 2D, WebGL, or WebGPU. For this field, product code may customize only the slider step; authored schema cannot set `enabled`, `min`, `defaultValue`, or `max`. Runtime resolves enabled render scale to `{ enabled: true, min: 1, defaultValue: 2, max: 2, step }`, using `0.25` when no step is authored. A custom step must be finite, between `0.01` and `1`, and evenly partition the canonical `1..2` range so `2` remains reachable; invalid input fails fast. Do not enable it for DOM/SVG/vector-native previews. Enabling it requires one browser runtime acceptance row targeting `canvas.renderScale` with `renderScaleCoverage.kind: "selected-backing-pixels"` and exact sorted states `["interaction", "steady"]`, plus `"playback"` when timeline is enabled. The fixed `canvas-render-scale-backing` recipe proves actual backing pixels rather than timing.
|
|
55
55
|
|
|
56
56
|
## Media Defaults
|
|
57
57
|
|
|
@@ -321,11 +321,7 @@ Product apps expose delivery through sticky `panelActions`, not canvas UI or ord
|
|
|
321
321
|
|
|
322
322
|
Every app with `Export PNG` includes `Image Export` controls with `export.image.format` and `export.image.resolution`. Apps with `Export Video` also include `Video Export` controls with `export.video.format` and `export.video.resolution`.
|
|
323
323
|
|
|
324
|
-
Use standard
|
|
325
|
-
|
|
326
|
-
- `createToolcraftPngExportCanvas({ resolution, includeBackground, state, render })`
|
|
327
|
-
- `shouldIncludeToolcraftPreviewBackground(state)`
|
|
328
|
-
- `getToolcraftVideoExportSize({ resolution, state })`
|
|
324
|
+
Use the standard runtime path: declare typed `export-image`/`export-video` panel actions and their settings, provide one `ToolcraftAppComposition.exportRenderer` when product code contributes pixels, and draw one deterministic frame from the supplied state/time in scene coordinates. Call `shouldIncludeToolcraftPreviewBackground(state)` only for bounded live preview; runtime owns artifact composition, sizing, encoding, download, progress, and errors.
|
|
329
325
|
|
|
330
326
|
Detailed Setup, Background, Image Export, Video Export, sticky action, icon, and progress rules live in `core/setup-export.md`.
|
|
331
327
|
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
<!-- toolcraft-performance-lifecycle: first-delivery=functional; later-delivery=functional-targeted; complaint=one-authority-targeted-performance-iteration; full-audit=explicit-only -->
|
|
4
4
|
<!-- toolcraft-performance-iteration: authority=exact-request-evidence+canonical-path-ids; fixture=reachable-development; after-pass=return-app-to-user+stop -->
|
|
5
5
|
<!-- 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 -->
|
|
6
|
+
<!-- toolcraft-performance-routing: localized=agent-targeted; ambiguous=one-user-facing-choice; broad=offer-targeted-or-full; full=explicit-only -->
|
|
6
7
|
|
|
7
8
|
This file is the app-local routing layer for Toolcraft work. It does not replace the detailed contracts; it tells an agent which contract to read and which verification path to use before editing.
|
|
8
9
|
|
|
@@ -65,7 +66,7 @@ For product app work, update `docs/toolcraft/agent-worklog.md` before reporting
|
|
|
65
66
|
- state/output mapping from controls, commands, timeline, layers, media, or renderer to the visible product;
|
|
66
67
|
- one bare-delivery verification narrative;
|
|
67
68
|
- risks or follow-ups.
|
|
68
|
-
- for
|
|
69
|
+
- for localized performance work, or a post-clarification targeted choice, an exact request quote and the canonical affected performance path IDs; classifier output establishes complaint authority only, and unresolved localization records neither performance-iteration intent nor canonical path authority regardless of classifier result.
|
|
69
70
|
- updated high-level decisions for renderer, view interaction, interaction ownership, timeline, layers, controls, export, and performance when those choices change.
|
|
70
71
|
|
|
71
72
|
If the folder is still the neutral starter, do not invent product decisions. Once it becomes a product, switch the worklog to product mode and keep it concrete.
|
|
@@ -102,9 +103,13 @@ Use the smallest focused unit and browser checks while implementation is changin
|
|
|
102
103
|
|
|
103
104
|
1. **First product delivery:** bare `pnpm verify:delivery` proves complete product contracts, performs one production build, runs full functional acceptance, and runs no measured performance.
|
|
104
105
|
2. **Later functional-targeted delivery:** bare `pnpm verify:delivery` compares the immediate previous and current semantic functional proof models. It selects exact changed acceptance contracts, their direct owners and domains, and tests reached through the affected product-unit dependency graph.
|
|
105
|
-
3. **
|
|
106
|
+
3. **Localized or clarified targeted work:** only a localized complaint or a post-clarification targeted choice records domain authority in the worklog—an exact request quote plus canonical affected path IDs—and runs one bare `pnpm verify:delivery`. It executes one targeted iteration against the reachable development fixture, returns the verified app, and waits for user evaluation. Classifier output alone never localizes a path; unresolved localization creates neither performance-iteration intent nor canonical path authority, whether classification returned high-confidence `performance-iteration` or `needs-agent-judgment`.
|
|
106
107
|
4. **Full audit:** only an explicit operator request or accepted offer authorizes `pnpm verify:perf`. It performs one fresh build and the complete maximum-fixture performance matrix without advancing the delivery anchor.
|
|
107
108
|
|
|
109
|
+
Performance authority and localization are separate decisions. Classifier output establishes complaint authority only and never path localization. A localized complaint lets the agent select the affected canonical paths and run one targeted iteration without asking the user. For an ambiguous complaint, ask one user-facing question that names the visible operation and offers targeted diagnosis or a complete performance review; never ask the user to choose internal path IDs, and record no performance-iteration intent or canonical path authority before the answer. For a broad or honestly unlocalizable problem, the agent may recommend the complete performance review in that single targeted/full choice, but the user still chooses. A direct request for the complete performance review runs `pnpm verify:perf` without another clarification.
|
|
110
|
+
|
|
111
|
+
Store agent-produced browser diagnostics under `.toolcraft/browser-artifacts/`. Browser integrations may instead use their external tool-owned storage; diagnostic files never belong in product source.
|
|
112
|
+
|
|
108
113
|
The unchanged `sourceHash` fast path does not collect the catalog, dependency graph, or functional proof model and does not execute proof. For a changed source, the current catalog, input roles, ownership inventory, and semantic model are collected once and remain the immutable basis for that delivery plan. `workflow-observation.md` is a post-delivery summary, not execution authority, so it stays outside this hash; `agent-worklog.md` remains included.
|
|
109
114
|
|
|
110
115
|
After two consecutive compatible targeted iterations, offer the slower full audit if the user remains unsatisfied. A complaint, filename, or touched subsystem never authorizes it. Canonical classification details, failure behavior, and evidence wording live in `core/performance.md` and `performance.md`.
|
|
@@ -1,10 +1,43 @@
|
|
|
1
1
|
import { expect, test } from "@playwright/test";
|
|
2
2
|
import { expectToolcraftExportedArtifact } from "./browser-acceptance-outcome-helpers";
|
|
3
3
|
import {
|
|
4
|
+
expectToolcraftDiscreteSliderMarkers,
|
|
4
5
|
expectToolcraftSegmentedControlCellsPreservePadding,
|
|
5
6
|
getToolcraftFieldByLabel,
|
|
6
7
|
} from "./performance-helpers";
|
|
7
8
|
|
|
9
|
+
test("discrete slider layout accepts one interior marker and rejects none", async ({
|
|
10
|
+
page,
|
|
11
|
+
}) => {
|
|
12
|
+
await page.setContent(`
|
|
13
|
+
<div data-slot="toolcraft-runtime-app">
|
|
14
|
+
<div data-toolcraft-control-target="canvas.renderScale">
|
|
15
|
+
<div data-slot="field" style="display:block;width:200px;height:24px;">
|
|
16
|
+
<div data-slot="slider" data-variant="discrete" style="display:block;width:200px;height:24px;">
|
|
17
|
+
<span data-slot="slider-marker" style="display:block;width:1px;height:6px;"></span>
|
|
18
|
+
</div>
|
|
19
|
+
</div>
|
|
20
|
+
</div>
|
|
21
|
+
</div>
|
|
22
|
+
`);
|
|
23
|
+
|
|
24
|
+
await expectToolcraftDiscreteSliderMarkers(page, "canvas.renderScale");
|
|
25
|
+
|
|
26
|
+
await page.setContent(`
|
|
27
|
+
<div data-slot="toolcraft-runtime-app">
|
|
28
|
+
<div data-toolcraft-control-target="canvas.renderScale">
|
|
29
|
+
<div data-slot="field" style="display:block;width:200px;height:24px;">
|
|
30
|
+
<div data-slot="slider" data-variant="discrete" style="display:block;width:200px;height:24px;"></div>
|
|
31
|
+
</div>
|
|
32
|
+
</div>
|
|
33
|
+
</div>
|
|
34
|
+
`);
|
|
35
|
+
|
|
36
|
+
await expect(
|
|
37
|
+
expectToolcraftDiscreteSliderMarkers(page, "canvas.renderScale"),
|
|
38
|
+
).rejects.toThrow(/at least one interior marker/);
|
|
39
|
+
});
|
|
40
|
+
|
|
8
41
|
test("segmented layout helper catches paddingless or colliding cells", async ({ page }) => {
|
|
9
42
|
await page.setContent(`
|
|
10
43
|
<div data-slot="field">FX Preset
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { test } from "@playwright/test";
|
|
1
|
+
import { expect, test } from "@playwright/test";
|
|
2
2
|
|
|
3
3
|
import {
|
|
4
4
|
expectInfiniteCanvasObservation,
|
|
@@ -6,6 +6,10 @@ import {
|
|
|
6
6
|
expectToolcraftInfinityCanvasVideoExportEvidence,
|
|
7
7
|
observeInfinityCanvas,
|
|
8
8
|
} from "./browser-infinity-canvas-evidence";
|
|
9
|
+
import {
|
|
10
|
+
readToolcraftCanvasViewport,
|
|
11
|
+
zoomToolcraftCanvasViewport,
|
|
12
|
+
} from "./performance-canvas-helpers";
|
|
9
13
|
|
|
10
14
|
test("toolcraft Infinity canvas observation reads runtime-owned geometry", async ({
|
|
11
15
|
page,
|
|
@@ -24,6 +28,44 @@ test("toolcraft Infinity canvas observation reads runtime-owned geometry", async
|
|
|
24
28
|
);
|
|
25
29
|
});
|
|
26
30
|
|
|
31
|
+
test("toolcraft canvas viewport observation reads the runtime world transform", async ({
|
|
32
|
+
page,
|
|
33
|
+
}) => {
|
|
34
|
+
await page.setContent(`
|
|
35
|
+
<div
|
|
36
|
+
data-toolcraft-canvas-world
|
|
37
|
+
data-toolcraft-canvas-offset-x="96"
|
|
38
|
+
data-toolcraft-canvas-offset-y="-64"
|
|
39
|
+
data-toolcraft-canvas-zoom="125"
|
|
40
|
+
></div>
|
|
41
|
+
`);
|
|
42
|
+
|
|
43
|
+
await expect(readToolcraftCanvasViewport(page)).resolves.toEqual({
|
|
44
|
+
offsetX: 96,
|
|
45
|
+
offsetY: -64,
|
|
46
|
+
zoom: 125,
|
|
47
|
+
});
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
test("toolcraft canvas zoom helper performs only the requested direction", async ({
|
|
51
|
+
page,
|
|
52
|
+
}) => {
|
|
53
|
+
await page.setContent(`
|
|
54
|
+
<button aria-label="Zoom in" onclick="document.body.dataset.zoomIn = String(Number(document.body.dataset.zoomIn ?? 0) + 1)"></button>
|
|
55
|
+
<button aria-label="Zoom out" onclick="document.body.dataset.zoomOut = String(Number(document.body.dataset.zoomOut ?? 0) + 1)"></button>
|
|
56
|
+
`);
|
|
57
|
+
|
|
58
|
+
await zoomToolcraftCanvasViewport(page, {
|
|
59
|
+
direction: "in",
|
|
60
|
+
repetitions: 2,
|
|
61
|
+
});
|
|
62
|
+
await expect(page.locator("body")).toHaveAttribute("data-zoom-in", "2");
|
|
63
|
+
await expect(page.locator("body")).not.toHaveAttribute("data-zoom-out");
|
|
64
|
+
|
|
65
|
+
await zoomToolcraftCanvasViewport(page, { direction: "out" });
|
|
66
|
+
await expect(page.locator("body")).toHaveAttribute("data-zoom-out", "1");
|
|
67
|
+
});
|
|
68
|
+
|
|
27
69
|
test("toolcraft Infinity canvas video evidence compares decoded envelopes", async () => {
|
|
28
70
|
await expectToolcraftInfinityCanvasVideoExportEvidence(
|
|
29
71
|
{
|
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
import { expect, test, type Page, type TestInfo } from "@playwright/test";
|
|
2
2
|
|
|
3
|
+
import {
|
|
4
|
+
parseToolcraftBrowserRuntimeEvidence,
|
|
5
|
+
} from "../src/app/test-evidence/browser-runtime-contract";
|
|
3
6
|
import { attachedEvidenceTypes } from "./browser-semantic-evidence-test-helpers";
|
|
4
7
|
import {
|
|
5
8
|
expectToolcraftCanvasRenderScaleEvidence,
|
|
@@ -89,6 +92,24 @@ test("selected 2x rejects a 1x backing store and attaches no evidence", async ({
|
|
|
89
92
|
expect(testInfo.attachments).toHaveLength(attachmentCount);
|
|
90
93
|
});
|
|
91
94
|
|
|
95
|
+
test("selected 2x rejects a 3x backing store and attaches no evidence", async ({
|
|
96
|
+
page,
|
|
97
|
+
}, testInfo: TestInfo) => {
|
|
98
|
+
await installCanvas(page, 3);
|
|
99
|
+
const attachmentCount = testInfo.attachments.length;
|
|
100
|
+
|
|
101
|
+
await expect(
|
|
102
|
+
expectToolcraftCanvasRenderScaleEvidence(page, {
|
|
103
|
+
canvasSelector,
|
|
104
|
+
requirementId: "canvas.render-scale",
|
|
105
|
+
selectedScale: 2,
|
|
106
|
+
stateTransitions: [transition("steady", () => setBackingScale(page, 3))],
|
|
107
|
+
target: "canvas.renderScale",
|
|
108
|
+
}),
|
|
109
|
+
).rejects.toThrow(/backing width.*one physical pixel/iu);
|
|
110
|
+
expect(testInfo.attachments).toHaveLength(attachmentCount);
|
|
111
|
+
});
|
|
112
|
+
|
|
92
113
|
test("a backing clamp introduced by a state transition fails the whole recipe", async ({
|
|
93
114
|
page,
|
|
94
115
|
}, testInfo: TestInfo) => {
|
|
@@ -164,15 +185,23 @@ test("performance render-scale evidence attaches only after real backing proof",
|
|
|
164
185
|
const attachmentCount = testInfo.attachments.length;
|
|
165
186
|
|
|
166
187
|
await expectToolcraftPerformanceRenderScaleBackingEvidence(page, {
|
|
188
|
+
baselineCssSize: { height: 80, width: 120 },
|
|
167
189
|
canvasSelector,
|
|
190
|
+
cssSizePolicy: "fixed",
|
|
168
191
|
pathId: "performance-path:render-scale",
|
|
169
|
-
|
|
192
|
+
phase: "cold",
|
|
170
193
|
target: "canvas.renderScale",
|
|
171
194
|
});
|
|
172
195
|
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
196
|
+
const [qualityEvidence] = testInfo.attachments
|
|
197
|
+
.slice(attachmentCount)
|
|
198
|
+
.map(parseToolcraftBrowserRuntimeEvidence)
|
|
199
|
+
.filter(
|
|
200
|
+
(evidence) => evidence?.evidenceType === "performance-render-scale",
|
|
201
|
+
);
|
|
202
|
+
expect(qualityEvidence?.requirementId).toBe(
|
|
203
|
+
"performance-path:render-scale#cold",
|
|
204
|
+
);
|
|
176
205
|
});
|
|
177
206
|
|
|
178
207
|
test("performance render-scale evidence rejects a quality clamp without attaching", async ({
|
|
@@ -183,11 +212,36 @@ test("performance render-scale evidence rejects a quality clamp without attachin
|
|
|
183
212
|
|
|
184
213
|
await expect(
|
|
185
214
|
expectToolcraftPerformanceRenderScaleBackingEvidence(page, {
|
|
215
|
+
baselineCssSize: { height: 80, width: 120 },
|
|
186
216
|
canvasSelector,
|
|
217
|
+
cssSizePolicy: "fixed",
|
|
187
218
|
pathId: "performance-path:render-scale",
|
|
188
|
-
|
|
219
|
+
phase: "warm",
|
|
189
220
|
target: "canvas.renderScale",
|
|
190
221
|
}),
|
|
191
|
-
).rejects.toThrow(/backing width
|
|
222
|
+
).rejects.toThrow(/warm.*backing width/iu);
|
|
223
|
+
expect(testInfo.attachments).toHaveLength(attachmentCount);
|
|
224
|
+
});
|
|
225
|
+
|
|
226
|
+
test("performance render-scale evidence rejects a changed CSS baseline", async ({
|
|
227
|
+
page,
|
|
228
|
+
}, testInfo: TestInfo) => {
|
|
229
|
+
await installCanvas(page, 2);
|
|
230
|
+
const attachmentCount = testInfo.attachments.length;
|
|
231
|
+
await page.locator(canvasSelector).evaluate((canvas) => {
|
|
232
|
+
canvas.setAttribute("style", "display:block;width:60px;height:40px");
|
|
233
|
+
});
|
|
234
|
+
await setBackingScale(page, 2);
|
|
235
|
+
|
|
236
|
+
await expect(
|
|
237
|
+
expectToolcraftPerformanceRenderScaleBackingEvidence(page, {
|
|
238
|
+
baselineCssSize: { height: 80, width: 120 },
|
|
239
|
+
canvasSelector,
|
|
240
|
+
cssSizePolicy: "fixed",
|
|
241
|
+
pathId: "performance-path:render-scale",
|
|
242
|
+
phase: "sustained",
|
|
243
|
+
target: "canvas.renderScale",
|
|
244
|
+
}),
|
|
245
|
+
).rejects.toThrow(/sustained.*preserve.*CSS width/iu);
|
|
192
246
|
expect(testInfo.attachments).toHaveLength(attachmentCount);
|
|
193
247
|
});
|
|
@@ -1,7 +1,14 @@
|
|
|
1
1
|
import { expect, test } from "@playwright/test";
|
|
2
|
+
import {
|
|
3
|
+
defineToolcraft,
|
|
4
|
+
type ToolcraftPerformancePath,
|
|
5
|
+
} from "@repo/toolcraft-runtime";
|
|
2
6
|
|
|
3
7
|
import type { ToolcraftComponentAcceptance } from "../src/app/starter-acceptance";
|
|
4
|
-
import {
|
|
8
|
+
import {
|
|
9
|
+
deriveToolcraftBrowserRuntimeRequirements,
|
|
10
|
+
deriveToolcraftPerformancePathRuntimeRequirements,
|
|
11
|
+
} from "./browser-runtime-evidence-requirements";
|
|
5
12
|
|
|
6
13
|
function renderScaleAcceptance(
|
|
7
14
|
states: readonly ("interaction" | "playback" | "steady")[],
|
|
@@ -57,6 +64,20 @@ test("render-scale coverage derives one exact functional requirement per state",
|
|
|
57
64
|
]);
|
|
58
65
|
});
|
|
59
66
|
|
|
67
|
+
test("render-scale backing replaces generic visual-change evidence", () => {
|
|
68
|
+
const requirements = deriveToolcraftBrowserRuntimeRequirements([
|
|
69
|
+
renderScaleAcceptance(["interaction", "steady"]),
|
|
70
|
+
]);
|
|
71
|
+
|
|
72
|
+
expect(requirements.map(({ evidenceType }) => evidenceType)).not.toContain(
|
|
73
|
+
"product-observable-change",
|
|
74
|
+
);
|
|
75
|
+
expect(requirements.map(({ evidenceType }) => evidenceType)).toEqual([
|
|
76
|
+
"canvas-render-scale-backing",
|
|
77
|
+
"canvas-render-scale-backing",
|
|
78
|
+
]);
|
|
79
|
+
});
|
|
80
|
+
|
|
60
81
|
test("render-scale functional requirements remain separate from performance evidence", () => {
|
|
61
82
|
const requirements = deriveToolcraftBrowserRuntimeRequirements([
|
|
62
83
|
renderScaleAcceptance(["interaction", "steady"]),
|
|
@@ -69,3 +90,58 @@ test("render-scale functional requirements remain separate from performance evid
|
|
|
69
90
|
"performance-render-scale",
|
|
70
91
|
);
|
|
71
92
|
});
|
|
93
|
+
|
|
94
|
+
const unrelatedPerformancePaths = [
|
|
95
|
+
{
|
|
96
|
+
id: "performance-path:color",
|
|
97
|
+
interaction: "control-drag",
|
|
98
|
+
invalidates: ["composite"],
|
|
99
|
+
profile: "live-interaction",
|
|
100
|
+
runsOn: ["main"],
|
|
101
|
+
targets: ["appearance.color"],
|
|
102
|
+
workloadDimensions: [],
|
|
103
|
+
},
|
|
104
|
+
{
|
|
105
|
+
id: "performance-path:viewport",
|
|
106
|
+
interaction: "viewport-drag",
|
|
107
|
+
invalidates: ["composite"],
|
|
108
|
+
profile: "frame-budget",
|
|
109
|
+
runsOn: ["main"],
|
|
110
|
+
targets: [],
|
|
111
|
+
workloadDimensions: [],
|
|
112
|
+
},
|
|
113
|
+
] as const satisfies readonly ToolcraftPerformancePath[];
|
|
114
|
+
|
|
115
|
+
test("every raster performance path derives cold, warm, and sustained 2x requirements", () => {
|
|
116
|
+
const requirements = deriveToolcraftPerformancePathRuntimeRequirements(
|
|
117
|
+
unrelatedPerformancePaths,
|
|
118
|
+
defineToolcraft({
|
|
119
|
+
canvas: { enabled: true, renderScale: true },
|
|
120
|
+
panels: {},
|
|
121
|
+
}),
|
|
122
|
+
).filter(({ evidenceType }) => evidenceType === "performance-render-scale");
|
|
123
|
+
|
|
124
|
+
expect(requirements).toEqual([
|
|
125
|
+
expect.objectContaining({ requirementId: "performance-path:color#cold" }),
|
|
126
|
+
expect.objectContaining({ requirementId: "performance-path:color#warm" }),
|
|
127
|
+
expect.objectContaining({
|
|
128
|
+
requirementId: "performance-path:color#sustained",
|
|
129
|
+
}),
|
|
130
|
+
expect.objectContaining({ requirementId: "performance-path:viewport#cold" }),
|
|
131
|
+
expect.objectContaining({ requirementId: "performance-path:viewport#warm" }),
|
|
132
|
+
expect.objectContaining({
|
|
133
|
+
requirementId: "performance-path:viewport#sustained",
|
|
134
|
+
}),
|
|
135
|
+
]);
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
test("vector performance paths derive no raster quality requirements", () => {
|
|
139
|
+
const requirements = deriveToolcraftPerformancePathRuntimeRequirements(
|
|
140
|
+
unrelatedPerformancePaths,
|
|
141
|
+
defineToolcraft({ canvas: { enabled: true }, panels: {} }),
|
|
142
|
+
);
|
|
143
|
+
|
|
144
|
+
expect(requirements.map(({ evidenceType }) => evidenceType)).not.toContain(
|
|
145
|
+
"performance-render-scale",
|
|
146
|
+
);
|
|
147
|
+
});
|
|
@@ -23,6 +23,13 @@ test("protected observable evidence is attached only after its assertion succeed
|
|
|
23
23
|
const output = document.createElement("div");
|
|
24
24
|
output.dataset.toolcraftProductOutput = "";
|
|
25
25
|
output.dataset.toolcraftProofProductOutput = "";
|
|
26
|
+
output.style.backgroundColor = "#000000";
|
|
27
|
+
output.style.height = "24px";
|
|
28
|
+
output.style.left = "0";
|
|
29
|
+
output.style.position = "fixed";
|
|
30
|
+
output.style.top = "0";
|
|
31
|
+
output.style.width = "24px";
|
|
32
|
+
output.style.zIndex = "100";
|
|
26
33
|
output.textContent = "Before";
|
|
27
34
|
const button = document.createElement("button");
|
|
28
35
|
button.id = "change-output";
|
|
@@ -46,6 +53,7 @@ test("protected observable evidence is attached only after its assertion succeed
|
|
|
46
53
|
button.dataset.activated = "true";
|
|
47
54
|
});
|
|
48
55
|
await currentPage.locator('[data-toolcraft-proof-product-output]').evaluate((output) => {
|
|
56
|
+
(output as HTMLElement).style.backgroundColor = "#FFFFFF";
|
|
49
57
|
output.textContent = "After";
|
|
50
58
|
});
|
|
51
59
|
}),
|
|
@@ -135,6 +135,41 @@ test("performance marker requires every derived scenario evidence type", () => {
|
|
|
135
135
|
expect(errors).toContainEqual(expect.stringContaining("performance-budget"));
|
|
136
136
|
});
|
|
137
137
|
|
|
138
|
+
test("performance reporter rejects missing warm and sustained raster quality evidence", () => {
|
|
139
|
+
const testName = "browser perf: toolcraft path performance-path:color";
|
|
140
|
+
const requirements: ToolcraftBrowserRuntimeRequirement[] = [
|
|
141
|
+
"cold",
|
|
142
|
+
"warm",
|
|
143
|
+
"sustained",
|
|
144
|
+
].map((phase) => ({
|
|
145
|
+
evidenceType: "performance-render-scale",
|
|
146
|
+
requirementId: `performance-path:color#${phase}`,
|
|
147
|
+
target: "appearance.color",
|
|
148
|
+
testName,
|
|
149
|
+
}));
|
|
150
|
+
const marker = fakeTestCase({
|
|
151
|
+
file: "/product/e2e/app-browser-runtime-evidence.spec.ts",
|
|
152
|
+
title: TOOLCRAFT_BROWSER_PERFORMANCE_MARKER_TEST_NAME,
|
|
153
|
+
});
|
|
154
|
+
const scenarioTest = fakeTestCase({
|
|
155
|
+
results: [passedResultWithEvidence(requirements[0]!)],
|
|
156
|
+
title: testName,
|
|
157
|
+
});
|
|
158
|
+
const errors: string[] = [];
|
|
159
|
+
const reporter = new ToolcraftBrowserRuntimeEvidenceReporter({
|
|
160
|
+
acceptanceRequirements: [],
|
|
161
|
+
performanceRequirements: requirements,
|
|
162
|
+
reportError: (error) => errors.push(error),
|
|
163
|
+
});
|
|
164
|
+
|
|
165
|
+
reporter.onBegin?.({} as never, fakeSuite([marker, scenarioTest]));
|
|
166
|
+
expect(reporter.onEnd?.({ status: "passed" } as never)).toEqual({
|
|
167
|
+
status: "failed",
|
|
168
|
+
});
|
|
169
|
+
expect(errors).toContainEqual(expect.stringContaining("#warm"));
|
|
170
|
+
expect(errors).toContainEqual(expect.stringContaining("#sustained"));
|
|
171
|
+
});
|
|
172
|
+
|
|
138
173
|
test("runtime evidence reporter requires render-scale proof for every covered state", () => {
|
|
139
174
|
const testName = "browser: canvas render scale backing pixels";
|
|
140
175
|
const requirements: ToolcraftBrowserRuntimeRequirement[] = [
|
|
@@ -35,7 +35,10 @@ test("derived paths own one canonical browser evidence requirement set", () => {
|
|
|
35
35
|
},
|
|
36
36
|
] as const;
|
|
37
37
|
|
|
38
|
-
const requirements = deriveToolcraftPerformancePathRuntimeRequirements(
|
|
38
|
+
const requirements = deriveToolcraftPerformancePathRuntimeRequirements(
|
|
39
|
+
[path],
|
|
40
|
+
defineToolcraft({ canvas: { enabled: true }, panels: {} }),
|
|
41
|
+
);
|
|
39
42
|
|
|
40
43
|
expect(requirements.map(({ evidenceType }) => evidenceType)).toEqual([
|
|
41
44
|
"performance-measurement",
|
|
@@ -169,7 +169,6 @@ test("layer recipes reject collection substitution and prove exact reorder", asy
|
|
|
169
169
|
expect(attachedEvidenceTypes(testInfo, attachmentCount)).toEqual([
|
|
170
170
|
"product-observable-change",
|
|
171
171
|
"layer-reorder",
|
|
172
|
-
"product-observable-change",
|
|
173
172
|
"layer-selection",
|
|
174
173
|
]);
|
|
175
174
|
});
|
|
@@ -74,11 +74,19 @@ test("product observable helper catches changed and unchanged output", async ({
|
|
|
74
74
|
`);
|
|
75
75
|
|
|
76
76
|
const snapshot = await getToolcraftProductObservableSnapshot(page);
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
77
|
+
const decodedSnapshot = JSON.parse(snapshot) as {
|
|
78
|
+
height: number;
|
|
79
|
+
rasterHash: string;
|
|
80
|
+
width: number;
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
expect(decodedSnapshot).toMatchObject({
|
|
84
|
+
height: expect.any(Number),
|
|
85
|
+
rasterHash: expect.stringMatching(/^[a-f0-9]+$/u),
|
|
86
|
+
width: expect.any(Number),
|
|
87
|
+
});
|
|
88
|
+
expect(decodedSnapshot.height).toBeGreaterThan(0);
|
|
89
|
+
expect(decodedSnapshot.width).toBeGreaterThan(0);
|
|
82
90
|
|
|
83
91
|
await expectToolcraftProductObservableToChange(page, async () => {
|
|
84
92
|
await page.locator("#change-output").evaluate((button) => {
|
|
@@ -1,4 +1,11 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type {
|
|
2
|
+
ToolcraftPerformanceCanvasBacking,
|
|
3
|
+
ToolcraftPerformancePathAdapter,
|
|
4
|
+
} from "./performance-path-adapter-contract";
|
|
5
|
+
|
|
6
|
+
export const appPerformanceCanvasBacking:
|
|
7
|
+
| ToolcraftPerformanceCanvasBacking
|
|
8
|
+
| undefined = undefined;
|
|
2
9
|
|
|
3
10
|
export const appPerformancePathAdapters =
|
|
4
11
|
[] as const satisfies readonly ToolcraftPerformancePathAdapter[];
|