#!/usr/bin/env python3 """Reviewed separate DEVELOPMENT jobs after the preserved Core failure. Default is read-only preflight. Only --run launches the four remaining native jobs. """ from pathlib import Path import argparse,fcntl,hashlib,json,os,subprocess,sys ROOT=Path('/home/jakub/devel/escapistsrereintroduced') SESSION=Path('/tmp/erri-radio-maintenance-packages') COMPAT=Path('/tmp/erri-headless-compat') STEAM=Path('/home/jakub/.local/share/Steam') EXTERNAL=STEAM/'steamapps/compatdata/1874880/pfx/drive_c/users/steamuser/Documents/My Games/ArmaReforger/addons' sys.path.insert(0,str(ROOT/'tools')) import native_package as pack EXPECTED={ 'package-report.json':'b2a1b0966a4add6af5272ddf2ea619cbe9b5d88c0446e7588fa58f4d514be436', 'authored-before.json':'811dff0d2800f7ac2e0c5954c6885695eb8a31d3af82ba294677191312a9e592', 'external-addons-before.json':'d560c4771507ce4c08574807d22519511081c608cf6fe8f05695ceccddfc671b', 'external-addons-policy.json':'2cdcacde92c4726e8c43809263548d0bc26d83685f70b48afac8c495605457be', 'external-source-cache.json':'d6c118d6628d7bcd70c532fb3212e9117d8a143a91accb2b92dc1006e7909eb9', 'evidence/core/report.json':'1894ba8544c66dd802f2ffe082394d2600ab1dc1a9c90c0eb66bd3fcd0cb70e3', 'evidence/core/native-exit.json':'b151b29d2b2dfcf355d70f71b552ec7de8f3b3ae8325ca5d15bc48ac711e1af5', } REMAINING=('ai','intel','map','chat') def require(value,message): if not value:raise ValueError(message) def read(name):return json.loads((SESSION/name).read_text()) def retained_inputs(): require(SESSION.is_dir() and SESSION==SESSION.resolve(),'Session redirected') for name,sha in EXPECTED.items(): path=SESSION/name;require(path.is_file() and not path.is_symlink() and pack.digest(path)==sha,'Preserved baseline changed: '+name) original=read('package-report.json');authored=read('authored-before.json') require(original['stopped_reason']=='native completion not safely observed' and original['skipped_projects']==list(REMAINING),'Wrong stopped package attempt') require(len(original['projects'])==1 and original['projects'][0]==read('evidence/core/report.json'),'Original Core report mismatch') require(original['source_repository_unchanged'] is True and original['external_addons_unchanged_after_run'] is True,'Original input integrity failed') require(pack.inventory(ROOT)==authored and pack.copied_authored_snapshot(SESSION/'source',authored)==authored,'Frozen authored source changed') core=original['projects'][0] require(set((SESSION/'packages/core').iterdir())=={SESSION/'packages/core'/n for n in ('addon.gproj','data.pak','resourceDatabase.rdb')},'Core runtime files changed') for name,record in core['packages'].items(): path=SESSION/'packages/core'/name require(path.is_file() and not path.is_symlink() and pack.digest(path)==record['sha256'],'Preserved Core artifact changed: '+name) return original,authored def external_context(cache_path): context=pack.external_request('chat',EXTERNAL,ROOT) require(context['policy_sha256']==read('package-report.json')['external_policy_sha256'] and context['policy']==read('external-addons-policy.json'),'Frozen external policy changed') context.update(records=read('external-addons-before.json'),cache_path=cache_path,cache_sha256=pack.digest(cache_path)) pack.verify_external(context) return context def safe_completion(report,needs_external): # Error-bearing candidate verdicts remain failed. This predicate permits only # separately reviewed later jobs after independent idle verification below. return (report.get('native_exit_code')==0 and report.get('cleanup_confirmed') is True and report.get('runner_error') is None and report.get('copied_authored_unchanged') is True and report.get('profile_addons_unchanged') is True and report.get('completion_observed') in {'process-exited','native-child-exited','native-or-launcher-exited-prefix-active'} and report.get('external_addons_unchanged_after_run') is True and report.get('external_addon_count')==(6 if needs_external else 0)) def main(): parser=argparse.ArgumentParser(description=__doc__);parser.add_argument('--run',action='store_true');args=parser.parse_args() original,authored=retained_inputs();context=external_context(SESSION/'external-source-cache.json') require(safe_completion(original['projects'][0],False),'Original Core cannot support a separate later job') for name in REMAINING: for folder in ('profiles','packages','cache','evidence'):require(not (SESSION/folder/name).exists() and not (SESSION/folder/name).is_symlink(),'Remaining addon path already exists: '+folder+'/'+name) pack.assert_no_game();pack.assert_prefix_idle(COMPAT) if not args.run: print(json.dumps({'preflight':'ready','native_launched':False,'authored_files':len(authored),'external_addons':6,'remaining':REMAINING,'original_report_sha256':EXPECTED['package-report.json'],'build_qualified':False}));return 0 descriptor=os.open(COMPAT/'.erri-workbench.lock',os.O_RDWR|os.O_NOFOLLOW) with os.fdopen(descriptor,'a') as lock: fcntl.flock(lock,fcntl.LOCK_EX|fcntl.LOCK_NB) pack.assert_no_game();pack.assert_prefix_idle(COMPAT);retained_inputs() output=SESSION/'separate-remaining-development';output.mkdir(exist_ok=False) cache=output/'external-source-cache.json';cache.write_bytes((SESSION/'external-source-cache.json').read_bytes()) context=external_context(cache) report={'kind':'SEPARATE DEVELOPMENT PACKAGE JOBS','original_report_sha256':EXPECTED['package-report.json'],'reason':'Core native exit0 with verified owned cleanup; original prefix-active and native-error verdicts retained. Each later job requires independently idle prefix and unchanged frozen inputs.','projects':list(original['projects']),'stopped_reason':None,'build_qualified':False,'release_qualified':False,'published':False} try: for name in REMAINING: retained_inputs();pack.verify_external(context);pack.assert_no_game();pack.assert_prefix_idle(COMPAT) for folder in ('profiles','packages','cache','evidence'):require(not (SESSION/folder/name).exists() and not (SESSION/folder/name).is_symlink(),'Addon job would overwrite existing evidence: '+name) result=pack.run_project(STEAM,COMPAT,SESSION,name,300,authored,True,False,external=context if name in {'ai','chat'} else None) report['projects'].append(result);pack.save(output/'report.json',report) require(safe_completion(result,name in {'ai','chat'}),'Native status/cleanup/input guard stopped separate jobs at '+name) retained_inputs();pack.verify_external(context);pack.assert_no_game();pack.assert_prefix_idle(COMPAT) print(name,'native_exit',result['native_exit_code'],'original_verdict',result['result'],'candidate_created',result['candidate_created'],flush=True) except (OSError,ValueError,subprocess.SubprocessError) as error: report['stopped_reason']=str(error) finally: report['all_jobs_finished']=len(report['projects'])==5 and report['stopped_reason'] is None report['all_candidate_verdicts_successful']=all(r.get('candidate_created') is True for r in report['projects']) pack.save(output/'report.json',report) require(report['all_jobs_finished'],'Separate jobs stopped: '+str(report['stopped_reason'])) print('Separate DEVELOPMENT jobs completed. Original failed verdicts retained; PAK content and packed startup still require verification.') return 0 if __name__=='__main__': try:raise SystemExit(main()) except (OSError,ValueError,subprocess.SubprocessError) as error:print('REFUSED: '+str(error),file=sys.stderr);raise SystemExit(1)