#!/usr/bin/env python3 import json from pathlib import Path ROOT=Path(__file__).resolve().parents[1] OUT=ROOT/'small_fix' errors=[] required=[OUT/'SMALL_FIX.md',OUT/'result.json',OUT/'plans',OUT/'reports',OUT/'patches'] for p in required: if not p.exists(): errors.append(f'missing: {p.relative_to(ROOT)}') res={} if (OUT/'result.json').exists(): try: res=json.loads((OUT/'result.json').read_text(encoding='utf-8')) except Exception as e: errors.append(f'invalid result.json: {e}') plans=list((OUT/'plans').glob('*.md')) if (OUT/'plans').exists() else [] reports=list((OUT/'reports').glob('*_small_fix_report.md')) if (OUT/'reports').exists() else [] patches=list((OUT/'patches').glob('*_small_fix.patch.md')) if (OUT/'patches').exists() else [] if res: if res.get('mode')!='READ_ONLY_SMALL_FIX': errors.append('wrong mode') if res.get('plans')!=len(plans): errors.append('plan count mismatch') if res.get('reports')!=len(reports): errors.append('report count mismatch') if res.get('patch_artifacts')!=len(patches): errors.append('patch count mismatch') for r in reports: txt=r.read_text(encoding='utf-8',errors='ignore').lower() for must in ['root-cause note','proposed minimal fix','verifier','no source files were changed']: if must not in txt: errors.append(f'{r.relative_to(ROOT)} missing {must}') for p in patches: txt=p.read_text(encoding='utf-8',errors='ignore').lower() for must in ['do not apply without samuel approval','verifier']: if must not in txt: errors.append(f'{p.relative_to(ROOT)} missing {must}') for jp in res.get('jobs',[]): p=Path(jp) if not p.exists(): errors.append(f'missing job {jp}'); continue data=json.loads(p.read_text(encoding='utf-8')) if data.get('mode')!='review_only': errors.append(f'{p.name} not review_only') f=' '.join(data.get('forbidden_actions',[])).lower(); a=' '.join(data.get('allowed_actions',[])).lower() if 'modify source project files' not in f or 'commit' not in f or 'push' not in f: errors.append(f'{p.name} missing forbids') if 'projectintaketower' not in a: errors.append(f'{p.name} missing ProjectIntakeTower-only write action') if errors: print('verify_small_fix: failed') for e in errors: print('-',e) raise SystemExit(1) print(f'verify_small_fix: passed ({len(plans)} plans, {len(reports)} reports, {len(patches)} patch artifacts)')