#!/usr/bin/env python3 """Apply only the approved R03 documentary addendum; never alter frozen source. Exit 0 means deterministic composition/validation succeeded, NOT semantic approval. The unchanged R01 verifier is used for baseline source checks only, without any adjudication. No candidate, Action 3/4, or old unit-test suite is executed. """ from __future__ import annotations import argparse, copy, hashlib, importlib.util, json, sys, zipfile from pathlib import Path PRIMARY_SHA = 'd0054ef48173343dc3ba7d08b1a3bb031019b3f0b459c44f8546ae86a27d2c8b' BASE_RECEIPT_SHA = '3893f7cae4a710cb2fd20de405e769b419e5d46b9775c6c55022cbab3a03c3fb' GL = 'controls/VPL_V5_06_GUIDELINE_REGISTRY_R02.json' AGENT = 'controls/VPL_V5_06_AGENT_REGISTRY_R02.json' O1 = 'O1_ADOPTED_AMENDMENTS.md' V4_GL = 'Treat model persistence as untrusted. Load every canonical V4 operating control with equal persistence priority; evaluate trigger/risk/applicability only after complete load. Prove semantic completeness with manifest/receipt, balanced challenge coverage and immediate behavioral test. Self-report, recap or selective retrieval is not evidence. Consequential mutation requires PERSISTENCE_STATE=CURRENT.' APPROVAL = 'Approve replacing the A01/M26 mandatory-when-proven 15-minute Hostinger producer with invocation-bound V5 persistence. Cron is optional and cannot establish CURRENT. Retain every V4 GL-15 safeguard; V5 GL-15 is additive. This is design approval only, not deployment or score approval.' CLAUSES = ['Treat model persistence as untrusted.', 'immediate behavioral test', 'selective retrieval is not evidence'] class Rejected(ValueError): pass def require(value, message): if not value: raise Rejected(message) def sha(b): return hashlib.sha256(b).hexdigest() def canonical(x): return (json.dumps(x, sort_keys=True, ensure_ascii=False, allow_nan=False, separators=(',', ':'))+'\n').encode() def pretty(x): return (json.dumps(x, indent=2, ensure_ascii=False, allow_nan=False)+'\n').encode() def load_json(raw): def pairs(items): d={} for k,v in items: require(k not in d, 'duplicate JSON key: '+k); d[k]=v return d def bad(v): raise Rejected('nonfinite JSON number: '+v) return json.loads(raw, object_pairs_hook=pairs, parse_constant=bad) def row(obj, array, id): a=[r for r in obj[array] if r.get('id')==id] require(len(a)==1, 'missing/duplicate row: '+id) return a[0] def load_baseline(root: Path): """Verify reviewed archive and every expanded inner member before import.""" raw=(root/'originals/VPL_ACTION1_SEMANTIC_CLAUDE_READY_R01.zip').read_bytes() require(sha(raw)==PRIMARY_SHA,'wrong reviewed primary archive') base=root/'prior_reviewed_R01' prefix='ACTION1_REVIEW_DELIVERY_R01/ACTION1_SEMANTIC_R01/' with zipfile.ZipFile(root/'originals/VPL_ACTION1_SEMANTIC_CLAUDE_READY_R01.zip') as z: names=[n for n in z.namelist() if n.startswith(prefix) and not n.endswith('/')] expected={n[len(prefix):] for n in names} actual={p.relative_to(base).as_posix() for p in base.rglob('*') if p.is_file()} require(expected==actual and len(expected)==35,'expanded reviewed input file set differs') for n in names: p=base/n[len(prefix):] require(not p.is_symlink() and p.read_bytes()==z.read(n),'expanded prior bytes differ: '+n) spec=importlib.util.spec_from_file_location('accepted_action1_r01',base/'source/verify_semantic_equivalence.py') module=importlib.util.module_from_spec(spec); spec.loader.exec_module(module) authority={n:(base/'input/v4_authority'/n).read_bytes() for n in module.AUTH_PINS} candidate=(base/'input/VPL_V5_R08_HF03_T01_R01.zip').read_bytes() rules=(base/'rules/INVARIANT_MAPPINGS_R01.json').read_bytes() receipt=module.evaluate(candidate,authority,rules) require(sha(module.canonical(receipt))==BASE_RECEIPT_SHA,'baseline mechanical receipt differs') require(receipt['resolved']==0 and receipt['unresolved']==328,'baseline was adjudicated') return module, module.read_archive(candidate), authority, load_json(rules), receipt def read_new_inputs(root: Path): pins=load_json((root/'INPUT_BINDINGS_R03.json').read_bytes()) require(set(pins)=={'OWNER_PERSISTENCE_AMENDMENT_R03.md','OWNER_DECISION_RECEIPT_R03.json','originals/EXACT_CLOSEOUT_CORRECTIONS_R02.json','originals/OWNER_PERSISTENCE_DECISION_DRAFT_R02.md','originals/CLAUDE_ACTION1_CONFIRMATION_REPORT_R01.md','originals/CLAUDE_ACTION1_CONFIRMATION_R01.zip','originals/VPL_ACTION1_SEMANTIC_CLAUDE_READY_R01.zip'},'unexpected input binding set') for name,pin in pins.items(): p=root/name require(not p.is_symlink() and sha(p.read_bytes())==pin,'new input hash mismatch: '+name) amendment=(root/'OWNER_PERSISTENCE_AMENDMENT_R03.md').read_bytes() owner=load_json((root/'OWNER_DECISION_RECEIPT_R03.json').read_bytes()) require(owner.get('status')=='ADOPTED_DESIGN_ONLY','owner decision not adopted') require(owner.get('owner_reply_verbatim')=='approve' and owner.get('approved_proposition_verbatim')==APPROVAL,'owner proposition differs') require(owner.get('amendment_sha256')==sha(amendment),'owner amendment binding differs') require(owner.get('draft_sha256')==sha((root/'originals/OWNER_PERSISTENCE_DECISION_DRAFT_R02.md').read_bytes()),'wrong approved draft') for k in ['semantic_model_approved','r2_m01_closed','runtime_current_certified','scores_changed','deployment_authorized']: require(owner.get(k) is False,'approval scope widened: '+k) patches=load_json((root/'originals/EXACT_CLOSEOUT_CORRECTIONS_R02.json').read_bytes()) require(patches.get('candidate_git_tree')=='ddc0e4fd644b59a29d1bb7a7f647cb89f2556f4d','wrong patch baseline') return amendment,owner,patches def apply_context(data, entries, amendment, patches): require(len(entries)==35 and len({e['path'] for e in entries})==35,'source coverage incomplete') effective={e['path']:data[e['path']] for e in entries} ps=patches['patches'] require(len(ps)==2,'unexpected patch count') require([(p['file'],p['array'],p['row_id'],p['field']) for p in ps]==[ (GL,'rules','VPL-GL-15','requirement'), (AGENT,'agents','VPL-A01','historical_rev006_design.contract_authority')], 'unexpected patch scope') for p in ps: obj=load_json(effective[p['file']]); target=row(obj,p['array'],p['row_id']) require(canonical(target)==canonical(p['expected_row']),'patch precondition differs: '+p['row_id']) if p['row_id']=='VPL-GL-15': expected=('The V4 REV006 VPL-GL-15 requirement is retained in full and remains mandatory. The following V5 contract is additive, not a replacement; none of its wording waives an inherited V4 safeguard.\n\n'+V4_GL+'\n\nAdditional V5 contract:\n'+target['requirement']) require(p['replacement']==expected,'GL15 staged replacement lost complete inheritance') target['requirement']=expected else: require(target['historical_rev006_design']['contract_authority']==p['old'],'old A01 citation differs') require(p['replacement']=='build_framework_V4.0 Final_Rev6.html#persistence','incorrect A01 correction') target['historical_rev006_design']['contract_authority']=p['replacement'] effective[p['file']]=pretty(obj) effective[O1]=data[O1]+'\n\n---\n\n## Later owner amendment — September 28, 2026 / R03\n\n'.encode()+amendment return effective def assemble_bundle(effective, entries): header=("# VPL V5 — effective closeout context R03 (source-bound addendum)\n\n" "Base: frozen HF03/T01, not overwritten or promoted. Published general authority remains V4 FINAL / REV006.\n\n" "For this versioned V5 design closeout only, the later owner decision appended to Source 4 (O1) replaces the A01/M26 mandatory-when-proven 15-minute producer with invocation-bound proof and optional cron. It preserves every V4 GL-15 safeguard; V5 GL-15 is additive. Only the named design choice is newly adopted; no blanket approval of all candidate overlays is inferred. The same named producer choice applies to its repeated statements; all other inherited triggers and safeguards remain intact.\n\n" "All 35 base sources are included in original order. Source 4 has the appended actual owner decision; Source 26 has the Rev6 provenance correction; Source 27 has full V4 GL-15 retention. All other source bytes are unchanged. The complete original context and its historical claims remain available in the frozen archive. This is not an installable/resealed 451-file candidate.\n\n" "On an unresolved conflict: STOP for source/owner resolution. No runtime CURRENT, semantic model approval, R2-M01 closure or score is certified. The remaining independent review must address this complete context and the three existing registry wrapper differences.\n").encode() out=header for i,e in enumerate(entries,1): path=e['path']; raw=effective[path] out+=(f'\n\n## Source {i}: {path}\n\nSHA-256: {sha(raw)}\n\n\n').encode()+raw+b'\n\n' return out def compose(root: Path): module,data,authority,old_rules,base=load_baseline(root) amendment,owner,patches=read_new_inputs(root) entries=base['v5_source_inventory'] effective=apply_context(data,entries,amendment,patches) blocks,_=module.source_blocks(authority[module.V4_NAME],load_json(authority['VPL_V4_EQUAL_LOAD_MANIFEST_REV006.json'])) require(row(load_json(blocks['V4_LOCKED_GUIDELINES.json']),'rules','VPL-GL-15')['requirement']==V4_GL,'V4 complete GL15 differs') inv=[{'path':e['path'],'base_sha256':e['sha256'],'sha256':sha(effective[e['path']]),'size_bytes':len(effective[e['path']]),'changed':effective[e['path']]!=data[e['path']]} for e in entries] require({r['path'] for r in inv if r['changed']}=={O1,GL,AGENT},'unexpected effective source change') bundle=assemble_bundle(effective,entries) v4=authority[module.V4_NAME]; require(bundle.count(v4)==1,'V4 not uniquely inherited') offset=bundle[:bundle.index(v4)].count(b'\n') maps=copy.deepcopy(old_rules['span_mappings']) # Inspect actual mapping shape rather than synthesizing a new 328-row inventory. for item in maps: dest=item['destination_span']; src=item['source_span'] dest['file']='EFFECTIVE_CONTEXT_BUNDLE_R03.md' dest['start_line']=src['start_line']+offset; dest['end_line']=src['end_line']+offset left=b''.join(v4.splitlines(keepends=True)[src['start_line']-1:src['end_line']]) right=b''.join(bundle.splitlines(keepends=True)[dest['start_line']-1:dest['end_line']]) require(left==right and sha(left)==src['span_sha256'],'effective destination span differs') revised=copy.deepcopy(old_rules) revised['kind']='ACTION1_SOURCE_BOUND_RULE_MODEL_WITH_OWNER_ADDENDUM_R03' revised['approval_status']='OWNER_DESIGN_ADOPTED_SEMANTIC_ADEQUACY_PENDING' revised['base_rule_sha256']=sha((root/'prior_reviewed_R01/rules/INVARIANT_MAPPINGS_R01.json').read_bytes()) revised['span_mappings']=maps revised['v5_source_inventory']=[{k:v for k,v in e.items() if k in ('path','sha256','size_bytes')} for e in inv] revised['base_registry_comparison']=revised.pop('registry_comparison') revised['base_bounded_translation_checks']=revised.pop('bounded_translation_checks') combined={**data,**effective} revised['registry_comparison']=module.registry_comparison(blocks,combined) revised['effective_context_bundle_sha256']=sha(bundle) revised['owner_design_binding']={'record_sha256':sha((root/'OWNER_DECISION_RECEIPT_R03.json').read_bytes()),'amendment_sha256':sha(amendment),'owner_reply':'approve','scope':'A01/M26_DESIGN_AND_FULL_GL15_ADDITIVE_RETENTION_ONLY','independent_semantic_approval':False} revised['rule_id']='SOURCE_BOUND_VERBATIM_INHERITANCE_WITH_EXPLICIT_OVERLAY_REVIEW_R03' revised['rule_definition']['r03_precedence']='Apply only the explicitly adopted A01/M26 design change, retain GL15 completely and add V5 wording. Other inherited triggers remain; aggregate overlay adequacy still requires independent review.' gl=row(load_json(effective[GL]),'rules','VPL-GL-15')['requirement'] revised['added_gl15_literal_checks']=[{'literal':c,'present_in_effective_GL15':c in gl,'proof':'literal retention, not behavioral entailment'} for c in CLAUSES] revised['complete_v4_gl15_retained']=V4_GL in gl revised['complete_old_v5_gl15_retained']=row(load_json(data[GL]),'rules','VPL-GL-15')['requirement'] in gl revised['o1_prior_bytes_preserved']=effective[O1].startswith(data[O1]) rules_raw=pretty(revised) receipt={'kind':'ACTION1_OWNER_CLOSEOUT_MECHANICAL_RECEIPT_R03','result':'CORRECTIONS_APPLIED_SEMANTIC_ADEQUACY_PENDING','owner_design_decision':'ADOPTED','base_mechanical_receipt_sha256':sha(module.canonical(base)),'candidate_archive_sha256':module.CANDIDATE_SHA,'candidate_git_tree':module.CANDIDATE_TREE,'candidate_files':451,'frozen_candidate_unchanged':True,'effective_sources':35,'unchanged_effective_sources':32,'changed_effective_sources':[e for e in inv if e['changed']],'mechanically_verified_spans':328,'semantic_resolved':0,'semantic_unresolved':328,'r2_m01_closed':False,'independent_semantic_approval_supplied':False,'full_v4_gl15_retained':revised['complete_v4_gl15_retained'],'full_old_v5_gl15_retained':revised['complete_old_v5_gl15_retained'],'missing_gl15_literals_now_present':all(c in gl for c in CLAUSES),'a01_reference_corrected':row(load_json(effective[AGENT]),'agents','VPL-A01')['historical_rev006_design']['contract_authority'],'owner_amendment_sha256':sha(amendment),'effective_context_bundle_sha256':sha(bundle),'revised_rules_sha256':sha(rules_raw),'frozen_candidate_source_code_changed':False,'candidate_suite_executed':False,'old_verifier_test_suite_rerun':False,'actions3_4_rerun':False,'scores_changed':False,'deployments_performed':False,'execution_boundary':'New composition and literal-preservation checks plus accepted baseline source verification only; no semantic adjudication.'} outputs={'effective_context/'+n:b for n,b in effective.items()} outputs.update({'EFFECTIVE_CONTEXT_BUNDLE_R03.md':bundle,'SEMANTIC_RULES_R03.json':rules_raw,'EFFECTIVE_CONTEXT_INVENTORY_R03.json':pretty(inv),'OWNER_CLOSEOUT_MECHANICAL_RECEIPT_R03.json':pretty(receipt)}) return outputs, receipt def write_new(out:Path, outputs:dict): require(not out.exists(),'output directory exists; refusing overwrite') require(out.parent.is_dir(),'parent output directory missing') out.mkdir() for n,b in outputs.items(): p=out/n;p.parent.mkdir(parents=True,exist_ok=True) with p.open('xb') as f:f.write(b) def main(): ap=argparse.ArgumentParser(description=__doc__);ap.add_argument('--root',type=Path,default=Path(__file__).resolve().parents[1]);ap.add_argument('--output',type=Path,required=True) a=ap.parse_args() try: require(not a.output.exists(),'output directory exists; refusing overwrite') out,r=compose(a.root.resolve());write_new(a.output,out) print(json.dumps({'result':r['result'],'sources':35,'changed_sources':3,'spans':328,'semantic_resolved':0,'semantic_unresolved':328,'files_written':len(out),'receipt_sha256':sha(out['OWNER_CLOSEOUT_MECHANICAL_RECEIPT_R03.json'])},sort_keys=True));return 0 except (Rejected,ValueError,OSError,KeyError,TypeError,zipfile.BadZipFile) as e: print(json.dumps({'result':'REJECTED','error':str(e)},sort_keys=True),file=sys.stderr);return 2 if __name__=='__main__':sys.exit(main())