backblaze-proofframe / scripts /final_launch_plan.py
ADJCJH's picture
Sync post-live local Genblaze proof evidence
74ee21a verified
Raw
History Blame Contribute Delete
17.6 kB
#!/usr/bin/env python3
"""Build a no-secret launch plan for the final ProofFrame submission pass."""
from __future__ import annotations
import argparse
import json
from datetime import datetime, timezone
from pathlib import Path
from typing import Any
ROOT = Path(__file__).resolve().parents[1]
DEFAULT_JSON = ROOT / "docs" / "assets" / "final-launch-plan.json"
DEFAULT_MD = ROOT / "docs" / "assets" / "final-launch-plan.md"
SCHEMA = "proofframe.final_launch_plan.v1"
EXPECTED_SECRET_MISSING_IDS = {"b2_key_id", "b2_application_key", "genblaze_api_key"}
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]:
try:
return json.loads(path.read_text(encoding="utf-8"))
except (FileNotFoundError, json.JSONDecodeError):
return {}
def task_statuses(root: Path) -> dict[str, str]:
data = load_json(root / "tasks.json")
return {
str(task.get("id", "")).upper(): str(task.get("status", "missing"))
for task in data.get("tasks", [])
}
def report(root: Path, relative_path: str) -> dict[str, Any]:
data = load_json(root / relative_path)
data["_path"] = relative_path
data["_present"] = bool(data)
return data
def evidence_status(root: Path, relative_path: str) -> dict[str, Any]:
data = load_json(root / relative_path)
ok = bool(data.get("ok") or data.get("safe_to_commit") or data.get("evidence_ok"))
return {
"path": relative_path,
"present": bool(data),
"ok": ok,
"mode": data.get("mode") or data.get("status"),
}
def blocking_ids(final_control: dict[str, Any]) -> set[str]:
ids: set[str] = set()
for item in final_control.get("blocking_items", []):
if isinstance(item, dict):
item_id = item.get("id")
else:
item_id = item
if item_id:
ids.add(str(item_id))
return ids
def make_phase(
*,
phase_id: str,
title: str,
status: str,
detail: str,
command: str | None,
expected_artifacts: list[str],
safe_to_commit_after_scan: list[str] | None = None,
task_updates_after_success: list[str] | None = None,
) -> dict[str, Any]:
return {
"id": phase_id,
"title": title,
"status": status,
"detail": detail,
"command": command,
"expected_artifacts": expected_artifacts,
"safe_to_commit_after_scan": safe_to_commit_after_scan or [],
"task_updates_after_success": task_updates_after_success or [],
}
def build_phases(root: Path, reports: dict[str, dict[str, Any]], statuses: dict[str, str]) -> list[dict[str, Any]]:
handoff = reports["credential_handoff"]
operator = reports["operator_brief"]
final_control = reports["final_control"]
recording = reports["recording_assets"]
submission_audit = reports["submission_audit"]
receipt = reports["devpost_receipt"]
b2_evidence = reports["b2_evidence"]
final_evidence = reports["final_evidence"]
b2_checklist = reports["b2_key_scope_checklist"]
b2_confirmation = b2_checklist.get("pre_key_creation_confirmation") or {}
b2_confirmation_phrase = b2_confirmation.get("required_phrase")
missing_ids = {str(item) for item in handoff.get("missing_ids", [])}
blocks = blocking_ids(final_control)
credential_done = bool(handoff.get("ok"))
credential_ready = bool(
operator.get("ready_for_secret_entry")
and missing_ids == EXPECTED_SECRET_MISSING_IDS
and not credential_done
)
if credential_done:
credential_status = "done"
credential_detail = "Live credential handoff is complete; no missing ids remain."
elif credential_ready:
credential_status = "ready"
credential_detail = "Only expected secret ids are missing; operator can enter them locally."
else:
credential_status = "blocked"
credential_detail = "Credential handoff is missing unexpected setup values; regenerate .env.final.local prefill."
b2_done = statuses.get("T020") == "done" and b2_evidence.get("ok")
b2_status = "done" if b2_done else ("ready" if credential_done else "blocked")
b2_detail = (
"Sanitized B2 evidence is present and T020 is done."
if b2_done
else "Run after live credential handoff is complete."
if credential_done
else "Waiting on credential entry."
)
genblaze_done = statuses.get("T021") == "done" and final_evidence.get("ok")
genblaze_status = "done" if genblaze_done else ("ready" if b2_done else "blocked")
genblaze_detail = (
"Sanitized final B2 plus Genblaze evidence is present and T021 is done."
if genblaze_done
else "Run after the B2-only proof passes so final evidence has storage and generation proof."
if b2_done
else "Waiting on the B2-only proof."
)
video_done = bool(recording.get("final_video_ready"))
video_status = "done" if video_done else ("ready" if genblaze_done else "blocked")
video_detail = (
"Final public video is verified by recording assets."
if video_done
else "Record and upload the public demo after live proof evidence exists."
if genblaze_done
else "Waiting on live B2 plus Genblaze evidence."
)
safety_done = statuses.get("T041A") == "done" and statuses.get("T041") == "done" and bool(
submission_audit.get("ok")
)
safety_status = "done" if safety_done else ("ready" if video_done else "blocked")
safety_detail = (
"Final secret scan and submission audit are signed off."
if safety_done
else "Run final scan and strict submission audit after the public video is ready."
if video_done
else "Waiting on final public video."
)
devpost_done = statuses.get("T042") == "done" and bool(receipt.get("ok"))
devpost_ready = safety_done and blocks <= {"devpost_submitted"}
devpost_status = "done" if devpost_done else ("ready" if devpost_ready else "blocked")
devpost_detail = (
"Devpost receipt is present and T042 is done."
if devpost_done
else "Submit Devpost and generate the public receipt."
if devpost_ready
else "Waiting on all pre-submit gates."
)
return [
make_phase(
phase_id="credential_entry",
title="Enter final credentials locally",
status=credential_status,
detail=credential_detail,
command="python scripts/final_env_wizard.py --output .env.final.local --missing-only --force",
expected_artifacts=[
"docs/assets/b2-key-scope-checklist.md reviewed before key creation",
(
f"B2 pre-key confirmation phrase recorded without secrets: {b2_confirmation_phrase}"
if b2_confirmation_phrase
else "B2 pre-key confirmation phrase recorded without secrets"
),
".env.final.local (git-ignored, never committed)",
],
),
make_phase(
phase_id="b2_live_proof",
title="Capture Backblaze B2 live proof",
status=b2_status,
detail=b2_detail,
command="python scripts/run_b2_live_proof.py --env-file .env.final.local --evidence-out docs/assets/b2-live-proof-evidence.json",
expected_artifacts=["docs/assets/b2-live-proof-evidence.json"],
safe_to_commit_after_scan=["docs/assets/b2-live-proof-evidence.json"],
task_updates_after_success=[
'python3 scripts/task.py done T020 --note "B2 live proof evidence captured in docs/assets/b2-live-proof-evidence.json."'
],
),
make_phase(
phase_id="genblaze_live_proof",
title="Capture final B2 plus Genblaze proof",
status=genblaze_status,
detail=genblaze_detail,
command=(
"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"
),
expected_artifacts=["docs/assets/final-live-proof-evidence.json"],
safe_to_commit_after_scan=["docs/assets/final-live-proof-evidence.json"],
task_updates_after_success=[
'python3 scripts/task.py done T021 --note "Final B2 plus Genblaze live proof evidence captured in docs/assets/final-live-proof-evidence.json."'
],
),
make_phase(
phase_id="public_video",
title="Record and verify public demo video",
status=video_status,
detail=video_detail,
command='python scripts/devpost_packet.py --post-live --video-url "$PROOFFRAME_PUBLIC_VIDEO_URL"',
expected_artifacts=[
"public demo video URL",
"docs/assets/devpost-submission-packet.json",
"docs/assets/devpost-form-kit.json",
"docs/assets/demo-storyboard.json",
"docs/assets/recording-assets.json",
],
safe_to_commit_after_scan=[
"docs/assets/devpost-submission-packet.json",
"docs/assets/devpost-form-kit.json",
"docs/assets/demo-storyboard.json",
"docs/assets/recording-assets.json",
],
),
make_phase(
phase_id="final_safety_audit",
title="Run final secret scan and submission audit",
status=safety_status,
detail=safety_detail,
command="python scripts/secret_scan.py && python scripts/submission_audit.py --strict-final",
expected_artifacts=[
"docs/assets/secret-scan-report.json",
"docs/assets/submission-audit-report.json",
],
safe_to_commit_after_scan=[
"docs/assets/secret-scan-report.json",
"docs/assets/submission-audit-report.json",
],
task_updates_after_success=[
'python3 scripts/task.py done T041A --note "Final secret scan clear after live proof and public video."',
'python3 scripts/task.py done T041 --note "Final submission audit passed after live proof and public video."',
],
),
make_phase(
phase_id="devpost_submit",
title="Submit Devpost and capture receipt",
status=devpost_status,
detail=devpost_detail,
command='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."',
expected_artifacts=["docs/assets/devpost-submission-receipt.json"],
safe_to_commit_after_scan=["docs/assets/devpost-submission-receipt.json"],
task_updates_after_success=[
'python3 scripts/task.py done T042 --note "Devpost project submitted and public receipt captured."'
],
),
]
def first_open_phase(phases: list[dict[str, Any]]) -> dict[str, Any] | None:
for phase in phases:
if phase["status"] != "done":
return phase
return None
def build_report(root: Path = ROOT) -> dict[str, Any]:
root = root.resolve()
statuses = task_statuses(root)
reports = {
"credential_handoff": report(root, "docs/assets/live-credential-handoff.json"),
"operator_brief": report(root, "docs/assets/final-operator-brief.json"),
"b2_key_scope_checklist": report(root, "docs/assets/b2-key-scope-checklist.json"),
"final_control": report(root, "docs/assets/final-submission-control.json"),
"recording_assets": report(root, "docs/assets/recording-assets.json"),
"submission_audit": report(root, "docs/assets/submission-audit-report.json"),
"devpost_receipt": report(root, "docs/assets/devpost-submission-receipt.json"),
"b2_evidence": evidence_status(root, "docs/assets/b2-live-proof-evidence.json"),
"final_evidence": evidence_status(root, "docs/assets/final-live-proof-evidence.json"),
}
phases = build_phases(root, reports, statuses)
open_phase = first_open_phase(phases)
done_count = sum(1 for phase in phases if phase["status"] == "done")
ready_count = sum(1 for phase in phases if phase["status"] == "ready")
blocked_count = sum(1 for phase in phases if phase["status"] == "blocked")
ok = open_phase is None
if ok:
mode = "submitted"
elif open_phase["status"] == "ready":
mode = f"ready_for_{open_phase['id']}"
else:
mode = f"blocked_at_{open_phase['id']}"
return {
"schema": SCHEMA,
"created_at": utc_now(),
"ok": ok,
"mode": mode,
"repo_root": str(root),
"current_phase": open_phase["id"] if open_phase else "complete",
"summary": {
"total": len(phases),
"done": done_count,
"ready": ready_count,
"blocked": blocked_count,
},
"task_statuses": {
task: statuses.get(task, "missing")
for task in ["T020", "T021", "T040", "T041", "T041A", "T042"]
},
"phases": phases,
"next_command": open_phase.get("command") if open_phase else None,
"next_detail": open_phase.get("detail") if open_phase else "Final launch is complete.",
"safety_policy": {
"no_secret_values_in_report": True,
"never_commit": [
".env.final.local",
"Backblaze key IDs or application keys",
"Genblaze provider API keys",
"Devpost cookies or browser session files",
"raw signed URLs or provider temporary URLs",
],
"source_env_files": False,
},
}
def render_markdown(plan: dict[str, Any]) -> str:
lines = [
"# ProofFrame Final Launch Plan",
"",
f"Mode: `{plan['mode']}`",
f"Current phase: `{plan['current_phase']}`",
f"Complete: `{str(plan['ok']).lower()}`",
(
f"Progress: `{plan['summary']['done']} / {plan['summary']['total']}` done; "
f"`{plan['summary']['ready']}` ready, `{plan['summary']['blocked']}` blocked."
),
"",
"## Next Command",
"",
]
if plan.get("next_command"):
lines.extend(["```bash", plan["next_command"], "```"])
lines.append(plan["next_detail"])
else:
lines.append("- Final launch is complete.")
lines.extend(["", "## Phases", ""])
for phase in plan["phases"]:
lines.append(f"### {phase['id']} - {phase['title']}")
lines.append(f"- Status: `{phase['status']}`")
lines.append(f"- Detail: {phase['detail']}")
if phase.get("command"):
lines.extend(["- Command:", "```bash", phase["command"], "```"])
lines.append("- Expected artifacts:")
lines.extend(f" - `{artifact}`" for artifact in phase["expected_artifacts"])
if phase["safe_to_commit_after_scan"]:
lines.append("- Safe to commit after scan:")
lines.extend(f" - `{artifact}`" for artifact in phase["safe_to_commit_after_scan"])
if phase["task_updates_after_success"]:
lines.append("- Task ledger updates after success:")
lines.extend(f" - `{command}`" for command in phase["task_updates_after_success"])
lines.append("")
lines.extend(["## Safety Policy", ""])
lines.append("- This report contains command strings and artifact paths only; no secret values.")
lines.append("- Do not commit:")
lines.extend(f" - {item}" for item in plan["safety_policy"]["never_commit"])
lines.append("- Do not source `.env.final.local`; use the parser-based `--env-file` commands.")
return "\n".join(lines).rstrip() + "\n"
def write_outputs(plan: 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(plan, indent=2) + "\n", encoding="utf-8")
markdown_path.write_text(render_markdown(plan), encoding="utf-8")
def build_parser() -> argparse.ArgumentParser:
parser = argparse.ArgumentParser(description="Build a no-secret final launch plan.")
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 every launch phase is done.")
return parser
def main() -> None:
args = build_parser().parse_args()
plan = build_report(args.root)
write_outputs(plan, args.json_out, args.markdown_out)
print(
json.dumps(
{
"ok": plan["ok"],
"mode": plan["mode"],
"json": str(args.json_out),
"markdown": str(args.markdown_out),
"current_phase": plan["current_phase"],
"next_command": plan["next_command"],
},
indent=2,
)
)
if args.strict_final and not plan["ok"]:
raise SystemExit(2)
if __name__ == "__main__":
main()