the-custodian/docs/changes/netkingdom-target-2026-10-16/browser-adapter-next/finalize_receipt_r3.py
codex b53eed4073
All checks were successful
CI Smoke / host-smoke (push) Successful in 0s
CI Smoke / container-smoke (push) Successful in 2s
Reject generated-only commits / generated-commit-guard (push) Successful in 13s
docs: preserve reviewed NetKingdom session artifacts
2026-11-01 22:46:55 +01:00

32 lines
4.9 KiB
Python

from pathlib import Path
from collections import Counter
import hashlib,json,re,subprocess,urllib.request
import yaml
p=Path(__file__).resolve().parent;owner=Path('/home/worsch/net-kingdom');backup=p/'publication-backup';manifest=json.loads((p/'manifest.json').read_text());wp='e31f3f9d-0748-5137-b0a3-7b3ca46a66eb'
sha=lambda data:'sha256:'+hashlib.sha256(data).hexdigest()
git=lambda *args:subprocess.check_output(['git','-C',str(owner),*args])
get=lambda path:json.load(urllib.request.urlopen('http://127.0.0.1:8000'+path,timeout=10))
final=json.loads((p/'normal-final.stdout.json').read_text());
if final['summary']['fail'] or final['summary']['automation_error']:raise SystemExit('final sync failed')
text=(owner/'workplans/NK-WP-0048-security-case-types.md').read_text();blocks=[yaml.safe_load(block) for block in re.findall(r'```task\n(.*?)```',text,re.S)];rows=get('/tasks/?workplan_id='+wp);by_uuid={r['id']:r for r in rows}
for b in blocks:
if b['state_hub_task_id'] not in by_uuid or by_uuid[b['state_hub_task_id']]['status']!=b['status']:raise SystemExit('task status or UUID mismatch '+b['id'])
original=(p/'preimages/workplans/NK-WP-0048-security-case-types.md').read_text();old={b['id']:b for b in [yaml.safe_load(t) for t in re.findall(r'```task\n(.*?)```',original,re.S)]}
for name in ('NK-WP-0048-T10','NK-WP-0048-T11','NK-WP-0048-T12'):
actual=next(b for b in blocks if b['id']==name)
if actual['state_hub_task_id']!=old[name]['state_hub_task_id']:raise SystemExit('prior UUID changed')
if git('show',':.custodian-brief.md') != (backup/'brief.index').read_bytes() or (owner/'.custodian-brief.md').read_bytes() != (backup/'brief.worktree').read_bytes():raise SystemExit('brief not preserved')
for rel,h in manifest['postimages'].items():
if not rel.startswith('workplans/') and sha((owner/rel).read_bytes())!=h:raise SystemExit('reviewed source drift')
source_commit='0775c2bfa41af236301155bb5a69bbf088bdf823';current=git('rev-parse','HEAD').decode().strip();published=git('rev-parse','origin/main').decode().strip()
if current!=published:raise SystemExit('owner not published')
deps=get('/workplans/'+wp+'/dependencies/')
(p/'projection-tasks.json').write_text(json.dumps([{'id':b['id'],'uuid':b['state_hub_task_id'],'status':b['status'],'depends_on':b.get('depends_on',[])} for b in blocks],indent=2)+'\n')
(p/'projection-dependencies.json').write_text(json.dumps(deps,indent=2)+'\n')
summary='NetKingdom reviewed registration adapter source published as '+source_commit+'; source task/projection receipt '+current+'. 36 fixtures pass with canonical Warden d97652a producer pins. T13 source done; T12/T14/T15/T16 remain todo and T06 native acceptance remains wait. R1 checker-binding refusal preserved, corrected R2/R3. Both generated-only sync hook refusals and exact staged/working brief preservation retained; no credentials or native action.'
task=next(b for b in blocks if b['id']=='NK-WP-0048-T13')['state_hub_task_id']
req=urllib.request.Request('http://127.0.0.1:8000/progress/',data=json.dumps({'summary':summary,'event_type':'note','author':'codex','workplan_id':wp,'task_id':task}).encode(),headers={'Content-Type':'application/json'},method='POST')
progress=json.load(urllib.request.urlopen(req,timeout=10));(p/'owner-progress.json').write_text(json.dumps(progress,indent=2)+'\n')
syncs={name:{'exit_code':json.loads((p/(name+'.exit.json')).read_text())['exit_code'],'summary':json.loads((p/(name+'.stdout.json')).read_text())['summary'],'result':json.loads((p/(name+'.stdout.json')).read_text())['result'],'stderr':(p/(name+'.stderr.txt')).read_text()} for name in ('normal-sync','normal-writeback','normal-final')}
receipt={'schema':'custodian.netkingdom.registration-source-integration.v1','source_commit':source_commit,'publication_record_commit':current,'published_origin':published,'reviewed_r3_manifest_sha256':sha((p/'reviewed-r3/manifest.json').read_bytes()),'root_review':'coordinator-review-r3.json','source_integration':True,'normal_syncs':syncs,'file_task_count':len(blocks),'projected_task_count':len(rows),'status_counts':dict(Counter(b['status'] for b in blocks)),'prior_T10_T11_T12_UUIDs_preserved':True,'reviewed_source_hashes_preserved':True,'exact_original_staged_brief_preserved':True,'exact_original_working_brief_preserved':True,'final_source_hashes':{rel:sha((owner/rel).read_bytes()) for rel in manifest['postimages']},'status_after':git('status','--porcelain=v1').decode(),'tests':{'passed':36,'synthetic':True,'producer_pins_overridden':False},'native_acceptance':False,'new_credential_or_live_action':False,'next_tasks':['NK-WP-0048-T12','NK-WP-0048-T14','NK-WP-0048-T15','NK-WP-0048-T16'],'retained_failed_revision':'rejected-r1/','progress_id':progress.get('id')}
(p/'canonical-integration.json').write_text(json.dumps(receipt,indent=2)+'\n');print(json.dumps({'source_commit':source_commit,'final_commit':current,'count':len(blocks),'statuses':receipt['status_counts'],'progress_id':progress.get('id')}))