Include the current addon, terrain integrations, tools, regression tests and recorded development evidence.
96 lines
9.4 KiB
Python
96 lines
9.4 KiB
Python
#!/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()
|