@pixel-point/toolcraft 0.0.18 → 0.0.19
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-demand-only-lifecycle.test.mjs +9 -1
- package/src/generate-performance-iteration-execution.test.mjs +3 -3
- package/src/generate-test-delivery-architecture-assertions.mjs +11 -73
- package/src/generate-test-demand-only-lifecycle.mjs +34 -36
- package/src/generate-test-dependency-sandbox.mjs +4 -1
- package/src/generate-test-doc-assertions.mjs +55 -53
- package/src/generate-test-doc-assertions.test.mjs +1 -1
- package/src/generate-test-doc-policy-assertions.mjs +1 -1
- package/src/generate-test-export-doc-assertions.mjs +53 -0
- package/src/generate-test-file-assertions.mjs +51 -8
- package/src/generate-test-integrity-assertions.mjs +5 -2
- package/src/generate-test-integrity-manifest-assertions.mjs +16 -5
- package/src/generate-test-integrity-ownership-assertions.mjs +36 -4
- package/src/generate-test-package-assertions.mjs +4 -0
- package/src/generate-test-product-fixture.mjs +1 -1
- package/src/generate-test-product-fixture.test.mjs +35 -38
- package/src/generate-test-product-iteration.mjs +0 -1
- package/src/generate-test-product-iteration.test.mjs +0 -1
- package/src/generate-test-runtime-boundary-assertions.mjs +20 -0
- package/src/generate-test-standalone-assertions.mjs +0 -16
- package/src/generate-test-standalone-delivery-stages.mjs +2 -86
- package/src/generate.test.mjs +4 -0
- package/src/generated-integrity-manifest.test.mjs +5 -2
- package/src/generated-source-policy.test.mjs +0 -4
- package/src/package-json.mjs +5 -0
- package/src/package-json.test.mjs +21 -0
- package/src/package-manager.mjs +2 -0
- package/src/package-manager.test.mjs +8 -0
- package/src/packaged-template-fallback-helpers.mjs +254 -0
- package/src/packaged-template-fallback.test.mjs +121 -46
- package/src/packaged-template-runtime-assertions.mjs +150 -0
- package/templates/runtime/contracts/component-contracts.choices.test.ts +18 -3
- package/templates/runtime/contracts/component-contracts.choices.ts +8 -3
- package/templates/runtime/contracts/component-contracts.inputs.ts +2 -1
- package/templates/runtime/contracts/component-contracts.runtime-acceptance.test.ts +18 -4
- package/templates/runtime/contracts/component-contracts.runtime-shell.test.ts +1 -1
- package/templates/runtime/contracts/component-contracts.runtime.ts +7 -4
- package/templates/runtime/contracts/component-contracts.visual.test.ts +20 -0
- package/templates/runtime/contracts/component-contracts.visual.ts +4 -0
- package/templates/runtime/contracts/decision-contracts.test.ts +18 -12
- package/templates/runtime/contracts/decision-contracts.ts +4 -4
- package/templates/runtime/contracts/performance-verification-policy.test.ts +12 -12
- package/templates/runtime/contracts/performance-verification-policy.ts +8 -8
- package/templates/runtime/export/artifact-download.ts +1 -0
- package/templates/runtime/export/artifact-frame-renderer.ts +2 -15
- package/templates/runtime/export/artifact-frame-state.ts +16 -0
- package/templates/runtime/export/artifact-scene-frame.test.ts +3 -3
- package/templates/runtime/export/artifact-scene-frame.ts +3 -3
- package/templates/runtime/export/export-error.ts +5 -0
- package/templates/runtime/export/export-renderer-coverage.ts +54 -0
- package/templates/runtime/export/image-artifact-export.ts +2 -2
- package/templates/runtime/export/index.ts +13 -0
- package/templates/runtime/export/product-export-renderer.test.ts +38 -4
- package/templates/runtime/export/product-export-renderer.ts +0 -41
- package/templates/runtime/export/product-svg-export-renderer.ts +21 -0
- package/templates/runtime/export/svg-artifact-export.test.ts +98 -0
- package/templates/runtime/export/svg-artifact-export.ts +120 -0
- package/templates/runtime/export/svg-document.test.ts +221 -0
- package/templates/runtime/export/svg-document.ts +400 -0
- package/templates/runtime/export/svg-policy.ts +47 -0
- package/templates/runtime/index.ts +1 -0
- package/templates/runtime/react/app-shell/runtime-public-api.test.tsx +10 -0
- package/templates/runtime/react/app-shell/settings-transfer.test.ts +21 -0
- package/templates/runtime/react/app-shell/toolcraft-app.scene-export.test.tsx +46 -1
- package/templates/runtime/react/app-shell/toolcraft-app.tsx +7 -2
- package/templates/runtime/react/canvas/canvas-shell-interactions.test.tsx +30 -0
- package/templates/runtime/react/canvas/canvas-shell-pinch-interactions.test.tsx +155 -0
- package/templates/runtime/react/canvas/canvas-shell-rendering.test.tsx +1 -1
- package/templates/runtime/react/canvas/canvas-shell.tsx +16 -5
- package/templates/runtime/react/canvas/canvas-viewport-geometry.ts +133 -0
- package/templates/runtime/react/canvas/use-canvas-viewport-interactions.ts +285 -36
- package/templates/runtime/react/controls-panel/__tests__/controls-panel.color-applicability-layout.test.tsx +134 -0
- package/templates/runtime/react/controls-panel/__tests__/controls-panel.color-layout.test.tsx +16 -1
- package/templates/runtime/react/controls-panel/__tests__/controls-panel.render-isolation.test.tsx +2 -0
- package/templates/runtime/react/controls-panel/__tests__/controls-panel.scene-export-actions.test.tsx +39 -0
- package/templates/runtime/react/controls-panel/__tests__/controls-panel.setup-controls.test.tsx +3 -3
- package/templates/runtime/react/controls-panel/__tests__/controls-panel.source-collection.test.tsx +1 -4
- package/templates/runtime/react/controls-panel/__tests__/controls-panel.source-color-bank.test.tsx +2 -2
- package/templates/runtime/react/controls-panel/__tests__/controls-panel.vector-interaction.test.tsx +6 -6
- package/templates/runtime/react/controls-panel/actions/controls-panel-actions.ts +2 -2
- package/templates/runtime/react/controls-panel/actions/export-action-runner.ts +40 -11
- package/templates/runtime/react/controls-panel/layout/controls-panel-layout.tsx +31 -2
- package/templates/runtime/react/controls-panel/layout/controls-panel-section.tsx +4 -1
- package/templates/runtime/react/controls-panel/renderers/controls-panel-action-renderer.tsx +2 -1
- package/templates/runtime/react/controls-panel/renderers/controls-panel-basic-renderers.tsx +2 -52
- package/templates/runtime/react/controls-panel/values/controls-panel-color-values.ts +1 -9
- package/templates/runtime/react/controls-panel/values/controls-panel-spatial-values.ts +7 -2
- package/templates/runtime/react/orientation-gizmo/orientation-gizmo-math.ts +9 -69
- package/templates/runtime/schema/artifact-export-actions.test.ts +47 -0
- package/templates/runtime/schema/artifact-export-actions.ts +49 -0
- package/templates/runtime/schema/types.ts +1 -0
- package/templates/runtime/state/canvas-background-state.ts +1 -16
- package/templates/runtime/state/canvas-state.ts +32 -20
- package/templates/runtime/state/control-value-codecs.test.ts +98 -0
- package/templates/runtime/state/control-value-codecs.ts +700 -0
- package/templates/runtime/state/control-value-normalization.test.ts +1 -1
- package/templates/runtime/state/control-value-normalization.ts +127 -135
- package/templates/runtime/state/controls-reducer.ts +26 -3
- package/templates/runtime/state/create-template-state.test.ts +30 -0
- package/templates/runtime/state/create-template-state.ts +52 -31
- package/templates/runtime/state/orientation-pose.ts +80 -0
- package/templates/runtime/state/persistence-control-values.test.ts +59 -0
- package/templates/runtime/state/reducer-controls.test.ts +45 -0
- package/templates/runtime/state/reducer-media-reset.test.ts +1 -1
- package/templates/runtime/state/reducer-timeline.test.ts +70 -0
- package/templates/runtime/state/timeline-reducer.ts +40 -2
- package/templates/runtime/testing/performance-schema-queries.ts +2 -2
- package/templates/starter/AGENTS.md +21 -22
- package/templates/starter/docs/toolcraft/README.md +6 -6
- package/templates/starter/docs/toolcraft/acceptance-testing.md +20 -20
- package/templates/starter/docs/toolcraft/agent-worklog.md +88 -1
- package/templates/starter/docs/toolcraft/assembly-workflow.md +7 -6
- package/templates/starter/docs/toolcraft/component-rules.md +2 -2
- package/templates/starter/docs/toolcraft/core/layout.md +6 -1
- package/templates/starter/docs/toolcraft/core/media-upload.md +1 -1
- package/templates/starter/docs/toolcraft/core/performance.md +8 -10
- package/templates/starter/docs/toolcraft/core/reference-study.md +80 -19
- package/templates/starter/docs/toolcraft/core/runtime-boundary.md +2 -1
- package/templates/starter/docs/toolcraft/core/setup-export.md +43 -13
- package/templates/starter/docs/toolcraft/decision-contract.md +2 -2
- package/templates/starter/docs/toolcraft/performance.md +8 -10
- package/templates/starter/docs/toolcraft/renderer-technique.md +2 -2
- package/templates/starter/docs/toolcraft/schema-reference.md +8 -8
- package/templates/starter/docs/toolcraft/workflow.md +14 -14
- package/templates/starter/e2e/app-browser-infinity-canvas-evidence.spec.ts +38 -0
- package/templates/starter/e2e/app-browser-motion-reference-requirements.spec.ts +411 -0
- package/templates/starter/e2e/app-browser-proof-session.spec.ts +85 -0
- package/templates/starter/e2e/app-browser-runtime-requirements.spec.ts +62 -0
- package/templates/starter/e2e/app-browser-selection-scope-evidence.spec.ts +137 -0
- package/templates/starter/e2e/browser-infinity-canvas-svg-evidence.ts +42 -0
- package/templates/starter/e2e/browser-layer-evidence-helpers.ts +15 -36
- package/templates/starter/e2e/browser-product-raster-snapshot.ts +213 -0
- package/templates/starter/e2e/browser-proof-session.ts +43 -15
- package/templates/starter/e2e/browser-proof-surface-action.ts +79 -0
- package/templates/starter/e2e/browser-proof-types.ts +15 -0
- package/templates/starter/e2e/browser-runtime-evidence-requirements.ts +22 -6
- package/templates/starter/e2e/browser-selection-scope-evidence.ts +269 -0
- package/templates/starter/e2e/browser-semantic-evidence-test-helpers.ts +97 -0
- package/templates/starter/e2e/browser-svg-export-evidence.spec.ts +79 -0
- package/templates/starter/e2e/browser-svg-export-evidence.ts +152 -0
- package/templates/starter/e2e/export-artifact-helpers.ts +70 -0
- package/templates/starter/e2e/product-observable-helpers.ts +8 -129
- package/templates/starter/e2e/svg-artifact-inspection.spec.ts +136 -0
- package/templates/starter/e2e/svg-artifact-inspection.ts +331 -0
- package/templates/starter/e2e/svg-download-test-fixtures.ts +34 -0
- package/templates/starter/e2e/toolcraft-delivery-catalog-reporter.ts +198 -15
- package/templates/starter/e2e/toolcraft-feature-verification-reporter.spec.ts +149 -0
- package/templates/starter/e2e/toolcraft-feature-verification-reporter.ts +223 -0
- package/templates/starter/package.json +2 -0
- package/templates/starter/scripts/check-toolcraft-docs.mjs +1 -0
- package/templates/starter/scripts/create-toolcraft-motion-reference-study.mjs +233 -0
- package/templates/starter/scripts/create-toolcraft-motion-reference-study.test.mjs +459 -0
- package/templates/starter/scripts/playwright-test-title-selection.d.mts +45 -0
- package/templates/starter/scripts/run-browser-performance-test-helpers.mjs +0 -27
- package/templates/starter/scripts/run-browser-performance.mjs +0 -6
- package/templates/starter/scripts/run-browser-performance.test.mjs +5 -13
- package/templates/starter/scripts/run-delivery-verification-test-helpers.mjs +0 -34
- package/templates/starter/scripts/run-delivery-verification.test.mjs +135 -31
- package/templates/starter/scripts/run-feature-verification.mjs +212 -0
- package/templates/starter/scripts/run-feature-verification.test.mjs +226 -0
- package/templates/starter/scripts/toolcraft-checkpoint-delivery-state.mjs +21 -8
- package/templates/starter/scripts/toolcraft-checkpoint-delivery-state.test.mjs +32 -92
- package/templates/starter/scripts/toolcraft-checkpoint-transaction.mjs +28 -6
- package/templates/starter/scripts/toolcraft-checkpoint-transaction.test.mjs +8 -63
- package/templates/starter/scripts/toolcraft-delivery-anchor.mjs +28 -8
- package/templates/starter/scripts/toolcraft-delivery-anchor.test.mjs +50 -61
- package/templates/starter/scripts/toolcraft-delivery-architecture-inventory.mjs +0 -4
- package/templates/starter/scripts/toolcraft-delivery-architecture-policy.mjs +0 -1
- package/templates/starter/scripts/toolcraft-delivery-evidence.test.mjs +2 -3
- package/templates/starter/scripts/toolcraft-delivery-executor.mjs +5 -1
- package/templates/starter/scripts/toolcraft-delivery-executor.test.mjs +5 -12
- package/templates/starter/scripts/toolcraft-delivery-functional-context.d.mts +1 -48
- package/templates/starter/scripts/toolcraft-delivery-functional-context.mjs +2 -30
- package/templates/starter/scripts/toolcraft-delivery-functional-context.test.mjs +4 -166
- package/templates/starter/scripts/toolcraft-delivery-functional-initial.test.mjs +4 -20
- package/templates/starter/scripts/toolcraft-delivery-lifecycle.mjs +43 -192
- package/templates/starter/scripts/toolcraft-delivery-performance-iteration-lifecycle.test.mjs +79 -440
- package/templates/starter/scripts/toolcraft-delivery-plan-authority.test.mjs +102 -297
- package/templates/starter/scripts/toolcraft-delivery-plan-construction.mjs +46 -97
- package/templates/starter/scripts/toolcraft-delivery-plan-test-helpers.mjs +125 -0
- package/templates/starter/scripts/toolcraft-delivery-plan-validation.test.mjs +58 -0
- package/templates/starter/scripts/toolcraft-delivery-plan.d.mts +19 -31
- package/templates/starter/scripts/toolcraft-delivery-plan.mjs +254 -252
- package/templates/starter/scripts/toolcraft-delivery-plan.test.mjs +29 -457
- package/templates/starter/scripts/toolcraft-delivery-receipt-performance.test.mjs +1 -1
- package/templates/starter/scripts/toolcraft-delivery-receipt-strictness.test.mjs +3 -72
- package/templates/starter/scripts/toolcraft-delivery-receipt-test-helpers.mjs +7 -36
- package/templates/starter/scripts/toolcraft-delivery-receipt.d.mts +3 -5
- package/templates/starter/scripts/toolcraft-delivery-receipt.mjs +15 -31
- package/templates/starter/scripts/toolcraft-delivery-receipt.test.mjs +28 -20
- package/templates/starter/scripts/toolcraft-delivery-receipt.types.test.mts +22 -30
- package/templates/starter/scripts/toolcraft-feature-verification-plan.d.mts +20 -0
- package/templates/starter/scripts/toolcraft-feature-verification-plan.mjs +146 -0
- package/templates/starter/scripts/toolcraft-feature-verification-plan.test.mjs +96 -0
- package/templates/starter/scripts/toolcraft-functional-proof-model.d.mts +3 -18
- package/templates/starter/scripts/toolcraft-functional-proof-model.mjs +39 -127
- package/templates/starter/scripts/toolcraft-functional-proof-model.test.mjs +25 -362
- package/templates/starter/scripts/toolcraft-functional-proof-model.types.test.mts +6 -83
- package/templates/starter/scripts/toolcraft-functional-proof-primitives.d.mts +11 -0
- package/templates/starter/scripts/toolcraft-functional-proof-primitives.mjs +61 -0
- package/templates/starter/scripts/toolcraft-functional-proof-primitives.test.mjs +43 -0
- package/templates/starter/scripts/toolcraft-integrity-policy.mjs +13 -0
- package/templates/starter/scripts/toolcraft-integrity-test-utils.mjs +1 -1
- package/templates/starter/scripts/toolcraft-layer-evidence-facade.test.mjs +58 -22
- package/templates/starter/scripts/toolcraft-motion-reference-analysis-protocol.mjs +111 -0
- package/templates/starter/scripts/toolcraft-motion-reference-analysis-protocol.test.mjs +116 -0
- package/templates/starter/scripts/toolcraft-motion-reference-artifacts.d.mts +37 -0
- package/templates/starter/scripts/toolcraft-motion-reference-artifacts.mjs +308 -0
- package/templates/starter/scripts/toolcraft-motion-reference-artifacts.test.mjs +420 -0
- package/templates/starter/scripts/toolcraft-motion-reference-artifacts.types.test.mts +50 -0
- package/templates/starter/scripts/toolcraft-motion-reference-command-options.mjs +195 -0
- package/templates/starter/scripts/toolcraft-motion-reference-command-options.test.mjs +224 -0
- package/templates/starter/scripts/toolcraft-motion-reference-contact-sheet.mjs +122 -0
- package/templates/starter/scripts/toolcraft-motion-reference-contact-sheet.test.mjs +153 -0
- package/templates/starter/scripts/toolcraft-motion-reference-evidence-assembly.mjs +270 -0
- package/templates/starter/scripts/toolcraft-motion-reference-evidence.test.mjs +432 -0
- package/templates/starter/scripts/toolcraft-motion-reference-ffmpeg-output.mjs +275 -0
- package/templates/starter/scripts/toolcraft-motion-reference-ffmpeg-output.test.mjs +294 -0
- package/templates/starter/scripts/toolcraft-motion-reference-group-inventory.mjs +148 -0
- package/templates/starter/scripts/toolcraft-motion-reference-image-filter-graph.mjs +101 -0
- package/templates/starter/scripts/toolcraft-motion-reference-image-filter-graph.test.mjs +124 -0
- package/templates/starter/scripts/toolcraft-motion-reference-image-source.mjs +259 -0
- package/templates/starter/scripts/toolcraft-motion-reference-image-source.test.mjs +338 -0
- package/templates/starter/scripts/toolcraft-motion-reference-image-timing.mjs +96 -0
- package/templates/starter/scripts/toolcraft-motion-reference-image-timing.test.mjs +81 -0
- package/templates/starter/scripts/toolcraft-motion-reference-lock-recovery.test.mjs +144 -0
- package/templates/starter/scripts/toolcraft-motion-reference-media-source.mjs +165 -0
- package/templates/starter/scripts/toolcraft-motion-reference-media-source.test.mjs +299 -0
- package/templates/starter/scripts/toolcraft-motion-reference-publication-discovery.test.mjs +142 -0
- package/templates/starter/scripts/toolcraft-motion-reference-publication-isolation.test.mjs +234 -0
- package/templates/starter/scripts/toolcraft-motion-reference-publication-journal-schema.mjs +122 -0
- package/templates/starter/scripts/toolcraft-motion-reference-publication-journal-store.mjs +89 -0
- package/templates/starter/scripts/toolcraft-motion-reference-publication-journal-store.test.mjs +74 -0
- package/templates/starter/scripts/toolcraft-motion-reference-publication-journal.mjs +264 -0
- package/templates/starter/scripts/toolcraft-motion-reference-publication-journal.test.mjs +377 -0
- package/templates/starter/scripts/toolcraft-motion-reference-publication-lease.mjs +254 -0
- package/templates/starter/scripts/toolcraft-motion-reference-publication-lease.test.mjs +130 -0
- package/templates/starter/scripts/toolcraft-motion-reference-publication-staging.mjs +128 -0
- package/templates/starter/scripts/toolcraft-motion-reference-publication-staging.test.mjs +209 -0
- package/templates/starter/scripts/toolcraft-motion-reference-publication.fixtures.mjs +88 -0
- package/templates/starter/scripts/toolcraft-motion-reference-publication.mjs +249 -0
- package/templates/starter/scripts/toolcraft-motion-reference-publication.test.mjs +433 -0
- package/templates/starter/scripts/toolcraft-motion-reference-review-output.mjs +38 -0
- package/templates/starter/scripts/toolcraft-motion-reference-review-output.test.mjs +29 -0
- package/templates/starter/scripts/toolcraft-motion-reference-root-safety.test.mjs +84 -0
- package/templates/starter/scripts/toolcraft-motion-reference-roots.mjs +83 -0
- package/templates/starter/scripts/toolcraft-motion-reference-sampling.mjs +283 -0
- package/templates/starter/scripts/toolcraft-motion-reference-sampling.test.mjs +480 -0
- package/templates/starter/scripts/toolcraft-motion-reference-stage-registry.mjs +39 -0
- package/templates/starter/scripts/toolcraft-motion-reference-stage-registry.test.mjs +59 -0
- package/templates/starter/scripts/toolcraft-motion-reference-stage.mjs +71 -0
- package/templates/starter/scripts/toolcraft-motion-reference-stage.test.mjs +61 -0
- package/templates/starter/scripts/toolcraft-motion-review-numeric.mjs +74 -0
- package/templates/starter/scripts/toolcraft-motion-review-numeric.test.mjs +65 -0
- package/templates/starter/scripts/toolcraft-motion-review-partitioning.mjs +268 -0
- package/templates/starter/scripts/toolcraft-motion-review-partitioning.test.mjs +329 -0
- package/templates/starter/scripts/toolcraft-motion-review-protected-units.mjs +120 -0
- package/templates/starter/scripts/toolcraft-motion-review-protected-units.test.mjs +111 -0
- package/templates/starter/scripts/toolcraft-performance-escalation-policy.test.mjs +77 -345
- package/templates/starter/scripts/toolcraft-performance-receipt-validation.test.mjs +19 -21
- package/templates/starter/scripts/toolcraft-product-boundary-ast.mjs +1 -1
- package/templates/starter/scripts/toolcraft-product-export-boundary.mjs +10 -1
- package/templates/starter/scripts/toolcraft-product-export-boundary.test.mjs +5 -1
- package/templates/starter/scripts/toolcraft-product-observable-facade.test.mjs +46 -1
- package/templates/starter/scripts/toolcraft-receipt-boundary.test.mjs +0 -1
- package/templates/starter/scripts/toolcraft-source-inventory.d.mts +5 -0
- package/templates/starter/scripts/toolcraft-source-inventory.mjs +25 -14
- package/templates/starter/scripts/toolcraft-source-inventory.test.mjs +48 -0
- package/templates/starter/scripts/toolcraft-source-ownership.mjs +0 -1
- package/templates/starter/scripts/toolcraft-source-ownership.test.mjs +30 -5
- package/templates/starter/scripts/toolcraft-verification-receipt-test-helpers.mjs +5 -26
- package/templates/starter/scripts/toolcraft-verification-receipt.d.mts +0 -7
- package/templates/starter/scripts/toolcraft-verification-receipt.mjs +3 -47
- package/templates/starter/scripts/toolcraft-verification-receipt.test.mjs +6 -87
- package/templates/starter/scripts/toolcraft-vitest-runtime-contract.d.mts +19 -0
- package/templates/starter/scripts/toolcraft-vitest-runtime-contract.mjs +107 -0
- package/templates/starter/scripts/toolcraft-vitest-runtime-contract.test.mjs +114 -0
- package/templates/starter/scripts/toolcraft-vitest-runtime-evidence-reporter.mjs +13 -2
- package/templates/starter/scripts/toolcraft-vitest-runtime-evidence-reporter.test.mjs +110 -0
- package/templates/starter/src/app/acceptance/artifact-export-intent.ts +51 -20
- package/templates/starter/src/app/acceptance/canvas-coverage.ts +1 -0
- package/templates/starter/src/app/acceptance/color-bank-labels.ts +11 -15
- package/templates/starter/src/app/acceptance/color-row-grouping.ts +33 -0
- package/templates/starter/src/app/acceptance/control-acceptance-coverage.ts +0 -11
- package/templates/starter/src/app/acceptance/control-acceptance-policy.ts +2 -3
- package/templates/starter/src/app/acceptance/control-component-rules.ts +0 -10
- package/templates/starter/src/app/acceptance/control-labels.ts +30 -1
- package/templates/starter/src/app/acceptance/control-layout-section-rules.ts +18 -0
- package/templates/starter/src/app/acceptance/export-artifact-coverage.ts +25 -17
- package/templates/starter/src/app/acceptance/feature-verification-selection.test.ts +295 -0
- package/templates/starter/src/app/acceptance/feature-verification-selection.ts +161 -0
- package/templates/starter/src/app/acceptance/infinity-canvas.ts +10 -0
- package/templates/starter/src/app/acceptance/interaction-ownership-intent.ts +19 -1
- package/templates/starter/src/app/acceptance/interaction-ownership.ts +18 -2
- package/templates/starter/src/app/acceptance/motion-reference-evidence-validation.mjs +65 -0
- package/templates/starter/src/app/acceptance/motion-reference-evidence-windows.mjs +301 -0
- package/templates/starter/src/app/acceptance/motion-reference-evidence.d.mts +11 -0
- package/templates/starter/src/app/acceptance/motion-reference-evidence.mjs +145 -0
- package/templates/starter/src/app/acceptance/motion-reference-study-behaviors.ts +162 -0
- package/templates/starter/src/app/acceptance/motion-reference-study-coverage.ts +206 -0
- package/templates/starter/src/app/acceptance/motion-reference-study-identity.ts +129 -0
- package/templates/starter/src/app/acceptance/motion-reference-study-partitions.ts +186 -0
- package/templates/starter/src/app/acceptance/motion-reference-study.ts +90 -0
- package/templates/starter/src/app/acceptance/output-export-actions.test.ts +9 -1
- package/templates/starter/src/app/acceptance/output-export-actions.ts +14 -22
- package/templates/starter/src/app/acceptance/output-export-layout-rules.ts +5 -1
- package/templates/starter/src/app/acceptance/output-export-model.ts +4 -0
- package/templates/starter/src/app/acceptance/output-export.ts +4 -1
- package/templates/starter/src/app/acceptance/reference-study-types.ts +167 -17
- package/templates/starter/src/app/acceptance/runtime-coverage.ts +6 -1
- package/templates/starter/src/app/acceptance/selection-scope.test.ts +200 -0
- package/templates/starter/src/app/acceptance/selection-scope.ts +151 -0
- package/templates/starter/src/app/acceptance/transfer-mode-types.ts +114 -0
- package/templates/starter/src/app/acceptance/types.ts +43 -118
- package/templates/starter/src/app/acceptance/validate-coverage.ts +14 -3
- package/templates/starter/src/app/starter-acceptance-data.ts +1 -0
- package/templates/starter/src/app/starter-acceptance.animation-intent.test.ts +3 -0
- package/templates/starter/src/app/starter-acceptance.artifact-export-intent.test.ts +81 -8
- package/templates/starter/src/app/starter-acceptance.background-export.test.ts +2 -0
- package/templates/starter/src/app/starter-acceptance.canvas-sizing.test.ts +2 -0
- package/templates/starter/src/app/starter-acceptance.color-row-grouping.test.ts +118 -0
- package/templates/starter/src/app/starter-acceptance.compound-part-coverage.test.ts +1 -1
- package/templates/starter/src/app/starter-acceptance.contract-fixtures.ts +1 -0
- package/templates/starter/src/app/starter-acceptance.control-applicability.test.ts +1 -0
- package/templates/starter/src/app/starter-acceptance.control-label-context.test.ts +1 -0
- package/templates/starter/src/app/starter-acceptance.control-labels.test.ts +78 -3
- package/templates/starter/src/app/starter-acceptance.control-naming-rules.test.ts +77 -1
- package/templates/starter/src/app/starter-acceptance.custom-control-fit-check.test.ts +1 -1
- package/templates/starter/src/app/starter-acceptance.export-artifact.test.ts +74 -3
- package/templates/starter/src/app/starter-acceptance.export-test-utils.ts +5 -1
- package/templates/starter/src/app/starter-acceptance.image-export-settings.test.ts +2 -0
- package/templates/starter/src/app/starter-acceptance.interaction-ownership.test.ts +19 -3
- package/templates/starter/src/app/starter-acceptance.layers.test.ts +2 -1
- package/templates/starter/src/app/starter-acceptance.media-upload.test.ts +28 -0
- package/templates/starter/src/app/starter-acceptance.motion-reference-evidence.test.ts +17 -0
- package/templates/starter/src/app/starter-acceptance.motion-reference-identity.test.ts +163 -0
- package/templates/starter/src/app/starter-acceptance.motion-reference-mapping.test.ts +133 -0
- package/templates/starter/src/app/starter-acceptance.motion-reference-ownership.test.ts +121 -0
- package/templates/starter/src/app/starter-acceptance.motion-reference-partitions.test.ts +210 -0
- package/templates/starter/src/app/starter-acceptance.motion-reference-test-utils.ts +313 -0
- package/templates/starter/src/app/starter-acceptance.output-export-intent.test.ts +78 -4
- package/templates/starter/src/app/starter-acceptance.product-output-export.test.ts +6 -1
- package/templates/starter/src/app/starter-acceptance.product-readiness.test.ts +18 -1
- package/templates/starter/src/app/starter-acceptance.product-worklog.test.ts +123 -2
- package/templates/starter/src/app/starter-acceptance.reference-custom-timeline.test.ts +2 -0
- package/templates/starter/src/app/starter-acceptance.reference-foundation.test.ts +8 -0
- package/templates/starter/src/app/starter-acceptance.reference-timeline.test.ts +7 -0
- package/templates/starter/src/app/starter-acceptance.section-cohesion.test.ts +2 -0
- package/templates/starter/src/app/starter-acceptance.timeline-playback.test.ts +5 -0
- package/templates/starter/src/app/starter-acceptance.ts +34 -8
- package/templates/starter/src/app/starter-acceptance.video-export-settings.test.ts +2 -0
- package/templates/starter/src/app/starter-acceptance.video-reference-study.test.ts +34 -236
- package/templates/starter/src/app/starter-acceptance.view-interaction.test.ts +4 -0
- package/templates/starter/src/app/starter-acceptance.worklog-test-utils.ts +122 -6
- package/templates/starter/src/app/starter-acceptance.worklog.test.ts +152 -16
- package/templates/starter/src/app/starter-performance.gates.test.ts +20 -18
- package/templates/starter/src/app/starter-performance.lifecycle.test.ts +22 -6
- package/templates/starter/src/app/test-evidence/browser-runtime-contract.ts +3 -0
- package/templates/starter/tsconfig.json +1 -0
- package/templates/starter/scripts/toolcraft-browser-impact-selection.mjs +0 -26
- package/templates/starter/scripts/toolcraft-delivery-framework-refresh.test.mjs +0 -141
- package/templates/starter/scripts/toolcraft-delivery-functional-changed.test.mjs +0 -468
- package/templates/starter/scripts/toolcraft-delivery-functional-model-lifecycle.test.mjs +0 -254
- package/templates/starter/scripts/toolcraft-delivery-functional-semantic-regression.test.mjs +0 -135
- package/templates/starter/scripts/toolcraft-delivery-performance-iteration-durability.test.mjs +0 -329
- package/templates/starter/scripts/toolcraft-delivery-plan-errors.test.mjs +0 -430
- package/templates/starter/scripts/toolcraft-delivery-plan-functional-model.test.mjs +0 -358
- package/templates/starter/scripts/toolcraft-functional-proof-regression-fixtures.mjs +0 -298
- package/templates/starter/scripts/toolcraft-verification-impact-inventory.d.mts +0 -62
- package/templates/starter/scripts/toolcraft-verification-impact-inventory.mjs +0 -344
- package/templates/starter/scripts/toolcraft-verification-impact-inventory.test.mjs +0 -204
- package/templates/starter/scripts/toolcraft-verification-impact-performance-candidates.test.mjs +0 -210
- package/templates/starter/scripts/toolcraft-verification-impact-resolution.mjs +0 -344
- package/templates/starter/scripts/toolcraft-verification-impact-resolution.test.mjs +0 -270
- package/templates/starter/scripts/toolcraft-verification-impact-semantic-resolution.test.mjs +0 -449
- package/templates/starter/scripts/toolcraft-verification-impact-support-resolution.test.mjs +0 -195
- package/templates/starter/scripts/toolcraft-verification-impact.d.mts +0 -132
- package/templates/starter/scripts/toolcraft-verification-impact.mjs +0 -169
- package/templates/starter/scripts/toolcraft-verification-impact.test.mjs +0 -300
- package/templates/starter/scripts/toolcraft-verification-input-roles.mjs +0 -197
- package/templates/starter/scripts/toolcraft-verification-input-roles.test.mjs +0 -225
- package/templates/starter/src/app/acceptance/video-reference-study.ts +0 -217
- package/templates/starter/src/app/starter-verification-impact.json +0 -15
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
ToolcraftComponentAcceptance,
|
|
3
|
+
ToolcraftMotionReferenceInput,
|
|
4
|
+
} from "./types";
|
|
5
|
+
|
|
6
|
+
type AcceptanceRowsById = ReadonlyMap<
|
|
7
|
+
string,
|
|
8
|
+
readonly ToolcraftComponentAcceptance[]
|
|
9
|
+
>;
|
|
10
|
+
|
|
11
|
+
function validateAcceptanceFields({
|
|
12
|
+
acceptance,
|
|
13
|
+
behaviorId,
|
|
14
|
+
}: {
|
|
15
|
+
acceptance: ToolcraftComponentAcceptance;
|
|
16
|
+
behaviorId: string;
|
|
17
|
+
}): string[] {
|
|
18
|
+
const errors: string[] = [];
|
|
19
|
+
if (!acceptance.automated || !acceptance.automatedTestName.trim()) {
|
|
20
|
+
errors.push(
|
|
21
|
+
`motion reference behavior "${behaviorId}" acceptance "${acceptance.id}" must have automated coverage.`,
|
|
22
|
+
);
|
|
23
|
+
}
|
|
24
|
+
if (!acceptance.browser || !acceptance.browserTestName.trim()) {
|
|
25
|
+
errors.push(
|
|
26
|
+
`motion reference behavior "${behaviorId}" acceptance "${acceptance.id}" must have browser coverage.`,
|
|
27
|
+
);
|
|
28
|
+
}
|
|
29
|
+
if (!acceptance.expectedObservable.trim()) {
|
|
30
|
+
errors.push(
|
|
31
|
+
`motion reference behavior "${behaviorId}" acceptance "${acceptance.id}" must describe its observable result.`,
|
|
32
|
+
);
|
|
33
|
+
}
|
|
34
|
+
return errors;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export function validateBehaviorCoverage(
|
|
38
|
+
input: ToolcraftMotionReferenceInput,
|
|
39
|
+
acceptanceRowsById: AcceptanceRowsById,
|
|
40
|
+
): string[] {
|
|
41
|
+
const errors: string[] = [];
|
|
42
|
+
const studiesById = new Map<
|
|
43
|
+
string,
|
|
44
|
+
ToolcraftMotionReferenceInput["studies"]
|
|
45
|
+
>();
|
|
46
|
+
const usedBehaviorIds = new Set<string>();
|
|
47
|
+
|
|
48
|
+
for (const study of input.studies) {
|
|
49
|
+
studiesById.set(study.studyId, [
|
|
50
|
+
...(studiesById.get(study.studyId) ?? []),
|
|
51
|
+
study,
|
|
52
|
+
]);
|
|
53
|
+
for (const phase of study.phases) {
|
|
54
|
+
phase.behaviorIds.forEach((behaviorId) => usedBehaviorIds.add(behaviorId));
|
|
55
|
+
}
|
|
56
|
+
for (const event of study.events) {
|
|
57
|
+
if (event.classification === "product-behavior") {
|
|
58
|
+
event.behaviorIds.forEach((behaviorId) => usedBehaviorIds.add(behaviorId));
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
for (const behavior of input.behaviors) {
|
|
64
|
+
if (!behavior.description.trim()) {
|
|
65
|
+
errors.push(
|
|
66
|
+
`motion reference behavior "${behavior.id}" must include a non-empty description.`,
|
|
67
|
+
);
|
|
68
|
+
}
|
|
69
|
+
if (!behavior.implementationIntent.trim()) {
|
|
70
|
+
errors.push(
|
|
71
|
+
`motion reference behavior "${behavior.id}" must include a non-empty implementationIntent.`,
|
|
72
|
+
);
|
|
73
|
+
}
|
|
74
|
+
if (!usedBehaviorIds.has(behavior.id)) {
|
|
75
|
+
errors.push(
|
|
76
|
+
`motion reference behavior "${behavior.id}" must be used by a phase or product event.`,
|
|
77
|
+
);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
const timingClaimStudies = new Map<string, Set<string>>();
|
|
81
|
+
for (const timingClaim of behavior.timingClaims) {
|
|
82
|
+
const studyIds = timingClaimStudies.get(timingClaim.claim) ?? new Set();
|
|
83
|
+
if (studyIds.has(timingClaim.studyId)) {
|
|
84
|
+
errors.push(
|
|
85
|
+
`motion reference behavior "${behavior.id}" timing claim "${timingClaim.claim}" for study "${timingClaim.studyId}" is duplicated.`,
|
|
86
|
+
);
|
|
87
|
+
}
|
|
88
|
+
studyIds.add(timingClaim.studyId);
|
|
89
|
+
timingClaimStudies.set(timingClaim.claim, studyIds);
|
|
90
|
+
|
|
91
|
+
const studies = studiesById.get(timingClaim.studyId) ?? [];
|
|
92
|
+
if (studies.length === 0) {
|
|
93
|
+
errors.push(
|
|
94
|
+
`motion reference behavior "${behavior.id}" timing claim "${timingClaim.claim}" names missing study "${timingClaim.studyId}".`,
|
|
95
|
+
);
|
|
96
|
+
} else if (studies.length > 1) {
|
|
97
|
+
errors.push(
|
|
98
|
+
`motion reference behavior "${behavior.id}" timing claim "${timingClaim.claim}" studyId "${timingClaim.studyId}" resolves to ${studies.length} studies; expected exactly one.`,
|
|
99
|
+
);
|
|
100
|
+
} else {
|
|
101
|
+
const [study] = studies;
|
|
102
|
+
if (study.evidence.timingMode !== "seconds") {
|
|
103
|
+
errors.push(
|
|
104
|
+
`motion reference behavior "${behavior.id}" timing claim "${timingClaim.claim}" must name a seconds-timed study; "${timingClaim.studyId}" is ordinal.`,
|
|
105
|
+
);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
const acceptanceRows = acceptanceRowsById.get(behavior.acceptanceId) ?? [];
|
|
111
|
+
if (acceptanceRows.length === 0) {
|
|
112
|
+
errors.push(
|
|
113
|
+
`motion reference behavior "${behavior.id}" points to missing acceptanceId "${behavior.acceptanceId}".`,
|
|
114
|
+
);
|
|
115
|
+
continue;
|
|
116
|
+
}
|
|
117
|
+
for (const acceptance of acceptanceRows) {
|
|
118
|
+
errors.push(...validateAcceptanceFields({ acceptance, behaviorId: behavior.id }));
|
|
119
|
+
}
|
|
120
|
+
if (acceptanceRows.length > 1) {
|
|
121
|
+
errors.push(
|
|
122
|
+
`motion reference behavior "${behavior.id}" acceptanceId "${behavior.acceptanceId}" resolves to ${acceptanceRows.length} acceptance rows; expected exactly one.`,
|
|
123
|
+
);
|
|
124
|
+
continue;
|
|
125
|
+
}
|
|
126
|
+
const [acceptance] = acceptanceRows;
|
|
127
|
+
|
|
128
|
+
const matchingReverseCoverage = (
|
|
129
|
+
acceptance.motionReferenceCoverage ?? []
|
|
130
|
+
).filter(
|
|
131
|
+
(coverage) =>
|
|
132
|
+
coverage.referenceId === input.referenceId &&
|
|
133
|
+
coverage.behaviorId === behavior.id,
|
|
134
|
+
);
|
|
135
|
+
if (matchingReverseCoverage.length !== 1) {
|
|
136
|
+
errors.push(
|
|
137
|
+
`motion reference behavior "${behavior.id}" acceptance "${acceptance.id}" must own exactly one reverse coverage pair; found ${matchingReverseCoverage.length}.`,
|
|
138
|
+
);
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
let coverageOwnerCount = 0;
|
|
142
|
+
for (const rows of acceptanceRowsById.values()) {
|
|
143
|
+
for (const entry of rows) {
|
|
144
|
+
const ownsCoverage = (entry.motionReferenceCoverage ?? []).some(
|
|
145
|
+
(coverage) =>
|
|
146
|
+
coverage.referenceId === input.referenceId &&
|
|
147
|
+
coverage.behaviorId === behavior.id,
|
|
148
|
+
);
|
|
149
|
+
if (ownsCoverage) {
|
|
150
|
+
coverageOwnerCount += 1;
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
if (coverageOwnerCount > 1) {
|
|
155
|
+
errors.push(
|
|
156
|
+
`motion reference behavior "${behavior.id}" coverage pair has ${coverageOwnerCount} acceptance owners; expected exactly one.`,
|
|
157
|
+
);
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
return errors;
|
|
162
|
+
}
|
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
import type { ToolcraftMotionReferenceInput } from "./types";
|
|
2
|
+
|
|
3
|
+
const validEventClassifications = new Set<string>([
|
|
4
|
+
"edit-boundary",
|
|
5
|
+
"encoding-artifact",
|
|
6
|
+
"irrelevant-change",
|
|
7
|
+
"product-behavior",
|
|
8
|
+
]);
|
|
9
|
+
|
|
10
|
+
export function validateStudyCoverage(
|
|
11
|
+
input: ToolcraftMotionReferenceInput,
|
|
12
|
+
): string[] {
|
|
13
|
+
const errors: string[] = [];
|
|
14
|
+
const behaviorsById = new Map<string, typeof input.behaviors>();
|
|
15
|
+
for (const behavior of input.behaviors) {
|
|
16
|
+
behaviorsById.set(behavior.id, [
|
|
17
|
+
...(behaviorsById.get(behavior.id) ?? []),
|
|
18
|
+
behavior,
|
|
19
|
+
]);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function validateBehaviorId({
|
|
23
|
+
behaviorId,
|
|
24
|
+
owner,
|
|
25
|
+
}: {
|
|
26
|
+
behaviorId: string;
|
|
27
|
+
owner: string;
|
|
28
|
+
}): void {
|
|
29
|
+
const behaviors = behaviorsById.get(behaviorId) ?? [];
|
|
30
|
+
if (behaviors.length === 0) {
|
|
31
|
+
errors.push(`${owner} cites unknown behaviorId "${behaviorId}".`);
|
|
32
|
+
} else if (behaviors.length > 1) {
|
|
33
|
+
errors.push(
|
|
34
|
+
`${owner} cites ambiguous behaviorId "${behaviorId}" with ${behaviors.length} definitions.`,
|
|
35
|
+
);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
for (const study of input.studies) {
|
|
40
|
+
const reviewedFrameIndex = new Map(
|
|
41
|
+
study.evidence.reviewedFrames.map((frame, index) => [frame.frameId, index]),
|
|
42
|
+
);
|
|
43
|
+
const overviewFrameIndex = new Map<string, number>();
|
|
44
|
+
let previousReviewedFrameIndex = -1;
|
|
45
|
+
|
|
46
|
+
for (const [overviewIndex, frameId] of study.evidence.overviewFrameIds.entries()) {
|
|
47
|
+
const reviewedIndex = reviewedFrameIndex.get(frameId);
|
|
48
|
+
|
|
49
|
+
if (reviewedIndex === undefined) {
|
|
50
|
+
errors.push(
|
|
51
|
+
`motion reference study "${study.studyId}" overview frameId "${frameId}" must resolve to reviewedFrames.`,
|
|
52
|
+
);
|
|
53
|
+
continue;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
if (reviewedIndex <= previousReviewedFrameIndex) {
|
|
57
|
+
errors.push(
|
|
58
|
+
`motion reference study "${study.studyId}" overviewFrameIds must follow reviewedFrames order; "${frameId}" resolves to index ${reviewedIndex} after ${previousReviewedFrameIndex}.`,
|
|
59
|
+
);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
previousReviewedFrameIndex = reviewedIndex;
|
|
63
|
+
overviewFrameIndex.set(frameId, overviewIndex);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
if (study.evidence.overviewFrameIds.length > 0 && study.phases.length === 0) {
|
|
67
|
+
errors.push(
|
|
68
|
+
`motion reference study "${study.studyId}" phases must cover the complete overview range.`,
|
|
69
|
+
);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
let previousPhaseEndIndex: number | undefined;
|
|
73
|
+
for (const [phaseIndex, phase] of study.phases.entries()) {
|
|
74
|
+
const fromIndex = overviewFrameIndex.get(phase.fromFrameId);
|
|
75
|
+
const toIndex = overviewFrameIndex.get(phase.toFrameId);
|
|
76
|
+
|
|
77
|
+
if (fromIndex === undefined || toIndex === undefined) {
|
|
78
|
+
errors.push(
|
|
79
|
+
`motion reference study "${study.studyId}" phase "${phase.id}" frame range must resolve to overviewFrameIds.`,
|
|
80
|
+
);
|
|
81
|
+
continue;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
if (fromIndex > toIndex) {
|
|
85
|
+
errors.push(
|
|
86
|
+
`motion reference study "${study.studyId}" phase "${phase.id}" must not end before it starts.`,
|
|
87
|
+
);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
if (phaseIndex === 0 && fromIndex !== 0) {
|
|
91
|
+
errors.push(
|
|
92
|
+
`motion reference study "${study.studyId}" phases must start at overview index 0; "${phase.id}" starts at ${fromIndex}.`,
|
|
93
|
+
);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
if (
|
|
97
|
+
previousPhaseEndIndex !== undefined &&
|
|
98
|
+
fromIndex !== previousPhaseEndIndex + 1
|
|
99
|
+
) {
|
|
100
|
+
errors.push(
|
|
101
|
+
`motion reference study "${study.studyId}" phases must be contiguous; "${phase.id}" starts at overview index ${fromIndex} after index ${previousPhaseEndIndex}.`,
|
|
102
|
+
);
|
|
103
|
+
}
|
|
104
|
+
previousPhaseEndIndex = toIndex;
|
|
105
|
+
|
|
106
|
+
if (!phase.visualState.trim()) {
|
|
107
|
+
errors.push(
|
|
108
|
+
`motion reference study "${study.studyId}" phase "${phase.id}" must describe its visualState.`,
|
|
109
|
+
);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
for (const behaviorId of phase.behaviorIds) {
|
|
113
|
+
validateBehaviorId({
|
|
114
|
+
behaviorId,
|
|
115
|
+
owner: `motion reference study "${study.studyId}" phase "${phase.id}"`,
|
|
116
|
+
});
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
const lastOverviewIndex = study.evidence.overviewFrameIds.length - 1;
|
|
121
|
+
if (
|
|
122
|
+
study.phases.length > 0 &&
|
|
123
|
+
previousPhaseEndIndex !== undefined &&
|
|
124
|
+
previousPhaseEndIndex !== lastOverviewIndex
|
|
125
|
+
) {
|
|
126
|
+
errors.push(
|
|
127
|
+
`motion reference study "${study.studyId}" phases must end at overview index ${lastOverviewIndex}; received ${previousPhaseEndIndex}.`,
|
|
128
|
+
);
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
const evidenceEventsById = new Map(
|
|
132
|
+
study.evidence.detectedEvents.map((event) => [event.eventId, event]),
|
|
133
|
+
);
|
|
134
|
+
const authoredEventsByEvidenceId = new Map<string, typeof study.events>();
|
|
135
|
+
|
|
136
|
+
for (const event of study.events) {
|
|
137
|
+
const existing = authoredEventsByEvidenceId.get(event.evidenceEventId) ?? [];
|
|
138
|
+
authoredEventsByEvidenceId.set(event.evidenceEventId, [...existing, event]);
|
|
139
|
+
|
|
140
|
+
if (!evidenceEventsById.has(event.evidenceEventId)) {
|
|
141
|
+
errors.push(
|
|
142
|
+
`motion reference study "${study.studyId}" event "${event.id}" must cite a detected evidenceEventId.`,
|
|
143
|
+
);
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
if (!validEventClassifications.has(event.classification)) {
|
|
147
|
+
errors.push(
|
|
148
|
+
`motion reference study "${study.studyId}" event "${event.id}" has unsupported classification "${String(event.classification)}".`,
|
|
149
|
+
);
|
|
150
|
+
continue;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
if (event.classification === "product-behavior") {
|
|
154
|
+
if (event.behaviorIds.length === 0) {
|
|
155
|
+
errors.push(
|
|
156
|
+
`motion reference study "${study.studyId}" product event "${event.id}" must cite at least one behaviorId.`,
|
|
157
|
+
);
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
for (const behaviorId of event.behaviorIds) {
|
|
161
|
+
validateBehaviorId({
|
|
162
|
+
behaviorId,
|
|
163
|
+
owner: `motion reference study "${study.studyId}" event "${event.id}"`,
|
|
164
|
+
});
|
|
165
|
+
}
|
|
166
|
+
} else {
|
|
167
|
+
if (event.behaviorIds.length !== 0) {
|
|
168
|
+
errors.push(
|
|
169
|
+
`motion reference study "${study.studyId}" event "${event.id}" classification "${event.classification}" must not cite behaviorIds.`,
|
|
170
|
+
);
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
if (!event.reason.trim()) {
|
|
174
|
+
errors.push(
|
|
175
|
+
`motion reference study "${study.studyId}" event "${event.id}" classification "${event.classification}" requires a non-empty reason.`,
|
|
176
|
+
);
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
for (const evidenceEvent of study.evidence.detectedEvents) {
|
|
182
|
+
const matchingAuthoredEvents =
|
|
183
|
+
authoredEventsByEvidenceId.get(evidenceEvent.eventId) ?? [];
|
|
184
|
+
if (matchingAuthoredEvents.length !== 1) {
|
|
185
|
+
errors.push(
|
|
186
|
+
`motion reference study "${study.studyId}" evidence event "${evidenceEvent.eventId}" must have exactly one authored event; found ${matchingAuthoredEvents.length}.`,
|
|
187
|
+
);
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
for (const frameId of [
|
|
191
|
+
evidenceEvent.beforeFrameId,
|
|
192
|
+
evidenceEvent.peakFrameId,
|
|
193
|
+
evidenceEvent.afterFrameId,
|
|
194
|
+
...evidenceEvent.windowFrameIds,
|
|
195
|
+
]) {
|
|
196
|
+
if (!reviewedFrameIndex.has(frameId)) {
|
|
197
|
+
errors.push(
|
|
198
|
+
`motion reference study "${study.studyId}" evidence event "${evidenceEvent.eventId}" frameId "${frameId}" must resolve to reviewedFrames.`,
|
|
199
|
+
);
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
return errors;
|
|
206
|
+
}
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
import type { ToolcraftMotionReferenceInput } from "./types";
|
|
2
|
+
|
|
3
|
+
export function getMotionReferenceSourceSha256(
|
|
4
|
+
input: ToolcraftMotionReferenceInput,
|
|
5
|
+
): string | undefined {
|
|
6
|
+
const sourceSha256Values = new Set(
|
|
7
|
+
input.studies.map((study) => study.evidence.sourceSha256),
|
|
8
|
+
);
|
|
9
|
+
|
|
10
|
+
return sourceSha256Values.size === 1
|
|
11
|
+
? sourceSha256Values.values().next().value
|
|
12
|
+
: undefined;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export function validateStudyIdentity(
|
|
16
|
+
input: ToolcraftMotionReferenceInput,
|
|
17
|
+
): string[] {
|
|
18
|
+
const errors: string[] = [];
|
|
19
|
+
const studyIds = new Set<string>();
|
|
20
|
+
const evidencePaths = new Set<string>();
|
|
21
|
+
const sourceSha256Values = new Set<string>();
|
|
22
|
+
const behaviorIds = new Set<string>();
|
|
23
|
+
|
|
24
|
+
if (input.studies.length === 0) {
|
|
25
|
+
errors.push(
|
|
26
|
+
`motion reference "${input.referenceId}" must declare at least one study.`,
|
|
27
|
+
);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
for (const behavior of input.behaviors) {
|
|
31
|
+
if (!behavior.id.trim()) {
|
|
32
|
+
errors.push(
|
|
33
|
+
`motion reference "${input.referenceId}" behaviors must include non-empty ids.`,
|
|
34
|
+
);
|
|
35
|
+
} else if (behaviorIds.has(behavior.id)) {
|
|
36
|
+
errors.push(
|
|
37
|
+
`motion reference "${input.referenceId}" behavior id "${behavior.id}" is duplicated.`,
|
|
38
|
+
);
|
|
39
|
+
}
|
|
40
|
+
behaviorIds.add(behavior.id);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
for (const study of input.studies) {
|
|
44
|
+
const studyLabel = study.studyId.trim() || "<missing-study-id>";
|
|
45
|
+
|
|
46
|
+
if (!study.studyId.trim()) {
|
|
47
|
+
errors.push(
|
|
48
|
+
`motion reference "${input.referenceId}" studies must include non-empty studyId values.`,
|
|
49
|
+
);
|
|
50
|
+
} else if (studyIds.has(study.studyId)) {
|
|
51
|
+
errors.push(
|
|
52
|
+
`motion reference "${input.referenceId}" studyId "${study.studyId}" is duplicated.`,
|
|
53
|
+
);
|
|
54
|
+
}
|
|
55
|
+
studyIds.add(study.studyId);
|
|
56
|
+
|
|
57
|
+
if (evidencePaths.has(study.evidencePath)) {
|
|
58
|
+
errors.push(
|
|
59
|
+
`motion reference "${input.referenceId}" evidencePath "${study.evidencePath}" is duplicated.`,
|
|
60
|
+
);
|
|
61
|
+
}
|
|
62
|
+
evidencePaths.add(study.evidencePath);
|
|
63
|
+
|
|
64
|
+
if (study.evidence.studyId !== study.studyId) {
|
|
65
|
+
errors.push(
|
|
66
|
+
`motion reference study "${studyLabel}" must match evidence.studyId "${study.evidence.studyId}".`,
|
|
67
|
+
);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
sourceSha256Values.add(study.evidence.sourceSha256);
|
|
71
|
+
|
|
72
|
+
const frameIds = new Set<string>();
|
|
73
|
+
for (const frame of study.evidence.reviewedFrames) {
|
|
74
|
+
if (frameIds.has(frame.frameId)) {
|
|
75
|
+
errors.push(
|
|
76
|
+
`motion reference study "${studyLabel}" reviewed frameId "${frame.frameId}" is duplicated.`,
|
|
77
|
+
);
|
|
78
|
+
}
|
|
79
|
+
frameIds.add(frame.frameId);
|
|
80
|
+
|
|
81
|
+
if (frame.sourceSha256 !== study.evidence.sourceSha256) {
|
|
82
|
+
errors.push(
|
|
83
|
+
`motion reference study "${studyLabel}" frame "${frame.frameId}" sourceSha256 must match its evidence sourceSha256.`,
|
|
84
|
+
);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
const evidenceEventIds = new Set<string>();
|
|
89
|
+
for (const event of study.evidence.detectedEvents) {
|
|
90
|
+
if (evidenceEventIds.has(event.eventId)) {
|
|
91
|
+
errors.push(
|
|
92
|
+
`motion reference study "${studyLabel}" evidence eventId "${event.eventId}" is duplicated.`,
|
|
93
|
+
);
|
|
94
|
+
}
|
|
95
|
+
evidenceEventIds.add(event.eventId);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
const eventIds = new Set<string>();
|
|
99
|
+
for (const event of study.events) {
|
|
100
|
+
if (eventIds.has(event.id)) {
|
|
101
|
+
errors.push(
|
|
102
|
+
`motion reference study "${studyLabel}" event id "${event.id}" is duplicated.`,
|
|
103
|
+
);
|
|
104
|
+
}
|
|
105
|
+
eventIds.add(event.id);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
const phaseIds = new Set<string>();
|
|
109
|
+
for (const phase of study.phases) {
|
|
110
|
+
if (phaseIds.has(phase.id)) {
|
|
111
|
+
errors.push(
|
|
112
|
+
`motion reference study "${studyLabel}" phase id "${phase.id}" is duplicated.`,
|
|
113
|
+
);
|
|
114
|
+
}
|
|
115
|
+
phaseIds.add(phase.id);
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
if (sourceSha256Values.size > 1) {
|
|
120
|
+
const sortedSourceSha256Values = [...sourceSha256Values].sort((left, right) =>
|
|
121
|
+
left.localeCompare(right, "en"),
|
|
122
|
+
);
|
|
123
|
+
errors.push(
|
|
124
|
+
`motion reference "${input.referenceId}" studies must share one sourceSha256; found ${sortedSourceSha256Values.join(", ")}.`,
|
|
125
|
+
);
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
return errors;
|
|
129
|
+
}
|
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
import type { ToolcraftMotionReferenceInput } from "./types";
|
|
2
|
+
|
|
3
|
+
type TimedStudyEvidence = Extract<
|
|
4
|
+
ToolcraftMotionReferenceInput["studies"][number]["evidence"],
|
|
5
|
+
{ timingMode: "seconds" }
|
|
6
|
+
>;
|
|
7
|
+
type ReviewPartition = NonNullable<TimedStudyEvidence["reviewPartition"]>;
|
|
8
|
+
|
|
9
|
+
type PartitionStudy = {
|
|
10
|
+
evidence: TimedStudyEvidence;
|
|
11
|
+
partition: ReviewPartition;
|
|
12
|
+
studyId: string;
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
function formatNumber(value: number): string {
|
|
16
|
+
return String(value);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function compareNumbers(left: number, right: number): number {
|
|
20
|
+
if (Number.isFinite(left) && Number.isFinite(right)) {
|
|
21
|
+
return left - right;
|
|
22
|
+
}
|
|
23
|
+
return formatNumber(left).localeCompare(formatNumber(right), "en");
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export function validatePartitionCoverage(
|
|
27
|
+
input: ToolcraftMotionReferenceInput,
|
|
28
|
+
): string[] {
|
|
29
|
+
const errors: string[] = [];
|
|
30
|
+
const groups = new Map<string, PartitionStudy[]>();
|
|
31
|
+
|
|
32
|
+
for (const study of input.studies) {
|
|
33
|
+
if (study.evidence.timingMode !== "seconds") {
|
|
34
|
+
continue;
|
|
35
|
+
}
|
|
36
|
+
const partition = study.evidence.reviewPartition;
|
|
37
|
+
if (!partition) {
|
|
38
|
+
continue;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
const group = groups.get(partition.groupId) ?? [];
|
|
42
|
+
group.push({ evidence: study.evidence, partition, studyId: study.studyId });
|
|
43
|
+
groups.set(partition.groupId, group);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
for (const [groupId, group] of groups) {
|
|
47
|
+
const countValues = [...new Set(group.map(({ partition }) => partition.count))];
|
|
48
|
+
const invalidCounts = countValues
|
|
49
|
+
.filter((count) => !Number.isSafeInteger(count) || count <= 0)
|
|
50
|
+
.sort(compareNumbers);
|
|
51
|
+
for (const count of invalidCounts) {
|
|
52
|
+
errors.push(
|
|
53
|
+
`motion reference partition "${groupId}" count must be a finite safe positive integer; received ${formatNumber(count)}.`,
|
|
54
|
+
);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
const countIsShared = countValues.length === 1;
|
|
58
|
+
if (!countIsShared) {
|
|
59
|
+
errors.push(
|
|
60
|
+
`motion reference partition "${groupId}" studies must declare one shared count.`,
|
|
61
|
+
);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
const validCounts = countValues.filter(
|
|
65
|
+
(count) => Number.isSafeInteger(count) && count > 0,
|
|
66
|
+
);
|
|
67
|
+
const oversizedCounts = validCounts
|
|
68
|
+
.filter((count) => count > group.length)
|
|
69
|
+
.sort(compareNumbers);
|
|
70
|
+
for (const count of oversizedCounts) {
|
|
71
|
+
errors.push(
|
|
72
|
+
`motion reference partition "${groupId}" count ${count} cannot exceed group cardinality ${group.length}.`,
|
|
73
|
+
);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
const planValues = new Set(
|
|
77
|
+
group.map(({ partition }) => partition.planSha256),
|
|
78
|
+
);
|
|
79
|
+
const planIsShared = planValues.size === 1;
|
|
80
|
+
if (!planIsShared) {
|
|
81
|
+
errors.push(
|
|
82
|
+
`motion reference partition "${groupId}" studies must declare one shared planSha256.`,
|
|
83
|
+
);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
const scopeStarts = new Set(
|
|
87
|
+
group.map(({ partition }) => partition.scope.startSeconds),
|
|
88
|
+
);
|
|
89
|
+
const scopeEnds = new Set(
|
|
90
|
+
group.map(({ partition }) => partition.scope.endSeconds),
|
|
91
|
+
);
|
|
92
|
+
const scopeIsShared = scopeStarts.size === 1 && scopeEnds.size === 1;
|
|
93
|
+
if (!scopeIsShared) {
|
|
94
|
+
errors.push(
|
|
95
|
+
`motion reference partition "${groupId}" studies must declare one shared scope.`,
|
|
96
|
+
);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
const metadataIsValid =
|
|
100
|
+
invalidCounts.length === 0 &&
|
|
101
|
+
oversizedCounts.length === 0 &&
|
|
102
|
+
countIsShared &&
|
|
103
|
+
planIsShared &&
|
|
104
|
+
scopeIsShared;
|
|
105
|
+
if (!metadataIsValid) {
|
|
106
|
+
continue;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
const [expectedCount] = countValues;
|
|
110
|
+
const [scopeStart] = scopeStarts;
|
|
111
|
+
const [scopeEnd] = scopeEnds;
|
|
112
|
+
if (
|
|
113
|
+
expectedCount === undefined ||
|
|
114
|
+
scopeStart === undefined ||
|
|
115
|
+
scopeEnd === undefined
|
|
116
|
+
) {
|
|
117
|
+
continue;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
const orderedGroup = [...group].sort((left, right) => {
|
|
121
|
+
const indexOrder = compareNumbers(
|
|
122
|
+
left.partition.index,
|
|
123
|
+
right.partition.index,
|
|
124
|
+
);
|
|
125
|
+
return indexOrder || left.studyId.localeCompare(right.studyId, "en");
|
|
126
|
+
});
|
|
127
|
+
const receivedIndices = orderedGroup.map(({ partition }) => partition.index);
|
|
128
|
+
const indicesAreExact =
|
|
129
|
+
orderedGroup.length === expectedCount &&
|
|
130
|
+
receivedIndices.every((index, position) => index === position);
|
|
131
|
+
if (!indicesAreExact) {
|
|
132
|
+
errors.push(
|
|
133
|
+
`motion reference partition "${groupId}" must contain indices 0..${expectedCount - 1} exactly; received ${receivedIndices.join(", ")}.`,
|
|
134
|
+
);
|
|
135
|
+
continue;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
let previousEnd: number | undefined;
|
|
139
|
+
for (const { evidence, partition, studyId } of orderedGroup) {
|
|
140
|
+
const segment = evidence.segment;
|
|
141
|
+
if (!segment) {
|
|
142
|
+
errors.push(
|
|
143
|
+
`motion reference partition "${groupId}" study "${studyId}" must declare an explicit segment.`,
|
|
144
|
+
);
|
|
145
|
+
continue;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
if (segment.endSeconds <= segment.startSeconds) {
|
|
149
|
+
errors.push(
|
|
150
|
+
`motion reference partition "${groupId}" study "${studyId}" segment must have positive duration.`,
|
|
151
|
+
);
|
|
152
|
+
}
|
|
153
|
+
if (partition.index === 0 && segment.startSeconds !== scopeStart) {
|
|
154
|
+
errors.push(
|
|
155
|
+
`motion reference partition "${groupId}" segments must start at scope.startSeconds ${scopeStart}; received ${segment.startSeconds}.`,
|
|
156
|
+
);
|
|
157
|
+
}
|
|
158
|
+
if (previousEnd !== undefined && segment.startSeconds !== previousEnd) {
|
|
159
|
+
errors.push(
|
|
160
|
+
`motion reference partition "${groupId}" segments must be contiguous and non-overlapping; index ${partition.index} starts at ${segment.startSeconds} after ${previousEnd}.`,
|
|
161
|
+
);
|
|
162
|
+
}
|
|
163
|
+
previousEnd = segment.endSeconds;
|
|
164
|
+
|
|
165
|
+
const scopeCoversFullSource =
|
|
166
|
+
scopeStart === 0 && scopeEnd === evidence.durationSeconds;
|
|
167
|
+
if (
|
|
168
|
+
scopeCoversFullSource &&
|
|
169
|
+
partition.index !== 0 &&
|
|
170
|
+
evidence.detectedEvents.some((event) => event.kind === "loop-seam")
|
|
171
|
+
) {
|
|
172
|
+
errors.push(
|
|
173
|
+
`motion reference partition "${groupId}" original loop-seam evidence must appear only in index 0.`,
|
|
174
|
+
);
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
if (previousEnd !== undefined && previousEnd !== scopeEnd) {
|
|
179
|
+
errors.push(
|
|
180
|
+
`motion reference partition "${groupId}" segments must end at scope.endSeconds ${scopeEnd}; received ${previousEnd}.`,
|
|
181
|
+
);
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
return errors;
|
|
186
|
+
}
|