Files
jakub 6a667d8deb Capture current mission, AI, map and administration development
Include the current addon, terrain integrations, tools, regression tests and recorded development evidence.
2026-10-13 22:52:53 +02:00

65 lines
10 KiB
Python

#!/usr/bin/env python3
"""Source-only checkpoint. Scratch preview by default; parent alone uses --apply."""
from pathlib import Path
import argparse,hashlib,json,shutil,subprocess,sys,zipfile
HERE=Path(__file__).resolve().parent;ROOT=Path('/home/jakub/devel/escapistsrereintroduced');BASE=Path('/tmp/erri-playtest-1631')
FROZEN=ROOT/'.build/playtest-source-2026-10-02-gameplay-fixes';ZIP=Path('/tmp/erri-playtest-source-20261002-gameplay-fixes.zip')
NORMAL=ROOT/'docs/evidence/native-2026-10-02-gameplay-fixes-normal';FAILED=ROOT/'docs/evidence/native-2026-10-02-gameplay-fixes-cleanup-refusal'
ARCHIVE=ROOT/'docs/evidence/source-refresh-2026-10-02-gameplay-fixes';PACK_LOG=Path('/tmp/erri-package-20261002-gameplay-fixes-final.log');PACK_SESSION=Path('/tmp/erri-package-20261002-gameplay-fixes-final')
load=lambda p:json.loads(p.read_text())
sha=lambda p:hashlib.sha256(p.read_bytes()).hexdigest()
def dump(p,d):p.parent.mkdir(parents=True,exist_ok=True);p.write_text(json.dumps(d,indent=2)+'\n')
def mismatches(root,m):return {n:{'expected':h,'actual':sha(root/n) if (root/n).is_file() else None} for n,h in m.items() if not (root/n).is_file() or sha(root/n)!=h}
def copy(a,b):
b.parent.mkdir(parents=True,exist_ok=True)
if a.is_dir():shutil.copytree(a,b,dirs_exist_ok=True)
else:shutil.copyfile(a,b)
def main():
ap=argparse.ArgumentParser(description=__doc__);ap.add_argument('--apply',action='store_true');ap.add_argument('--review',type=Path,default=Path('/tmp/erri-playtest-review-1631/review.json'));a=ap.parse_args()
b=load(BASE/'baseline.json');frozen=load(BASE/'frozen-source.json');suite=load(BASE/'suite-result.json');inputs=load(BASE/'suite-inputs.json')
sys.path.insert(0,str(FROZEN/'tools'));from source_manifest import addon_manifest
assert addon_manifest(FROZEN)==frozen and addon_manifest(ROOT)==frozen,'Source differs: review any concurrent delta before finalizing'
assert suite['exit']==0 and suite['inputs_unchanged'] and not suite.get('resumed')
count=suite['regression_programs'];assert count==len(suite['programs'])==len({p['path'] for p in suite['programs']}) and all(p['exit']==0 for p in suite['programs'])
assert not mismatches(FROZEN,inputs) and all(inputs.get(n)==h for n,h in frozen.items())
report=load(NORMAL/'report.json');runner=load(NORMAL/'runner.json.txt')
assert load(NORMAL/'source-sha256.json')==frozen==load(NORMAL/'launch-source-sha256.json')
assert report['smoke_completions']==1 and not report['script_or_runtime_failures'] and len(report['engine_errors'])==13
assert runner['cleanup_confirmed'] and runner['source_unchanged_after_run'] and runner['external_addons_unchanged_after_run'] and sha(NORMAL/'console.log.txt')==runner['console_sha256']
assert load(FAILED/'runner.json')['cleanup_confirmed'] is False
refusal=load(PACK_LOG);assert refusal['result']=='refused-or-failed' and 'game process already exists' in refusal['error'] and not PACK_SESSION.exists()
assert not (ROOT/'.build/playtest-2026-10-02-gameplay-fixes').exists(),'A runtime candidate now exists; re-evaluate source-only scope'
with zipfile.ZipFile(ZIP) as z:
m=json.loads(z.read('BUNDLE-MANIFEST.json'));names=z.namelist();assert len(names)==len(set(names))==len({n.casefold() for n in names})
assert set(names)==set(m['files'])|{'BUNDLE-MANIFEST.json'} and {n:h for n,h in m['files'].items() if n.startswith('addons/')}==frozen
assert all(hashlib.sha256(z.read(n)).hexdigest()==h for n,h in m['files'].items())
docs={'docs/current-status.md':BASE/'current-status.md','docs/playtest-build.md':BASE/'playtest-build.md','README.md':HERE/'docs-baseline/README.md'}
assert all((ROOT/n).read_bytes()==p.read_bytes() for n,p in docs.items()),'Concurrent documentation edit: refuse overwrite'
preserved={'inherited_paths':len(b['working_modified']),'inherited_mismatches':mismatches(ROOT,b['working_modified']),
'index_unchanged':hashlib.sha256(subprocess.check_output(['git','diff','--cached','--binary'],cwd=ROOT)).hexdigest()==b['index_diff_sha256']}
prior=ROOT/'.build/playtest-2026-10-02-hq-memory-menu-fix';pm=load(ROOT/'docs/evidence/playtest-refresh-2026-10-02-hq-memory-menu-fix/candidate-sha256.json')
preserved['previous_package_mismatches']=mismatches(prior,pm);preserved['previous_package_extra_files']=sorted(str(p.relative_to(prior)) for p in prior.rglob('*') if p.is_file() and str(p.relative_to(prior)) not in pm)
original=load(Path('/tmp/erri-heartbeat-20260930-1542-reference.json'));preserved['original_reference_mismatches']=mismatches(Path('/home/jakub/devel/escapists'),original);preserved['original_reference_files']=len(original)
assert preserved['index_unchanged'] and not any(v for k,v in preserved.items() if k.endswith('mismatches') or k.endswith('extra_files'))
note=f'**2 October 2026 — gameplay-fixes source verified; new runtime package deferred:** the [sealed editable source](../.build/playtest-source-2026-10-02-gameplay-fixes/PENDING-PACKAGE.md) contains **{len(frozen)} authored files** and passes **{count} registered regression programs**. The confirmed normal Everon route completes **{report["assertions_passed"]} assertions and one round**, with zero script/runtime failures and **13 retained engine errors**.\n\nThe snapshot includes the rifle-supply callback cancellation guard, bounded HQ road-survey fairness, and inherited terrain/admin-item integration. These are source inclusions; manual UI behavior, physical resupply/interception, complete human escape, multiplayer and comparative FPS remain unqualified.\n\nPackaging was refused before creating its session because an existing Reforger session was detected. It was left untouched, and no new runtime candidate was produced. No native process was left waiting to launch; continuation will recheck host idleness before packaging. The [latest existing playtest package](../.build/playtest-2026-10-02-hq-memory-menu-fix/README.md) remains unchanged. The first normal-route cleanup refusal (a transient compiler process could not be classified), successful retry and package refusal are all [retained with source bindings and review](evidence/source-refresh-2026-10-02-gameplay-fixes/review.json). Strict failures are not waived.\n\n'
status=(BASE/'current-status.md').read_text();heading,tail=status.split('\n',1);proposed=heading+'\n\n'+note+tail.lstrip('\n')
pending=f'# Gameplay-fixes source checkpoint — runtime package pending\n\nThis frozen snapshot contains {len(frozen)} authored files. `source.zip` is the matching editable development bundle (SHA-256 `{sha(ZIP)}`). The exact snapshot passes {count} registered regression programs; the confirmed scripted normal Everon route completes {report["assertions_passed"]} assertions and one round with zero script/runtime failures and thirteen retained engine errors.\n\nIncluded source changes: rifle-supply callback cancellation guard, bounded HQ road-survey fairness, and inherited terrain/admin-item integration. Manual UI, physical resupply/interception, full human play, real multiplayer and comparative FPS are not demonstrated by this checkpoint. No release or publication is claimed.\n\nNo new runtime package exists. Packaging was refused because an existing Reforger session was detected; it was left untouched. The prior menu-fix playtest remains the latest packaged handoff. No native process was left waiting to launch. Check running processes with full host visibility: a restricted sandbox can miss an existing game. Once the host and private environment are idle, continuation may review and run the [preserved package helpers](../../docs/evidence/source-refresh-2026-10-02-gameplay-fixes/package-helpers/HANDOFF.md). All native concurrency, canonical /tmp-session, hidden-display, muted-audio, external dependency and cleanup guards remain mandatory. Recheck source/evidence seals and fresh output paths before resuming; do not overwrite any package.\n\nThe first cleanup refusal, the confirmed normal retry and the package refusal remain in [evidence](../../docs/evidence/source-refresh-2026-10-02-gameplay-fixes/review.json). This folder is a source checkpoint, not a packaged runtime candidate.\n'
out=HERE/'source-only-proposal';out.mkdir(exist_ok=True);(out/'current-status.md').write_text(proposed);(out/'PENDING-PACKAGE.md').write_text(pending)
review={'scope':'source-only checkpoint; runtime packaging deferred before session creation','source_files':len(frozen),'source_zip_sha256':sha(ZIP),'suite_programs':count,'normal_report':report,'normal_runner':runner,'package_refusal':refusal,'package_session_created':False,'new_runtime_candidate_created':False,'native_process_waiting_to_launch':False,'resume_requires_host_visible_idle_check':True,'preservation':preserved,'original_cleanup_failure_retained':True,'independent_review_sha256':sha(a.review) if a.review.is_file() else None,'source_manifest_sha256':sha(BASE/'frozen-source.json')}
dump(HERE/'source-only-proposal.json',review)
if a.apply:
assert a.review.is_file(),'Final independent review missing'
assert not ARCHIVE.exists() and not (FROZEN/'source.zip').exists() and not (FROZEN/'PENDING-PACKAGE.md').exists(),'New checkpoint output already exists; refuse overwrite'
ARCHIVE.mkdir()
for n in ['baseline.json','frozen-source.json','suite-inputs.json','suite-result.json','regressions.log','source-bundle.log','concurrent-source-delta.json','normal-native.log','normal-native-retry.log','host-idle-inspection.json']:
if (BASE/n).exists():copy(BASE/n,ARCHIVE/n)
copy(FAILED,ARCHIVE/'initial-normal-cleanup-refusal');copy(PACK_LOG,ARCHIVE/'package-refusal.log.txt');copy(a.review,ARCHIVE/'independent-review.json');copy(Path(__file__),ARCHIVE/'finalize-source.py')
for n in ['emit.py','preflight.py','assemble.py','verify.py','config.json','commands.json','commands.txt','template-provenance.json','prep-review.json','HANDOFF.md','finalize_docs.py']:
copy(HERE/n,ARCHIVE/'package-helpers'/n)
with ZIP.open('rb') as src,(FROZEN/'source.zip').open('xb') as dst:shutil.copyfileobj(src,dst)
assert sha(FROZEN/'source.zip')==sha(ZIP)
copy(out/'PENDING-PACKAGE.md',FROZEN/'PENDING-PACKAGE.md');copy(out/'current-status.md',ROOT/'docs/current-status.md');dump(ARCHIVE/'review.json',review)
dump(ARCHIVE/'outputs-sha256.json',{'source.zip':sha(FROZEN/'source.zip'),'PENDING-PACKAGE.md':sha(FROZEN/'PENDING-PACKAGE.md'),'docs/current-status.md':sha(ROOT/'docs/current-status.md')})
print(json.dumps({'proposal':str(out),'source_files':len(frozen),'regression_programs':count,'applied':a.apply,'new_runtime_candidate':False,'independent_review_present':a.review.is_file()},indent=2))
if __name__=='__main__':main()