#!/usr/bin/env python3 """Build a single operator-facing control report for final Devpost submission.""" from __future__ import annotations import argparse import json from datetime import datetime, timezone 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" / "final-submission-control.json" DEFAULT_MD = ROOT / "docs" / "assets" / "final-submission-control.md" EVENT_SNAPSHOT = { "name": "Backblaze Generative Media Hackathon", "devpost_url": "https://backblaze-generative-media.devpost.com/", "rules_url": "https://backblaze-generative-media.devpost.com/rules", "deadline_et": "2026-08-03 17:00 EDT", "deadline_beijing": "2026-08-04 05:00 Asia/Shanghai", "prize_total_usd": 10000, "participant_count_observed": None, "participant_count_checked_at": None, "participant_count_note": "Dynamic Devpost count; use docs/assets/devpost-event-snapshot.json before final public claims.", } EVENT_SNAPSHOT_SCHEMA = "proofframe.devpost_event_snapshot.v1" OPERATOR_COMMANDS = [ "python scripts/final_env_wizard.py --prefill-non-secret --output .env.final.local", "python scripts/final_env_wizard.py --output .env.final.local --missing-only --force", "python scripts/live_env_handoff.py --env-file .env.final.local", "python scripts/run_b2_live_proof.py --env-file .env.final.local --evidence-out docs/assets/b2-live-proof-evidence.json", "python scripts/run_final_live_proof.py --env-file .env.final.local --genblaze-provider local --genblaze-image-model local-svg-v1 --evidence-out docs/assets/final-live-proof-evidence.json", "python scripts/agent_handoff_check.py", "python scripts/public_space_sync.py", "python scripts/demo_storyboard.py --strict-final", 'python scripts/public_video_check.py --video-url "$PROOFFRAME_PUBLIC_VIDEO_URL" --verify-url --strict-final', "python scripts/final_video_publish_kit.py", "python scripts/demo_readiness.py --strict-final", "python scripts/recording_assets.py --verify-public --strict-final", "python scripts/secret_scan.py", 'python scripts/devpost_packet.py --post-live --video-url "$PROOFFRAME_PUBLIC_VIDEO_URL"', "python scripts/devpost_form_kit.py --strict-final", "python scripts/devpost_submission_checklist.py --strict-final", "python scripts/submission_audit.py --strict-final", 'python scripts/devpost_submission_receipt.py --project-url "$PROOFFRAME_DEVPOST_PROJECT_URL" --submitted-at "$PROOFFRAME_DEVPOST_SUBMITTED_AT" --confirmation-note "Devpost accepted/submitted the ProofFrame project."', "python scripts/secret_scan.py", "python scripts/final_submission_control.py --strict-final", "python scripts/final_video_publish_kit.py --strict-final", "python scripts/final_launch_plan.py --strict-final", "python scripts/devpost_submission_preview.py --strict-final", "python scripts/judge_evidence_index.py", "python scripts/judge_decision_brief.py", "python scripts/submission_bundle.py --strict-final", ] CONTROL_HEALTH_REQUIREMENT_IDS = { "devpost_registration", "public_mock_demo", "official_event_snapshot", "agent_handoff", "public_space_sync", "final_launch_plan", "recording_assets", "source_report_schemas", "award_readiness", } def utc_now() -> str: return datetime.now(timezone.utc).replace(microsecond=0).isoformat().replace("+00:00", "Z") 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") or {} return { str(task.get("id", "")).upper(): str(task.get("status", "missing")) for task in tasks.get("tasks", []) } def display_path(path: Path, root: Path) -> str: try: return str(path.resolve().relative_to(root.resolve())) except ValueError: return str(path) def report_summary(path: Path, expected_schema: str | None = None, root: Path = ROOT) -> dict[str, Any]: report = load_json(path) if report is None: return {"present": False, "path": display_path(path, root)} schema_ok = expected_schema is None or report.get("schema") == expected_schema return { "present": True, "path": display_path(path, root), "schema_ok": schema_ok, "schema": report.get("schema"), "mode": report.get("mode"), "ok": report.get("ok"), "score": report.get("score"), "max_score": report.get("max_score"), "final_form_ready": report.get("final_form_ready"), "safe_to_submit": report.get("safe_to_submit"), "final_video_ready": report.get("final_video_ready"), "final_recording_ready": report.get("final_recording_ready"), "mock_recording_ready": report.get("mock_recording_ready"), "public_mock_verified": report.get("public_mock_verified"), "public_video_ready": report.get("public_video_ready"), "current_phase": report.get("current_phase"), "next_command": report.get("next_command"), "next_detail": report.get("next_detail"), "bus_status": (report.get("bus") or {}).get("status"), "active_role_cwd_ok": (report.get("bus") or {}).get("active_role_cwd_ok"), } def evidence_summary(root: Path, relative_path: str, expected: dict[str, Any]) -> dict[str, Any]: path = root / relative_path evidence = load_json(path) if evidence is None: return { "ok": False, "status": "missing", "path": relative_path, "findings": [{"field": relative_path, "detail": "Evidence file is missing or invalid JSON."}], } findings: list[dict[str, str]] = [] for key, expected_value in expected.items(): actual = evidence.get(key) if actual != expected_value: findings.append({"field": key, "detail": f"Expected {expected_value!r}, got {actual!r}."}) for key in ("asset_sha256", "manifest_sha256", "asset_storage_key", "manifest_key"): if not evidence.get(key): findings.append({"field": key, "detail": "Required evidence value is missing."}) return { "ok": not findings, "status": "verified" if not findings else "incomplete", "path": relative_path, "findings": findings, } def credential_summary(root: Path) -> dict[str, Any]: report = load_json(root / "docs" / "assets" / "live-credential-handoff.json") or {} missing = [item.get("id") for item in report.get("required", []) if not item.get("ok")] ready = bool(report.get("ready_for_live_proof")) or bool( report.get("ok") is True and report.get("mode") == "live_env_ready" ) return { "present": bool(report), "mode": report.get("mode"), "ready": ready, "missing_ids": [item for item in missing if item], "secret_policy": "Report records only variable names and presence; credential values are never printed.", } def event_snapshot_summary(root: Path) -> dict[str, Any]: path = root / "docs" / "assets" / "devpost-event-snapshot.json" report = load_json(path) if report is None: return { "present": False, "path": display_path(path, root), "schema_ok": False, "schema": None, "validation_ok": False, "event": EVENT_SNAPSHOT, } validation = report.get("validation", {}) return { "present": True, "path": display_path(path, root), "schema_ok": report.get("schema") == EVENT_SNAPSHOT_SCHEMA, "schema": report.get("schema"), "mode": report.get("mode"), "validation_ok": bool(validation.get("ok")), "submission_open": bool(validation.get("submission_open")), "checked_at": report.get("checked_at"), "age_days": validation.get("age_days"), "event": {**EVENT_SNAPSHOT, **(report.get("event") or {})}, } def requirement( requirement_id: str, label: str, ok: bool, detail: str, evidence: str, ) -> dict[str, Any]: return { "id": requirement_id, "label": label, "ok": ok, "detail": detail, "evidence": evidence, } def build_requirements( *, gate: dict[str, Any], b2_evidence: dict[str, Any], statuses: dict[str, str], form: dict[str, Any], devpost_checklist: dict[str, Any], event_snapshot: dict[str, Any], agent_handoff: dict[str, Any], final_launch_plan: dict[str, Any], public_space_sync: dict[str, Any], storyboard: dict[str, Any], public_video_check: dict[str, Any], demo: dict[str, Any], recording: dict[str, Any], award: dict[str, Any], credentials: dict[str, Any], secret_scan: dict[str, Any], submission_audit: dict[str, Any], devpost_receipt: dict[str, Any], ) -> list[dict[str, Any]]: summaries = { "Devpost form": form, "Devpost submission checklist": devpost_checklist, "Devpost event snapshot": event_snapshot, "Agent handoff": agent_handoff, "Final launch plan": final_launch_plan, "Public Space sync": public_space_sync, "Demo storyboard": storyboard, "Public video check": public_video_check, "Demo readiness": demo, "Recording assets": recording, "Award readiness": award, "Secret scan": secret_scan, "Submission audit": submission_audit, "Devpost submission receipt": devpost_receipt, } bad_schemas = [ f"{name}: {summary.get('schema') or 'missing'}" for name, summary in summaries.items() if not summary.get("present") or not summary.get("schema_ok") ] schemas_ok = not bad_schemas b2_proof_ok = b2_evidence["ok"] or gate["evidence_gate"]["ok"] launch_plan_actionable = bool(final_launch_plan.get("ok")) or bool(final_launch_plan.get("next_command")) return [ requirement( "devpost_registration", "Devpost registration complete", statuses.get("T040") == "done", f"T040 is {statuses.get('T040', 'missing')}.", "tasks.json", ), requirement( "public_mock_demo", "Credential-free public demo is ready", bool(form.get("present")) and bool(form.get("schema_ok")) and form.get("mode") in {"pre_live_form_ready", "final_form_ready"}, f"Devpost form kit mode is {form.get('mode')}.", "docs/assets/devpost-form-kit.json", ), requirement( "devpost_submission_checklist", "Final Devpost web submission checklist is ready", bool(devpost_checklist.get("present")) and bool(devpost_checklist.get("schema_ok")) and bool(devpost_checklist.get("safe_to_submit")), ( f"Devpost submission checklist mode is {devpost_checklist.get('mode')}; " f"safe_to_submit is {devpost_checklist.get('safe_to_submit')}." ), "docs/assets/devpost-submission-checklist.json", ), requirement( "official_event_snapshot", "Official Devpost event snapshot is fresh", bool(event_snapshot.get("present")) and bool(event_snapshot.get("schema_ok")) and bool(event_snapshot.get("validation_ok")) and bool(event_snapshot.get("submission_open")), ( f"Snapshot checked at {event_snapshot.get('checked_at')}; " f"submission open is {event_snapshot.get('submission_open')}; " f"age days is {event_snapshot.get('age_days')}." ), "docs/assets/devpost-event-snapshot.json", ), requirement( "agent_handoff", "Agent handoff metadata points at the current repo", bool(agent_handoff.get("present")) and bool(agent_handoff.get("schema_ok")) and bool(agent_handoff.get("ok")), ( f"Agent handoff mode is {agent_handoff.get('mode')}; ok is {agent_handoff.get('ok')}; " f"bus status is {agent_handoff.get('bus_status')}; " f"active role cwd ok is {agent_handoff.get('active_role_cwd_ok')}." ), "docs/assets/agent-handoff-report.json", ), requirement( "public_space_sync", "Public Space is synced to the current judge-facing demo", bool(public_space_sync.get("present")) and bool(public_space_sync.get("schema_ok")) and bool(public_space_sync.get("ok")), f"Public Space sync mode is {public_space_sync.get('mode')}; ok is {public_space_sync.get('ok')}.", "docs/assets/public-space-sync-report.json", ), requirement( "final_launch_plan", "Final launch plan exposes the current operator step", bool(final_launch_plan.get("present")) and bool(final_launch_plan.get("schema_ok")) and bool(final_launch_plan.get("current_phase")) and launch_plan_actionable, ( f"Launch plan mode is {final_launch_plan.get('mode')}; " f"current phase is {final_launch_plan.get('current_phase')}; " f"next command is {final_launch_plan.get('next_command') or 'none'}." ), "docs/assets/final-launch-plan.json", ), requirement( "recording_assets", "Recording assets are ready", bool(recording.get("present")) and bool(recording.get("schema_ok")) and bool(recording.get("mock_recording_ready")), ( f"Recording assets mode is {recording.get('mode')}; " f"public mock verified is {recording.get('public_mock_verified')}." ), "docs/assets/recording-assets.json", ), requirement( "source_report_schemas", "Control input reports match expected schemas", schemas_ok, "All input report schemas are current." if schemas_ok else "; ".join(bad_schemas), "docs/assets/*.json readiness reports", ), requirement( "b2_live_proof", "Backblaze B2 live proof captured", statuses.get("T020") == "done" and b2_proof_ok, ( f"T020 is {statuses.get('T020', 'missing')}; " f"B2 evidence status is {b2_evidence['status']}; " f"final evidence status is {gate['evidence_gate']['status']}." ), "tasks.json, docs/assets/b2-live-proof-evidence.json, and docs/assets/final-live-proof-evidence.json", ), requirement( "genblaze_live_proof", "Genblaze live proof captured", statuses.get("T021") == "done" and gate["evidence_gate"]["ok"], f"T021 is {statuses.get('T021', 'missing')}; final evidence status is {gate['evidence_gate']['status']}.", "tasks.json and docs/assets/final-live-proof-evidence.json", ), requirement( "credential_handoff", "Live credential handoff is ready", credentials["ready"], f"Credential handoff mode is {credentials.get('mode')}; missing ids: {', '.join(credentials['missing_ids']) or 'none'}.", "docs/assets/live-credential-handoff.json", ), requirement( "public_video", "Final public demo video URL is ready", bool(storyboard.get("present")) and bool(storyboard.get("schema_ok")) and bool(storyboard.get("public_video_ready")), f"Storyboard mode is {storyboard.get('mode')}; public video ready is {storyboard.get('public_video_ready')}.", "docs/assets/demo-storyboard.json", ), requirement( "public_video_check", "Final public demo video URL is accessible and safe", bool(public_video_check.get("present")) and bool(public_video_check.get("schema_ok")) and bool(public_video_check.get("safe_to_submit")), ( f"Public video check mode is {public_video_check.get('mode')}; " f"safe_to_submit is {public_video_check.get('safe_to_submit')}." ), "docs/assets/public-video-check.json", ), requirement( "final_recording", "Final recording gate is ready", bool(demo.get("present")) and bool(demo.get("schema_ok")) and bool(demo.get("final_recording_ready")), f"Demo readiness mode is {demo.get('mode')}; final recording ready is {demo.get('final_recording_ready')}.", "docs/assets/demo-readiness-report.json", ), requirement( "award_readiness", "Award readiness score remains competitive", bool(award.get("present")) and bool(award.get("schema_ok")) and int(award.get("score") or 0) >= 75, f"Award readiness score is {award.get('score')}/{award.get('max_score')}.", "docs/assets/award-readiness-report.json", ), requirement( "final_secret_scan", "Final secret scan is complete", statuses.get("T041A") == "done" and bool(secret_scan.get("present")) and bool(secret_scan.get("schema_ok")) and bool(secret_scan.get("ok")), ( f"T041A is {statuses.get('T041A', 'missing')}; " f"secret scan mode is {secret_scan.get('mode')}; " f"secret scan ok is {secret_scan.get('ok')}." ), "tasks.json and docs/assets/secret-scan-report.json", ), requirement( "final_submission_audit", "Final submission audit is complete", statuses.get("T041") == "done" and bool(submission_audit.get("present")) and bool(submission_audit.get("schema_ok")) and bool(submission_audit.get("ok")), ( f"T041 is {statuses.get('T041', 'missing')}; " f"audit mode is {submission_audit.get('mode')}; " f"audit ok is {submission_audit.get('ok')}." ), "tasks.json and docs/assets/submission-audit-report.json", ), requirement( "devpost_submitted", "Devpost project submitted", statuses.get("T042") == "done" and bool(devpost_receipt.get("present")) and bool(devpost_receipt.get("schema_ok")) and bool(devpost_receipt.get("ok")), ( f"T042 is {statuses.get('T042', 'missing')}; " f"receipt mode is {devpost_receipt.get('mode')}; " f"receipt ok is {devpost_receipt.get('ok')}." ), "tasks.json and docs/assets/devpost-submission-receipt.json", ), ] def next_actions(requirements: list[dict[str, Any]]) -> list[str]: actions: list[str] = [] missing = {item["id"] for item in requirements if not item["ok"]} if "source_report_schemas" in missing: actions.append("Regenerate readiness reports so every control input has the expected schema.") if "credential_handoff" in missing: actions.append("Complete .env.final.local with B2_KEY_ID, B2_APPLICATION_KEY, and Genblaze provider API key values.") if "official_event_snapshot" in missing: actions.append("Refresh the official Devpost event snapshot before recording or submitting.") if "agent_handoff" in missing: actions.append("Run the Agent handoff check so future Codex and Agent Bus sessions use the current repo path.") if "public_space_sync" in missing: actions.append("Run the public Space sync verifier and refresh the HF Space if the runtime/artifacts drift.") if "final_launch_plan" in missing: actions.append("Regenerate the final launch plan so the control report exposes the next safe operator command.") if "b2_live_proof" in missing: actions.append("Run the B2 live proof runner and save sanitized B2 evidence.") if "genblaze_live_proof" in missing: actions.append("Run the final B2 plus Genblaze proof runner and save sanitized final evidence.") if "public_video" in missing or "final_recording" in missing: actions.append("Record and upload the public demo video after live proof is captured.") if "public_video_check" in missing: actions.append("Verify the public video URL with python scripts/public_video_check.py --verify-url --strict-final.") if "recording_assets" in missing: actions.append("Regenerate recording assets and run the public GET-only verifier.") if "devpost_submission_checklist" in missing: actions.append("Regenerate the final Devpost submission checklist after the form kit and final control gates are current.") if "final_secret_scan" in missing: actions.append("Run and mark the final secret scan after live evidence/video assets are ready.") if "final_submission_audit" in missing: actions.append("Run final submission audit after proof, video, and secret scan pass.") if "devpost_submitted" in missing: actions.append("Submit Devpost after every preceding control item is green, then generate the public submission receipt.") return actions[:8] def build_warnings(agent_handoff: dict[str, Any]) -> list[dict[str, str]]: warnings: list[dict[str, str]] = [] bus_status = agent_handoff.get("bus_status") if bus_status == "stale": warnings.append( { "id": "agent_handoff_bus_stale", "level": "warning", "detail": ( "Agent Bus durable team project metadata is stale; AGENTS.md and active role cwd remain the repo authority." ), "evidence": "docs/assets/agent-handoff-report.json", } ) return warnings def build_control_report(root: Path = ROOT) -> dict[str, Any]: root = root.resolve() gate = build_submission_gate(root) statuses = task_statuses(root) form = report_summary(root / "docs" / "assets" / "devpost-form-kit.json", "proofframe.devpost_form_kit.v1", root) devpost_checklist = report_summary( root / "docs" / "assets" / "devpost-submission-checklist.json", "proofframe.devpost_submission_checklist.v1", root, ) event_snapshot = event_snapshot_summary(root) agent_handoff = report_summary( root / "docs" / "assets" / "agent-handoff-report.json", "proofframe.agent_handoff.v1", root, ) public_space_sync = report_summary( root / "docs" / "assets" / "public-space-sync-report.json", "proofframe.public_space_sync.v1", root, ) final_launch_plan = report_summary( root / "docs" / "assets" / "final-launch-plan.json", "proofframe.final_launch_plan.v1", root, ) storyboard = report_summary(root / "docs" / "assets" / "demo-storyboard.json", "proofframe.demo_storyboard.v1", root) public_video_check = report_summary( root / "docs" / "assets" / "public-video-check.json", "proofframe.public_video_check.v1", root, ) demo = report_summary(root / "docs" / "assets" / "demo-readiness-report.json", "proofframe.demo_readiness.v1", root) recording = report_summary(root / "docs" / "assets" / "recording-assets.json", "proofframe.recording_assets.v1", root) award = report_summary(root / "docs" / "assets" / "award-readiness-report.json", "proofframe.award_readiness.v1", root) secret_scan = report_summary( root / "docs" / "assets" / "secret-scan-report.json", "proofframe.secret_scan.v1", root, ) submission_audit = report_summary( root / "docs" / "assets" / "submission-audit-report.json", "proofframe.submission_audit.v1", root, ) devpost_receipt = report_summary( root / "docs" / "assets" / "devpost-submission-receipt.json", "proofframe.devpost_submission_receipt.v1", root, ) b2_evidence = evidence_summary( root, "docs/assets/b2-live-proof-evidence.json", {"ok": True, "storage_backend": "b2", "asset_storage_backend": "b2"}, ) credentials = credential_summary(root) requirements = build_requirements( gate=gate, b2_evidence=b2_evidence, statuses=statuses, form=form, devpost_checklist=devpost_checklist, event_snapshot=event_snapshot, agent_handoff=agent_handoff, final_launch_plan=final_launch_plan, public_space_sync=public_space_sync, storyboard=storyboard, public_video_check=public_video_check, demo=demo, recording=recording, award=award, credentials=credentials, secret_scan=secret_scan, submission_audit=submission_audit, devpost_receipt=devpost_receipt, ) ready = all(item["ok"] for item in requirements) control_ok = all( item["ok"] for item in requirements if item["id"] in CONTROL_HEALTH_REQUIREMENT_IDS ) warnings = build_warnings(agent_handoff) return { "schema": "proofframe.final_submission_control.v1", "created_at": utc_now(), "ok": control_ok, "control_health_ok": control_ok, "mode": "final_submit_ready" if ready else "pre_live_control", "safe_to_submit": ready, "event": event_snapshot["event"], "public_demo_url": "https://adjcjh-backblaze-proofframe.hf.space/?judge=1", "repository_url": "https://github.com/adjcjh777/backblaze-proofframe", "submission_gate": { "ok": gate["ok"], "mode": gate["mode"], "summary": gate["summary"], "evidence_status": gate["evidence_gate"]["status"], "b2_evidence_status": b2_evidence["status"], "packet_status": gate["packet_gate"]["status"], }, "task_statuses": {task: statuses.get(task, "missing") for task in ["T020", "T021", "T040", "T041", "T041A", "T042"]}, "report_inputs": { "devpost_form": form, "devpost_submission_checklist": devpost_checklist, "devpost_event_snapshot": event_snapshot, "agent_handoff": agent_handoff, "public_space_sync": public_space_sync, "final_launch_plan": final_launch_plan, "demo_storyboard": storyboard, "public_video_check": public_video_check, "demo_readiness": demo, "recording_assets": recording, "award_readiness": award, "secret_scan": secret_scan, "b2_live_evidence": b2_evidence, "credential_handoff": credentials, "submission_audit": submission_audit, "devpost_submission_receipt": devpost_receipt, }, "requirements": requirements, "blocking_items": [item for item in requirements if not item["ok"]], "warnings": warnings, "next_actions": next_actions(requirements), "operator_commands": OPERATOR_COMMANDS, "launch_plan": { "mode": final_launch_plan.get("mode"), "current_phase": final_launch_plan.get("current_phase"), "next_command": final_launch_plan.get("next_command"), "next_detail": final_launch_plan.get("next_detail"), "path": final_launch_plan.get("path"), }, "claim_boundary": "Public demo remains local/mock and pre_live_safe until final live B2 plus Genblaze proof is captured.", } def render_markdown(report: dict[str, Any]) -> str: lines = [ "# ProofFrame Final Submission Control", "", f"Mode: `{report['mode']}`", f"Control health OK: `{str(report['control_health_ok']).lower()}`", f"Safe to submit: `{str(report['safe_to_submit']).lower()}`", f"Created: `{report['created_at']}`", f"Public demo: {report['public_demo_url']}", f"Repository: {report['repository_url']}", "", "## Event Snapshot", "", f"- Event: {report['event']['name']}", f"- Deadline: {report['event']['deadline_et']} / {report['event']['deadline_beijing']}", f"- Prize total: `${report['event']['prize_total_usd']}`", f"- Observed participants: `{report['event']['participant_count_observed']}` checked `{report['event']['participant_count_checked_at']}`", f"- Source: {report['event']['devpost_url']}", f"- Note: {report['event']['participant_count_note']}", "", "## Submission Gate", "", f"- Mode: `{report['submission_gate']['mode']}`", f"- Tasks: `{report['submission_gate']['summary']['done']} / {report['submission_gate']['summary']['required']}` done", f"- B2 evidence: `{report['submission_gate']['b2_evidence_status']}`", f"- Live evidence: `{report['submission_gate']['evidence_status']}`", f"- Devpost packet: `{report['submission_gate']['packet_status']}`", "", "## Launch Plan", "", f"- Mode: `{report['launch_plan']['mode']}`", f"- Current phase: `{report['launch_plan']['current_phase']}`", f"- Next command: `{report['launch_plan']['next_command'] or 'none'}`", f"- Detail: {report['launch_plan']['next_detail'] or 'n/a'}", f"- Source: `{report['launch_plan']['path']}`", "", "## Warnings", "", ] if report.get("warnings"): for item in report["warnings"]: lines.append(f"- `{item['id']}`: {item['detail']} Evidence: `{item['evidence']}`") else: lines.append("- None.") lines.extend( [ "", "## Requirements", "", "| Status | Requirement | Detail | Evidence |", "| --- | --- | --- | --- |", ] ) for item in report["requirements"]: status = "OK" if item["ok"] else "PENDING" lines.append(f"| {status} | {item['label']} | {item['detail']} | `{item['evidence']}` |") lines.extend(["", "## Next Actions", ""]) if report["next_actions"]: lines.extend(f"- {action}" for action in report["next_actions"]) else: lines.append("- Ready for final Devpost submit.") lines.extend(["", "## Operator Commands", ""]) lines.append("```bash") lines.extend(report["operator_commands"]) lines.append("```") lines.extend(["", "## Claim Boundary", "", report["claim_boundary"], ""]) return "\n".join(lines) 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 the ProofFrame final submission control report.") 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 submission is ready.") return parser def main() -> None: args = build_parser().parse_args() report = build_control_report(ROOT) write_outputs(report, args.json_out, args.markdown_out) print( json.dumps( { "ok": report["safe_to_submit"] if args.strict_final else report["ok"], "control_health_ok": report["control_health_ok"], "mode": report["mode"], "safe_to_submit": report["safe_to_submit"], "json": str(args.json_out), "markdown": str(args.markdown_out), "blocking_items": [item["id"] for item in report["blocking_items"]], }, indent=2, ) ) if args.strict_final and not report["safe_to_submit"]: raise SystemExit(2) if __name__ == "__main__": main()