backblaze-proofframe / scripts /judge_crosswalk.py
ADJCJH's picture
Sync post-live local Genblaze proof evidence
74ee21a verified
Raw
History Blame Contribute Delete
18.9 kB
#!/usr/bin/env python3
"""Build a judge-facing crosswalk from official criteria to ProofFrame evidence."""
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]
SCHEMA = "proofframe.judge_crosswalk.v1"
DEFAULT_JSON = ROOT / "docs" / "assets" / "judge-crosswalk.json"
DEFAULT_MD = ROOT / "docs" / "assets" / "judge-crosswalk.md"
SOURCE_SCHEMAS = {
"docs/assets/devpost-event-snapshot.json": "proofframe.devpost_event_snapshot.v1",
"docs/assets/judge-brief.json": "proofframe.judge_brief.v1",
"docs/assets/sponsor-fit-audit.json": "proofframe.sponsor_fit_audit.v1",
"docs/assets/award-readiness-report.json": "proofframe.award_readiness.v1",
"docs/assets/final-submission-control.json": "proofframe.final_submission_control.v1",
}
REQUIRED_EVIDENCE_FILES = [
"README.md",
"docs/prd.md",
"docs/spec.md",
"docs/sponsor_fit_matrix.md",
"docs/assets/devpost-form-kit.md",
"docs/assets/devpost-submission-checklist.md",
"docs/assets/public-video-check.md",
"apps/web/index.html",
"scripts/run_b2_live_proof.py",
"scripts/run_final_live_proof.py",
"scripts/submission_audit.py",
]
DEFAULT_CRITERIA = [
"Real-world Utility",
"Production Readiness",
"B2 Storage + Data Orchestration",
"Use of Genblaze",
]
SUBMISSION_REQUIREMENT_LABELS = {
"working_app_url": "Working application URL",
"github_repo_url": "GitHub repository URL",
"demo_video": "Demonstration video",
"video_under_three_minutes": "Video under three minutes",
"public_video_host": "Public video host",
"b2_usage": "Backblaze B2 usage",
"genblaze_usage": "Genblaze usage",
}
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]:
data = load_json(root / "tasks.json") or {}
return {
str(task.get("id", "")).upper(): str(task.get("status", "missing"))
for task in data.get("tasks", [])
}
def source_record(root: Path, relative_path: str, expected_schema: str) -> dict[str, Any]:
report = load_json(root / relative_path)
actual_schema = report.get("schema") if report else None
return {
"path": relative_path,
"present": report is not None,
"expected_schema": expected_schema,
"actual_schema": actual_schema,
"ok": report is not None and actual_schema == expected_schema,
}
def source_reports(root: Path) -> list[dict[str, Any]]:
return [
source_record(root, relative_path, schema)
for relative_path, schema in SOURCE_SCHEMAS.items()
]
def evidence_file_records(root: Path) -> list[dict[str, Any]]:
records = []
for relative_path in REQUIRED_EVIDENCE_FILES:
path = root / relative_path
records.append({"path": relative_path, "present": path.exists()})
return records
def official_criteria(event_snapshot: dict[str, Any] | None) -> list[dict[str, Any]]:
criteria = (event_snapshot or {}).get("rules", {}).get("judging_criteria") or []
if not criteria:
return [{"name": name, "present": False} for name in DEFAULT_CRITERIA]
return [
{"name": str(item.get("name", "")), "present": bool(item.get("present"))}
for item in criteria
]
def criterion_present(criteria: list[dict[str, Any]], name: str) -> bool:
return any(item["name"] == name and item["present"] for item in criteria)
def blocking_items(final_control: dict[str, Any] | None) -> list[dict[str, str]]:
return [
{
"id": str(item.get("id", "")),
"label": str(item.get("label", "")),
"detail": str(item.get("detail", "")),
"evidence": str(item.get("evidence", "")),
}
for item in (final_control or {}).get("blocking_items", [])
]
def requirement_crosswalk(event_snapshot: dict[str, Any] | None) -> list[dict[str, Any]]:
requirements = (event_snapshot or {}).get("rules", {}).get("requirements") or {}
evidence = {
"working_app_url": "Public mock demo URL in README and Devpost form kit",
"github_repo_url": "Public GitHub repository and submission bundle manifest",
"demo_video": "Public video check and storyboard reports; final URL still gated",
"video_under_three_minutes": "Demo storyboard duration gate",
"public_video_host": "Public video check URL rules",
"b2_usage": "B2 adapter, B2 live setup record, and T020 live proof runner",
"genblaze_usage": "Genblaze provider adapter and T021 final live proof runner",
}
return [
{
"id": requirement_id,
"label": SUBMISSION_REQUIREMENT_LABELS.get(requirement_id, requirement_id),
"official_marker_present": bool(present),
"evidence": evidence.get(requirement_id, "Tracked in submission docs."),
}
for requirement_id, present in requirements.items()
]
def row(
*,
criterion_id: str,
criterion: str,
official_present: bool,
evidence: list[str],
safe_claim: str,
final_gate: str,
demo_shot: str,
readiness: str,
) -> dict[str, Any]:
return {
"id": criterion_id,
"criterion": criterion,
"official_present": official_present,
"readiness": readiness,
"evidence": evidence,
"safe_claim": safe_claim,
"final_gate": final_gate,
"demo_shot": demo_shot,
}
def build_rows(criteria: list[dict[str, Any]], statuses: dict[str, str]) -> list[dict[str, Any]]:
b2_done = statuses.get("T020") == "done"
genblaze_done = statuses.get("T021") == "done"
return [
row(
criterion_id="real_world_utility",
criterion="Real-world Utility",
official_present=criterion_present(criteria, "Real-world Utility"),
readiness="public_mock_ready",
evidence=[
"docs/prd.md",
"docs/spec.md",
"apps/web/index.html",
"docs/assets/judge-brief.md",
"Downloadable evidence ZIP from /api/campaigns/{id}/packet.zip",
],
safe_claim=(
"ProofFrame is a working provenance and review desk for generated media, "
"with the public demo running in credential-free local/mock mode."
),
final_gate="Final video must show one creator workflow end-to-end with live proof context.",
demo_shot="Judge Demo packet, asset review, approval state, manifest preview, evidence ZIP.",
),
row(
criterion_id="production_readiness",
criterion="Production Readiness",
official_present=criterion_present(criteria, "Production Readiness"),
readiness="pre_live_fail_closed",
evidence=[
".github/workflows/ci.yml",
"scripts/secret_scan.py",
"scripts/claim_lint.py",
"scripts/final_submission_control.py",
"scripts/submission_audit.py",
],
safe_claim=(
"The repo has fail-closed readiness gates, CI checks, secret scanning, "
"claim linting, and generated submission reports."
),
final_gate="T041A secret scan, T041 strict audit, and T042 receipt must close before submit.",
demo_shot="Final gate dashboard and judge recording slate showing current safe-to-submit state.",
),
row(
criterion_id="b2_storage_data_orchestration",
criterion="B2 Storage + Data Orchestration",
official_present=criterion_present(criteria, "B2 Storage + Data Orchestration"),
readiness="live_verified" if b2_done else "code_ready_live_proof_pending",
evidence=[
"src/proofframe/storage.py",
"docs/assets/b2-live-setup.md",
"scripts/run_b2_live_proof.py",
"docs/assets/live-credential-handoff.md",
"docs/assets/final-submission-control.md",
],
safe_claim=(
"ProofFrame has captured live B2 media and manifest evidence with sanitized object keys and checksums."
if b2_done
else "ProofFrame includes B2-compatible storage paths and B2-ready manifests; completed live B2 proof is not claimed until T020 is done."
),
final_gate=(
"B2 proof is done; final submission still needs public video, audit, scan, and Devpost receipt."
if b2_done
else "T020 must capture sanitized B2 asset and manifest object keys plus checksums."
),
demo_shot="B2 Object Route and manifest checksum fields in the first-screen evidence model.",
),
row(
criterion_id="use_of_genblaze",
criterion="Use of Genblaze",
official_present=criterion_present(criteria, "Use of Genblaze"),
readiness="live_verified" if genblaze_done else "adapter_ready_live_proof_pending",
evidence=[
"src/proofframe/providers.py",
"scripts/run_final_live_proof.py",
".env.final.example",
"docs/assets/live-credential-handoff.md",
"docs/assets/devpost-submission-packet.md",
],
safe_claim=(
"ProofFrame has captured Genblaze Pipeline proof with provider/model metadata and B2-backed manifest output."
if genblaze_done
else "ProofFrame includes a Genblaze-compatible provider path and manifest fields; completed live Genblaze proof is not claimed until T021 is done."
),
final_gate=(
"Genblaze proof is done; final submission still needs public video, audit, scan, and Devpost receipt."
if genblaze_done
else "T021 must capture live provider/model metadata from the final Genblaze path."
),
demo_shot="Genblaze Step, provider/model metadata, and prompt lineage in the manifest preview.",
),
]
def build_crosswalk(root: Path = ROOT) -> dict[str, Any]:
root = root.resolve()
reports = {
relative_path: load_json(root / relative_path)
for relative_path in SOURCE_SCHEMAS
}
event_snapshot = reports["docs/assets/devpost-event-snapshot.json"]
final_control = reports["docs/assets/final-submission-control.json"]
judge_brief = reports["docs/assets/judge-brief.json"]
sponsor_audit = reports["docs/assets/sponsor-fit-audit.json"]
award = reports["docs/assets/award-readiness-report.json"]
statuses = task_statuses(root)
sources = source_reports(root)
evidence_files = evidence_file_records(root)
criteria = official_criteria(event_snapshot)
source_ok = all(item["ok"] for item in sources)
evidence_ok = all(item["present"] for item in evidence_files)
official_ok = bool((event_snapshot or {}).get("validation", {}).get("ok")) and all(
item["present"] for item in criteria
)
safe_to_submit = bool((final_control or {}).get("safe_to_submit"))
ok = source_ok and evidence_ok and official_ok
return {
"schema": SCHEMA,
"created_at": utc_now(),
"mode": (
"final_crosswalk_ready"
if ok and safe_to_submit
else "pre_live_crosswalk_ready"
if ok
else "needs_crosswalk_sources"
),
"ok": ok,
"safe_to_submit": safe_to_submit,
"event": {
"name": (event_snapshot or {}).get("event", {}).get("name", "Backblaze Generative Media Hackathon"),
"deadline_utc": (event_snapshot or {}).get("event", {}).get("deadline_utc"),
"deadline_beijing": (event_snapshot or {}).get("event", {}).get("deadline_beijing"),
"devpost_url": (event_snapshot or {}).get("event", {}).get("devpost_url"),
"rules_url": (event_snapshot or {}).get("event", {}).get("rules_url"),
},
"source_reports": sources,
"missing_evidence_files": [item["path"] for item in evidence_files if not item["present"]],
"official_criteria": criteria,
"submission_requirements": requirement_crosswalk(event_snapshot),
"rows": build_rows(criteria, statuses),
"summary": {
"judge_brief_safe_to_submit": (judge_brief or {}).get("status", {}).get("safe_to_submit"),
"sponsor_fit_mode": (sponsor_audit or {}).get("mode"),
"sponsor_fit_ok": (sponsor_audit or {}).get("ok"),
"award_score": (award or {}).get("score"),
"award_max_score": (award or {}).get("max_score"),
"final_control_mode": (final_control or {}).get("mode"),
},
"task_statuses": {
task_id: statuses.get(task_id, "missing")
for task_id in ["T020", "T021", "T040", "T041", "T041A", "T042"]
},
"blocking_items": blocking_items(final_control),
"claim_boundaries": [
(
"B2 live storage proof may be claimed with sanitized evidence."
if statuses.get("T020") == "done"
else "Do not claim completed B2 live storage until T020 is done and evidence is sanitized."
),
(
"Genblaze Pipeline proof may be claimed with sanitized provider/model evidence."
if statuses.get("T021") == "done"
else "Do not claim completed Genblaze live generation until T021 is done and provider/model metadata is captured."
),
"Do not mark safe_to_submit true until public video, strict audit, final secret scan, and Devpost receipt are complete.",
],
"next_actions": [
"Use this crosswalk as the judge-facing map while recording the final video and filling Devpost.",
(
"After the public video URL is ready, rerun final reports and regenerate this crosswalk."
if statuses.get("T020") == "done" and statuses.get("T021") == "done"
else "After T020/T021 and the public video URL are ready, rerun final reports and regenerate this crosswalk."
),
"Submit only after final_submission_control.py --strict-final and submission_audit.py --strict-final pass.",
],
}
def compact_list(items: list[str]) -> str:
return "<br>".join(f"`{item}`" if item.startswith(("docs/", "scripts/", "src/", "apps/", ".github/")) else item for item in items)
def render_markdown(report: dict[str, Any]) -> str:
lines = [
"# ProofFrame Judge Crosswalk",
"",
f"Created: `{report['created_at']}`",
f"Mode: `{report['mode']}`",
f"OK: `{str(report['ok']).lower()}`",
f"Safe to submit: `{str(report['safe_to_submit']).lower()}`",
f"Event: {report['event']['name']}",
f"Deadline: `{report['event']['deadline_utc']}` / `{report['event']['deadline_beijing']}`",
"",
"## Source Health",
"",
]
for source in report["source_reports"]:
marker = "OK" if source["ok"] else "TODO"
lines.append(
f"- {marker} `{source['path']}` schema `{source['actual_schema']}` "
f"(expected `{source['expected_schema']}`)"
)
if report["missing_evidence_files"]:
lines.extend(["", "Missing evidence files:"])
lines.extend(f"- `{path}`" for path in report["missing_evidence_files"])
lines.extend(
[
"",
"## Official Requirements",
"",
"| Requirement | Official Marker | Evidence |",
"| --- | --- | --- |",
]
)
for requirement in report["submission_requirements"]:
marker = "yes" if requirement["official_marker_present"] else "no"
lines.append(f"| {requirement['label']} | `{marker}` | {requirement['evidence']} |")
lines.extend(
[
"",
"## Judging Crosswalk",
"",
"| Official angle | Current evidence | Safe claim | Final gate | Demo shot |",
"| --- | --- | --- | --- | --- |",
]
)
for item in report["rows"]:
criterion = f"{item['criterion']}<br>`{item['readiness']}`"
evidence = compact_list(item["evidence"])
lines.append(
f"| {criterion} | {evidence} | {item['safe_claim']} | "
f"{item['final_gate']} | {item['demo_shot']} |"
)
lines.extend(["", "## Blocking Items", ""])
if report["blocking_items"]:
for item in report["blocking_items"]:
lines.append(f"- `{item['id']}`: {item['detail']} Evidence: `{item['evidence']}`")
else:
lines.append("- None.")
lines.extend(["", "## Claim Boundaries", ""])
lines.extend(f"- {item}" for item in report["claim_boundaries"])
lines.extend(["", "## Next Actions", ""])
lines.extend(f"- {item}" for item in report["next_actions"])
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 the ProofFrame judge evidence crosswalk.")
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)
return parser
def main() -> None:
args = build_parser().parse_args()
report = build_crosswalk(args.root)
write_outputs(report, args.json_out, args.markdown_out)
print(
json.dumps(
{
"ok": report["ok"],
"mode": report["mode"],
"safe_to_submit": report["safe_to_submit"],
"json": str(args.json_out),
"markdown": str(args.markdown_out),
"blocking_items": len(report["blocking_items"]),
},
indent=2,
)
)
raise SystemExit(0 if report["ok"] else 2)
if __name__ == "__main__":
main()