114 lines
7.0 KiB
Python
114 lines
7.0 KiB
Python
"""Bind immutable executed P08 proof to a merge with equal product/input bytes.
|
|
|
|
Only progress-summary line 3 in IMPLEMENTATION_PLAN may differ. Never mutate
|
|
the executed index, receipts, screenshots or full-regression evidence.
|
|
"""
|
|
import difflib
|
|
import hashlib
|
|
import importlib.util
|
|
import json
|
|
import subprocess
|
|
from pathlib import Path
|
|
|
|
E = 'd369d1c399f7506bdb8d980b642ad0da216d14f4'
|
|
R = Path('/Users/korizatou114/Projects/とりにく工房ゲーム制作部')
|
|
W = Path('/Users/korizatou114/Projects/TheTree-managed-worktrees/p08-03-code/P08-03-implement')
|
|
D = R/'docs/plans/original-completion-2026-10-02/execution/p08-03'
|
|
A = R/'artifacts/original-completion-2026-10-02'
|
|
PROOF = A/'p08-03-native-os-d369d1c-r19-calibrated'
|
|
FULL = A/'p08-03-final-regression-d369d1c-r01'
|
|
PLAN = 'docs/plans/original-completion-2026-10-02/IMPLEMENTATION_PLAN.md'
|
|
INDEX_SHA = 'fc66a95ab228e70fd1e1aa40b92f8cb6890f8c9879c2be79940e969cb9a3521b'
|
|
|
|
def sha(p):
|
|
h = hashlib.sha256()
|
|
with Path(p).open('rb') as f:
|
|
for b in iter(lambda:f.read(1048576), b''): h.update(b)
|
|
return h.hexdigest()
|
|
|
|
def git(p, *args):
|
|
return subprocess.check_output(['git','-C',str(p),*args])
|
|
|
|
def need(ok, message):
|
|
if not ok: raise ValueError(message)
|
|
|
|
def load(p): return json.loads(Path(p).read_bytes())
|
|
|
|
def checker():
|
|
spec = importlib.util.spec_from_file_location('p08_executed_checker',W/'tools/check_p08_normal_placement_evidence.py')
|
|
m = importlib.util.module_from_spec(spec); spec.loader.exec_module(m); return m
|
|
|
|
def bind(runner, task_id, wt, final_sha, kind):
|
|
need(runner.config['project']=='the-tree-p08-03-code' and task_id=='P08-03', 'exact task')
|
|
need(kind in ('integration','final'), 'exact verification kind')
|
|
wt=Path(wt); task=runner.task(task_id)
|
|
need(task['submitted']==E and git(W,'rev-parse','HEAD').decode().strip()==E, 'executed revision')
|
|
need(not git(W,'status','--porcelain').strip() and not git(wt,'status','--porcelain').strip(), 'clean sources')
|
|
need(git(wt,'rev-parse','HEAD').decode().strip()==final_sha, 'final revision')
|
|
runner.has_evidence(task_id,'validation',E); runner.has_evidence(task_id,'review',E)
|
|
parents=git(wt,'show','-s','--format=%P',final_sha).decode().split()
|
|
need(len(parents)==2 and parents[1]==E, 'exact two-parent merge')
|
|
main_base=parents[0]
|
|
if kind=='integration': need(git(runner.repo,'rev-parse','main').decode().strip()==main_base, 'current main base')
|
|
else: need(task['candidate']==final_sha and task['main_base']==main_base, 'registered final binding')
|
|
# The merge must be exactly Git's ordinary merge of these two inputs.
|
|
merged=git(wt,'merge-tree','--write-tree',main_base,E).decode().splitlines()[0]
|
|
need(merged==git(wt,'rev-parse',final_sha+'^{tree}').decode().strip(), 'unexpected merge edits')
|
|
need(sha(PROOF/'index.json')==INDEX_SHA, 'immutable raw index')
|
|
idx=load(PROOF/'index.json'); need(idx['commit']==E, 'raw executed SHA')
|
|
gate=checker()
|
|
sources={n:v['sha256'] for n,v in gate.runner.source_refs(wt).items()}
|
|
need(sources==idx['source_map'], 'product/source inventory changed')
|
|
inputs=gate.input_map(wt); old=idx['inputs']
|
|
need(set(inputs)==set(old), 'input roster changed')
|
|
need(all(v==old[n] for n,v in inputs.items() if n!=PLAN), 'non-plan input changed')
|
|
before=(W/PLAN).read_bytes(); after=(wt/PLAN).read_bytes()
|
|
a=before.splitlines(keepends=True); b=after.splitlines(keepends=True)
|
|
need(len(a)==len(b) and a[:2]==b[:2] and a[3:]==b[3:], 'normative plan changed beyond progress line 3')
|
|
need('現在の閉鎖対象はP08-03。'.encode() in a[2] and '現在の閉鎖対象はP08-03。'.encode() in b[2], 'progress summary format')
|
|
need(sha(wt/gate.CONTRACT)==idx['marker_contract_sha256'], 'marker changed')
|
|
deficits=gate.check(W,PROOF); need(deficits==[], 'unchanged original checker failed: '+str(deficits))
|
|
# Rehash every raw proof member even though unchanged checker also does so.
|
|
manifest={}
|
|
for n,pin in idx['artifacts'].items():
|
|
p=PROOF/n
|
|
need(not p.is_symlink() and p.resolve().is_relative_to(PROOF.resolve()), 'unsafe proof path')
|
|
need(p.stat().st_size==pin['bytes'] and sha(p)==pin['sha256'], 'raw proof changed: '+n)
|
|
manifest[n]=pin
|
|
need(sha(D/'FULL_REGRESSION_D369D1C_R01_PASS.json')=='1591f0c7fceac1586f044dd3632f6fd286599d921f8da38b7a07971c3d5a2681', 'immutable full receipt manifest')
|
|
full_pin=load(D/'FULL_REGRESSION_D369D1C_R01_PASS.json')
|
|
for pin in full_pin['references']:
|
|
p=R/pin['path']; need(sha(p)==pin['sha256'] and p.stat().st_size==pin['bytes'], 'full receipt changed')
|
|
f=load(FULL/'runner-input.json'); end=load(FULL/'runner-ended.json')
|
|
need(f['selected_source_map']==sources, 'full regression source mismatch')
|
|
need(end['passed'] and end['engine_ended'] and end['actual_suites']==176 and end['strict_accepted_checks']==20488, 'full regression incomplete')
|
|
need(not any(end[k] for k in ('error','cleanup_errors','guard_errors','missing_marker_paths','remaining_unexecuted_tests','failed_suites')), 'full regression failures')
|
|
native=load(PROOF/'runner-input.json')
|
|
need(f['engine']==native['engine'] and f['engine_sha256']==native['engine_sha256']==sha(f['engine']), 'engine changed')
|
|
# Exact source map and unchanged gate bind fixture, environment subset,
|
|
# command/configuration, teardown, audio and image request provenance.
|
|
out=Path(runner.allocation(task_id)['build_dir'])/('integration-applicability-'+final_sha+'-'+kind+'.json')
|
|
result={'passed':True,'kind':kind,'executed_sha':E,'final_sha':final_sha,'main_base':main_base,
|
|
'raw_index_sha256':INDEX_SHA,'raw_artifacts':manifest,'source_files_equal':len(sources),
|
|
'input_map_executed':old,'input_map_final':inputs,'unchanged_checker_deficits':deficits,
|
|
'plan_executed':before.decode(),'plan_final':after.decode(),
|
|
'plan_diff':''.join(difflib.unified_diff(before.decode().splitlines(True),after.decode().splitlines(True),fromfile=E,tofile=final_sha)),
|
|
'plan_only_progress_summary_changed':True,'full_suites':176,'strict_checks':20488,
|
|
'full_receipt_pins':full_pin['references'],'engine_sha256':f['engine_sha256'],
|
|
'new_native_runs':0,'new_os_runs':0,'raw_evidence_modified':False,
|
|
'scope':'P08-03 macOS acceptance only; diagnostic setup; no Windows acceptance'}
|
|
raw=(json.dumps(result,ensure_ascii=False,indent=2)+'\n').encode()
|
|
if out.exists(): need(out.read_bytes()==raw,'existing binding changed')
|
|
else: out.write_bytes(raw)
|
|
runner.evidence(task_id,kind,final_sha,out,True)
|
|
runner.event(task_id,'immutable_executed_proof_applicability',{'executed_sha':E,'final_sha':final_sha,'kind':kind,'report':str(out),'sha256':sha(out)})
|
|
return out
|
|
|
|
def install(core):
|
|
original=core.Runner.verify
|
|
def verify(runner, task_id, wt, sha_, kind):
|
|
if runner.config['project']=='the-tree-p08-03-code' and task_id=='P08-03' and kind in ('integration','final'):
|
|
return bind(runner,task_id,wt,sha_,kind)
|
|
return original(runner,task_id,wt,sha_,kind)
|
|
core.Runner.verify=verify
|