#!/usr/bin/env python3 """Append corrections for published evidence records. Dry run unless --apply. Original progress events are immutable. Corrections never clear the daily due bit and never claim that the missing brief was recovered. """ import argparse import json import os from pathlib import Path import urllib.request from verify_brief_durability import CORRECTION_TYPE, Origin, REPO, ROOT, brief_path, git, load_events def main(): ap = argparse.ArgumentParser(description=__doc__) ap.add_argument('--evidence', default='docs/evidence/2026-09-14-brief-completion-reconciliation.json') ap.add_argument('--hub', default=os.environ.get('STATE_HUB_URL', 'http://127.0.0.1:8000')) ap.add_argument('--apply', action='store_true') args = ap.parse_args() origin = Origin(ROOT) path = Path(args.evidence) if path.is_absolute() or '..' in path.parts: raise ValueError('evidence must be a repository-relative path') if not origin.blob(path.as_posix()): raise RuntimeError('publish the evidence to origin/main before reconciliation') evidence = json.loads(git(ROOT, 'show', f'{origin.sha}:{path.as_posix()}').stdout) originals = {e['id']: e for e in load_events(args.hub, 10)} existing = load_events(args.hub, 10, CORRECTION_TYPE) pending = [] for record in evidence['corrections']: original = originals[record['original_event_id']] d = original['detail'] expected = brief_path(record['date']) if (d.get('repo') != REPO or d.get('date') != record['date'] or d.get('path') != expected or record['path'] != expected or record['disposition'] != 'false_completion' or origin.blob(expected)): raise RuntimeError('original event or missing-file evidence changed; audit again') if any(e.get('detail', {}).get('original_event_id') == original['id'] and origin.correction_recorded(e.get('detail', {})) for e in existing): print(f"already reconciled {record['date']} {original['id']}") continue detail = {k: record[k] for k in ('original_event_id', 'date', 'path', 'disposition', 'reason')} detail.update(repo=REPO, evidence_sha=origin.sha, evidence_path=path.as_posix()) pending.append(dict(event_type=CORRECTION_TYPE, author='codex', summary=f"FI {record['date']}: acknowledge false completion; brief remains missing", detail=detail)) # Validate all records before making the first write. for event in pending: if not args.apply: print(json.dumps(event)) continue original_id = event['detail']['original_event_id'] req = urllib.request.Request(f"{args.hub.rstrip('/')}/progress/", data=json.dumps(event).encode(), method='POST', headers={'Content-Type': 'application/json', 'Idempotency-Key': f'fi-false-completion-{original_id}'}) with urllib.request.urlopen(req, timeout=15) as response: created = json.load(response) print(f"reconciled {event['detail']['date']} correction={created['id']}") return 0 if __name__ == '__main__': raise SystemExit(main())