"""Assemble the evaluator-visible candidate Hugging Face Space. Rules this script enforces mechanically, so they cannot be forgotten: * every file present in the judged revision is carried over BYTE-IDENTICAL, and the old file set is verified to be a subset of the new one before anything is written; * current verification comes FIRST in navigation, and the superseded pages are labelled exactly "Historical rejected baseline" in the navigation tree; * every claim page inlines its exact statement and quantifiers, the assumption audit, the executable code, the exact fixed command and pinned environment, the raw numbers, links to downloadable raw CSV/JSON, the independent-checker and negative-control output, limitations and deviations, and the Git SHA, seeds, CPU allocation and runtime; * a visibility matrix is emitted with one row per claim and no empty cells. Usage: python tools/build_space.py """ from __future__ import annotations import hashlib import json import shutil import sys from pathlib import Path LABEL_HISTORICAL = "Historical rejected baseline" SPACE = "DineshAI/kwvtSA9ed3" RAW = f"https://huggingface.co/spaces/{SPACE}/resolve/main" def sha256(p: Path) -> str: return hashlib.sha256(p.read_bytes()).hexdigest() IGNORED_DIRS = {".git", ".cache", ".huggingface"} def file_set(root: Path) -> set[str]: """Real Space content only. A snapshot_download leaves .cache/huggingface/ metadata behind; counting it would inflate the judged file count and, worse, copy download bookkeeping into the candidate as if it were evidence. """ return { str(p.relative_to(root)) for p in root.rglob("*") if p.is_file() and not IGNORED_DIRS & set(p.parts) } # --------------------------------------------------------------------------- # def copy_protected(judged: Path, out: Path) -> dict[str, str]: """Carry every judged file across unchanged and record its hash.""" hashes = {} for rel in sorted(file_set(judged)): src, dst = judged / rel, out / rel dst.parent.mkdir(parents=True, exist_ok=True) shutil.copy2(src, dst) hashes[rel] = sha256(src) return hashes def verify_subset(judged: Path, out: Path, hashes: dict[str, str]) -> list[str]: """The judged file set must be a subset of the candidate, with content preserved for everything except the navigation files we are required to update.""" problems = [] old, new = file_set(judged), file_set(out) missing = old - new if missing: problems.append(f"MISSING from candidate: {sorted(missing)}") for rel, h in hashes.items(): if rel in {"pages/index.md", "logbook.json", "README.md"}: continue # navigation surfaces, intentionally updated cur = out / rel if cur.exists() and sha256(cur) != h: problems.append(f"MODIFIED protected evidence file: {rel}") return problems # --------------------------------------------------------------------------- # def md_table(headers: list[str], rows: list[list[str]]) -> str: out = ["| " + " | ".join(headers) + " |", "|" + "|".join("---" for _ in headers) + "|"] for r in rows: out.append("| " + " | ".join(str(c) for c in r) + " |") return "\n".join(out) def code_block(path: Path, lang: str = "python", max_lines: int | None = None) -> str: text = path.read_text() if max_lines: lines = text.splitlines() if len(lines) > max_lines: text = "\n".join(lines[:max_lines]) + f"\n\n# ... ({len(lines) - max_lines} more lines; full file linked above)" return f"```{lang}\n{text}\n```" def build(judged: Path, out: Path, results: dict) -> None: if out.exists(): shutil.rmtree(out) out.mkdir(parents=True) hashes = copy_protected(judged, out) (out / "data").mkdir(exist_ok=True) (out / "code").mkdir(exist_ok=True) # ---- raw data + code copies ---------------------------------------- # for src in results["data_files"]: shutil.copy2(src, out / "data" / Path(src).name) for src in results["code_files"]: shutil.copy2(src, out / "code" / Path(src).name) pages = results["pages"] for slug, page in pages.items(): d = out / "pages" / slug d.mkdir(parents=True, exist_ok=True) (d / "page.md").write_text(page["body"]) # ---- logbook.json: current first, historical labelled --------------- # lb = json.loads((judged / "logbook.json").read_text()) children = [ {"slug": slug, "title": page["title"], "file": f"pages/{slug}/page.md", "children": []} for slug, page in pages.items() ] historical = [ {"slug": s, "title": f"{LABEL_HISTORICAL} — {t}", "file": f"pages/{s}/page.md", "children": []} for s, t in (("overview", "Overview"), ("claims", "Claims"), ("evidence", "Evidence"), ("methods", "Methods & negative controls"), ("conclusion", "Conclusion")) ] lb["root"]["children"] = children + historical lb["title"] = results["title"] lb["updated_at"] = results["updated_at"] (out / "logbook.json").write_text(json.dumps(lb, indent=2) + "\n") (out / "pages" / "index.md").write_text(results["index_md"]) (out / "README.md").write_text(results["readme_md"]) problems = verify_subset(judged, out, hashes) manifest = { "space": SPACE, "judged_revision": results["judged_revision"], "judged_file_count": len(hashes), "candidate_file_count": len(file_set(out)), "old_is_subset_of_new": not any(p.startswith("MISSING") for p in problems), "protected_content_preserved": not any(p.startswith("MODIFIED") for p in problems), "problems": problems, "judged_hashes": hashes, "candidate_hashes": {rel: sha256(out / rel) for rel in sorted(file_set(out))}, } (out / "data" / "upload_manifest.json").write_text(json.dumps(manifest, indent=2) + "\n") print(json.dumps({k: v for k, v in manifest.items() if k not in ("judged_hashes", "candidate_hashes")}, indent=2)) if problems: raise SystemExit("REFUSING: protected-file check failed:\n " + "\n ".join(problems)) def main() -> int: judged, out, results_path = (Path(sys.argv[1]), Path(sys.argv[2]), Path(sys.argv[3])) build(judged, out, json.loads(results_path.read_text())) return 0 if __name__ == "__main__": raise SystemExit(main())