backblaze-proofframe / scripts /demo_readiness.py
junhaocheng
Sync post-credential gate hardening
f54fafa
Raw
History Blame Contribute Delete
8.66 kB
#!/usr/bin/env python3
"""Build a fail-closed demo recording readiness report."""
from __future__ import annotations
import argparse
import importlib.util
import json
from pathlib import Path
from typing import Any
from proofframe.submission_gate import build_submission_gate
ROOT = Path(__file__).resolve().parents[1]
DEFAULT_JSON = ROOT / "docs" / "assets" / "demo-readiness-report.json"
DEFAULT_MD = ROOT / "docs" / "assets" / "demo-readiness-report.md"
CLAIM_LINT_PATH = ROOT / "scripts" / "claim_lint.py"
CLAIM_LINT_SPEC = importlib.util.spec_from_file_location("claim_lint", CLAIM_LINT_PATH)
claim_lint = importlib.util.module_from_spec(CLAIM_LINT_SPEC)
assert CLAIM_LINT_SPEC.loader is not None
CLAIM_LINT_SPEC.loader.exec_module(claim_lint)
REQUIRED_MOCK_RECORDING_FILES = [
"README.md",
"docs/demo_script.md",
"docs/public_claim_freeze.md",
"docs/assets/devpost-submission-packet.json",
"docs/assets/devpost-submission-packet.md",
"docs/assets/devpost-form-kit.json",
"docs/assets/devpost-form-kit.md",
"docs/assets/submission-bundle-manifest.json",
"docs/assets/submission-bundle-manifest.md",
"docs/assets/live-credential-handoff.json",
"docs/assets/live-credential-handoff.md",
"docs/assets/demo-storyboard.json",
"docs/assets/demo-storyboard.md",
"docs/assets/public-video-check.json",
"docs/assets/public-video-check.md",
"docs/assets/proofframe-local-ui-smoke.png",
"docs/assets/proofframe-review-console-smoke.png",
"docs/assets/proofframe-hf-public-smoke.png",
".env.final.example",
"scripts/claim_lint.py",
"scripts/devpost_form_kit.py",
"scripts/demo_storyboard.py",
"scripts/public_video_check.py",
"scripts/live_env_handoff.py",
"scripts/run_b2_live_proof.py",
"scripts/secret_scan.py",
]
def load_json(path: Path) -> dict[str, Any] | None:
try:
return json.loads(path.read_text(encoding="utf-8"))
except (FileNotFoundError, json.JSONDecodeError):
return None
def task_statuses(root: Path) -> dict[str, str]:
tasks = load_json(root / "tasks.json")
if not tasks:
return {}
return {
str(task.get("id", "")).upper(): str(task.get("status", "missing"))
for task in tasks.get("tasks", [])
}
def file_record(root: Path, relative_path: str) -> dict[str, Any]:
path = root / relative_path
return {
"path": relative_path,
"present": path.exists(),
"bytes": path.stat().st_size if path.exists() else 0,
}
def missing_files(records: list[dict[str, Any]]) -> list[str]:
return [record["path"] for record in records if not record["present"]]
def build_next_actions(
*,
missing: list[str],
claim_ok: bool,
statuses: dict[str, str],
evidence_ok: bool,
) -> list[str]:
actions: list[str] = []
if missing:
actions.append("Restore missing demo recording files before recording.")
if not claim_ok:
actions.append("Run python scripts/claim_lint.py and fix unsafe public claims.")
if statuses.get("T020") != "done":
actions.append("Capture the live Backblaze B2 asset and manifest proof.")
if statuses.get("T021") != "done":
actions.append("Capture the live Genblaze generation proof.")
if not evidence_ok:
actions.append("Write docs/assets/final-live-proof-evidence.json with sanitized live proof.")
if statuses.get("T041A") != "done":
actions.append("Run the final secret scan before recording the final sponsor-backed video.")
return actions[:6]
def build_report(root: Path = ROOT) -> dict[str, Any]:
root = root.resolve()
files = [file_record(root, path) for path in REQUIRED_MOCK_RECORDING_FILES]
missing = missing_files(files)
packet = load_json(root / "docs" / "assets" / "devpost-submission-packet.json") or {}
gate = build_submission_gate(root)
statuses = task_statuses(root)
claim_report = claim_lint.build_claim_report(root)
mock_recording_ready = (
not missing
and claim_report["ok"]
and packet.get("mode") in {"pre_live_safe", "post_live_verified"}
and str(packet.get("demo_url", "")).endswith("/?judge=1")
and statuses.get("T040") == "done"
)
final_recording_ready = (
mock_recording_ready
and gate["evidence_gate"]["ok"]
and statuses.get("T020") == "done"
and statuses.get("T021") == "done"
and statuses.get("T041A") == "done"
)
return {
"schema": "proofframe.demo_readiness.v1",
"mode": "final_ready" if final_recording_ready else "pre_live_mock_ready",
"mock_recording_ready": mock_recording_ready,
"final_recording_ready": final_recording_ready,
"public_demo_url": packet.get("demo_url"),
"devpost_packet_mode": packet.get("mode"),
"task_statuses": {task: statuses.get(task, "missing") for task in ["T020", "T021", "T040", "T041", "T041A", "T042"]},
"submission_gate": {
"ok": gate["ok"],
"mode": gate["mode"],
"summary": gate["summary"],
"evidence_status": gate["evidence_gate"]["status"],
"packet_status": gate["packet_gate"]["status"],
},
"claim_lint": {
"ok": claim_report["ok"],
"mode": claim_report["mode"],
"findings": claim_report["findings"],
},
"files": files,
"missing_files": missing,
"next_actions": build_next_actions(
missing=missing,
claim_ok=claim_report["ok"],
statuses=statuses,
evidence_ok=gate["evidence_gate"]["ok"],
),
}
def render_markdown(report: dict[str, Any]) -> str:
lines = [
"# ProofFrame Demo Readiness",
"",
f"Mode: `{report['mode']}`",
f"Mock recording ready: `{str(report['mock_recording_ready']).lower()}`",
f"Final recording ready: `{str(report['final_recording_ready']).lower()}`",
f"Public demo: {report['public_demo_url']}",
f"Devpost packet mode: `{report['devpost_packet_mode']}`",
"",
"## Current Gate",
"",
f"- Submission gate: `{report['submission_gate']['mode']}`",
f"- Live evidence: `{report['submission_gate']['evidence_status']}`",
f"- Claim lint: `{str(report['claim_lint']['ok']).lower()}`",
"",
"## Task Status",
"",
]
lines.extend(f"- {task}: `{status}`" for task, status in report["task_statuses"].items())
lines.extend(["", "## Required Recording Files", ""])
for item in report["files"]:
marker = "OK" if item["present"] else "MISSING"
lines.append(f"- {marker} `{item['path']}`")
lines.extend(["", "## Next Actions", ""])
if report["next_actions"]:
lines.extend(f"- {action}" for action in report["next_actions"])
else:
lines.append("- Ready to record the final sponsor-backed demo.")
return "\n".join(lines) + "\n"
def write_outputs(report: dict[str, Any], json_path: Path, markdown_path: Path) -> None:
json_path.parent.mkdir(parents=True, exist_ok=True)
markdown_path.parent.mkdir(parents=True, exist_ok=True)
json_path.write_text(json.dumps(report, indent=2) + "\n", encoding="utf-8")
markdown_path.write_text(render_markdown(report), encoding="utf-8")
def build_parser() -> argparse.ArgumentParser:
parser = argparse.ArgumentParser(description="Build a ProofFrame demo recording readiness report.")
parser.add_argument("--root", type=Path, default=ROOT)
parser.add_argument("--json-out", type=Path, default=DEFAULT_JSON)
parser.add_argument("--markdown-out", type=Path, default=DEFAULT_MD)
parser.add_argument(
"--strict-final",
action="store_true",
help="Fail unless final B2/Genblaze recording evidence is ready.",
)
return parser
def main() -> None:
args = build_parser().parse_args()
report = build_report(args.root)
write_outputs(report, args.json_out, args.markdown_out)
print(
json.dumps(
{
"ok": report["final_recording_ready"] if args.strict_final else report["mock_recording_ready"],
"mode": report["mode"],
"json": str(args.json_out),
"markdown": str(args.markdown_out),
"next_actions": report["next_actions"],
},
indent=2,
)
)
raise SystemExit(
0
if (report["final_recording_ready"] if args.strict_final else report["mock_recording_ready"])
else 2
)
if __name__ == "__main__":
main()