from pathlib import Path import hashlib,json,shutil,sys,zipfile r=Path('/home/jakub/devel/escapistsrereintroduced');sys.path.insert(0,str(r/'tools')) from source_bundle import canonical,inventory as source_inventory from packed_external import inventory as runtime_inventory from pak_inventory import inventory as pak_inventory def digest(p): return hashlib.sha256(p.read_bytes()).hexdigest() def write(p,v): p.write_text(json.dumps(v,indent=2)+'\n') package=Path('/tmp/erri-package-20261002-objective-map') build=r/'.build/playtest-2026-10-02-objective-map' e=r/'docs/evidence/package-2026-10-02-objective-map' route=r/'docs/evidence/native-2026-10-02-objective-map-normal' authored=json.loads((package/'authored-before.json').read_text()) assert authored==source_inventory(r)==json.loads((route/'source-sha256.json').read_text()) report=json.loads((package/'package-report.json').read_text()) assert report['project_order']==['core'] and len(report['projects'])==1 and report['source_repository_unchanged'] attempt=report['projects'][0] assert attempt['project']=='core' and attempt['copied_authored_unchanged'] and attempt['profile_addons_unchanged'] assert attempt['cleanup_confirmed'] and attempt['external_addons_unchanged_after_run'] and report['external_addons_unchanged_after_run'] assert attempt['native_exit_code']==0 payload=pak_inventory(package/'packages/core/data.pak') actual={x['path']:x['sha256'] for x in payload['entries']} assert len(actual)==len(payload['entries']) expected={name.removeprefix('addons/core/'):sha for name,sha in authored.items() if not name.endswith('.meta') and not name.endswith('/addon.gproj')} assert actual==expected runtime=runtime_inventory(package/'packages') assert runtime['core/addon.gproj']['sha256']==authored['addons/core/addon.gproj'] zip_path=Path('/tmp/erri-playtest-source-20261002-objective-map.zip') with zipfile.ZipFile(zip_path) as z: manifest=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(manifest['files'])|{'BUNDLE-MANIFEST.json'} assert {n:h for n,h in manifest['files'].items() if n.startswith('addons/')}==authored contents={} for n in names: canonical(n);contents[n]=z.read(n) if n in manifest['files']:assert hashlib.sha256(contents[n]).hexdigest()==manifest['files'][n] route_report=json.loads((route/'report.json').read_text()) assert route_report['smoke_completions']==1 and not route_report['script_or_runtime_failures'] route_runner=json.loads((route/'runner.json.txt').read_text()) assert route_runner['cleanup_confirmed'] and route_runner['source_unchanged_after_run'] assert digest(route/'console.log.txt')==route_runner['console_sha256'] for p in (build,e):p.mkdir(exist_ok=False) shutil.copytree(package/'packages',build/'packages') shutil.copyfile(zip_path,build/'source.zip') assert digest(zip_path)==digest(build/'source.zip') for n,content in contents.items(): p=build/'source'/n;p.parent.mkdir(parents=True,exist_ok=True);p.write_bytes(content) assert {str(p.relative_to(build/'source')) for p in (build/'source').rglob('*') if p.is_file()}==set(contents) for n,content in contents.items(): assert (build/'source'/n).read_bytes()==content assert runtime_inventory(build/'packages')==runtime for p in package.iterdir(): if p.is_file() and p.suffix=='.json':shutil.copyfile(p,e/p.name) shutil.copytree(package/'evidence',e/'native') shutil.copyfile('/tmp/erri-package-20261002-objective-map.log',e/'runner-output.log.txt') write(e/'bundle-summary.json',{'archive_sha256':digest(zip_path),'archive_bytes':zip_path.stat().st_size,'payload_files':len(manifest['files']),'projects':manifest['projects']}) shutil.copyfile(__file__,e/'assemble-handoff.py') write(e/'pak-inventory.json',payload) review={'kind':'UNQUALIFIED DEVELOPMENT CANDIDATE','authored_files':len(authored),'pak_payload_files':len(actual),'runtime_files':runtime,'source_zip_sha256':digest(zip_path),'source_manifest_sha256':route_runner['launch_source_sha256'],'route_package_source_match':True,'normal_route_reused_without_source_changes':True,'normal_route_console_sha256':digest(route/'console.log.txt'),'package_candidate_created':attempt['candidate_created'],'package_result':attempt['result'],'package_stopped_reason':report['stopped_reason'],'original_packaging_failure_retained':not attempt['candidate_created'],'normal_mission_ready':False,'local_participant_verified':False,'strict_packed_consumption_verified':False,'release_qualified':False,'published':False,'source_zip_exact_membership_and_hashes_verified':True,'extracted_source_and_runtime_reverified':True,'evidence':{'route':str(route.relative_to(r)),'package':str(e.relative_to(r)),'startup':'docs/evidence/packed-2026-10-02-objective-map'}} for p in [build/'handoff.json',e/'handoff.json']:write(p,review) (build/'README.md').write_text('# Development candidate preparation\n\nMatching source and package hashes are verified in handoff.json. Ordinary packed startup is still pending. This is not a release or Workshop publication.\n') print(json.dumps({'build':str(build),'authored_files':len(authored),'pak_payload_files':len(actual),'runtime_files':len(runtime),'source_zip_sha256':review['source_zip_sha256']},indent=2))