1"""Assemble the named deliverables of the mandate (§24) from the repository state."""2import os, sys, csv, shutil, subprocess, zipfile, json, datetime3REPO = os.path.abspath(os.path.join(os.path.dirname(__file__), "..")); REL = os.path.join(REPO, "release")4def main():5 shutil.copy(os.path.join(REPO, "paper", "main.pdf"), os.path.join(REL, "WP29_Autonomous_Research_Claude_Code_Boucher.pdf"))6 shutil.copy(os.path.join(REPO, "results", "analysis_data", "runs.csv"), os.path.join(REL, "WP29_Experiment_Results.csv"))7 # human interventions + costs8 rows = list(csv.DictReader(open(os.path.join(REPO, "reports", "interventions.csv"))))9 acc = open(os.path.join(REPO, "reports", "experiment_accounting.md")).read()10 with open(os.path.join(REL, "WP29_Human_Interventions_and_Costs.csv"), "w", newline="") as fh:11 w = csv.writer(fh); w.writerow(["record_type", "id", "timestamp_utc", "actor_or_split", "category", "description", "duration_or_cost", "measurement_basis"])12 for r in rows: w.writerow(["human_intervention", r["intervention_id"], r["timestamp_utc"], r["actor"], r["category"], r["description"], r["duration_minutes"], r["measurement_basis"]])13 import pandas as pd14 s = pd.read_csv(os.path.join(REPO, "results", "analysis_data", "sessions.csv")); rr = pd.read_csv(os.path.join(REPO, "results", "analysis_data", "runs.csv"))15 for sp, d in rr.groupby("split"):16 w.writerow(["inference_cost", sp, "", sp, "inference", f"{len(d)} runs, {int(s.run_id.isin(d.run_id).sum())} sessions", f"{s[s.run_id.isin(d.run_id)].session_cost_usd.sum():.2f} USD", "client list-price estimate (total_cost_usd)"])17 be = json.load(open(os.path.join(REPO, "reports", "builder_effort.json")))18 for k, v in be["subagents"].items(): w.writerow(["builder_subsession", k, "", "builder", "delegated work", k, f"{v['tokens']} tokens / {v['duration_min']:.0f} min", "client task notification"])19 for k, v in be["builder"].items(): w.writerow(["builder_session", k, "", "builder", "builder work", k, v["wall"], v["basis"]])20 # visual evidence appendix (standalone PDF of the gallery)21 tex = r"""\documentclass[11pt]{article}\usepackage[margin=0.8in]{geometry}\usepackage{graphicx}\usepackage{hyperref}\usepackage[T1]{fontenc}\usepackage[utf8]{inputenc}22\DeclareUnicodeCharacter{2192}{$\rightarrow$}\DeclareUnicodeCharacter{2014}{---}\DeclareUnicodeCharacter{2013}{--}23\begin{document}\title{WP29 --- Visual Evidence Appendix}\author{Simon-Pierre Boucher, UQO}\date{October 2026}\maketitle24\noindent Companion to UQO Working Paper 29. All captures are authentic (native \texttt{screencapture}); modes, hashes and selection rules follow the screenshot manifest. Capture failures are listed without images.25\input{../paper/appendices/gallery}\end{document}"""26 os.makedirs(os.path.join(REL, "_visual"), exist_ok=True); open(os.path.join(REL, "_visual", "visual.tex"), "w").write(tex)27 r = subprocess.run(["pdflatex", "-interaction=nonstopmode", "visual.tex"], cwd=os.path.join(REL, "_visual"), capture_output=True, text=True)28 if os.path.exists(os.path.join(REL, "_visual", "visual.pdf")): shutil.copy(os.path.join(REL, "_visual", "visual.pdf"), os.path.join(REL, "WP29_Visual_Evidence_Appendix.pdf"))29 else: print("visual appendix failed", r.stdout[-500:])30 # method + implementation zip31 z = zipfile.ZipFile(os.path.join(REL, "WP29_Method_and_Implementation.zip"), "w", zipfile.ZIP_DEFLATED)32 for d in ["src", "protocol", "configs", "evaluators", "tests", "analysis", "tasks", "README.md", "Makefile", "pyproject.toml", "CLAUDE.md", "reports"]:33 p = os.path.join(REPO, d)34 if os.path.isfile(p): z.write(p, d)35 else:36 for root, _, fs in os.walk(p):37 for f in fs:38 if "__pycache__" in root or f.endswith(".pyc"): continue39 q = os.path.join(root, f); z.write(q, os.path.relpath(q, REPO))40 z.close()41 # final review (concatenate the three sub-session reviews + builder response)42 parts = ["# WP29 — Final review record", "", f"Assembled {datetime.datetime.now(datetime.timezone.utc).isoformat(timespec='seconds')}. Reviewers are Claude Code sub-sessions (claude-fable-5-1) of the same client family as the builder; no human review had occurred at build time. The builder's responses are in `reports/final_review.md`.", ""]43 for f in ["review_systems_isolation.md", "review_statistics_evaluators.md", "review_manuscript.md", "final_review.md"]:44 p = os.path.join(REPO, "reports", f)45 if os.path.exists(p): parts += [f"\n\n---\n\n<!-- {f} -->\n", open(p).read()]46 open(os.path.join(REL, "WP29_Final_Review.md"), "w").write("\n".join(parts))47 print("release assembled:", sorted(os.listdir(REL)))48if __name__ == "__main__": main()49