| """Run a frozen list of episodes with explicit per-episode input files. |
| |
| The manifest is JSON list. Each row supplies spec, registry, controller, trace. |
| This runner records failures and never invents task inputs or fills results. |
| """ |
| import argparse,json,subprocess,sys,hashlib |
| from pathlib import Path |
|
|
| def main(): |
| p=argparse.ArgumentParser();p.add_argument('manifest');p.add_argument('--output',required=True);p.add_argument('--dry-run',action='store_true');a=p.parse_args() |
| rows=json.loads(Path(a.manifest).read_text());report=[] |
| for r in rows: |
| hashes={} |
| for key in ['spec','registry','controller']: |
| path=Path(r[key]);hashes[key]=hashlib.sha256(path.read_bytes()).hexdigest() |
| command=[sys.executable,'-m','peppa.cli','run','--spec',r['spec'],'--registry',r['registry'],'--controller',r['controller'],'--trace',r['trace']] |
| status={'episode':r.get('episode',r['trace']),'hashes':hashes,'command':command} |
| if a.dry_run:status['status']='validated paths' |
| else: |
| result=subprocess.run(command,text=True,capture_output=True,check=False) |
| status.update(returncode=result.returncode,stdout=result.stdout,stderr=result.stderr) |
| report.append(status) |
| Path(a.output).parent.mkdir(parents=True,exist_ok=True);Path(a.output).write_text(json.dumps(report,indent=2)) |
| if __name__=='__main__':main() |
|
|