motionloom 2.3.0 → 2.5.0
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/CHANGELOG.md +45 -1
- package/README.md +97 -11
- package/ROADMAP.md +8 -5
- package/SKILL.md +24 -5
- package/agent-card.json +34 -5
- package/agent-surfaces.json +1 -1
- package/artifact-adapter-registry.json +53 -0
- package/bin/motionloom.mjs +43 -6
- package/capability-registry.json +308 -0
- package/docs/CHECKLIST.md +16 -0
- package/docs/STATUS.md +2 -2
- package/docs/apple-distribution.md +24 -0
- package/docs/architecture/apple/cloudkit.md +18 -0
- package/docs/architecture/apple/contracts.md +9 -0
- package/docs/architecture/apple/implementation-status-2026-08-16.md +55 -0
- package/docs/architecture/apple/studio.md +35 -0
- package/docs/audits/data/deep-stress-latest.json +65 -65
- package/docs/releases/2.4.0.md +31 -0
- package/docs/releases/2.5.0.md +31 -0
- package/docs/releases/npm-publish-from-workstation.md +19 -8
- package/docs/research/agent-skill-ecosystem-notes.md +47 -0
- package/docs/research/ai-animation-tools-2026-notes.md +81 -0
- package/docs/research/ai-animation-tools-2026-report.md +168 -0
- package/docs/research/ai-pilot-ingest-notes.md +29 -0
- package/docs/research/ai-repository-benchmark-2026-08-16.md +75 -0
- package/examples/agent-consumer/ai-pilot-scout/CHATGPT-HANDOFF.md +70 -0
- package/examples/agent-consumer/ai-pilot-scout/CODEX-HANDOFF.md +80 -0
- package/examples/agent-consumer/ai-pilot-scout/README.md +39 -0
- package/examples/agent-consumer/ai-pilot-scout/chatgpt-geometry-correction-prompt.md +55 -0
- package/examples/agent-consumer/ai-pilot-scout/chatgpt-pose-generation-prompt.md +78 -0
- package/examples/agent-consumer/ai-pilot-scout/partial-handoff.json +73 -0
- package/examples/agent-consumer/artifact-intake/hero-motion-controls.json +15 -0
- package/examples/agent-consumer/artifact-intake/hero-motion-export.json +14 -0
- package/examples/agent-consumer/artifact-intake/hero-motion-provenance.json +20 -0
- package/examples/agent-consumer/artifact-intake/hero-motion-receipt.json +16 -0
- package/examples/agent-consumer/asset-consistency/assets/forest-back.png +0 -0
- package/examples/agent-consumer/asset-consistency/assets/forest-front.png +0 -0
- package/examples/agent-consumer/asset-consistency/assets/forest-mid.png +0 -0
- package/examples/agent-consumer/asset-consistency/assets/hero-atlas.png +0 -0
- package/examples/agent-consumer/asset-consistency/assets/hero-frame-00.png +0 -0
- package/examples/agent-consumer/asset-consistency/assets/hero-frame-01.png +0 -0
- package/examples/agent-consumer/asset-consistency/assets/hero-frame-02.png +0 -0
- package/examples/agent-consumer/asset-consistency/assets/hero-frame-03.png +0 -0
- package/examples/agent-consumer/asset-consistency/forest-layered-map.json +53 -0
- package/examples/agent-consumer/asset-consistency/hero-atlas-contract.json +24 -0
- package/examples/agent-consumer/asset-consistency/hero-identity.json +51 -0
- package/examples/agent-consumer/asset-consistency/hero-walk-action-set.json +27 -0
- package/examples/agent-consumer/asset-consistency/hero-walk-frame-geometry.json +63 -0
- package/examples/agent-consumer/rig-compatibility/hero-walk-fixture-rig.json +13 -0
- package/examples/agent-consumer/rive-package-gate/README.md +20 -0
- package/examples/agent-consumer/runtime-candidate/hero-walk-candidate.json +18 -0
- package/examples/agent-consumer/runtime-pilot/action-set.json +19 -0
- package/examples/agent-consumer/runtime-pilot/asset-identity.json +44 -0
- package/examples/agent-consumer/runtime-pilot/candidate.json +21 -0
- package/examples/agent-consumer/runtime-pilot/controls.json +35 -0
- package/examples/agent-consumer/runtime-pilot/export.json +17 -0
- package/examples/agent-consumer/runtime-pilot/provenance.json +38 -0
- package/examples/agent-consumer/runtime-pilot/receipt.json +30 -0
- package/package.json +58 -4
- package/references/browser-review-contract.md +7 -1
- package/references/intelligence-core.md +22 -1
- package/rig-adapter-registry.json +39 -0
- package/schemas/action-set.schema.json +42 -0
- package/schemas/artifact-adapter-registry.schema.json +16 -0
- package/schemas/asset-identity.schema.json +117 -0
- package/schemas/asset-provenance.schema.json +1 -1
- package/schemas/atlas-contract.schema.json +48 -0
- package/schemas/control-track.schema.json +18 -0
- package/schemas/export-manifest.schema.json +19 -0
- package/schemas/frame-geometry.schema.json +79 -0
- package/schemas/generation-receipt.schema.json +20 -0
- package/schemas/layered-map.schema.json +75 -0
- package/schemas/rig-adapter-registry.schema.json +14 -0
- package/schemas/rig-compatibility.schema.json +53 -0
- package/schemas/rive-package-manifest.schema.json +43 -0
- package/schemas/runtime-candidate.schema.json +48 -0
- package/schemas/scene-manifest.schema.json +18 -0
- package/scripts/artifact-intake.py +408 -0
- package/scripts/asset-consistency.py +517 -0
- package/scripts/asset-provenance.py +8 -3
- package/scripts/attestation.py +8 -0
- package/scripts/build-ai-pilot.py +504 -0
- package/scripts/evidence-verifier.py +18 -9
- package/scripts/intelligence.py +85 -18
- package/scripts/isolate-alpha-background.py +147 -0
- package/scripts/quality-gate.py +88 -2
- package/scripts/report.py +85 -0
- package/scripts/resolve-task-bundle.py +84 -0
- package/scripts/review-hook.py +26 -4
- package/scripts/rig-compatibility.py +251 -0
- package/scripts/rive-package-gate.py +245 -0
- package/scripts/runtime-candidate.py +301 -0
- package/scripts/setup.mjs +54 -26
- package/src/output/runtime-pilot-framer/asset-provenance.json +38 -0
- package/src/output/runtime-pilot-framer/browser-review.json +20 -0
- package/src/output/runtime-pilot-framer/framer-motion/runtime-telemetry.json +103 -0
- package/src/output/runtime-pilot-framer/manifest.json +31 -0
- package/src/output/runtime-pilot-framer/motion-spec.json +25 -0
- package/src/output/runtime-pilot-framer/runtime-evidence.json +96 -0
- package/src/output/runtime-pilot-framer/runtime-telemetry.json +100 -0
- package/src/output/runtime-pilot-framer/scene.jsx +52 -0
- package/src/output/runtime-pilot-framer/snapshot/.render-meta.json +9 -0
- package/src/output/runtime-pilot-framer/snapshot/frame-00.png +0 -0
- package/src/output/runtime-pilot-framer/snapshot/frame-100.png +0 -0
- package/src/output/runtime-pilot-framer/snapshot/frame-50.png +0 -0
- package/tests/scripts/run_tests.py +151 -0
- package/tests/scripts/test_ai_pilot_builder.py +128 -0
- package/tests/scripts/test_alpha_isolation.py +60 -0
- package/tests/scripts/test_artifact_intake.py +146 -0
- package/tests/scripts/test_asset_consistency.py +199 -0
- package/tests/scripts/test_rig_compatibility.py +105 -0
- package/tests/scripts/test_rive_package_gate.py +101 -0
- package/tests/scripts/test_runtime_candidate.py +95 -0
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": "1.0",
|
|
3
|
+
"scene": "runtime-pilot-framer",
|
|
4
|
+
"task_id": "runtime-pilot-001",
|
|
5
|
+
"category": "runtime-pilot",
|
|
6
|
+
"framework": "framer-motion",
|
|
7
|
+
"duration_s": 1.2,
|
|
8
|
+
"fps": 60,
|
|
9
|
+
"total_frames": 72,
|
|
10
|
+
"loop": true,
|
|
11
|
+
"easing": "piecewise scrubbed transform",
|
|
12
|
+
"camera": null,
|
|
13
|
+
"theme": {
|
|
14
|
+
"primary": "#2563EB",
|
|
15
|
+
"accent": "#F59E0B"
|
|
16
|
+
},
|
|
17
|
+
"performance": {
|
|
18
|
+
"max_file_kb": 300,
|
|
19
|
+
"max_layers": 8
|
|
20
|
+
},
|
|
21
|
+
"source_binding": "hash-bound code-authored scene.jsx; no generated visual asset is asserted",
|
|
22
|
+
"accessibility": {
|
|
23
|
+
"reduced_motion": "manual review uses discrete checkpoints"
|
|
24
|
+
}
|
|
25
|
+
}
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
{
|
|
2
|
+
"schema_version": "1.1",
|
|
3
|
+
"run_id": "1786808870894-145958",
|
|
4
|
+
"generated_at": "2026-08-15T15:47:52.510Z",
|
|
5
|
+
"mode": "runtime",
|
|
6
|
+
"harness": "tests/runtime-harness",
|
|
7
|
+
"status": "pass",
|
|
8
|
+
"frameworks": [
|
|
9
|
+
{
|
|
10
|
+
"run_id": "1786808870894-145958",
|
|
11
|
+
"framework": "framer-motion",
|
|
12
|
+
"url": "http://127.0.0.1:4179/tests/runtime-harness/index.html?framework=framer-motion",
|
|
13
|
+
"status": "pass",
|
|
14
|
+
"ready": true,
|
|
15
|
+
"runtime": "framer-motion@13.1.0",
|
|
16
|
+
"frames": [
|
|
17
|
+
{
|
|
18
|
+
"percent": 0,
|
|
19
|
+
"file": "artifacts/runtime-adapters/framer-motion/frame-00.png",
|
|
20
|
+
"state": {
|
|
21
|
+
"progress": 0,
|
|
22
|
+
"transform": "matrix(0.82, 0, 0, 0.82, 0, 0)",
|
|
23
|
+
"opacity": "0.28",
|
|
24
|
+
"contract": {
|
|
25
|
+
"framework": "framer-motion",
|
|
26
|
+
"checkpoints": [
|
|
27
|
+
0,
|
|
28
|
+
50,
|
|
29
|
+
100
|
|
30
|
+
],
|
|
31
|
+
"durationSeconds": 1.2,
|
|
32
|
+
"loop": true,
|
|
33
|
+
"sourceAuthority": "code_authored"
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
},
|
|
37
|
+
{
|
|
38
|
+
"percent": 50,
|
|
39
|
+
"file": "artifacts/runtime-adapters/framer-motion/frame-50.png",
|
|
40
|
+
"state": {
|
|
41
|
+
"progress": 0.5,
|
|
42
|
+
"transform": "matrix(1.02851, -0.256437, 0.256437, 1.02851, 176, -48)",
|
|
43
|
+
"opacity": "1",
|
|
44
|
+
"contract": {
|
|
45
|
+
"framework": "framer-motion",
|
|
46
|
+
"checkpoints": [
|
|
47
|
+
0,
|
|
48
|
+
50,
|
|
49
|
+
100
|
|
50
|
+
],
|
|
51
|
+
"durationSeconds": 1.2,
|
|
52
|
+
"loop": true,
|
|
53
|
+
"sourceAuthority": "code_authored"
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
},
|
|
57
|
+
{
|
|
58
|
+
"percent": 100,
|
|
59
|
+
"file": "artifacts/runtime-adapters/framer-motion/frame-100.png",
|
|
60
|
+
"state": {
|
|
61
|
+
"progress": 1,
|
|
62
|
+
"transform": "matrix(0.882948, 0.469472, -0.469472, 0.882948, 240, -36)",
|
|
63
|
+
"opacity": "1",
|
|
64
|
+
"contract": {
|
|
65
|
+
"framework": "framer-motion",
|
|
66
|
+
"checkpoints": [
|
|
67
|
+
0,
|
|
68
|
+
50,
|
|
69
|
+
100
|
|
70
|
+
],
|
|
71
|
+
"durationSeconds": 1.2,
|
|
72
|
+
"loop": true,
|
|
73
|
+
"sourceAuthority": "code_authored"
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
],
|
|
78
|
+
"console_errors": [],
|
|
79
|
+
"telemetry": {
|
|
80
|
+
"file": "framer-motion/runtime-telemetry.json",
|
|
81
|
+
"sha256": "1c11a49eac8fb6c87977ccb84cccc59162fa015c73424a07690785bac094e773",
|
|
82
|
+
"metrics": {
|
|
83
|
+
"sample_count": 3,
|
|
84
|
+
"raf_interval_count": 9,
|
|
85
|
+
"max_raf_interval_ms": 16.799999999999955,
|
|
86
|
+
"p95_raf_interval_ms": 16.799999999999955
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
],
|
|
91
|
+
"scene": "runtime-pilot-framer",
|
|
92
|
+
"task_id": "runtime-pilot-001",
|
|
93
|
+
"source_sha256": "f4c0e56de842cae6b234566b556e4f07154f8109dde382a474218a9de8b45a8b",
|
|
94
|
+
"manifest_sha256": "2b53bf3b6fc8dfe16a271ecd52ec51c2acb5b612c0b77d9fd4e485696989011d",
|
|
95
|
+
"motion_ir_sha256": "1d339ca2ec9c510e54e53c37ef32c2ddf1485dbaec97c5e9aa178a8f6c9c3287"
|
|
96
|
+
}
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
{
|
|
2
|
+
"schema_version": "1.0",
|
|
3
|
+
"run_id": "1786808553918-145614",
|
|
4
|
+
"generated_at": "2026-08-15T15:42:35.907Z",
|
|
5
|
+
"mode": "runtime-telemetry",
|
|
6
|
+
"task_id": "runtime-pilot-001",
|
|
7
|
+
"scene": "runtime-pilot-framer",
|
|
8
|
+
"framework": "framer-motion",
|
|
9
|
+
"runtime": "framer-motion@13.1.0",
|
|
10
|
+
"samples": [
|
|
11
|
+
{
|
|
12
|
+
"sequence": 0,
|
|
13
|
+
"percent": 0,
|
|
14
|
+
"captured_at_ms": 760.7000000476837,
|
|
15
|
+
"raf_intervals_ms": [
|
|
16
|
+
16.700000000000045,
|
|
17
|
+
16.600000000000023,
|
|
18
|
+
16.699999999999932
|
|
19
|
+
],
|
|
20
|
+
"state_sha256": "0769bb6867251ff8d951b01e7d817ab4644d1f7d4a38b0e91ad54ec5b0f81f6b",
|
|
21
|
+
"state": {
|
|
22
|
+
"progress": 0,
|
|
23
|
+
"transform": "matrix(0.82, 0, 0, 0.82, 0, 0)",
|
|
24
|
+
"opacity": "0.28",
|
|
25
|
+
"contract": {
|
|
26
|
+
"framework": "framer-motion",
|
|
27
|
+
"checkpoints": [
|
|
28
|
+
0,
|
|
29
|
+
50,
|
|
30
|
+
100
|
|
31
|
+
],
|
|
32
|
+
"durationSeconds": 1.2,
|
|
33
|
+
"loop": true,
|
|
34
|
+
"sourceAuthority": "code_authored"
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
},
|
|
38
|
+
{
|
|
39
|
+
"sequence": 1,
|
|
40
|
+
"percent": 50,
|
|
41
|
+
"captured_at_ms": 844.1000000238419,
|
|
42
|
+
"raf_intervals_ms": [
|
|
43
|
+
16.600000000000023,
|
|
44
|
+
16.799999999999955,
|
|
45
|
+
16.600000000000023
|
|
46
|
+
],
|
|
47
|
+
"state_sha256": "015ad2af517be79a3cf102b48b5f61282acd1843c7b5efe20472c2752d79c890",
|
|
48
|
+
"state": {
|
|
49
|
+
"progress": 0.5,
|
|
50
|
+
"transform": "matrix(1.02851, -0.256437, 0.256437, 1.02851, 176, -48)",
|
|
51
|
+
"opacity": "1",
|
|
52
|
+
"contract": {
|
|
53
|
+
"framework": "framer-motion",
|
|
54
|
+
"checkpoints": [
|
|
55
|
+
0,
|
|
56
|
+
50,
|
|
57
|
+
100
|
|
58
|
+
],
|
|
59
|
+
"durationSeconds": 1.2,
|
|
60
|
+
"loop": true,
|
|
61
|
+
"sourceAuthority": "code_authored"
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
},
|
|
65
|
+
{
|
|
66
|
+
"sequence": 2,
|
|
67
|
+
"percent": 100,
|
|
68
|
+
"captured_at_ms": 927.7000000476837,
|
|
69
|
+
"raf_intervals_ms": [
|
|
70
|
+
16.700000000000045,
|
|
71
|
+
16.699999999999932,
|
|
72
|
+
16.700000000000045
|
|
73
|
+
],
|
|
74
|
+
"state_sha256": "275a832573d3c660f6d820256f95412e253f0a29c02e8f535dfdec2c47e56de2",
|
|
75
|
+
"state": {
|
|
76
|
+
"progress": 1,
|
|
77
|
+
"transform": "matrix(0.882948, 0.469472, -0.469472, 0.882948, 240, -36)",
|
|
78
|
+
"opacity": "1",
|
|
79
|
+
"contract": {
|
|
80
|
+
"framework": "framer-motion",
|
|
81
|
+
"checkpoints": [
|
|
82
|
+
0,
|
|
83
|
+
50,
|
|
84
|
+
100
|
|
85
|
+
],
|
|
86
|
+
"durationSeconds": 1.2,
|
|
87
|
+
"loop": true,
|
|
88
|
+
"sourceAuthority": "code_authored"
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
],
|
|
93
|
+
"metrics": {
|
|
94
|
+
"sample_count": 3,
|
|
95
|
+
"raf_interval_count": 9,
|
|
96
|
+
"max_raf_interval_ms": 16.799999999999955,
|
|
97
|
+
"p95_raf_interval_ms": 16.799999999999955
|
|
98
|
+
},
|
|
99
|
+
"status": "pass"
|
|
100
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import React, { useEffect, useRef } from "react";
|
|
2
|
+
import { motion, motionValue, useTransform } from "framer-motion";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* MotionLoom runtime-first pilot source.
|
|
6
|
+
* Design: code-authored, deterministic scrub only; review remains human-gated.
|
|
7
|
+
*/
|
|
8
|
+
export const RUNTIME_PILOT_CONTRACT = Object.freeze({
|
|
9
|
+
framework: "framer-motion",
|
|
10
|
+
checkpoints: [0, 50, 100],
|
|
11
|
+
durationSeconds: 1.2,
|
|
12
|
+
loop: true,
|
|
13
|
+
sourceAuthority: "code_authored",
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
export function FramerRuntimePilot({ expose }) {
|
|
17
|
+
const progress = useRef(motionValue(0)).current;
|
|
18
|
+
const x = useTransform(progress, [0, 0.5, 1], [0, 176, 240]);
|
|
19
|
+
const y = useTransform(progress, [0, 0.5, 1], [0, -48, -36]);
|
|
20
|
+
const rotate = useTransform(progress, [0, 0.5, 1], [0, -14, 28]);
|
|
21
|
+
const opacity = useTransform(progress, [0, 0.15, 1], [0.28, 1, 1]);
|
|
22
|
+
const scale = useTransform(progress, [0, 0.5, 1], [0.82, 1.06, 1]);
|
|
23
|
+
|
|
24
|
+
useEffect(() => {
|
|
25
|
+
const box = document.querySelector(".runtime-pilot-box");
|
|
26
|
+
const adapter = {
|
|
27
|
+
framework: RUNTIME_PILOT_CONTRACT.framework,
|
|
28
|
+
runtime: "framer-motion@13.1.0",
|
|
29
|
+
status: "ready",
|
|
30
|
+
ready: true,
|
|
31
|
+
setProgress(value) {
|
|
32
|
+
progress.set(Math.max(0, Math.min(1, Number(value))));
|
|
33
|
+
},
|
|
34
|
+
getState() {
|
|
35
|
+
return {
|
|
36
|
+
progress: progress.get(),
|
|
37
|
+
transform: box ? getComputedStyle(box).transform : "",
|
|
38
|
+
opacity: box ? getComputedStyle(box).opacity : "",
|
|
39
|
+
contract: RUNTIME_PILOT_CONTRACT,
|
|
40
|
+
};
|
|
41
|
+
},
|
|
42
|
+
};
|
|
43
|
+
expose(adapter);
|
|
44
|
+
adapter.setProgress(0);
|
|
45
|
+
}, [expose, progress]);
|
|
46
|
+
|
|
47
|
+
return (
|
|
48
|
+
<div className="stage" aria-label="MotionLoom Framer Motion runtime pilot">
|
|
49
|
+
<motion.div className="motion-box runtime-pilot-box" style={{ x, y, rotate, opacity, scale }} />
|
|
50
|
+
</div>
|
|
51
|
+
);
|
|
52
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
{
|
|
2
|
+
"mode": "runtime",
|
|
3
|
+
"scene": "runtime-pilot-framer",
|
|
4
|
+
"framework": "framer-motion",
|
|
5
|
+
"run_id": "1786808553918-145614",
|
|
6
|
+
"source_sha256": "f4c0e56de842cae6b234566b556e4f07154f8109dde382a474218a9de8b45a8b",
|
|
7
|
+
"runtime_evidence": "../runtime-evidence.json",
|
|
8
|
+
"checkpoints": [0, 50, 100]
|
|
9
|
+
}
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -359,6 +359,42 @@ def test_deep_audit_contracts():
|
|
|
359
359
|
], capture_output=True, text=True)
|
|
360
360
|
check("review hook rejects expired candidate", expired.returncode != 0 and "expired" in expired.stdout)
|
|
361
361
|
|
|
362
|
+
|
|
363
|
+
def test_task_bundle_resolver_contract():
|
|
364
|
+
with tempfile.TemporaryDirectory() as td:
|
|
365
|
+
root = Path(td)
|
|
366
|
+
opaque_task = root / "artifacts" / "opaque-task-id"
|
|
367
|
+
shutil.copytree(ROOT / "artifacts/runtime-pilot-001", opaque_task)
|
|
368
|
+
resolver = ROOT / "scripts/resolve-task-bundle.py"
|
|
369
|
+
resolved = subprocess.run([
|
|
370
|
+
sys.executable, str(resolver), "--root", str(root), "--scene", "runtime-pilot-framer",
|
|
371
|
+
], capture_output=True, text=True)
|
|
372
|
+
check(
|
|
373
|
+
"task bundle resolver binds scene to opaque task directory",
|
|
374
|
+
resolved.returncode == 0 and resolved.stdout.strip() == "artifacts/opaque-task-id",
|
|
375
|
+
resolved.stdout.strip() or resolved.stderr.strip(),
|
|
376
|
+
)
|
|
377
|
+
|
|
378
|
+
duplicate = root / "artifacts" / "second-task-id"
|
|
379
|
+
shutil.copytree(opaque_task, duplicate)
|
|
380
|
+
ambiguous = subprocess.run([
|
|
381
|
+
sys.executable, str(resolver), "--root", str(root), "--scene", "runtime-pilot-framer",
|
|
382
|
+
], capture_output=True, text=True)
|
|
383
|
+
check(
|
|
384
|
+
"task bundle resolver rejects ambiguous scene bindings",
|
|
385
|
+
ambiguous.returncode != 0 and "ambiguous task bundles" in ambiguous.stderr,
|
|
386
|
+
ambiguous.stdout.strip() or ambiguous.stderr.strip(),
|
|
387
|
+
)
|
|
388
|
+
|
|
389
|
+
unsafe = subprocess.run([
|
|
390
|
+
sys.executable, str(resolver), "--root", str(root), "--scene", "../runtime-pilot-framer",
|
|
391
|
+
], capture_output=True, text=True)
|
|
392
|
+
check(
|
|
393
|
+
"task bundle resolver rejects unsafe scene identifiers",
|
|
394
|
+
unsafe.returncode != 0 and "unsafe scene" in unsafe.stderr,
|
|
395
|
+
unsafe.stdout.strip() or unsafe.stderr.strip(),
|
|
396
|
+
)
|
|
397
|
+
|
|
362
398
|
unsafe_runtime = subprocess.run(
|
|
363
399
|
["node", str(ROOT / "scripts/runtime-adapters.mjs")],
|
|
364
400
|
env={**os.environ, "RUNTIME_FRAMEWORKS": "../../escape"},
|
|
@@ -518,6 +554,35 @@ def test_intelligence_core_contracts():
|
|
|
518
554
|
"--registry", str(registry), "--capability", "runtime.rive",
|
|
519
555
|
], capture_output=True, text=True)
|
|
520
556
|
check("intelligence selects verified runtime", selected.returncode == 0 and '"status": "verified"' in selected.stdout)
|
|
557
|
+
|
|
558
|
+
capability_card = subprocess.run([
|
|
559
|
+
sys.executable, str(intelligence), "capabilities", "card",
|
|
560
|
+
"--registry", str(registry), "--format", "json",
|
|
561
|
+
], capture_output=True, text=True)
|
|
562
|
+
capability_card_data = json.loads(capability_card.stdout) if capability_card.returncode == 0 else {}
|
|
563
|
+
rive_card = next((entry for entry in capability_card_data.get("capabilities", []) if entry.get("id") == "runtime.rive"), {})
|
|
564
|
+
check(
|
|
565
|
+
"intelligence exports read-only capability card",
|
|
566
|
+
capability_card.returncode == 0
|
|
567
|
+
and capability_card_data.get("kind") == "motionloom-capability-card"
|
|
568
|
+
and capability_card_data.get("selection", {}).get("required") is True
|
|
569
|
+
and capability_card_data.get("review", {}).get("production_approval") == "not_derived"
|
|
570
|
+
and rive_card.get("declared_status") == "verified"
|
|
571
|
+
and rive_card.get("last_verified_at"),
|
|
572
|
+
capability_card.stderr.strip(),
|
|
573
|
+
)
|
|
574
|
+
|
|
575
|
+
capability_alias = subprocess.run([
|
|
576
|
+
"node", str(ROOT / "bin/motionloom.mjs"), "capability", "card",
|
|
577
|
+
"--registry", str(registry), "--format", "json",
|
|
578
|
+
], capture_output=True, text=True)
|
|
579
|
+
capability_alias_data = json.loads(capability_alias.stdout) if capability_alias.returncode == 0 else {}
|
|
580
|
+
check(
|
|
581
|
+
"motionloom capability alias exports card",
|
|
582
|
+
capability_alias.returncode == 0 and capability_alias_data.get("kind") == "motionloom-capability-card",
|
|
583
|
+
capability_alias.stderr.strip(),
|
|
584
|
+
)
|
|
585
|
+
|
|
521
586
|
scaffold = subprocess.run([
|
|
522
587
|
sys.executable, str(intelligence), "capabilities", "select",
|
|
523
588
|
"--registry", str(registry), "--capability", "runtime.spine",
|
|
@@ -546,6 +611,11 @@ def test_intelligence_core_contracts():
|
|
|
546
611
|
"--registry", str(tampered_path), "--capability", "runtime.rive",
|
|
547
612
|
], capture_output=True, text=True)
|
|
548
613
|
check("intelligence blocks tampered capability evidence", tampered_select.returncode != 0)
|
|
614
|
+
tampered_card = subprocess.run([
|
|
615
|
+
sys.executable, str(intelligence), "capabilities", "card",
|
|
616
|
+
"--registry", str(tampered_path), "--format", "json",
|
|
617
|
+
], capture_output=True, text=True)
|
|
618
|
+
check("intelligence blocks capability card with tampered evidence", tampered_card.returncode != 0)
|
|
549
619
|
|
|
550
620
|
replay = subprocess.run([
|
|
551
621
|
sys.executable, str(intelligence), "replay", "capture",
|
|
@@ -916,6 +986,16 @@ def test_quality_workflow_rebuilds_replay_after_generated_artifacts():
|
|
|
916
986
|
setup_tests.returncode == 0 and "setup onboarding tests: PASS" in setup_tests.stdout,
|
|
917
987
|
setup_tests.stdout.strip() or setup_tests.stderr.strip(),
|
|
918
988
|
)
|
|
989
|
+
apple_contract_tests = subprocess.run(
|
|
990
|
+
[sys.executable, str(ROOT / "tests/scripts/test_apple_contracts.py")],
|
|
991
|
+
capture_output=True,
|
|
992
|
+
text=True,
|
|
993
|
+
)
|
|
994
|
+
check(
|
|
995
|
+
"Apple review contracts preserve hash binding and human-governed boundaries",
|
|
996
|
+
apple_contract_tests.returncode == 0 and "apple contract tests: PASS" in apple_contract_tests.stdout,
|
|
997
|
+
apple_contract_tests.stdout.strip() or apple_contract_tests.stderr.strip(),
|
|
998
|
+
)
|
|
919
999
|
visual_tests = subprocess.run(
|
|
920
1000
|
[sys.executable, str(ROOT / "tests/scripts/test_visual_truth.py")],
|
|
921
1001
|
capture_output=True,
|
|
@@ -945,6 +1025,76 @@ def test_quality_workflow_rebuilds_replay_after_generated_artifacts():
|
|
|
945
1025
|
asset_provenance_tests.returncode == 0 and "asset provenance contract tests: PASS" in asset_provenance_tests.stdout,
|
|
946
1026
|
asset_provenance_tests.stdout.strip() or asset_provenance_tests.stderr.strip(),
|
|
947
1027
|
)
|
|
1028
|
+
asset_consistency_tests = subprocess.run(
|
|
1029
|
+
[sys.executable, str(ROOT / "tests/scripts/test_asset_consistency.py")],
|
|
1030
|
+
capture_output=True,
|
|
1031
|
+
text=True,
|
|
1032
|
+
)
|
|
1033
|
+
check(
|
|
1034
|
+
"Asset consistency preserves deterministic geometry, atlas and layered-map boundaries",
|
|
1035
|
+
asset_consistency_tests.returncode == 0 and "asset consistency contract tests: PASS" in asset_consistency_tests.stdout,
|
|
1036
|
+
asset_consistency_tests.stdout.strip() or asset_consistency_tests.stderr.strip(),
|
|
1037
|
+
)
|
|
1038
|
+
artifact_intake_tests = subprocess.run(
|
|
1039
|
+
[sys.executable, str(ROOT / "tests/scripts/test_artifact_intake.py")],
|
|
1040
|
+
capture_output=True,
|
|
1041
|
+
text=True,
|
|
1042
|
+
)
|
|
1043
|
+
check(
|
|
1044
|
+
"Artifact intake preserves provider-neutral controls, hash binding and approval boundaries",
|
|
1045
|
+
artifact_intake_tests.returncode == 0 and "artifact intake contract tests: PASS" in artifact_intake_tests.stdout,
|
|
1046
|
+
artifact_intake_tests.stdout.strip() or artifact_intake_tests.stderr.strip(),
|
|
1047
|
+
)
|
|
1048
|
+
runtime_candidate_tests = subprocess.run(
|
|
1049
|
+
[sys.executable, str(ROOT / "tests/scripts/test_runtime_candidate.py")],
|
|
1050
|
+
capture_output=True,
|
|
1051
|
+
text=True,
|
|
1052
|
+
)
|
|
1053
|
+
check(
|
|
1054
|
+
"Runtime candidate bridge binds intake exports, consistency and human review boundaries",
|
|
1055
|
+
runtime_candidate_tests.returncode == 0 and "runtime candidate contract tests: PASS" in runtime_candidate_tests.stdout,
|
|
1056
|
+
runtime_candidate_tests.stdout.strip() or runtime_candidate_tests.stderr.strip(),
|
|
1057
|
+
)
|
|
1058
|
+
rig_compatibility_tests = subprocess.run(
|
|
1059
|
+
[sys.executable, str(ROOT / "tests/scripts/test_rig_compatibility.py")],
|
|
1060
|
+
capture_output=True,
|
|
1061
|
+
text=True,
|
|
1062
|
+
)
|
|
1063
|
+
check(
|
|
1064
|
+
"Rig compatibility preserves skeleton, socket, event, runtime evidence and review boundaries",
|
|
1065
|
+
rig_compatibility_tests.returncode == 0 and "rig compatibility contract tests: PASS" in rig_compatibility_tests.stdout,
|
|
1066
|
+
rig_compatibility_tests.stdout.strip() or rig_compatibility_tests.stderr.strip(),
|
|
1067
|
+
)
|
|
1068
|
+
rive_package_gate_tests = subprocess.run(
|
|
1069
|
+
[sys.executable, str(ROOT / "tests/scripts/test_rive_package_gate.py")],
|
|
1070
|
+
capture_output=True,
|
|
1071
|
+
text=True,
|
|
1072
|
+
)
|
|
1073
|
+
check(
|
|
1074
|
+
"Rive package gate requires authentic package bytes, runtime proof and human review",
|
|
1075
|
+
rive_package_gate_tests.returncode == 0 and "rive package gate contract tests: PASS" in rive_package_gate_tests.stdout,
|
|
1076
|
+
rive_package_gate_tests.stdout.strip() or rive_package_gate_tests.stderr.strip(),
|
|
1077
|
+
)
|
|
1078
|
+
ai_pilot_builder_tests = subprocess.run(
|
|
1079
|
+
[sys.executable, str(ROOT / "tests/scripts/test_ai_pilot_builder.py")],
|
|
1080
|
+
capture_output=True,
|
|
1081
|
+
text=True,
|
|
1082
|
+
)
|
|
1083
|
+
check(
|
|
1084
|
+
"AI pilot builder requires real alpha, hash-binds artifacts and preserves review-first status",
|
|
1085
|
+
ai_pilot_builder_tests.returncode == 0 and "AI pilot builder tests: PASS" in ai_pilot_builder_tests.stdout,
|
|
1086
|
+
ai_pilot_builder_tests.stdout.strip() or ai_pilot_builder_tests.stderr.strip(),
|
|
1087
|
+
)
|
|
1088
|
+
alpha_isolation_tests = subprocess.run(
|
|
1089
|
+
[sys.executable, str(ROOT / "tests/scripts/test_alpha_isolation.py")],
|
|
1090
|
+
capture_output=True,
|
|
1091
|
+
text=True,
|
|
1092
|
+
)
|
|
1093
|
+
check(
|
|
1094
|
+
"Alpha isolation only removes reviewed edge-connected background and rejects residual contamination",
|
|
1095
|
+
alpha_isolation_tests.returncode == 0 and "alpha isolation tests: PASS" in alpha_isolation_tests.stdout,
|
|
1096
|
+
alpha_isolation_tests.stdout.strip() or alpha_isolation_tests.stderr.strip(),
|
|
1097
|
+
)
|
|
948
1098
|
|
|
949
1099
|
|
|
950
1100
|
if __name__ == "__main__":
|
|
@@ -961,6 +1111,7 @@ if __name__ == "__main__":
|
|
|
961
1111
|
test_placeholder_is_not_runtime_evidence()
|
|
962
1112
|
test_runtime_evidence_binding()
|
|
963
1113
|
test_deep_audit_contracts()
|
|
1114
|
+
test_task_bundle_resolver_contract()
|
|
964
1115
|
test_runtime_telemetry_verifier_contract()
|
|
965
1116
|
test_approved_browser_review_e2e_contract()
|
|
966
1117
|
test_intelligence_core_contracts()
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import json
|
|
4
|
+
import shutil
|
|
5
|
+
import struct
|
|
6
|
+
import subprocess
|
|
7
|
+
import sys
|
|
8
|
+
import tempfile
|
|
9
|
+
import zlib
|
|
10
|
+
from pathlib import Path
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
ROOT = Path(__file__).resolve().parents[2]
|
|
14
|
+
BUILDER = ROOT / "scripts" / "build-ai-pilot.py"
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def check(condition: bool, message: str) -> None:
|
|
18
|
+
if not condition:
|
|
19
|
+
raise AssertionError(message)
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def write_png(path: Path, alpha: bool, offset: int = 0, contamination: bool = False) -> None:
|
|
23
|
+
"""Write one compact, non-interlaced 4x4 PNG without third-party libraries."""
|
|
24
|
+
width = height = 4
|
|
25
|
+
channels = 4 if alpha else 3
|
|
26
|
+
rows = bytearray()
|
|
27
|
+
for y in range(height):
|
|
28
|
+
rows.append(0)
|
|
29
|
+
for x in range(width):
|
|
30
|
+
visible = (x == 1 + offset and y in {1, 2}) or (contamination and x == 3 and y == 1)
|
|
31
|
+
rows.extend((244, 124, 21))
|
|
32
|
+
if alpha:
|
|
33
|
+
rows.append(255 if visible else 0)
|
|
34
|
+
def chunk(kind: bytes, data: bytes) -> bytes:
|
|
35
|
+
return struct.pack(">I", len(data)) + kind + data + struct.pack(">I", zlib.crc32(kind + data) & 0xFFFFFFFF)
|
|
36
|
+
color_type = 6 if alpha else 2
|
|
37
|
+
path.write_bytes(b"\x89PNG\r\n\x1a\n" + chunk(b"IHDR", struct.pack(">IIBBBBB", width, height, 8, color_type, 0, 0, 0)) + chunk(b"IDAT", zlib.compress(bytes(rows))) + chunk(b"IEND", b""))
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def build(root: Path, sources: dict[str, Path], output: str, provider: str = "internal-imagegen") -> subprocess.CompletedProcess[str]:
|
|
41
|
+
return subprocess.run(
|
|
42
|
+
[
|
|
43
|
+
sys.executable, str(BUILDER),
|
|
44
|
+
"--idle", str(sources["idle"]),
|
|
45
|
+
"--contact-right", str(sources["contact-right"]),
|
|
46
|
+
"--passing", str(sources["passing"]),
|
|
47
|
+
"--contact-left", str(sources["contact-left"]),
|
|
48
|
+
"--root", str(root),
|
|
49
|
+
"--output", output,
|
|
50
|
+
"--generated-at", "2026-08-15T00:00:00Z",
|
|
51
|
+
"--provider", provider,
|
|
52
|
+
"--overwrite",
|
|
53
|
+
],
|
|
54
|
+
capture_output=True,
|
|
55
|
+
text=True,
|
|
56
|
+
)
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
def main() -> int:
|
|
60
|
+
output = ".motionloom/pilots/test-ai-pilot-builder"
|
|
61
|
+
workspace = ROOT / output
|
|
62
|
+
shutil.rmtree(workspace, ignore_errors=True)
|
|
63
|
+
with tempfile.TemporaryDirectory() as td:
|
|
64
|
+
source_dir = Path(td)
|
|
65
|
+
sources: dict[str, Path] = {}
|
|
66
|
+
for index, frame_id in enumerate(("idle", "contact-right", "passing", "contact-left")):
|
|
67
|
+
source = source_dir / f"{frame_id}.png"
|
|
68
|
+
write_png(source, alpha=True, offset=index % 2)
|
|
69
|
+
sources[frame_id] = source
|
|
70
|
+
result = build(ROOT, sources, output)
|
|
71
|
+
check(result.returncode == 0, result.stderr or result.stdout)
|
|
72
|
+
provenance = json.loads((workspace / "provenance.json").read_text())
|
|
73
|
+
candidate = json.loads((workspace / "candidate.json").read_text())
|
|
74
|
+
evidence = json.loads((workspace / "devlab-pilot-evidence.json").read_text())
|
|
75
|
+
check(provenance["authority"] == "ai_generated" and provenance["readiness"] == "runtime_ready", "builder must preserve AI authority/readiness")
|
|
76
|
+
check("human_approval" not in provenance and candidate["runtime"]["review_required"] is True, "builder must not self-approve a candidate")
|
|
77
|
+
check(evidence["state"] == "review_required" and evidence["production_approved"] is False, "Dev Lab evidence must retain review boundary")
|
|
78
|
+
intake = subprocess.run(
|
|
79
|
+
[sys.executable, str(ROOT / "scripts/artifact-intake.py"), "intake", "--root", str(ROOT), "--registry", str(ROOT / "artifact-adapter-registry.json"), "--controls", str(workspace / "controls.json"), "--receipt", str(workspace / "receipt.json"), "--export-manifest", str(workspace / "export.json"), "--json"],
|
|
80
|
+
capture_output=True,
|
|
81
|
+
text=True,
|
|
82
|
+
)
|
|
83
|
+
intake_data = json.loads(intake.stdout)
|
|
84
|
+
check(intake.returncode == 0 and intake_data["status"] == "review_required", intake.stderr or intake.stdout)
|
|
85
|
+
runtime = subprocess.run(
|
|
86
|
+
[sys.executable, str(ROOT / "scripts/runtime-candidate.py"), "validate", "--root", str(ROOT), "--input", str(workspace / "candidate.json"), "--json"],
|
|
87
|
+
capture_output=True,
|
|
88
|
+
text=True,
|
|
89
|
+
)
|
|
90
|
+
runtime_data = json.loads(runtime.stdout)
|
|
91
|
+
check(runtime.returncode == 0 and runtime_data["status"] == "review_required" and runtime_data["runtime_verified"] is False, runtime.stderr or runtime.stdout)
|
|
92
|
+
|
|
93
|
+
chatgpt_output = ".motionloom/pilots/test-ai-pilot-chatgpt"
|
|
94
|
+
chatgpt = build(ROOT, sources, chatgpt_output, provider="chatgpt")
|
|
95
|
+
check(chatgpt.returncode == 0, chatgpt.stderr or chatgpt.stdout)
|
|
96
|
+
chatgpt_workspace = ROOT / chatgpt_output
|
|
97
|
+
chatgpt_provenance = json.loads((chatgpt_workspace / "provenance.json").read_text())
|
|
98
|
+
chatgpt_receipt = json.loads((chatgpt_workspace / "receipt.json").read_text())
|
|
99
|
+
check(chatgpt_provenance["generator"]["source"] == "openai-chatgpt", "ChatGPT import must not be relabelled internal-imagegen")
|
|
100
|
+
check(chatgpt_receipt["provider"]["adapter_id"] == "openai.chatgpt" and chatgpt_receipt["provider"]["invocation_mode"] == "manual", "ChatGPT receipt must preserve user-mediated adapter truth")
|
|
101
|
+
|
|
102
|
+
opaque = source_dir / "opaque.png"
|
|
103
|
+
write_png(opaque, alpha=False)
|
|
104
|
+
opaque_output = ".motionloom/pilots/test-ai-pilot-opaque"
|
|
105
|
+
rejected = build(ROOT, {key: opaque for key in sources}, opaque_output)
|
|
106
|
+
check(rejected.returncode != 0 and "has no alpha channel" in (rejected.stderr + rejected.stdout), "RGB checkered source must be rejected as non-alpha")
|
|
107
|
+
opaque_handoff = json.loads((ROOT / opaque_output / "partial-handoff.json").read_text())
|
|
108
|
+
check(opaque_handoff["state"] == "partial" and opaque_handoff["production_approved"] is False, "early RGB rejection must emit a non-approving partial handoff")
|
|
109
|
+
check(len(opaque_handoff["rejected_source_attempts"][0]["frames"]) == 4, "partial handoff must bind every supplied source hash")
|
|
110
|
+
|
|
111
|
+
contaminated = source_dir / "contaminated.png"
|
|
112
|
+
write_png(contaminated, alpha=True, contamination=True)
|
|
113
|
+
contaminated_output = ".motionloom/pilots/test-ai-pilot-contaminated"
|
|
114
|
+
rejected = build(ROOT, {key: contaminated for key in sources}, contaminated_output, provider="chatgpt")
|
|
115
|
+
check(rejected.returncode != 0 and "clean padding" in (rejected.stderr + rejected.stdout), "detached canvas-edge contamination must be rejected")
|
|
116
|
+
contaminated_handoff = json.loads((ROOT / contaminated_output / "partial-handoff.json").read_text())
|
|
117
|
+
attempt = contaminated_handoff["rejected_source_attempts"][0]
|
|
118
|
+
check(attempt["provider"] == "openai-chatgpt" and attempt["state"] == "rejected_pre_ingest", "partial handoff must preserve ChatGPT provenance")
|
|
119
|
+
shutil.rmtree(workspace, ignore_errors=True)
|
|
120
|
+
shutil.rmtree(ROOT / ".motionloom/pilots/test-ai-pilot-chatgpt", ignore_errors=True)
|
|
121
|
+
shutil.rmtree(ROOT / ".motionloom/pilots/test-ai-pilot-opaque", ignore_errors=True)
|
|
122
|
+
shutil.rmtree(ROOT / ".motionloom/pilots/test-ai-pilot-contaminated", ignore_errors=True)
|
|
123
|
+
print("AI pilot builder tests: PASS")
|
|
124
|
+
return 0
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
if __name__ == "__main__":
|
|
128
|
+
raise SystemExit(main())
|