freedom-intelligence/scripts/reconcile_brief_completions.py

66 lines
3.2 KiB
Python
Raw Normal View History

#!/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())