"""Persistent sequential prototype run; records failures without hiding them.""" import datetime import hashlib import json import subprocess import sys from pathlib import Path ROOT=Path(__file__).resolve().parents[1] PY=str(ROOT/".venv/bin/python") STATE=ROOT/"artifacts/pipeline" def now():return datetime.datetime.now(datetime.timezone.utc).isoformat() def digest(path): h=hashlib.sha256() with path.open("rb") as f: for block in iter(lambda:f.read(1024*1024),b""):h.update(block) return h.hexdigest() def stage(name,command): marker=STATE/f"{name}.done.json" if marker.exists():return event={"stage":name,"status":"running","started_at":now(),"command":command} (STATE/"status.json").write_text(json.dumps(event,indent=2)+"\n") print(json.dumps(event),flush=True) with (STATE/f"{name}.log").open("a") as log: completed=subprocess.run(command,cwd=ROOT,stdout=log,stderr=subprocess.STDOUT) event.update(finished_at=now(),returncode=completed.returncode,status="complete" if completed.returncode==0 else "failed") (STATE/"status.json").write_text(json.dumps(event,indent=2)+"\n") if completed.returncode:raise RuntimeError(f"{name} failed; inspect {STATE/name}.log") marker.write_text(json.dumps(event,indent=2)+"\n") def main(): STATE.mkdir(parents=True,exist_ok=True) training_command=[PY,"scripts/train.py","--stride","42"] checkpoints=sorted((ROOT/"artifacts/training").glob("epoch-*/state.pt"),key=lambda p:int(p.parent.name.split("-")[-1])) if checkpoints:training_command.extend(["--resume",str(checkpoints[-1].parent)]) stage("train",training_command) stage("export",[PY,"scripts/export_model.py"]) for split in ("calibration_fit","calibration_select"): stage("predict-"+split,[PY,"scripts/predict.py","--input",f"data/prepared/{split}.jsonl","--output",f"artifacts/predictions/{split}.jsonl"]) stage("calibrate",[PY,"scripts/calibrate.py","--fit-gold","data/prepared/calibration_fit.jsonl","--fit-predictions","artifacts/predictions/calibration_fit.jsonl","--select-gold","data/prepared/calibration_select.jsonl","--select-predictions","artifacts/predictions/calibration_select.jsonl","--output","artifacts/bundle/calibration.json"]) bundle=ROOT/"artifacts/bundle" config=json.loads((bundle/"config.json").read_text()) config.update(calibration_file="calibration.json",status="experimental prototype; demonstration threshold only") (bundle/"config.json").write_text(json.dumps(config,indent=2)+"\n") stage("final-parity",[PY,"scripts/check_rust_parity.py","artifacts/bundle","--output","artifacts/bundle/rust-parity.json"]) freeze=STATE/"evaluation-freeze.json" files=[p for p in bundle.rglob("*") if p.is_file()]+[ROOT/"pii/core.py",ROOT/"pii/evaluation.py",ROOT/"scripts/predict.py",ROOT/"rust/target/release/datafog-pii"] hashes={str(p.relative_to(ROOT)):digest(p) for p in files} if freeze.exists(): original=json.loads(freeze.read_text()) if any(hashes.get(path)!=value for path,value in original["sha256"].items()):raise RuntimeError("Frozen evaluation inputs changed; owner-visible diagnostic review required") else:freeze.write_text(json.dumps({"frozen_at":now(),"sha256":hashes,"policy":"No tuning against test results"},indent=2)+"\n") for split,path in (("support-fixtures","data/support-fixtures.jsonl"),("test","data/prepared/test.jsonl")): prediction=f"artifacts/predictions/{split}.jsonl" stage("predict-"+split,[PY,"scripts/predict.py","--input",path,"--output",prediction]) stage("evaluate-"+split,[PY,"scripts/evaluate.py","--gold",path,"--predictions",prediction,"--calibration","artifacts/bundle/calibration.json","--output",f"artifacts/evaluation/{split}.json"]) stage("regex-test",[str(ROOT/"work/regex-env/bin/python"),"scripts/regex_baseline.py","--gold","data/prepared/test.jsonl","--output-dir","artifacts/regex-test","--model-predictions","artifacts/predictions/test.jsonl","--model-calibration","artifacts/bundle/calibration.json"]) stage("benchmark",[PY,"scripts/benchmark_native.py"]) stage("report",[PY,"scripts/report.py"]) (STATE/"status.json").write_text(json.dumps({"status":"complete","finished_at":now(),"next":"Review evidence and final report against contract"},indent=2)+"\n") if __name__=="__main__":main()