motionloom 2.2.0 → 2.4.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 (114) hide show
  1. package/CHANGELOG.md +48 -1
  2. package/README.md +123 -17
  3. package/ROADMAP.md +8 -3
  4. package/SKILL.md +34 -3
  5. package/agent-card.json +48 -5
  6. package/agent-surfaces.json +8 -1
  7. package/artifact-adapter-registry.json +53 -0
  8. package/bin/motionloom.mjs +41 -4
  9. package/docs/AGENT-INTEGRATION.md +15 -2
  10. package/docs/CHECKLIST.md +21 -0
  11. package/docs/STATUS.md +2 -2
  12. package/docs/audits/data/deep-stress-latest.json +65 -65
  13. package/docs/releases/2.3.0.md +33 -0
  14. package/docs/releases/2.4.0.md +31 -0
  15. package/docs/releases/npm-publish-from-workstation.md +19 -8
  16. package/docs/research/agent-skill-ecosystem-notes.md +47 -0
  17. package/docs/research/ai-animation-tools-2026-notes.md +81 -0
  18. package/docs/research/ai-animation-tools-2026-report.md +168 -0
  19. package/docs/research/ai-pilot-ingest-notes.md +29 -0
  20. package/examples/agent-consumer/ai-generated-pilot/hero-male.json +10 -0
  21. package/examples/agent-consumer/ai-generated-pilot-provenance.json +55 -0
  22. package/examples/agent-consumer/ai-pilot-scout/CHATGPT-HANDOFF.md +70 -0
  23. package/examples/agent-consumer/ai-pilot-scout/CODEX-HANDOFF.md +80 -0
  24. package/examples/agent-consumer/ai-pilot-scout/README.md +39 -0
  25. package/examples/agent-consumer/ai-pilot-scout/chatgpt-geometry-correction-prompt.md +55 -0
  26. package/examples/agent-consumer/ai-pilot-scout/chatgpt-pose-generation-prompt.md +78 -0
  27. package/examples/agent-consumer/ai-pilot-scout/partial-handoff.json +73 -0
  28. package/examples/agent-consumer/artifact-intake/hero-motion-controls.json +15 -0
  29. package/examples/agent-consumer/artifact-intake/hero-motion-export.json +14 -0
  30. package/examples/agent-consumer/artifact-intake/hero-motion-provenance.json +20 -0
  31. package/examples/agent-consumer/artifact-intake/hero-motion-receipt.json +16 -0
  32. package/examples/agent-consumer/asset-consistency/assets/forest-back.png +0 -0
  33. package/examples/agent-consumer/asset-consistency/assets/forest-front.png +0 -0
  34. package/examples/agent-consumer/asset-consistency/assets/forest-mid.png +0 -0
  35. package/examples/agent-consumer/asset-consistency/assets/hero-atlas.png +0 -0
  36. package/examples/agent-consumer/asset-consistency/assets/hero-frame-00.png +0 -0
  37. package/examples/agent-consumer/asset-consistency/assets/hero-frame-01.png +0 -0
  38. package/examples/agent-consumer/asset-consistency/assets/hero-frame-02.png +0 -0
  39. package/examples/agent-consumer/asset-consistency/assets/hero-frame-03.png +0 -0
  40. package/examples/agent-consumer/asset-consistency/forest-layered-map.json +53 -0
  41. package/examples/agent-consumer/asset-consistency/hero-atlas-contract.json +24 -0
  42. package/examples/agent-consumer/asset-consistency/hero-identity.json +51 -0
  43. package/examples/agent-consumer/asset-consistency/hero-walk-action-set.json +27 -0
  44. package/examples/agent-consumer/asset-consistency/hero-walk-frame-geometry.json +63 -0
  45. package/examples/agent-consumer/rig-compatibility/hero-walk-fixture-rig.json +13 -0
  46. package/examples/agent-consumer/rive-package-gate/README.md +20 -0
  47. package/examples/agent-consumer/runtime-candidate/hero-walk-candidate.json +18 -0
  48. package/examples/agent-consumer/runtime-pilot/action-set.json +19 -0
  49. package/examples/agent-consumer/runtime-pilot/asset-identity.json +44 -0
  50. package/examples/agent-consumer/runtime-pilot/candidate.json +21 -0
  51. package/examples/agent-consumer/runtime-pilot/controls.json +35 -0
  52. package/examples/agent-consumer/runtime-pilot/export.json +17 -0
  53. package/examples/agent-consumer/runtime-pilot/provenance.json +38 -0
  54. package/examples/agent-consumer/runtime-pilot/receipt.json +30 -0
  55. package/package.json +65 -4
  56. package/references/agent-interoperability.md +11 -0
  57. package/references/browser-review-contract.md +7 -1
  58. package/references/intelligence-core.md +29 -2
  59. package/rig-adapter-registry.json +39 -0
  60. package/schemas/action-set.schema.json +42 -0
  61. package/schemas/agent-surfaces.schema.json +1 -1
  62. package/schemas/artifact-adapter-registry.schema.json +16 -0
  63. package/schemas/asset-identity.schema.json +117 -0
  64. package/schemas/asset-provenance.schema.json +183 -0
  65. package/schemas/atlas-contract.schema.json +48 -0
  66. package/schemas/control-track.schema.json +18 -0
  67. package/schemas/export-manifest.schema.json +19 -0
  68. package/schemas/frame-geometry.schema.json +79 -0
  69. package/schemas/generation-receipt.schema.json +20 -0
  70. package/schemas/layered-map.schema.json +75 -0
  71. package/schemas/rig-adapter-registry.schema.json +14 -0
  72. package/schemas/rig-compatibility.schema.json +53 -0
  73. package/schemas/rive-package-manifest.schema.json +43 -0
  74. package/schemas/runtime-candidate.schema.json +48 -0
  75. package/schemas/scene-manifest.schema.json +19 -0
  76. package/scripts/artifact-intake.py +408 -0
  77. package/scripts/asset-consistency.py +517 -0
  78. package/scripts/asset-provenance.py +395 -0
  79. package/scripts/build-ai-pilot.py +504 -0
  80. package/scripts/docs-audit.py +14 -2
  81. package/scripts/isolate-alpha-background.py +147 -0
  82. package/scripts/pr.py +2 -0
  83. package/scripts/quality-gate.py +127 -3
  84. package/scripts/report.py +135 -0
  85. package/scripts/resolve-task-bundle.py +84 -0
  86. package/scripts/review-hook.py +26 -4
  87. package/scripts/rig-compatibility.py +251 -0
  88. package/scripts/rive-package-gate.py +245 -0
  89. package/scripts/runtime-candidate.py +301 -0
  90. package/scripts/setup.mjs +500 -0
  91. package/scripts/skill-doctor.py +2 -1
  92. package/src/output/browser-review-smoke/asset-provenance.json +77 -0
  93. package/src/output/browser-review-smoke/manifest.json +1 -0
  94. package/src/output/browser-review-smoke/visual-truth.json +3 -3
  95. package/src/output/runtime-pilot-framer/asset-provenance.json +38 -0
  96. package/src/output/runtime-pilot-framer/browser-review.json +20 -0
  97. package/src/output/runtime-pilot-framer/framer-motion/runtime-telemetry.json +103 -0
  98. package/src/output/runtime-pilot-framer/manifest.json +31 -0
  99. package/src/output/runtime-pilot-framer/motion-spec.json +25 -0
  100. package/src/output/runtime-pilot-framer/runtime-evidence.json +96 -0
  101. package/src/output/runtime-pilot-framer/runtime-telemetry.json +100 -0
  102. package/src/output/runtime-pilot-framer/scene.jsx +52 -0
  103. package/src/output/runtime-pilot-framer/snapshot/.render-meta.json +9 -0
  104. package/src/output/runtime-pilot-framer/snapshot/frame-00.png +0 -0
  105. package/src/output/runtime-pilot-framer/snapshot/frame-100.png +0 -0
  106. package/src/output/runtime-pilot-framer/snapshot/frame-50.png +0 -0
  107. package/tests/scripts/run_tests.py +131 -0
  108. package/tests/scripts/test_ai_pilot_builder.py +128 -0
  109. package/tests/scripts/test_alpha_isolation.py +60 -0
  110. package/tests/scripts/test_artifact_intake.py +146 -0
  111. package/tests/scripts/test_asset_consistency.py +199 -0
  112. package/tests/scripts/test_rig_compatibility.py +105 -0
  113. package/tests/scripts/test_rive_package_gate.py +101 -0
  114. package/tests/scripts/test_runtime_candidate.py +95 -0
@@ -0,0 +1,101 @@
1
+ """Regression tests for the fail-closed Rive package gate."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import argparse
6
+ import hashlib
7
+ import importlib.util
8
+ import json
9
+ import sys
10
+ import tempfile
11
+ import unittest
12
+ from pathlib import Path
13
+
14
+
15
+ ROOT = Path(__file__).resolve().parents[2]
16
+
17
+
18
+ def load_gate():
19
+ spec = importlib.util.spec_from_file_location("test_rive_package_gate", ROOT / "scripts" / "rive-package-gate.py")
20
+ assert spec and spec.loader
21
+ module = importlib.util.module_from_spec(spec)
22
+ sys.modules[spec.name] = module
23
+ spec.loader.exec_module(module)
24
+ return module
25
+
26
+
27
+ GATE = load_gate()
28
+
29
+
30
+ def sha(path: Path) -> str:
31
+ return hashlib.sha256(path.read_bytes()).hexdigest()
32
+
33
+
34
+ class RivePackageGateTests(unittest.TestCase):
35
+ def build_bundle(self, directory: Path) -> tuple[Path, Path]:
36
+ source = directory / "hero.riv"
37
+ source.write_bytes(b"RIVE\x07\x00demo-real-test-bytes")
38
+ runtime = directory / "runtime.json"
39
+ runtime.write_text(json.dumps({"schema_version": "1.0", "run_id": "rive-test", "generated_at": "2026-08-15T00:00:00Z", "mode": "runtime", "harness": "test", "source_sha256": sha(source), "status": "pass", "frameworks": [{"run_id": "rive-test", "framework": "rive", "runtime": "rive-web", "status": "pass", "ready": True, "frames": [0, 50, 100]}]}), encoding="utf-8")
40
+ provenance = directory / "provenance.json"
41
+ provenance.write_text(json.dumps({"schema_version": "1.0", "provenance_id": "test-rive", "asset": {"id": "hero-rive", "path": "hero.riv", "type": "character", "framework": "rive"}, "authority": "ai_generated", "readiness": "runtime_ready", "generator": {"model": "internal-imagegen", "task_id": "test", "source": "internal", "generated_at": "2026-08-15T00:00:00Z"}, "files": [{"path": "hero.riv", "role": "runtime-package", "sha256": sha(source), "bytes": source.stat().st_size}], "license": {"spdx": "LicenseRef-AI-Pilot", "source": "internal-imagegen", "attribution": "AI-generated pilot; human review required"}, "provenance_chain": [{"step": "generated", "actor": "agent", "source": "internal-imagegen", "timestamp": "2026-08-15T00:00:00Z"}], "created_at": "2026-08-15T00:00:00Z"}), encoding="utf-8")
42
+ manifest = directory / "manifest.json"
43
+ manifest.write_text(json.dumps({"schema_version": "0.1", "package_id": "hero-rive-package", "package_class": "ai_generated_pilot", "asset": {"id": "hero-rive", "path": "hero.riv", "sha256": sha(source), "bytes": source.stat().st_size}, "provenance_ref": "provenance.json", "runtime": {"adapter_id": "motionloom.rive-runtime", "target": "rive", "required_state_machines": ["Locomotion"], "required_inputs": ["speed"], "required_events": ["footstep"], "runtime_evidence_ref": "runtime.json", "runtime_evidence_sha256": sha(runtime), "review_required": True}, "actions": ["idle", "walk"], "review_required": True}), encoding="utf-8")
44
+ registry = directory / "registry.json"
45
+ adapter_source = directory / "adapter.mjs"
46
+ adapter_source.write_text("export const rive = true;\n", encoding="utf-8")
47
+ registry.write_text(json.dumps({"schema_version": "0.1", "adapters": [{"adapter_id": "motionloom.rive-runtime", "target": "rive", "framework": "rive", "status": "project_integrated", "evidence": [{"path": "adapter.mjs", "sha256": sha(adapter_source)}]}]}), encoding="utf-8")
48
+ return manifest, registry
49
+
50
+ def validate(self, manifest: Path, registry: Path, strict: bool = True):
51
+ return GATE.validate(argparse.Namespace(input=manifest, root=manifest.parent, registry=registry, strict=strict))
52
+
53
+ def test_valid_ai_pilot_is_runtime_test_ready_but_not_approved(self):
54
+ with tempfile.TemporaryDirectory() as raw:
55
+ manifest, registry = self.build_bundle(Path(raw))
56
+ result = self.validate(manifest, registry)
57
+ self.assertEqual(result["status"], "pass")
58
+ self.assertTrue(result["runtime_test_ready"])
59
+ self.assertTrue(result["review_required"])
60
+ self.assertFalse(result["production_approved"])
61
+
62
+ def test_rejects_tampered_riv_bytes(self):
63
+ with tempfile.TemporaryDirectory() as raw:
64
+ manifest, registry = self.build_bundle(Path(raw))
65
+ (Path(raw) / "hero.riv").write_bytes(b"RIVETwitter")
66
+ result = self.validate(manifest, registry)
67
+ self.assertEqual(result["status"], "blocked")
68
+ self.assertTrue(any(item["code"] == "asset_sha256_mismatch" for item in result["issues"]))
69
+
70
+ def test_strict_rejects_missing_runtime_proof(self):
71
+ with tempfile.TemporaryDirectory() as raw:
72
+ manifest, registry = self.build_bundle(Path(raw))
73
+ data = json.loads(manifest.read_text(encoding="utf-8"))
74
+ data["runtime"].pop("runtime_evidence_ref")
75
+ data["runtime"].pop("runtime_evidence_sha256")
76
+ manifest.write_text(json.dumps(data), encoding="utf-8")
77
+ result = self.validate(manifest, registry)
78
+ self.assertEqual(result["status"], "blocked")
79
+ self.assertTrue(any(item["code"] == "runtime_evidence_required" for item in result["issues"]))
80
+
81
+ def test_rejects_suppressed_review_and_unknown_adapter(self):
82
+ with tempfile.TemporaryDirectory() as raw:
83
+ manifest, registry = self.build_bundle(Path(raw))
84
+ data = json.loads(manifest.read_text(encoding="utf-8"))
85
+ data["review_required"] = False
86
+ data["runtime"]["adapter_id"] = "untrusted-rive"
87
+ manifest.write_text(json.dumps(data), encoding="utf-8")
88
+ result = self.validate(manifest, registry)
89
+ self.assertEqual(result["status"], "blocked")
90
+ codes = {item["code"] for item in result["issues"]}
91
+ self.assertIn("review_required", codes)
92
+ self.assertIn("unknown_adapter", codes)
93
+
94
+
95
+ if __name__ == "__main__":
96
+ runner = unittest.TextTestRunner(verbosity=2)
97
+ result = runner.run(unittest.defaultTestLoader.loadTestsFromTestCase(RivePackageGateTests))
98
+ if result.wasSuccessful():
99
+ print("rive package gate contract tests: PASS")
100
+ raise SystemExit(0)
101
+ raise SystemExit(1)
@@ -0,0 +1,95 @@
1
+ import copy
2
+ import importlib.util
3
+ import json
4
+ import shutil
5
+ import subprocess
6
+ import sys
7
+ import tempfile
8
+ from pathlib import Path
9
+ from types import SimpleNamespace
10
+
11
+
12
+ ROOT = Path(__file__).resolve().parents[2]
13
+ MODULE_PATH = ROOT / "scripts" / "runtime-candidate.py"
14
+ CANDIDATE_PATH = ROOT / "examples/agent-consumer/runtime-candidate/hero-walk-candidate.json"
15
+
16
+
17
+ def load_module():
18
+ spec = importlib.util.spec_from_file_location("runtime_candidate", MODULE_PATH)
19
+ if spec is None or spec.loader is None:
20
+ raise RuntimeError("cannot load runtime candidate")
21
+ module = importlib.util.module_from_spec(spec)
22
+ sys.modules[spec.name] = module
23
+ spec.loader.exec_module(module)
24
+ return module
25
+
26
+
27
+ MODULE = load_module()
28
+
29
+
30
+ def check(condition: bool, message: str) -> None:
31
+ if not condition:
32
+ raise AssertionError(message)
33
+
34
+
35
+ def evaluate(path: Path, strict: bool = False) -> dict:
36
+ return MODULE.validate_candidate(SimpleNamespace(root=ROOT, input=path, strict=strict))
37
+
38
+
39
+ def write(path: Path, document: dict) -> Path:
40
+ path.write_text(json.dumps(document, indent=2) + "\n", encoding="utf-8")
41
+ return path
42
+
43
+
44
+ def main() -> int:
45
+ with tempfile.TemporaryDirectory(dir=ROOT) as td:
46
+ temp = Path(td)
47
+ candidate = json.loads(CANDIDATE_PATH.read_text(encoding="utf-8"))
48
+ public = evaluate(CANDIDATE_PATH)
49
+ check(public["runtime_test_ready"] and public["status"] == "review_required", "hash-bound public candidate must be runtime-test-ready and review-required")
50
+ check(not public["runtime_verified"], "candidate without runtime evidence must not self-assert runtime verification")
51
+ check(not public["production_eligible"] and not public["production_approved"], "candidate bridge must never grant production authority")
52
+
53
+ strict = evaluate(CANDIDATE_PATH, strict=True)
54
+ check(not strict["runtime_test_ready"] and any(item["code"] == "runtime_evidence_required" for item in strict["errors"]), "strict candidate validation must require passing runtime evidence")
55
+
56
+ wrong_asset = copy.deepcopy(candidate)
57
+ wrong_asset["asset_id"] = "character/not-hero"
58
+ wrong_asset_path = write(temp / "wrong-asset.json", wrong_asset)
59
+ mismatch = evaluate(wrong_asset_path)
60
+ check(not mismatch["runtime_test_ready"] and any(item["code"] in {"asset_id_mismatch", "consistency_asset_id_mismatch"} for item in mismatch["errors"]), "candidate asset ID must bind receipt, export and consistency contracts")
61
+
62
+ missing_geometry = copy.deepcopy(candidate)
63
+ del missing_geometry["consistency"]["frame_geometry"]
64
+ missing_geometry_path = write(temp / "missing-geometry.json", missing_geometry)
65
+ geometry = evaluate(missing_geometry_path)
66
+ check(not geometry["runtime_test_ready"] and any(item["code"] == "missing_profile_contract" for item in geometry["errors"]), "frame sequence candidate must require frame geometry evidence")
67
+
68
+ no_review = copy.deepcopy(candidate)
69
+ no_review["runtime"]["review_required"] = False
70
+ no_review_path = write(temp / "no-review.json", no_review)
71
+ review = evaluate(no_review_path)
72
+ check(not review["runtime_test_ready"] and any(item["code"] == "review_required" for item in review["errors"]), "candidate cannot suppress human review")
73
+
74
+ evidence = {
75
+ "schema_version": "1.0", "run_id": "fixture-runtime", "generated_at": "2026-08-15T00:00:00Z",
76
+ "mode": "runtime", "harness": "fixture", "status": "pass",
77
+ "frameworks": [{"run_id": "fixture-runtime", "framework": "sprite", "runtime": "fixture", "status": "pass", "ready": True, "frames": [0, 50, 100]}]
78
+ }
79
+ evidence_path = temp / "runtime-evidence.json"
80
+ write(evidence_path, evidence)
81
+ verified_candidate = copy.deepcopy(candidate)
82
+ verified_candidate["runtime"]["runtime_evidence_ref"] = f"{temp.name}/runtime-evidence.json"
83
+ verified_path = write(temp / "verified.json", verified_candidate)
84
+ verified = evaluate(verified_path, strict=True)
85
+ check(verified["runtime_test_ready"] and verified["runtime_verified"], "strict candidate may be runtime-verified only with passing evidence")
86
+
87
+ cli = subprocess.run([sys.executable, str(MODULE_PATH), "validate", "--root", str(ROOT), "--input", str(CANDIDATE_PATH), "--json"], capture_output=True, text=True)
88
+ cli_result = json.loads(cli.stdout)
89
+ check(cli.returncode == 0 and cli_result["status"] == "review_required", "CLI must expose review-required candidate evidence")
90
+ print("runtime candidate contract tests: PASS")
91
+ return 0
92
+
93
+
94
+ if __name__ == "__main__":
95
+ raise SystemExit(main())