freedom-intelligence/scripts/verify_brief_durability.py

193 lines
8.8 KiB
Python
Raw Normal View History

#!/usr/bin/env python3
"""Audit Hub completions against a freshly resolved origin/main Git tree.
Exit 0: checks pass (or explicitly allowed acknowledged historical gaps).
Exit 1: missing/invalid evidence. Exit 2: verification could not run reliably.
--require-date YYYY-MM-DD also requires a valid completion for that day.
"""
from __future__ import annotations
import argparse
from datetime import date
import json
import os
from pathlib import Path
import re
import subprocess
import sys
import urllib.parse
import urllib.request
ROOT = Path(__file__).resolve().parents[1]
REPO = 'freedom-intelligence'
CONTRACT_START = '2026-09-13'
CORRECTION_TYPE = 'fi_daily_brief_reconciliation'
SHA = re.compile(r'[0-9a-f]{40}')
def brief_path(day: str) -> str:
if date.fromisoformat(day).isoformat() != day:
raise ValueError('date must be YYYY-MM-DD')
return f'briefs/{day[:4]}/{day[5:7]}/{day}.md'
def git(root: Path, *args: str, check=True):
result = subprocess.run(['git', '-C', str(root), *args], capture_output=True,
text=True, timeout=120)
if check and result.returncode:
raise RuntimeError(f'git {args[0]} failed: {result.stderr.strip()}')
return result
class Origin:
def __init__(self, root: Path):
self.root = root
# Never trust a stale tracking ref or the checked-out branch.
remote = git(root, 'ls-remote', '--exit-code', 'origin', 'refs/heads/main').stdout.split()
if len(remote) != 2 or not SHA.fullmatch(remote[0]):
raise RuntimeError('origin/main did not resolve to one commit')
self.sha = remote[0]
if git(root, 'cat-file', '-e', f'{self.sha}^{{commit}}', check=False).returncode:
git(root, 'fetch', '--no-tags', '--no-write-fetch-head', 'origin', 'refs/heads/main')
git(root, 'cat-file', '-e', f'{self.sha}^{{commit}}')
def blob(self, path: str, revision: str | None = None) -> bool:
revision = revision or self.sha
if not SHA.fullmatch(revision):
return False
entry = git(self.root, 'ls-tree', revision, '--', path, check=False)
fields = entry.stdout.split()
# Reject symlinks, trees and empty files as completion artifacts.
if entry.returncode or len(fields) != 4 or fields[0] not in {'100644', '100755'}:
return False
return fields[1] == 'blob' and int(git(self.root, 'cat-file', '-s', fields[2]).stdout) > 0
def published(self, revision: str, path: str) -> bool:
if not SHA.fullmatch(revision):
return False
return (git(self.root, 'merge-base', '--is-ancestor', revision, self.sha,
check=False).returncode == 0 and self.blob(path, revision))
def correction_recorded(self, detail: dict) -> bool:
revision = str(detail.get('evidence_sha', ''))
path = str(detail.get('evidence_path', ''))
if not self.published(revision, path):
return False
try:
evidence = json.loads(git(self.root, 'show', f'{revision}:{path}').stdout)
records = evidence['corrections']
except (ValueError, KeyError, TypeError):
return False
fields = ('original_event_id', 'date', 'path', 'disposition', 'reason')
return any(isinstance(row, dict) and all(row.get(k) == detail.get(k) for k in fields)
for row in records)
def load_events(url: str, timeout: float, event_type='fi_daily_brief', page_size=1000) -> list[dict]:
events = []
seen = set()
for offset in range(0, 1_000_000, page_size):
query = urllib.parse.urlencode(dict(event_type=event_type, limit=page_size, offset=offset))
req = urllib.request.Request(f"{url.rstrip('/')}/progress/?{query}",
headers={'Accept': 'application/json'})
with urllib.request.urlopen(req, timeout=timeout) as response:
page = json.load(response)
if not isinstance(page, list):
raise RuntimeError('unexpected Hub progress payload: expected a list')
for event in page:
if not isinstance(event, dict) or not event.get('id') or event['id'] in seen:
raise RuntimeError('malformed or unstable Hub pagination; retry verification')
seen.add(event['id'])
events.extend(page)
if len(page) < page_size:
return events
raise RuntimeError('Hub pagination exceeded safety bound')
def assess(events, corrections, origin, required_date=None):
result = {'origin_sha': origin.sha, 'valid': [], 'legacy': [], 'reconciled': [], 'failures': []}
correction_index = {}
for correction in corrections:
d = correction.get('detail') or {}
if not isinstance(d, dict) or d.get('repo') != REPO:
continue
if (d.get('disposition') == 'false_completion' and d.get('reason')
and origin.correction_recorded(d)):
correction_index.setdefault(d.get('original_event_id'), []).append(d)
for event in events:
d = event.get('detail') or {}
if not isinstance(d, dict):
result['failures'].append({'id': event.get('id'), 'reason': 'invalid event detail'})
continue
if d.get('repo') not in {None, REPO}:
continue
row = {'id': event.get('id'), 'date': d.get('date')}
try:
path = brief_path(d.get('date', ''))
except (ValueError, TypeError):
result['failures'].append(dict(row, reason='invalid or missing date'))
continue
if not origin.blob(path):
acknowledged = any(c.get('date') == d['date'] and c.get('path') == path
for c in correction_index.get(event.get('id'), []))
target = 'reconciled' if acknowledged else 'failures'
result[target].append(dict(row, reason='brief absent from origin/main', path=path))
continue
if d.get('path', path) != path:
result['failures'].append(dict(row, reason='event path does not match date'))
continue
modern = d['date'] >= CONTRACT_START or 'pushed' in d or 'origin_sha' in d
if modern:
if (d.get('pushed') is not True or d.get('path') != path
or not origin.published(str(d.get('origin_sha', '')), path)):
result['failures'].append(dict(row, reason='invalid pushed/origin_sha publication evidence'))
continue
result['valid'].append(row)
else:
# Historic files exist on origin but predate the push-attestation contract.
result['legacy'].append(row)
if required_date and required_date not in {r['date'] for r in result['valid']}:
result['failures'].append({'date': required_date, 'reason': 'required day has no valid published completion'})
return result
def main() -> int:
ap = argparse.ArgumentParser(description=__doc__)
ap.add_argument('--hub', default=os.environ.get('STATE_HUB_URL', 'http://127.0.0.1:8000'))
ap.add_argument('--timeout', type=float, default=8)
ap.add_argument('--require-hub', action='store_true', help='compatibility flag; Hub is always required')
ap.add_argument('--require-date', help='require a valid pushed completion for this exact day')
ap.add_argument('--allow-reconciled', action='store_true', help='acknowledged historical gaps do not fail the audit')
ap.add_argument('--json', action='store_true', help='machine-readable audit result')
args = ap.parse_args()
try:
if args.require_date:
brief_path(args.require_date)
origin = Origin(ROOT)
events = load_events(args.hub, args.timeout)
corrections = load_events(args.hub, args.timeout, CORRECTION_TYPE)
result = assess(events, corrections, origin, args.require_date)
except (OSError, ValueError, RuntimeError, subprocess.SubprocessError) as exc:
if args.json:
print(json.dumps({'ok': False, 'error': str(exc)}))
else:
print(f'ERROR verification unavailable: {exc}', file=sys.stderr)
return 2
result['ok'] = not result['failures'] and (args.allow_reconciled or not result['reconciled'])
if args.json:
print(json.dumps(result, indent=2))
else:
print(f'origin/main: {origin.sha} (resolved from live remote)')
print(f"valid published events: {len(result['valid'])}; legacy files on origin: {len(result['legacy'])}")
for row in result['reconciled']:
print(f"GAP acknowledged false completion: {row['date']} event={row['id']}")
for row in result['failures']:
print(f"FAIL {row.get('date', '-')}: {row['reason']}")
print('PASS (acknowledged gaps remain missing)' if result['ok'] and result['reconciled']
else 'PASS' if result['ok'] else 'FAIL durability audit')
return 0 if result['ok'] else 1
if __name__ == '__main__':
sys.exit(main())