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.
Files changed (113) hide show
  1. package/CHANGELOG.md +45 -1
  2. package/README.md +97 -11
  3. package/ROADMAP.md +8 -5
  4. package/SKILL.md +24 -5
  5. package/agent-card.json +34 -5
  6. package/agent-surfaces.json +1 -1
  7. package/artifact-adapter-registry.json +53 -0
  8. package/bin/motionloom.mjs +43 -6
  9. package/capability-registry.json +308 -0
  10. package/docs/CHECKLIST.md +16 -0
  11. package/docs/STATUS.md +2 -2
  12. package/docs/apple-distribution.md +24 -0
  13. package/docs/architecture/apple/cloudkit.md +18 -0
  14. package/docs/architecture/apple/contracts.md +9 -0
  15. package/docs/architecture/apple/implementation-status-2026-08-16.md +55 -0
  16. package/docs/architecture/apple/studio.md +35 -0
  17. package/docs/audits/data/deep-stress-latest.json +65 -65
  18. package/docs/releases/2.4.0.md +31 -0
  19. package/docs/releases/2.5.0.md +31 -0
  20. package/docs/releases/npm-publish-from-workstation.md +19 -8
  21. package/docs/research/agent-skill-ecosystem-notes.md +47 -0
  22. package/docs/research/ai-animation-tools-2026-notes.md +81 -0
  23. package/docs/research/ai-animation-tools-2026-report.md +168 -0
  24. package/docs/research/ai-pilot-ingest-notes.md +29 -0
  25. package/docs/research/ai-repository-benchmark-2026-08-16.md +75 -0
  26. package/examples/agent-consumer/ai-pilot-scout/CHATGPT-HANDOFF.md +70 -0
  27. package/examples/agent-consumer/ai-pilot-scout/CODEX-HANDOFF.md +80 -0
  28. package/examples/agent-consumer/ai-pilot-scout/README.md +39 -0
  29. package/examples/agent-consumer/ai-pilot-scout/chatgpt-geometry-correction-prompt.md +55 -0
  30. package/examples/agent-consumer/ai-pilot-scout/chatgpt-pose-generation-prompt.md +78 -0
  31. package/examples/agent-consumer/ai-pilot-scout/partial-handoff.json +73 -0
  32. package/examples/agent-consumer/artifact-intake/hero-motion-controls.json +15 -0
  33. package/examples/agent-consumer/artifact-intake/hero-motion-export.json +14 -0
  34. package/examples/agent-consumer/artifact-intake/hero-motion-provenance.json +20 -0
  35. package/examples/agent-consumer/artifact-intake/hero-motion-receipt.json +16 -0
  36. package/examples/agent-consumer/asset-consistency/assets/forest-back.png +0 -0
  37. package/examples/agent-consumer/asset-consistency/assets/forest-front.png +0 -0
  38. package/examples/agent-consumer/asset-consistency/assets/forest-mid.png +0 -0
  39. package/examples/agent-consumer/asset-consistency/assets/hero-atlas.png +0 -0
  40. package/examples/agent-consumer/asset-consistency/assets/hero-frame-00.png +0 -0
  41. package/examples/agent-consumer/asset-consistency/assets/hero-frame-01.png +0 -0
  42. package/examples/agent-consumer/asset-consistency/assets/hero-frame-02.png +0 -0
  43. package/examples/agent-consumer/asset-consistency/assets/hero-frame-03.png +0 -0
  44. package/examples/agent-consumer/asset-consistency/forest-layered-map.json +53 -0
  45. package/examples/agent-consumer/asset-consistency/hero-atlas-contract.json +24 -0
  46. package/examples/agent-consumer/asset-consistency/hero-identity.json +51 -0
  47. package/examples/agent-consumer/asset-consistency/hero-walk-action-set.json +27 -0
  48. package/examples/agent-consumer/asset-consistency/hero-walk-frame-geometry.json +63 -0
  49. package/examples/agent-consumer/rig-compatibility/hero-walk-fixture-rig.json +13 -0
  50. package/examples/agent-consumer/rive-package-gate/README.md +20 -0
  51. package/examples/agent-consumer/runtime-candidate/hero-walk-candidate.json +18 -0
  52. package/examples/agent-consumer/runtime-pilot/action-set.json +19 -0
  53. package/examples/agent-consumer/runtime-pilot/asset-identity.json +44 -0
  54. package/examples/agent-consumer/runtime-pilot/candidate.json +21 -0
  55. package/examples/agent-consumer/runtime-pilot/controls.json +35 -0
  56. package/examples/agent-consumer/runtime-pilot/export.json +17 -0
  57. package/examples/agent-consumer/runtime-pilot/provenance.json +38 -0
  58. package/examples/agent-consumer/runtime-pilot/receipt.json +30 -0
  59. package/package.json +58 -4
  60. package/references/browser-review-contract.md +7 -1
  61. package/references/intelligence-core.md +22 -1
  62. package/rig-adapter-registry.json +39 -0
  63. package/schemas/action-set.schema.json +42 -0
  64. package/schemas/artifact-adapter-registry.schema.json +16 -0
  65. package/schemas/asset-identity.schema.json +117 -0
  66. package/schemas/asset-provenance.schema.json +1 -1
  67. package/schemas/atlas-contract.schema.json +48 -0
  68. package/schemas/control-track.schema.json +18 -0
  69. package/schemas/export-manifest.schema.json +19 -0
  70. package/schemas/frame-geometry.schema.json +79 -0
  71. package/schemas/generation-receipt.schema.json +20 -0
  72. package/schemas/layered-map.schema.json +75 -0
  73. package/schemas/rig-adapter-registry.schema.json +14 -0
  74. package/schemas/rig-compatibility.schema.json +53 -0
  75. package/schemas/rive-package-manifest.schema.json +43 -0
  76. package/schemas/runtime-candidate.schema.json +48 -0
  77. package/schemas/scene-manifest.schema.json +18 -0
  78. package/scripts/artifact-intake.py +408 -0
  79. package/scripts/asset-consistency.py +517 -0
  80. package/scripts/asset-provenance.py +8 -3
  81. package/scripts/attestation.py +8 -0
  82. package/scripts/build-ai-pilot.py +504 -0
  83. package/scripts/evidence-verifier.py +18 -9
  84. package/scripts/intelligence.py +85 -18
  85. package/scripts/isolate-alpha-background.py +147 -0
  86. package/scripts/quality-gate.py +88 -2
  87. package/scripts/report.py +85 -0
  88. package/scripts/resolve-task-bundle.py +84 -0
  89. package/scripts/review-hook.py +26 -4
  90. package/scripts/rig-compatibility.py +251 -0
  91. package/scripts/rive-package-gate.py +245 -0
  92. package/scripts/runtime-candidate.py +301 -0
  93. package/scripts/setup.mjs +54 -26
  94. package/src/output/runtime-pilot-framer/asset-provenance.json +38 -0
  95. package/src/output/runtime-pilot-framer/browser-review.json +20 -0
  96. package/src/output/runtime-pilot-framer/framer-motion/runtime-telemetry.json +103 -0
  97. package/src/output/runtime-pilot-framer/manifest.json +31 -0
  98. package/src/output/runtime-pilot-framer/motion-spec.json +25 -0
  99. package/src/output/runtime-pilot-framer/runtime-evidence.json +96 -0
  100. package/src/output/runtime-pilot-framer/runtime-telemetry.json +100 -0
  101. package/src/output/runtime-pilot-framer/scene.jsx +52 -0
  102. package/src/output/runtime-pilot-framer/snapshot/.render-meta.json +9 -0
  103. package/src/output/runtime-pilot-framer/snapshot/frame-00.png +0 -0
  104. package/src/output/runtime-pilot-framer/snapshot/frame-100.png +0 -0
  105. package/src/output/runtime-pilot-framer/snapshot/frame-50.png +0 -0
  106. package/tests/scripts/run_tests.py +151 -0
  107. package/tests/scripts/test_ai_pilot_builder.py +128 -0
  108. package/tests/scripts/test_alpha_isolation.py +60 -0
  109. package/tests/scripts/test_artifact_intake.py +146 -0
  110. package/tests/scripts/test_asset_consistency.py +199 -0
  111. package/tests/scripts/test_rig_compatibility.py +105 -0
  112. package/tests/scripts/test_rive_package_gate.py +101 -0
  113. 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
+ }
@@ -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())