#!/usr/bin/env python3 """Prepare two documentation proposals; --verified --apply is an explicit parent step. Source-only mode changes current-status only and never advances the runtime pointer. """ import argparse,ast,hashlib,json,sys from pathlib import Path HERE=Path(__file__).resolve().parent load=lambda p:json.loads(p.read_text()) sha=lambda p:hashlib.sha256(p.read_bytes()).hexdigest() def main(): p=argparse.ArgumentParser(description=__doc__) p.add_argument('--mode',choices=['candidate','source-only'],default='candidate') p.add_argument('--verified',action='store_true');p.add_argument('--apply',action='store_true') p.add_argument('--review',type=Path);p.add_argument('--review-sha256',help='Explicit parent-approved hash of independent final review') p.add_argument('--blocker-log',type=Path,help='Required source-only evidence of packaging refusal/block; never inferred from missing outputs') a=p.parse_args();assert not a.apply or a.verified,'--apply requires --verified' c=load(HERE/'config.json');r=Path(c['root']);f=Path(c['frozen_root']);b=Path(c['build']);s=Path(c['suite_directory']);ne=Path(c['normal_evidence']);pe=Path(c['package_evidence']);se=Path(c['packed_evidence']) docs={n:HERE/'docs-baseline'/n for n in ['current-status.md','playtest-build.md']} assert all((r/'docs'/n).read_bytes()==q.read_bytes() for n,q in docs.items()),'Docs changed: explicitly rebase/review scratch; do not overwrite concurrent edits' frozen=load(s/'frozen-source.json');suite=None;normal=None;packed=None;handoff=None checks={'docs_before':{n:sha(q) for n,q in docs.items()},'frozen_source_sha256':sha(s/'frozen-source.json'),'source_count':len(frozen),'qualification_source_root':str(f),'live_source_match_not_claimed':True} if a.verified: assert a.review and a.review.is_file() and a.review_sha256==sha(a.review),'Require exact parent-approved independent final review' load(a.review);checks['independent_review']={'path':str(a.review),'sha256':sha(a.review)} sys.path.insert(0,str(f/'tools'));from source_manifest import addon_manifest assert addon_manifest(f)==frozen==load(f/'frozen-source.json') suite=load(s/'suite-result.json');inputs=load(s/'suite-inputs.json') tree=ast.parse((f/'tools/check.py').read_text());registered=[n.value for n in ast.walk(tree) if isinstance(n,ast.Constant) and isinstance(n.value,str) and n.value.endswith('.py') and (n.value.split('/')[-1].startswith('test_') or n.value.endswith('_regression.py'))] expected={'tests/'+Path(n).name for n in registered} assert len(expected)==len(registered) assert suite['exit']==0 and suite['inputs_unchanged'] and not suite.get('resumed') assert suite['regression_programs']==len(registered)==len(suite['programs']) assert {x['path'] for x in suite['programs']}==expected and all(x['exit']==0 for x in suite['programs']) assert all(inputs.get(n)==h for n,h in frozen.items()) assert all((f/n).is_file() and sha(f/n)==h for n,h in inputs.items()) assert 'All repository checks passed.' in (s/'regressions.log').read_text() normal=load(ne/'report.json');nr=load(ne/'runner.json.txt') assert load(ne/'source-sha256.json')==frozen==load(ne/'launch-source-sha256.json') assert normal['smoke_completions']==1 and not normal['script_or_runtime_failures'] assert nr['cleanup_confirmed'] and nr['source_unchanged_after_run'] and nr['external_addons_unchanged_after_run'] assert sha(ne/'console.log.txt')==nr['console_sha256'] checks['evidence_hashes']={str(q):sha(q) for q in [s/'suite-result.json',s/'suite-inputs.json',s/'regressions.log',ne/'report.json',ne/'runner.json.txt',ne/'console.log.txt']} if a.mode=='candidate': from source_bundle import inventory as source_inventory from packed_external import inventory as runtime_inventory handoff=load(b/'handoff.json');assert handoff==load(pe/'handoff.json') assert handoff['qualification_source_root']==str(f) and handoff['authored_files']==len(frozen) assert handoff['repository_regression_programs']==len(registered) and handoff['normal_mission_ready'] and handoff['local_participant_verified'] assert not handoff['release_qualified'] and not handoff['published'] assert load(pe/'authored-before.json')==frozen==source_inventory(b/'source') assert runtime_inventory(b/'packages')==handoff['runtime_files'] assert sha(b/'source.zip')==handoff['source_zip_sha256'] packed=load(se/'packed-startup-report.json');pr=load(se/'packed-runner.json');manifest=load(se/'packed-input-provenance.json');ar=load(se/'archive-review.json') assert packed['normal_mission_ready'] and packed['local_participant_verified'] and not packed['script_or_runtime_failures'] assert pr['cleanup_confirmed'] and pr['packed_inputs_unchanged_after_run'] and pr['packed_external_inputs_unchanged_after_run'] assert sha(se/'console.log.txt')==pr['console_sha256'] assert sha(se/'packed-input-provenance.json')==pr['packed_input_provenance_sha256'] assert manifest['artifacts']==handoff['runtime_files'] and manifest['launch_id']==pr['packed_launch_id'] assert ar['raw_console_sha256_verified'] and ar['strict_failure_retained'] assert len(packed['engine_errors'])==handoff['packed_resource_errors'] checks['evidence_hashes'].update({str(q):sha(q) for q in [b/'handoff.json',pe/'package-report.json',se/'packed-startup-report.json',se/'packed-runner.json',se/'archive-review.json']}) else: assert a.blocker_log and a.blocker_log.is_file(),'Source-only outcome requires explicit preserved blocker evidence' checks['blocker']={'path':str(a.blocker_log),'sha256':sha(a.blocker_log)} count=suite['regression_programs'] if suite else 'pending' inclusion='This snapshot includes the radio-hop interruption checks, cancellation guards for finite supply transactions, and the deleted-mortar-shell settlement fix. Earlier terrain/admin integration and bounded HQ travel-history planning remain included.' limits='Qualification covers this frozen snapshot, not later live edits. Physical resupply/interception, manual UI behavior, complete human escape, real multiplayer/JIP/reconnect/twelve-player play, comparative FPS and a clean release remain unverified. Existing packages and original missions remain unchanged.' if a.verified: normal_text=f"The exact frozen source passes **{count} registered regression programs**. The normal Everon route completes **{normal['assertions_passed']} assertions and {normal['smoke_completions']} round**, with zero script/runtime failures and **{len(normal['engine_errors'])} retained engine errors**." else:normal_text='Verification is pending. This preview makes no passing-suite, native-route or packaged-startup claim.' if a.mode=='candidate': title='**2 October 2026 — transfer-fixes development handoff:** [sealed package manifest and editable source](../.build/playtest-2026-10-02-transfer-fixes/handoff.json).' counts=(f"**{len(frozen)} authored files**, **{handoff['pak_payload_files']} exact PAK payloads** and **{len(handoff['runtime_files'])} runtime files** match the sealed source." if handoff else f'{len(frozen)} authored files; package counts pending verification.') startup=(f"Ordinary startup verifies the normal mission and local prisoner with zero script/runtime failures and **{len(packed['engine_errors'])} retained engine errors**. Strict packaging/startup failures remain recorded; this is an **unqualified development candidate**." if packed else 'Package and startup verification remain pending.') section='\n\n'.join([title,counts,inclusion,normal_text,startup,limits]) else: title='**2 October 2026 — transfer-fixes source verified; runtime refresh deferred:**' if a.verified else '**Pending transfer-fixes source checkpoint:**' section='\n\n'.join([title+f' The sealed snapshot at `.build/playtest-source-2026-10-02-transfer-fixes` contains **{len(frozen)} authored files**.',inclusion,normal_text,'Packaging did not complete; the explicit refusal/block evidence is preserved in the finalization record. The previous runtime handoff remains the latest candidate. No native process is left waiting to launch; continuation must recheck host-visible idle state.',limits]) status=docs['current-status.md'].read_text();first,tail=status.split('\n',1) proposed={'current-status.md':first+'\n\n'+section+'\n\n'+tail.lstrip('\n')} if a.mode=='candidate': guide=docs['playtest-build.md'].read_text();first,tail=guide.split('\n',1) old='The latest retained handoff is ';assert tail.count(old)==1 tail=tail.replace(old,'## Earlier handoff (historical)\n\nThe previously retained handoff was ',1) proposed['playtest-build.md']=first+'\n\n'+section+'\n\n'+tail.lstrip('\n') out=HERE/('proposed-docs-'+a.mode);out.mkdir(exist_ok=True) for n,content in proposed.items():(out/n).write_text(content) review={'state':'verified proposal' if a.verified else 'pending preview','mode':a.mode,'checks':checks,'suite_programs':count,'proposed_sha256':{n:sha(out/n) for n in proposed},'updates_latest_runtime_pointer':a.mode=='candidate','target_writes':a.apply} (HERE/('docs-proposal-'+a.mode+'.json')).write_text(json.dumps(review,indent=2)+'\n') if a.apply: assert all((r/'docs'/n).read_bytes()==q.read_bytes() for n,q in docs.items()),'Docs changed during validation' for n in proposed:(r/'docs'/n).write_bytes((out/n).read_bytes()) (HERE/('docs-after-'+a.mode+'.json')).write_text(json.dumps({n:sha(r/'docs'/n) for n in proposed},indent=2)+'\n') print(json.dumps({'mode':a.mode,'verified':a.verified,'applied':a.apply,'proposal':str(out),'changed_docs':list(proposed)},indent=2)) if __name__=='__main__':main()