#!/usr/bin/env python3 """ candidate_certification_bridge.py — Bridges entropy exploration candidates into the Lean RRC certification pipeline. Generates a Lean source file containing candidate BraidState fixtures that can be compiled and certified by the existing crossStep → eigensolid_convergence → receipt_invertible theorem chain. Usage: # After running geometric_entropy_explorer.py --batch 50 python3 candidate_certification_bridge.py --candidates-dir --output lean/Candidates.lean Architecture (per AGENTS.md Programming Choice Flow): This script is pure I/O + code generation (Python-owned). No gating, alignment, scorin or promotion decisions. Lean owns all certification. """ import os import json import sys import argparse def to_lean_str(s: str) -> str: """Python string → Lean escaped string literal.""" escaped = s.replace("\\", "\\\\").replace('"', '\\"') return f'"{escaped}"' def to_lean_q16(val: int) -> str: """Q16_16 integer → Lean Q16_16 literal.""" return f"Q16_16.ofRawInt {val}" def to_lean_bool(b: bool) -> str: return "true" if b else "false" def generate_lean_candidates(candidates: list, output_path: str) -> str: """Generate a Lean file with candidate BraidState fixtures.""" lines = [ "import Semantics.BraidEigensolid", "import Semantics.BraidStrand", "import Semantics.BraidBracket", "import Semantics.FixedPoint", "", "open Semantics.BraidEigensolid", "open Semantics.BraidStrand", "open Semantics.BraidBracket", "open Semantics.FixedPoint", "", "namespace Semantics.RRC.EntropyCandidates", "", "/--", " Auto-generated candidate BraidState fixtures from entropy exploration.", f" Source: geometric_entropy_explorer.py ({len(candidates)} candidates)", " These are exploration-phase candidates for Lean certification.", " No promotion or alignment decisions are made here.", "-/", "", ] # Generate each candidate as a named def for i, cand in enumerate(candidates): label = cand.get("label", f"entropy_explorer_{i:03d}") eq_id = cand.get("equation_id", f"rrc_eq_{label}") braid = cand.get("braid_state", cand) # if wrapped strands = braid.get("strands", []) if not strands: continue lines.append(f"/-- Candidate {label}: entropy={cand.get('genesis', {}).get('entropy_final', '?')} -/") # Generate strand entries as a Fin 8 → BraidStrand lambda strand_cases = [] for si, s in enumerate(strands): phase = s.get("phaseAcc", {}) bx = s.get("bracket", {}) strand_cases.append( f" | ⟨{si}, _⟩ => {{ phaseAcc := {{ x := {to_lean_q16(phase.get('x', 0))}, y := {to_lean_q16(phase.get('y', 0))} }}\n" f" , parity := {to_lean_bool(s.get('parity', False))}\n" f" , slot := {s.get('slot', 0)}\n" f" , residue := {to_lean_q16(s.get('residue', 0))}\n" f" , jitter := {to_lean_q16(s.get('jitter', 0))}\n" f" , bracket := {{ lower := {to_lean_q16(bx.get('lower', 0))}\n" f" , upper := {to_lean_q16(bx.get('upper', 0))}\n" f" , gap := {to_lean_q16(bx.get('gap', 0))}\n" f" , kappa := {to_lean_q16(bx.get('kappa', 0))}\n" f" , phi := {to_lean_q16(bx.get('phi', 0))}\n" f" , admissible := {to_lean_bool(bx.get('admissible', False))} }} }}" ) # Build the BraidState definition lines.append(f"def candidate_{label} : BraidState :=") lines.append(f" {{ strands := λ") for sc in strand_cases: lines.append(sc) lines.append(f" , step_count := 0") lines.append(f" }}") lines.append("") # Build the candidate list for batch certification lines.append(f"/-- All {len(candidates)} candidates in a list for batch certification -/") lines.append(f"def allCandidates : List BraidStateSud :=") lines.append(" [") for i, cand in enumerate(candidates): label = cand.get("label", f"entropy_explorer_{i:03d}") lines.append(f" candidate_{label},") lines.append(" ]") lines.append("") # Generate a verification function that runs crossStep on all candidates lines.append("/-- Verify all candidates: run crossStep and check eigensolid convergence -/") lines.append("def verifyAllCandidates : List (String × Bool) :=") lines.append(" allCandidates.map (fun s =>") lines.append(" let s' := crossStep s") lines.append(" let converged := IsEigensolid s'") lines.append(" (s'.step_count.repr, converged)") lines.append(" )") lines.append("") lines.append("end Semantics.RRC.EntropyCandidates") lines.append("") # Join content = "\n".join(lines) with open(output_path, "w") as f: f.write(content) print(f"Generated {output_path} — {len(candidates)} candidates, {len(lines)} lines") return content def load_candidates_from_dir(dir_path: str) -> list: """Load all candidate JSON files from a directory.""" candidates = [] if not os.path.isdir(dir_path): print(f"Directory not found: {dir_path}", file=sys.stderr) return candidates for fname in sorted(os.listdir(dir_path)): if not fname.endswith(".json") or fname == "manifest.json": continue path = os.path.join(dir_path, fname) try: with open(path) as f: cand = json.load(f) label = os.path.splitext(fname)[0] # Strip prefixes that make bad Lean identifiers label = label.replace("candidate_", "").replace("-", "_") cand["label"] = label candidates.append(cand) except (json.JSONDecodeError, KeyError) as e: print(f" Skipping {fname}: {e}") return candidates def main(): parser = argparse.ArgumentParser( description="Entropy candidate → Lean certification bridge" ) parser.add_argument( "--candidates-dir", type=str, required=True, help="Directory with candidate JSON files" ) parser.add_argument( "--output", type=str, required=True, help="Output Lean file path" ) args = parser.parse_args() candidates = load_candidates_from_dir(args.candidates_dir) if not candidates: print("No candidates found.") sys.exit(1) print(f"Loaded {len(candidates)} candidates from {args.candidates_dir}") generate_lean_candidates(candidates, args.output) if __name__ == "__main__": main()