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.
- package/LICENSE +21 -0
- package/README.md +179 -0
- package/SKILL.md +122 -0
- package/agent-card.json +161 -0
- package/assets/library/ATTRIBUTION.md +6 -0
- package/assets/library/README.md +20 -0
- package/assets/library/avatar-base.svg +19 -0
- package/assets/library/error-alert.json +1 -0
- package/assets/library/rive/ATTRIBUTION.md +12 -0
- package/assets/library/rive/state-machine-test.riv +0 -0
- package/assets/library/success-check.json +1 -0
- package/bin/motionloom.mjs +81 -0
- package/docs/BROWSER-REVIEW-E2E.md +78 -0
- package/docs/CATEGORIES.md +16 -0
- package/docs/CHECKLIST.md +31 -0
- package/docs/DEEP-AUDIT-WORKING-NOTES.md +22 -0
- package/docs/FRAMEWORK-SELECTION.md +26 -0
- package/docs/PROJECT-MANIFEST.md +37 -0
- package/docs/ROADMAP-INTELLIGENCE.md +224 -0
- package/docs/audits/1.10.0-attestation-research-notes.md +19 -0
- package/docs/audits/1.8.0-trust-boundary-hardening.md +56 -0
- package/docs/audits/1.9.0-evidence-interoperability-threat-model.md +37 -0
- package/docs/audits/2.0.0-attestation-acceptance.md +30 -0
- package/docs/releases/1.5.0.md +25 -0
- package/docs/releases/1.6.0.md +27 -0
- package/docs/releases/1.7.0.md +23 -0
- package/docs/releases/1.8.0.md +23 -0
- package/docs/releases/1.9.0.md +21 -0
- package/docs/releases/2.0.0.md +21 -0
- package/docs/releases/npm-publish-from-workstation.md +88 -0
- package/docs/research/AGENT-PROTOCOL-FINDINGS.md +43 -0
- package/examples/report-demo/REPORT.md +50 -0
- package/examples/report-demo/artifact-manifest.json +25 -0
- package/examples/report-demo/decision-log.jsonl +0 -0
- package/examples/report-demo/execution-report.json +70 -0
- package/examples/report-demo/handoff.json +22 -0
- package/examples/report-demo/issue-register.json +5 -0
- package/examples/report-demo/task.json +13 -0
- package/package.json +95 -0
- package/project-context.example.json +26 -0
- package/references/browser-review-contract.md +32 -0
- package/references/dotlottie-source-notes.md +21 -0
- package/references/intelligence-core.md +98 -0
- package/references/reporting-contract.md +38 -0
- package/references/runtime-capability.md +12 -0
- package/references/signed-attestation.md +31 -0
- package/schemas/artifact-manifest.schema.json +22 -0
- package/schemas/browser-review-candidate.schema.json +24 -0
- package/schemas/capability-registry.schema.json +61 -0
- package/schemas/continuity-report.schema.json +52 -0
- package/schemas/evidence-verifier-report.schema.json +35 -0
- package/schemas/execution-report.schema.json +35 -0
- package/schemas/fix-plan.schema.json +44 -0
- package/schemas/handoff.schema.json +19 -0
- package/schemas/motion-ir.schema.json +74 -0
- package/schemas/project-graph.schema.json +70 -0
- package/schemas/provenance.schema.json +76 -0
- package/schemas/runtime-evidence.schema.json +48 -0
- package/schemas/runtime-telemetry.schema.json +50 -0
- package/schemas/scene-manifest.schema.json +45 -0
- package/schemas/semantic-benchmark.schema.json +26 -0
- package/schemas/semantic-lint-report.schema.json +48 -0
- package/schemas/signed-attestation.schema.json +95 -0
- package/schemas/task.schema.json +39 -0
- package/schemas/trust-policy.schema.json +53 -0
- package/scripts/analyze.sh +13 -0
- package/scripts/attestation-keygen.py +63 -0
- package/scripts/attestation-verifier.py +178 -0
- package/scripts/attestation.py +288 -0
- package/scripts/capture-runtime-telemetry.sh +37 -0
- package/scripts/devlab.sh +77 -0
- package/scripts/eval-intelligence.py +377 -0
- package/scripts/evidence-verifier.py +222 -0
- package/scripts/fetch-library.sh +57 -0
- package/scripts/intelligence.py +1543 -0
- package/scripts/manifest.py +61 -0
- package/scripts/pr.sh +103 -0
- package/scripts/quality-gate.py +378 -0
- package/scripts/render-node.mjs +53 -0
- package/scripts/render.sh +37 -0
- package/scripts/report-contract.py +181 -0
- package/scripts/report.py +588 -0
- package/scripts/review-hook.py +199 -0
- package/scripts/runtime-adapters.mjs +187 -0
- package/scripts/skill-doctor.py +150 -0
- package/scripts/to-dotlottie.mjs +99 -0
- package/scripts/to-dotlottie.sh +25 -0
- package/scripts/validate-lottie.py +102 -0
- package/src/core/analyzer.py +226 -0
- package/src/core/snapshot.py +124 -0
- package/src/core/spec.py +240 -0
- package/src/output/browser-review-smoke/animation.json +57 -0
- package/src/output/browser-review-smoke/browser-review.json +21 -0
- package/src/output/browser-review-smoke/manifest.json +22 -0
- package/src/output/browser-review-smoke/motion-spec.json +28 -0
- package/src/output/browser-review-smoke/snapshot/.render-meta.json +10 -0
- package/src/output/browser-review-smoke/snapshot/frame-00.png +0 -0
- package/src/output/browser-review-smoke/snapshot/frame-100.png +0 -0
- package/src/output/browser-review-smoke/snapshot/frame-50.png +0 -0
- package/src/rig/README.md +35 -0
- package/src/rig/cutout_rig.py +211 -0
- package/templates/framer-motion/ui-micro.tsx +50 -0
- package/templates/gsap/scroll-scene.js +54 -0
- package/templates/lottie/README.md +21 -0
- package/templates/lottie/react-component.tsx +82 -0
- package/templates/lottie/scaffold/animation.json +57 -0
- package/templates/lottie/scaffold/character-rig.svg +19 -0
- package/templates/lottie/vanilla.js +68 -0
- package/templates/rive/README.md +36 -0
- package/tests/evals/intelligence-cases.json +131 -0
- package/tests/scripts/run_tests.py +843 -0
- package/tests/scripts/test_attestation.py +172 -0
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""Focused DSSE/trust-policy contract tests for MotionLoom."""
|
|
3
|
+
|
|
4
|
+
from __future__ import annotations
|
|
5
|
+
|
|
6
|
+
import base64
|
|
7
|
+
import hashlib
|
|
8
|
+
import json
|
|
9
|
+
import subprocess
|
|
10
|
+
import sys
|
|
11
|
+
import tempfile
|
|
12
|
+
from datetime import datetime, timedelta, timezone
|
|
13
|
+
from pathlib import Path
|
|
14
|
+
|
|
15
|
+
from cryptography.hazmat.primitives.asymmetric.ed25519 import Ed25519PrivateKey
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
ROOT = Path(__file__).resolve().parents[2]
|
|
19
|
+
BUILDER = ROOT / "scripts/attestation.py"
|
|
20
|
+
VERIFIER = ROOT / "scripts/attestation-verifier.py"
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def iso(value: datetime) -> str:
|
|
24
|
+
return value.astimezone(timezone.utc).isoformat().replace("+00:00", "Z")
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def run(command: list[str]) -> subprocess.CompletedProcess[str]:
|
|
28
|
+
return subprocess.run(command, cwd=ROOT, capture_output=True, text=True)
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def make_statement(**overrides: object) -> dict:
|
|
32
|
+
digest = "a" * 64
|
|
33
|
+
statement = {
|
|
34
|
+
"type": "https://motionloom.dev/attestation/v1",
|
|
35
|
+
"predicate_type": "https://motionloom.dev/predicate/animation-evidence/v1",
|
|
36
|
+
"subject": [{"name": "browser-review-smoke", "digest": {"sha256": digest}}],
|
|
37
|
+
"predicate": {
|
|
38
|
+
"task_id": "browser-review-smoke-task",
|
|
39
|
+
"scene": "browser-review-smoke",
|
|
40
|
+
"context_hash": digest,
|
|
41
|
+
"source_sha256": digest,
|
|
42
|
+
"manifest_sha256": digest,
|
|
43
|
+
"motion_ir_sha256": digest,
|
|
44
|
+
"evidence": {
|
|
45
|
+
"runtime_evidence_sha256": digest,
|
|
46
|
+
"runtime_telemetry_sha256": digest,
|
|
47
|
+
"verifier_report_sha256": digest,
|
|
48
|
+
},
|
|
49
|
+
"provenance_chain_hash": digest,
|
|
50
|
+
"policy_version": "1.0",
|
|
51
|
+
"generated_at": iso(datetime.now(timezone.utc)),
|
|
52
|
+
"builder": {"name": "motionloom-test-builder", "version": "1.0.0"},
|
|
53
|
+
},
|
|
54
|
+
}
|
|
55
|
+
predicate = statement["predicate"]
|
|
56
|
+
assert isinstance(predicate, dict)
|
|
57
|
+
predicate.update(overrides)
|
|
58
|
+
return statement
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
def sha256_file(path: Path) -> str:
|
|
62
|
+
return hashlib.sha256(path.read_bytes()).hexdigest()
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
def telemetry_bundle_sha256(task_dir: Path) -> str:
|
|
66
|
+
entries = []
|
|
67
|
+
for path in sorted(task_dir.glob("runtime-adapters/**/runtime-telemetry.json")):
|
|
68
|
+
entries.append({"path": path.resolve().relative_to(task_dir.resolve()).as_posix(), "sha256": sha256_file(path)})
|
|
69
|
+
body = json.dumps(entries, ensure_ascii=False, sort_keys=True, separators=(",", ":")).encode("utf-8")
|
|
70
|
+
return hashlib.sha256(body).hexdigest()
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
def make_policy(key_id: str, public_key: bytes, status: str = "active") -> dict:
|
|
74
|
+
now = datetime.now(timezone.utc)
|
|
75
|
+
key = {
|
|
76
|
+
"key_id": key_id,
|
|
77
|
+
"algorithm": "ed25519",
|
|
78
|
+
"public_key_base64": base64.b64encode(public_key).decode("ascii"),
|
|
79
|
+
"status": status,
|
|
80
|
+
"valid_from": iso(now - timedelta(days=1)),
|
|
81
|
+
}
|
|
82
|
+
if status == "revoked":
|
|
83
|
+
key["revoked_at"] = iso(now - timedelta(hours=1))
|
|
84
|
+
key["revocation_reason"] = "fixture compromise"
|
|
85
|
+
return {
|
|
86
|
+
"schema_version": "1.0",
|
|
87
|
+
"policy_id": "motionloom-test-policy",
|
|
88
|
+
"trust_domain": "https://motionloom.dev/trust/test",
|
|
89
|
+
"keys": [key],
|
|
90
|
+
"rotation": {"max_key_age_days": 90, "overlap_days": 7, "require_active_signer": True},
|
|
91
|
+
"revocation": {"mode": "local-policy", "fail_closed": True, "sources": ["fixture"]},
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
def main() -> int:
|
|
96
|
+
with tempfile.TemporaryDirectory() as temp_dir:
|
|
97
|
+
temp = Path(temp_dir)
|
|
98
|
+
private_key = Ed25519PrivateKey.generate()
|
|
99
|
+
key_id = "test-signer-v1"
|
|
100
|
+
key_file = temp / "private.key"
|
|
101
|
+
key_file.write_text(base64.b64encode(private_key.private_bytes_raw()).decode("ascii") + "\n", encoding="utf-8")
|
|
102
|
+
statement_file = temp / "statement.json"
|
|
103
|
+
statement_file.write_text(json.dumps(make_statement(), indent=2) + "\n", encoding="utf-8")
|
|
104
|
+
policy_file = temp / "trust-policy.json"
|
|
105
|
+
policy_file.write_text(json.dumps(make_policy(key_id, private_key.public_key().public_bytes_raw()), indent=2) + "\n", encoding="utf-8")
|
|
106
|
+
bundle_file = temp / "attestation.json"
|
|
107
|
+
|
|
108
|
+
built = run([sys.executable, str(BUILDER), "build", "--statement", str(statement_file), "--private-key", str(key_file), "--key-id", key_id, "--output", str(bundle_file)])
|
|
109
|
+
assert built.returncode == 0, built.stderr
|
|
110
|
+
valid = run([sys.executable, str(VERIFIER), "--attestation", str(bundle_file), "--trust-policy", str(policy_file), "--expected-task-id", "browser-review-smoke-task", "--expected-scene", "browser-review-smoke"])
|
|
111
|
+
valid_report = json.loads(valid.stdout)
|
|
112
|
+
assert valid.returncode == 0 and valid_report["verified"] is True and valid_report["approval"] is False
|
|
113
|
+
|
|
114
|
+
wrong_binding = run([sys.executable, str(VERIFIER), "--attestation", str(bundle_file), "--trust-policy", str(policy_file), "--expected-task-id", "different-task"])
|
|
115
|
+
assert wrong_binding.returncode == 14
|
|
116
|
+
|
|
117
|
+
tampered = json.loads(bundle_file.read_text(encoding="utf-8"))
|
|
118
|
+
tampered["envelope"]["payload_base64"] = base64.b64encode(b"tampered").decode("ascii")
|
|
119
|
+
tampered_file = temp / "tampered.json"
|
|
120
|
+
tampered_file.write_text(json.dumps(tampered, indent=2) + "\n", encoding="utf-8")
|
|
121
|
+
tamper_result = run([sys.executable, str(VERIFIER), "--attestation", str(tampered_file), "--trust-policy", str(policy_file)])
|
|
122
|
+
assert tamper_result.returncode == 11
|
|
123
|
+
|
|
124
|
+
revoked_file = temp / "revoked-policy.json"
|
|
125
|
+
revoked_file.write_text(json.dumps(make_policy(key_id, private_key.public_key().public_bytes_raw(), "revoked"), indent=2) + "\n", encoding="utf-8")
|
|
126
|
+
revoked = run([sys.executable, str(VERIFIER), "--attestation", str(bundle_file), "--trust-policy", str(revoked_file)])
|
|
127
|
+
assert revoked.returncode == 13
|
|
128
|
+
|
|
129
|
+
unknown_policy = make_policy("other-signer-v1", private_key.public_key().public_bytes_raw())
|
|
130
|
+
unknown_file = temp / "unknown-policy.json"
|
|
131
|
+
unknown_file.write_text(json.dumps(unknown_policy, indent=2) + "\n", encoding="utf-8")
|
|
132
|
+
unknown = run([sys.executable, str(VERIFIER), "--attestation", str(bundle_file), "--trust-policy", str(unknown_file)])
|
|
133
|
+
assert unknown.returncode == 13
|
|
134
|
+
|
|
135
|
+
scene_dir = ROOT / "src" / "output" / "browser-review-smoke"
|
|
136
|
+
task_dir = ROOT / "artifacts" / "browser-review-smoke-task"
|
|
137
|
+
task_statement = make_statement(
|
|
138
|
+
task_id="browser-review-smoke-task",
|
|
139
|
+
scene="browser-review-smoke",
|
|
140
|
+
context_hash=sha256_file(task_dir / "project-context.json"),
|
|
141
|
+
source_sha256=sha256_file(scene_dir / "animation.json"),
|
|
142
|
+
manifest_sha256=sha256_file(scene_dir / "manifest.json"),
|
|
143
|
+
motion_ir_sha256=sha256_file(task_dir / "motion-ir.json"),
|
|
144
|
+
evidence={
|
|
145
|
+
"runtime_evidence_sha256": sha256_file(task_dir / "runtime-adapters" / "runtime-evidence.json"),
|
|
146
|
+
"runtime_telemetry_sha256": telemetry_bundle_sha256(task_dir),
|
|
147
|
+
"verifier_report_sha256": sha256_file(task_dir / "evidence-verifier-report.json"),
|
|
148
|
+
},
|
|
149
|
+
provenance_chain_hash=sha256_file(task_dir / "provenance.json"),
|
|
150
|
+
)
|
|
151
|
+
smoke_statement_file = temp / "smoke-statement.json"
|
|
152
|
+
smoke_statement_file.write_text(json.dumps(task_statement, indent=2) + "\n", encoding="utf-8")
|
|
153
|
+
smoke_bundle_file = temp / "smoke-attestation.json"
|
|
154
|
+
smoke_built = run([sys.executable, str(BUILDER), "build", "--statement", str(smoke_statement_file), "--private-key", str(key_file), "--key-id", key_id, "--output", str(smoke_bundle_file)])
|
|
155
|
+
assert smoke_built.returncode == 0, smoke_built.stderr
|
|
156
|
+
quality_gate = run([
|
|
157
|
+
sys.executable,
|
|
158
|
+
str(ROOT / "scripts" / "quality-gate.py"),
|
|
159
|
+
"--scene", "browser-review-smoke",
|
|
160
|
+
"--context", str(task_dir / "project-context.json"),
|
|
161
|
+
"--task-dir", str(task_dir),
|
|
162
|
+
"--require-attestation",
|
|
163
|
+
"--attestation", str(smoke_bundle_file),
|
|
164
|
+
"--trust-policy", str(policy_file),
|
|
165
|
+
])
|
|
166
|
+
assert quality_gate.returncode == 0, quality_gate.stdout + quality_gate.stderr
|
|
167
|
+
print("attestation contract tests: PASS")
|
|
168
|
+
return 0
|
|
169
|
+
|
|
170
|
+
|
|
171
|
+
if __name__ == "__main__":
|
|
172
|
+
raise SystemExit(main())
|