the-custodian/docs/changes/netkingdom-target-2026-10-17/native-gates-r2/verify.py
codex 403e218dec
Some checks are pending
CI Smoke / host-smoke (push) Waiting to run
CI Smoke / container-smoke (push) Waiting to run
Reject generated-only commits / generated-commit-guard (push) Waiting to run
docs: preserve reviewed NetKingdom session artifacts
2026-11-01 23:28:00 +01:00

68 lines
4.3 KiB
Python

"""Read-only owner-source and primary projection check after source checkpoints."""
from concurrent.futures import ThreadPoolExecutor
from datetime import datetime, timezone
from pathlib import Path
import hashlib
import json
import re
import subprocess
import urllib.request
import yaml
HERE = Path(__file__).resolve().parent
PRIOR = HERE.parent / 'native-gates-r1/native-gate-audit.json'
prior = json.loads(PRIOR.read_text())
def get(path):
return json.load(urllib.request.urlopen('http://127.0.0.1:8000' + path, timeout=30))
def git(root, *args):
return subprocess.check_output(['git', '--no-optional-locks', '-C', str(root), *args], stderr=subprocess.PIPE).decode().strip()
health = get('/state/health')
assert (health['instance_role'], health['instance_label']) == ('primary', 'railiance01')
repos = sorted({Path(r['path']).parents[1].name for r in prior['owner_refs']})
with ThreadPoolExecutor(max_workers=4) as pool:
snapshots = dict(zip(repos, pool.map(lambda repo: get('/repos/' + repo + '/work-record-projection/snapshot'), repos)))
heads = {repo: git(Path('/home/worsch') / repo, 'rev-parse', 'HEAD') for repo in repos}
rows = []
issues = []
for ref in prior['owner_refs']:
path = Path(ref['path']); root = path.parents[1]; repo = root.name
raw = path.read_text(); front = yaml.safe_load(raw.split('---', 2)[1])
if git(root, 'show', heads[repo] + ':' + str(path.relative_to(root))) != raw.strip():
issues.append({'repo': repo, 'record': front['id'], 'field': 'source_not_committed',
'source': str(path), 'actual': heads[repo]})
tasks = [yaml.safe_load(s) for s in re.findall(r'```task\n(.*?)\n```', raw, re.S)]
snapshot = snapshots[repo]
plan = next(p for p in snapshot['workplans'] if p['id'] == front['state_hub_workstream_id'])
if plan['status'] != front['status']:
issues.append({'repo': repo, 'record': front['id'], 'field': 'status', 'source': front['status'], 'actual': plan['status']})
actual = [t for t in snapshot['tasks'] if t.get('workplan_id', t.get('workstream_id')) == plan['id']]
if len(actual) != len(tasks):
issues.append({'repo': repo, 'record': front['id'], 'field': 'task_count', 'source': len(tasks), 'actual': len(actual)})
byid = {t['id']: t for t in actual}
for task in tasks:
t = byid.get(task['state_hub_task_id'])
if t is None:
issues.append({'repo': repo, 'record': task['id'], 'field': 'uuid_or_parent', 'source': task['state_hub_task_id'], 'actual': None})
continue
for field, expected in [('record_id', task['id']), ('status', task['status'])]:
if t.get(field) != expected:
issues.append({'repo': repo, 'record': task['id'], 'uuid': t['id'], 'field': field, 'source': expected, 'actual': t.get(field)})
if 'needs_human' in task:
if bool(t.get('needs_human')) != bool(task['needs_human']):
issues.append({'repo': repo, 'record': task['id'], 'field': 'needs_human', 'source': bool(task['needs_human']), 'actual': bool(t.get('needs_human'))})
rows.append({'repo': repo, 'workplan': front['id'], 'commit': heads[repo],
'path': str(path), 'sha256': hashlib.sha256(path.read_bytes()).hexdigest(),
'status': front['status'], 'flavor': front.get('flavor'),
'tasks': [{'record_id': t['record_id'], 'uuid': t['id'], 'status': t['status']} for t in actual]})
out = {'schema': 'custodian.native-owner-binding-audit.v2',
'observed_at': datetime.now(timezone.utc).isoformat(), 'status': 'verified' if not issues else 'refused',
'primary': 'primary/railiance01', 'workplans': len(rows),
'task_bindings': sum(len(r['tasks']) for r in rows), 'rows': rows,
'prior_audit_sha256': hashlib.sha256(PRIOR.read_bytes()).hexdigest(),
'scope': 'Current source/primary workplan status, task UUID, status, parent and explicit human flag only; no new native gate acceptance inferred.',
'native_target_gates_closed': 0, 'issues': issues}
destination = 'owner-bindings.json' if not issues else 'owner-bindings-refused-r2.json'
(HERE / destination).write_text(json.dumps(out, indent=2) + '\n')
print(json.dumps({**{k: out[k] for k in ['status', 'workplans', 'task_bindings', 'native_target_gates_closed']},
'issues': issues}))
raise SystemExit(bool(issues))