1"""Hypothesis registry: parses hypotheses/H*.md (YAML front matter), validates required fields, optionally executes the2linked verification commands, and emits research_log/discovery_log.jsonl + paper/tables/hypotheses.tex.34Front matter schema (all required):5 id, title, axis (A|B|C|D), kind (algebraic|algorithmic|sha256|methodology|formal), statement, motivation, assumptions,6 expected_contribution, related_work (list of bib keys), possible_counterexamples, verification_strategy,7 computational_budget, status (planned|running|evaluated), classification (new_verified|known_reproduced|conditional|8 empirical_observation|rejected|unresolved|pending), evidence (list of result files), verdict (one sentence).9"""10import argparse, datetime, json, pathlib, re, subprocess, sys11import yaml12ROOT = pathlib.Path(__file__).resolve().parents[2]13REQ = ["id", "title", "axis", "kind", "statement", "motivation", "assumptions", "expected_contribution", "related_work",14 "possible_counterexamples", "verification_strategy", "computational_budget", "status", "classification", "evidence", "verdict"]15CLASSES = {"new_verified", "known_reproduced", "conditional", "empirical_observation", "rejected", "unresolved", "pending"}16LABEL = {"new_verified": "New verified result", "known_reproduced": "Known result reproduced", "conditional": "Conditional result",17 "empirical_observation": "Empirical observation", "rejected": "Rejected conjecture", "unresolved": "Unresolved direction", "pending": "Pending"}181920def load():21 hyps = []22 for f in sorted((ROOT / "hypotheses").glob("H*.md")):23 txt = f.read_text()24 m = re.match(r"^---\n(.*?)\n---\n(.*)$", txt, re.S)25 if not m:26 raise SystemExit(f"{f}: missing front matter")27 meta = yaml.safe_load(m.group(1)); meta["body"] = m.group(2).strip(); meta["file"] = f.name28 missing = [k for k in REQ if k not in meta]29 if missing:30 raise SystemExit(f"{f}: missing fields {missing}")31 if meta["classification"] not in CLASSES:32 raise SystemExit(f"{f}: bad classification {meta['classification']}")33 for ev in meta["evidence"]:34 if not (ROOT / ev).exists():35 print(f"WARNING {f.name}: evidence file missing: {ev}")36 hyps.append(meta)37 return hyps383940def tex_escape(s: str) -> str:41 return s.replace("&", r"\&").replace("%", r"\%").replace("_", r"\_").replace("#", r"\#").replace("√", r"$\sqrt{\ }$").replace("≈", r"$\approx$").replace("≤", r"$\le$").replace("≥", r"$\ge$").replace("→", r"$\to$").replace("×", r"$\times$").replace("²", r"$^2$").replace("Ω", r"$\Omega$").replace("φ", r"$\varphi$").replace("λ", r"$\lambda$").replace("β", r"$\beta$").replace("Σ", r"$\Sigma$").replace("σ", r"$\sigma$").replace("δ", r"$\delta$").replace("π", r"$\pi$").replace("ω", r"$\omega$").replace("−", "--").replace("–", "--").replace("—", "---").replace("·", r"$\cdot$").replace("∈", r"$\in$").replace("⊕", r"$\oplus$").replace("≡", r"$\equiv$").replace("∞", r"$\infty$").replace("ℓ", r"$\ell$").replace("ε", r"$\varepsilon$")424344def main():45 ap = argparse.ArgumentParser(); ap.add_argument("--run", action="store_true", help="execute verification commands in front matter `commands`")46 a = ap.parse_args()47 hyps = load()48 counts = {}49 for h in hyps:50 counts[h["kind"]] = counts.get(h["kind"], 0) + 151 log = ROOT / "research_log/discovery_log.jsonl"52 with log.open("w") as fh:53 for h in hyps:54 if a.run and h.get("commands"):55 for cmd in h["commands"]:56 print(f"[{h['id']}] $ {cmd}")57 subprocess.run(cmd, shell=True, check=True, cwd=ROOT)58 fh.write(json.dumps({"id": h["id"], "title": h["title"], "axis": h["axis"], "kind": h["kind"], "status": h["status"],59 "classification": h["classification"], "verdict": h["verdict"], "evidence": h["evidence"],60 "logged_utc": datetime.datetime.now(datetime.timezone.utc).isoformat(timespec="seconds")}) + "\n")61 # LaTeX table (longtable) for the paper62 rows = []63 for h in hyps:64 rows.append(f"{h['id']} & {h['axis']} & {tex_escape(h['title'])} & {LABEL[h['classification']]} & {tex_escape(h['verdict'])} \\\\")65 tex = ["% generated by experiments/automated_discovery/registry.py — do not edit",66 r"\begin{longtable}{@{}p{0.8cm}p{0.5cm}p{4.2cm}p{2.6cm}p{6.4cm}@{}}",67 r"\caption{Hypothesis portfolio and evaluation outcome. Classification vocabulary follows the mandate (new verified result; known result independently reproduced; conditional result; empirical observation; rejected conjecture; unresolved direction).}\label{tab:hypotheses}\\",68 r"\toprule ID & Axis & Hypothesis & Classification & Verdict \\ \midrule \endfirsthead",69 r"\toprule ID & Axis & Hypothesis & Classification & Verdict \\ \midrule \endhead",70 r"\bottomrule \endfoot"] + rows + [r"\end{longtable}"]71 (ROOT / "paper/tables").mkdir(exist_ok=True, parents=True)72 (ROOT / "paper/tables/hypotheses.tex").write_text("\n".join(tex) + "\n")73 summary = {"n_hypotheses": len(hyps), "by_kind": counts,74 "by_classification": {c: sum(1 for h in hyps if h["classification"] == c) for c in CLASSES}}75 (ROOT / "results/automated_discovery").mkdir(exist_ok=True, parents=True)76 (ROOT / "results/automated_discovery/hypothesis_summary.json").write_text(json.dumps(summary, indent=1))77 print(json.dumps(summary, indent=1))787980if __name__ == "__main__":81 main()82