motionloom 2.0.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 (112) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +179 -0
  3. package/SKILL.md +122 -0
  4. package/agent-card.json +161 -0
  5. package/assets/library/ATTRIBUTION.md +6 -0
  6. package/assets/library/README.md +20 -0
  7. package/assets/library/avatar-base.svg +19 -0
  8. package/assets/library/error-alert.json +1 -0
  9. package/assets/library/rive/ATTRIBUTION.md +12 -0
  10. package/assets/library/rive/state-machine-test.riv +0 -0
  11. package/assets/library/success-check.json +1 -0
  12. package/bin/motionloom.mjs +81 -0
  13. package/docs/BROWSER-REVIEW-E2E.md +78 -0
  14. package/docs/CATEGORIES.md +16 -0
  15. package/docs/CHECKLIST.md +31 -0
  16. package/docs/DEEP-AUDIT-WORKING-NOTES.md +22 -0
  17. package/docs/FRAMEWORK-SELECTION.md +26 -0
  18. package/docs/PROJECT-MANIFEST.md +37 -0
  19. package/docs/ROADMAP-INTELLIGENCE.md +224 -0
  20. package/docs/audits/1.10.0-attestation-research-notes.md +19 -0
  21. package/docs/audits/1.8.0-trust-boundary-hardening.md +56 -0
  22. package/docs/audits/1.9.0-evidence-interoperability-threat-model.md +37 -0
  23. package/docs/audits/2.0.0-attestation-acceptance.md +30 -0
  24. package/docs/releases/1.5.0.md +25 -0
  25. package/docs/releases/1.6.0.md +27 -0
  26. package/docs/releases/1.7.0.md +23 -0
  27. package/docs/releases/1.8.0.md +23 -0
  28. package/docs/releases/1.9.0.md +21 -0
  29. package/docs/releases/2.0.0.md +21 -0
  30. package/docs/releases/npm-publish-from-workstation.md +88 -0
  31. package/docs/research/AGENT-PROTOCOL-FINDINGS.md +43 -0
  32. package/examples/report-demo/REPORT.md +50 -0
  33. package/examples/report-demo/artifact-manifest.json +25 -0
  34. package/examples/report-demo/decision-log.jsonl +0 -0
  35. package/examples/report-demo/execution-report.json +70 -0
  36. package/examples/report-demo/handoff.json +22 -0
  37. package/examples/report-demo/issue-register.json +5 -0
  38. package/examples/report-demo/task.json +13 -0
  39. package/package.json +95 -0
  40. package/project-context.example.json +26 -0
  41. package/references/browser-review-contract.md +32 -0
  42. package/references/dotlottie-source-notes.md +21 -0
  43. package/references/intelligence-core.md +98 -0
  44. package/references/reporting-contract.md +38 -0
  45. package/references/runtime-capability.md +12 -0
  46. package/references/signed-attestation.md +31 -0
  47. package/schemas/artifact-manifest.schema.json +22 -0
  48. package/schemas/browser-review-candidate.schema.json +24 -0
  49. package/schemas/capability-registry.schema.json +61 -0
  50. package/schemas/continuity-report.schema.json +52 -0
  51. package/schemas/evidence-verifier-report.schema.json +35 -0
  52. package/schemas/execution-report.schema.json +35 -0
  53. package/schemas/fix-plan.schema.json +44 -0
  54. package/schemas/handoff.schema.json +19 -0
  55. package/schemas/motion-ir.schema.json +74 -0
  56. package/schemas/project-graph.schema.json +70 -0
  57. package/schemas/provenance.schema.json +76 -0
  58. package/schemas/runtime-evidence.schema.json +48 -0
  59. package/schemas/runtime-telemetry.schema.json +50 -0
  60. package/schemas/scene-manifest.schema.json +45 -0
  61. package/schemas/semantic-benchmark.schema.json +26 -0
  62. package/schemas/semantic-lint-report.schema.json +48 -0
  63. package/schemas/signed-attestation.schema.json +95 -0
  64. package/schemas/task.schema.json +39 -0
  65. package/schemas/trust-policy.schema.json +53 -0
  66. package/scripts/analyze.sh +13 -0
  67. package/scripts/attestation-keygen.py +63 -0
  68. package/scripts/attestation-verifier.py +178 -0
  69. package/scripts/attestation.py +288 -0
  70. package/scripts/capture-runtime-telemetry.sh +37 -0
  71. package/scripts/devlab.sh +77 -0
  72. package/scripts/eval-intelligence.py +377 -0
  73. package/scripts/evidence-verifier.py +222 -0
  74. package/scripts/fetch-library.sh +57 -0
  75. package/scripts/intelligence.py +1543 -0
  76. package/scripts/manifest.py +61 -0
  77. package/scripts/pr.sh +103 -0
  78. package/scripts/quality-gate.py +378 -0
  79. package/scripts/render-node.mjs +53 -0
  80. package/scripts/render.sh +37 -0
  81. package/scripts/report-contract.py +181 -0
  82. package/scripts/report.py +588 -0
  83. package/scripts/review-hook.py +199 -0
  84. package/scripts/runtime-adapters.mjs +187 -0
  85. package/scripts/skill-doctor.py +150 -0
  86. package/scripts/to-dotlottie.mjs +99 -0
  87. package/scripts/to-dotlottie.sh +25 -0
  88. package/scripts/validate-lottie.py +102 -0
  89. package/src/core/analyzer.py +226 -0
  90. package/src/core/snapshot.py +124 -0
  91. package/src/core/spec.py +240 -0
  92. package/src/output/browser-review-smoke/animation.json +57 -0
  93. package/src/output/browser-review-smoke/browser-review.json +21 -0
  94. package/src/output/browser-review-smoke/manifest.json +22 -0
  95. package/src/output/browser-review-smoke/motion-spec.json +28 -0
  96. package/src/output/browser-review-smoke/snapshot/.render-meta.json +10 -0
  97. package/src/output/browser-review-smoke/snapshot/frame-00.png +0 -0
  98. package/src/output/browser-review-smoke/snapshot/frame-100.png +0 -0
  99. package/src/output/browser-review-smoke/snapshot/frame-50.png +0 -0
  100. package/src/rig/README.md +35 -0
  101. package/src/rig/cutout_rig.py +211 -0
  102. package/templates/framer-motion/ui-micro.tsx +50 -0
  103. package/templates/gsap/scroll-scene.js +54 -0
  104. package/templates/lottie/README.md +21 -0
  105. package/templates/lottie/react-component.tsx +82 -0
  106. package/templates/lottie/scaffold/animation.json +57 -0
  107. package/templates/lottie/scaffold/character-rig.svg +19 -0
  108. package/templates/lottie/vanilla.js +68 -0
  109. package/templates/rive/README.md +36 -0
  110. package/tests/evals/intelligence-cases.json +131 -0
  111. package/tests/scripts/run_tests.py +843 -0
  112. package/tests/scripts/test_attestation.py +172 -0
@@ -0,0 +1,181 @@
1
+ """Validate task bundles bound to the scenes changed by a commit."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import argparse
6
+ import json
7
+ import subprocess
8
+ import sys
9
+ from pathlib import Path
10
+
11
+
12
+ def read_json(path: Path) -> dict:
13
+ try:
14
+ return json.loads(path.read_text(encoding="utf-8"))
15
+ except (OSError, json.JSONDecodeError):
16
+ return {}
17
+
18
+
19
+ def has_symlink_component(path: Path, root: Path) -> bool:
20
+ current = path
21
+ while True:
22
+ if current.is_symlink():
23
+ return True
24
+ if current == root:
25
+ return False
26
+ if current == current.parent:
27
+ return True
28
+ current = current.parent
29
+
30
+
31
+ def task_dirs(root: Path, scene: str) -> list[Path]:
32
+ artifacts = root / "artifacts"
33
+ if not artifacts.is_dir() or artifacts.is_symlink():
34
+ return []
35
+ resolved_artifacts = artifacts.resolve()
36
+ matched = []
37
+ for task_path in sorted(artifacts.glob("*/task.json")):
38
+ if has_symlink_component(task_path, root):
39
+ continue
40
+ try:
41
+ if not task_path.resolve().is_relative_to(resolved_artifacts):
42
+ continue
43
+ except (OSError, RuntimeError):
44
+ continue
45
+ task = read_json(task_path)
46
+ if task.get("scene") == scene:
47
+ matched.append(task_path.parent)
48
+ return matched
49
+
50
+
51
+ def main() -> int:
52
+ parser = argparse.ArgumentParser(description=__doc__)
53
+ parser.add_argument("--scenes-file", required=True)
54
+ parser.add_argument("--root", default=".")
55
+ parser.add_argument("--require-attestation", action="store_true")
56
+ args = parser.parse_args()
57
+ root = Path(args.root).resolve()
58
+ report_script = Path(__file__).resolve().with_name("report.py")
59
+ scenes_file = Path(args.scenes_file)
60
+ scenes = [line.strip() for line in scenes_file.read_text(encoding="utf-8").splitlines() if line.strip()]
61
+ if not scenes:
62
+ print("No changed scenes; report completeness check skipped.")
63
+ return 0
64
+
65
+ missing: list[str] = []
66
+ selected: list[str] = []
67
+ required = (
68
+ "task.json",
69
+ "execution-report.json",
70
+ "artifact-manifest.json",
71
+ "handoff.json",
72
+ "issue-register.json",
73
+ "semantic-lint-report.json",
74
+ "semantic-lint-benchmark.json",
75
+ "continuity-report.json",
76
+ "fix-plan.json",
77
+ "evidence-verifier-report.json",
78
+ )
79
+ if args.require_attestation:
80
+ required = required + ("attestation.json", "trust-policy.json")
81
+ attestation_verifier = Path(__file__).resolve().with_name("attestation-verifier.py")
82
+ for scene in scenes:
83
+ matched = task_dirs(root, scene)
84
+ if not matched:
85
+ missing.append(f"{scene}: no artifacts/<task-id>/task.json bound to scene")
86
+ continue
87
+ complete = [task_dir for task_dir in matched if all((task_dir / name).is_file() for name in required)]
88
+ if not complete:
89
+ missing.append(f"{scene}: incomplete task/report/P1 feedback bundle")
90
+ continue
91
+ passing: list[Path] = []
92
+ for task_dir in complete:
93
+ result = subprocess.run(
94
+ [sys.executable, str(report_script), "check", "--task-dir", str(task_dir)],
95
+ cwd=root,
96
+ capture_output=True,
97
+ text=True,
98
+ )
99
+ verification = read_json(task_dir / "evidence-verifier-report.json")
100
+ task = read_json(task_dir / "task.json")
101
+ verifier_ok = (
102
+ verification.get("verified") is True
103
+ and verification.get("approval") is False
104
+ and verification.get("bindings", {}).get("scene") == scene
105
+ and verification.get("bindings", {}).get("task_id") == task.get("task_id")
106
+ )
107
+ attestation_ok = True
108
+ if args.require_attestation:
109
+ attestation_result = subprocess.run(
110
+ [
111
+ sys.executable,
112
+ str(attestation_verifier),
113
+ "--attestation",
114
+ str(task_dir / "attestation.json"),
115
+ "--trust-policy",
116
+ str(task_dir / "trust-policy.json"),
117
+ "--expected-task-id",
118
+ str(task.get("task_id") or ""),
119
+ "--expected-scene",
120
+ scene,
121
+ ],
122
+ cwd=root,
123
+ capture_output=True,
124
+ text=True,
125
+ )
126
+ attestation_doc = read_json(task_dir / "attestation.json")
127
+ verifier_doc = read_json(task_dir / "attestation-verifier-report.json")
128
+ if not verifier_doc:
129
+ try:
130
+ verifier_doc = json.loads(attestation_result.stdout)
131
+ except json.JSONDecodeError:
132
+ verifier_doc = {}
133
+ attestation_ok = (
134
+ attestation_result.returncode == 0
135
+ and verifier_doc.get("verified") is True
136
+ and verifier_doc.get("approval") is False
137
+ and attestation_doc.get("approval") is False
138
+ and verifier_doc.get("bindings", {}).get("scene") == scene
139
+ and verifier_doc.get("bindings", {}).get("task_id") == task.get("task_id")
140
+ )
141
+ if result.returncode == 0 and verifier_ok and attestation_ok:
142
+ passing.append(task_dir)
143
+ if not passing:
144
+ missing.append(f"{scene}: semantic report check failed")
145
+ continue
146
+ state_rank = {"confirmed": 4, "ready_for_pr": 3, "validated": 2, "review_required": 1}
147
+ ranked = sorted(
148
+ passing,
149
+ key=lambda path: (
150
+ state_rank.get(str(read_json(path / "task.json").get("state")), 0),
151
+ str(read_json(path / "task.json").get("updated_at", "")),
152
+ str(read_json(path / "task.json").get("task_id", "")),
153
+ ),
154
+ reverse=True,
155
+ )
156
+ top_task = read_json(ranked[0] / "task.json")
157
+ top_key = (state_rank.get(str(top_task.get("state")), 0), str(top_task.get("updated_at", "")))
158
+ if sum(
159
+ 1
160
+ for path in passing
161
+ if (
162
+ state_rank.get(str(read_json(path / "task.json").get("state")), 0),
163
+ str(read_json(path / "task.json").get("updated_at", "")),
164
+ )
165
+ == top_key
166
+ ) > 1:
167
+ missing.append(f"{scene}: ambiguous passing task bundles share the same state and updated_at")
168
+ else:
169
+ selected.append(f"{scene}={top_task.get('task_id', ranked[0].name)}")
170
+
171
+ if missing:
172
+ print("FAIL: report/handoff contract is incomplete:")
173
+ print("\n".join(f" - {item}" for item in missing))
174
+ return 1
175
+ suffix = f" Selected bundles: {', '.join(selected)}." if selected else ""
176
+ print(f"Report contract passed for {len(scenes)} scene(s).{suffix}")
177
+ return 0
178
+
179
+
180
+ if __name__ == "__main__":
181
+ raise SystemExit(main())