Files
TheTree_mock/tools/run_all_tests.py
Takeshi b84bade045 Save The Tree implementation and acceptance work in progress
Preserve production code, regression fixtures, audio assets and provenance, test infrastructure, and the original-completion plan and progress ledger. Final game and Windows acceptance remain pending; excluded local artifacts and native experiments are not part of this snapshot.
2026-10-11 15:58:00 +09:00

312 lines
26 KiB
Python

#!/usr/bin/env python3
"""Exact standard suites in one private source copy, with owned native evidence.
Completion schemas and test bodies are unchanged. Import and every script start
in a fresh private startup profile before Godot creates logs/recovery locks;
the unchanged Guard then selects a separate fresh runtime profile.
"""
import argparse, hashlib, importlib.util, json, os, re, shutil, subprocess, sys, time, uuid
from datetime import datetime, timezone
from pathlib import Path
sys.dont_write_bytecode = True
HERE = Path(__file__).resolve().parent
SUPPORT = HERE / 'regression'
MIN_SPACE = 300 * 1024 * 1024
def module(name, path):
spec = importlib.util.spec_from_file_location(name, path)
value = importlib.util.module_from_spec(spec); spec.loader.exec_module(value); return value
def sha(path): return hashlib.sha256(Path(path).read_bytes()).hexdigest()
def read(path): return json.loads(Path(path).read_bytes())
def require(ok, reason):
if not ok: raise AssertionError(reason)
def write(path, value):
with Path(path).open('x', encoding='utf-8') as stream:
stream.write(json.dumps(value, ensure_ascii=False, indent=2) + '\n')
def now(): return datetime.now(timezone.utc).isoformat()
def roster(root):
return ['tests/run_tests.gd'] + sorted(str(p.relative_to(root)) for p in (root/'tests').rglob('test*.gd')
if 'export' not in p.relative_to(root).parts and str(p.relative_to(root)) != 'tests/s0/test_kernel_and_save.gd')
def inventory(base, stage=False):
base = Path(base); result = {}
if not base.exists(): return result
require(base.is_dir() and not base.is_symlink(), 'ordinary inventory directory')
for p in sorted(base.rglob('*')):
rel = p.relative_to(base)
if stage and '.godot' in rel.parts: continue
require(not p.is_symlink(), 'symlink is not an ordinary source/proof/profile')
if p.is_file(): result[rel.as_posix()] = {'bytes': p.stat().st_size, 'sha256': sha(p)}
return result
def source_refs(root):
paths = [p for folder in ('autoload','scripts','scenes','data','tests','assets','tools')
for p in (root/folder).rglob('*') if p.is_file() and p.name != '.DS_Store' and '__pycache__' not in p.parts]
paths += [root/p for p in ('project.godot','export_presets.cfg','Makefile','AGENTS.md')]
require(all(not p.is_symlink() for p in paths), 'source aliases refused')
return {p.relative_to(root).as_posix(): {'path':str(p),'bytes':p.stat().st_size,'sha256':sha(p)} for p in sorted(paths)}
def profile_path(name):
require(re.fullmatch(r'TheTree-PCK-tests-[0-9a-f]{32}-[a-z0-9-]+', name) is not None, 'owned profile nonce')
if sys.platform == 'darwin': return Path.home()/'Library/Application Support'/name
if sys.platform.startswith('linux'): return Path(os.environ.get('XDG_DATA_HOME', str(Path.home()/'.local/share')))/name
raise RuntimeError('profile platform requires a reviewed native runner; no Windows acceptance inference')
def default_player(root):
text = (root/'project.godot').read_text()
require('config/name="The Tree"' in text and 'config/use_custom_user_dir=' not in text, 'canonical default profile source')
if sys.platform == 'darwin': path = Path.home()/'Library/Application Support/Godot/app_userdata/The Tree'
elif sys.platform.startswith('linux'): path = Path(os.environ.get('XDG_DATA_HOME', str(Path.home()/'.local/share')))/'godot/app_userdata/The Tree'
else: raise RuntimeError('unreviewed default-profile platform')
return {'path':str(path),'existed':path.exists(),'files':inventory(path),'logs_and_locks_included':True}
def private_project(public_bytes, name):
text = public_bytes.decode('utf-8')
require(text.count('[application]\n\n') == 1 and 'config/use_custom_user_dir=' not in text
and 'config/custom_user_dir_name=' not in text, 'canonical project has no private execution settings')
addition = 'config/use_custom_user_dir=true\nconfig/custom_user_dir_name=' + json.dumps(name) + '\n'
altered = text.replace('[application]\n\n', '[application]\n\n' + addition, 1).encode()
require(altered.decode().replace(addition, '', 1).encode() == public_bytes, 'two private fields reverse to whole public bytes')
return altered
def fresh_environment(base): return {k:v for k,v in base.items() if not k.startswith('THE_TREE_')}
def phase_policy(test, source, policies):
value = policies.get(test, {'ordinary_cleanup_prepared':False,'ordinary_control_labels':[],
'old_bounded_teardown':False,'new_audio_QA':False})
if test in policies: require(value['source_sha256'] == sha(source), 'suite proof/IO policy source SHA changed')
return dict(value, path=test, source_sha256=sha(source), candidate_source_sha256=sha(source))
def dependency_paths(test, refs):
"""Reviewed literal GDScript dependencies, including relative inherited QA."""
pending=[test];seen=set()
while pending:
name=pending.pop()
if name in seen:continue
seen.add(name)
text=Path(refs[name]['path']).read_text()
for quoted in re.findall(r'[\"\']([^\"\']+\.gd)[\"\']',text):
target=quoted[6:] if quoted.startswith('res://') else str(Path(name).parent/quoted)
if target in refs and target not in seen:pending.append(target)
return seen
def policy_coverage(paths, refs, policy):
for test in paths:
dependencies=dependency_paths(test,refs);p=phase_policy(test,Path(refs[test]['path']),policy['suite_policies'])
if 'tests/helpers/ui_audio_fixture_cleanup.gd' in dependencies:
require(p['ordinary_cleanup_prepared'] and p['ordinary_control_labels'], 'unknown ordinary helper user requires exact reviewed policy '+test)
if 'tests/helpers/ui_audio_teardown.gd' in dependencies:
require(p['ordinary_cleanup_prepared'] or p['old_bounded_teardown'] or test=='tests/ui/test_ui_audio_feedback.gd', 'unknown bounded teardown user requires reviewed proof policy '+test)
def copy_source(source,target):
if sys.platform=='darwin':subprocess.run(['/bin/cp','-c',str(source),str(target)],check=True)
else:shutil.copyfile(source,target)
require(target.stat().st_ino!=Path(source).stat().st_ino or target.stat().st_dev!=Path(source).stat().st_dev,
'private source is a separate inode; hardlinks refused')
def profile_gate(path, report, name):
value = read(report)
require(value['verified'] is True and value['conditions'] == [True]*5 and value['conditions_checked'] == 5
and value['fresh_before_parent_initialize'] is True and value['profile_writes_before_guard'] == 0
and value['actual_user_data_dir'] == str(path) == value['expected_user_data_dir']
and value['globalized_probe'] == str(path/'guard-probe') and value['custom_user_dir_name'] == name
and value['project_settings_persisted'] is False, 'all five unchanged runtime Guard conditions')
return {'report':value,'report_sha256':sha(report),'whole_files':inventory(path)}
def process_gate(row, allowed):
return (row.get('exit_code') == 0 and row.get('helper_exit_code') == 0 and row.get('timed_out') is False
and row.get('error_lines') == allowed and row.get('owned_popen_ended') is True
and row.get('old_helper_engine_ended') is True and row.get('start_receipt_written') is True
and row.get('end_receipt_written') is True and row.get('lifecycle_error') is None
and row.get('external_process_or_group_signals') == 0
and row.get('adapter_owned_cleanup',{}).get('engine_ended') is True)
def routed_report(directory, route, test, pid, raw, stage, source_sha):
file = directory/route['filename']
require(set(directory.iterdir()) == {file} and file.is_file() and not file.is_symlink() and file.stat().st_size>0,
'exclusive fresh outside report file set')
value = read(file); matches = [l[len(route['stdout_prefix']):] for l in raw.splitlines() if l.startswith(route['stdout_prefix'])]
require(isinstance(value,dict) and value and len(matches)==1, 'whole report and unique actual stdout JSON')
printed = json.loads(matches[0]); require(isinstance(printed,dict) and printed, 'nonempty printed report')
require(value==printed if route['stdout_projection']=='full' else all(value.get(k)==v for k,v in printed.items()),
'literal original report payload agreement')
owner = route.get('inherited_source',test)
require(sha(stage/test)==source_sha and sha(stage/owner)==route['report_owner_source_sha256'], 'actual report source provenance')
return {'verified':True,'path':str(file),'bytes':file.stat().st_size,'sha256':sha(file),
'actual_engine_pid':pid,'producer_test':test,'producer_source_sha256':source_sha,'report_owner_source':owner,
'report_owner_source_sha256':sha(stage/owner),'payload_field_deletions':0,'whole_JSON':value}
def declared_reports(stage, expected, previous, requested):
actual = inventory(stage,stage=True)
require(not set(requested)&(set(expected)|set(previous)), 'new reports cannot overwrite source/previous reports')
require(set(actual)==set(expected)|set(previous)|set(requested), 'complete source/UID/generated report set')
require({n:actual[n] for n in expected}==expected and {n:actual[n] for n in previous}==previous, 'all source and earlier report bytes exact')
added = {n:actual[n] for n in requested}
for name,pin in added.items():
require(pin['bytes']>0, 'nonempty actual generated report')
if name.endswith('.json'): read(stage/name)
else: (stage/name).read_text()
return added
def totals(rows, paths):
names=[r['test'] for r in rows]; missing=[r['test'] for r in rows if r.get('reported_checks') is None]
remaining=[n for n in paths if n not in names]; available=sum(r.get('reported_checks') or 0 for r in rows)
return {'actual_suites':len(rows),'missing_marker_paths':missing,'remaining_unexecuted_tests':remaining,
'available_marker_checks':available,'actual_all_roster_checks':available if not missing and names==paths else None,
'strict_accepted_checks':sum(r['reported_checks'] for r in rows if not r['failed'] and r.get('reported_checks') is not None)}
def execute_phase(engine, stage, out, phase, test, selected, expected, previous, policy, contracts, paths,
public_project, protected_player, root, outer, markers, diagnostics, owners, audio):
require(not outer.native.helper['live_engines'](), 'one owned native engine at a time')
require(shutil.disk_usage(out).free>=MIN_SPACE, 'fresh phase disk admission')
require(source_refs(root)==selected['root_before'] and default_player(root)==protected_player, 'entire current source/default profile unchanged before phase')
require(inventory(stage,stage=True)==dict(expected,**previous), 'whole stage source/UID/report inventory before phase')
env=fresh_environment(os.environ); startup_name='TheTree-PCK-tests-'+uuid.uuid4().hex+'-standard-startup-'+phase
startup=profile_path(startup_name); require(not startup.exists(), 'fresh startup profile before native engine')
write(out/(phase+'-startup-profile-before.json'), {'path':str(startup),'existed':False,'files':{},'before_native_spawn':True})
private=private_project(public_project,startup_name); (stage/'project.godot').write_bytes(private)
folder=out/phase; proof=None; evidence=None; runtime=None; runtime_report=None; script=None
row={'phase':phase,'test':test,'passed':False,'failed':True,'validation_error':None,'profile_startup_name':startup_name,
'private_project_sha256':hashlib.sha256(private).hexdigest(),'private_project_fields':2,'source_sha256':sha(stage/test) if test else None}
try:
if phase=='import': argv=[engine,'--headless','--path',str(stage),'--editor','--import','--quit','--verbose']
else:
name='TheTree-PCK-tests-'+uuid.uuid4().hex+'-standard-runtime-'+phase; runtime=profile_path(name)
require(not runtime.exists(), 'fresh runtime profile before Guard')
runtime_report=out/(phase+'-profile.json')
env.update(THE_TREE_PACK_PROFILE_NAME=name,THE_TREE_PACK_PROFILE_EXPECTED=str(runtime),
THE_TREE_PACK_PROFILE_REPORT=str(runtime_report),THE_TREE_FULL_PARSE_PATHS=str(out/'parse-paths.json'))
if phase=='parse': script=out/'bootstrap/parse_full_dependencies.gd'
else:
script=out/'starters'/(phase+'.gd')
script.write_text('extends '+json.dumps('res://'+test)+'\nconst Guard := preload('+json.dumps(str(out/'bootstrap/profile_guard.gd'))+')\nfunc _initialize() -> void:\n\tif not Guard.activate():\n\t\tquit(97)\n\t\treturn\n\tsuper._initialize()\n')
p=phase_policy(test,stage/test,policy['suite_policies']); row['policy']=p
route=policy['outside_routes'].get(test)
if route:
evidence=out/(phase+'-evidence'); evidence.mkdir(); env[route['env']]=str(evidence)
if p['ordinary_cleanup_prepared'] or p['old_bounded_teardown'] or test=='tests/ui/test_ui_audio_feedback.gd':
proof=out/(phase+'-teardown'); proof.mkdir()
if p['ordinary_cleanup_prepared']:
(proof/'owned-audio').mkdir(); env.update(THE_TREE_AUDIO_TEARDOWN_PROOF_DIR=str(proof/'owned-audio'),THE_TREE_AUDIO_FIXTURE_CONTROL_PROOF=str(proof/'control.json'))
else: env['THE_TREE_AUDIO_TEARDOWN_PROOF_DIR']=str(proof)
if test=='tests/ui/test_ui_audio_feedback.gd':
audio_dir=out/(phase+'-audio-proof'); audio_dir.mkdir()
env.update(THE_TREE_AUDIO_PROOF_REPORT=str(audio_dir/'actual-ogg-decode-player-state.json'),
THE_TREE_AUDIO_COMMAND_PROOF_REPORT=str(audio_dir/'actual-command-result-buffer-proof.json'))
argv=[engine,'--headless','--path',str(stage),'--script',str(script),'--verbose']
logfile=folder/'engine-native.log'; require(logfile.is_absolute() and not logfile.exists(), 'fresh absolute native log')
argv+=['--log-file',str(logfile)]; row.update(command=argv,logical_script='res://'+test if test else None,
starter_sha256=sha(script) if script else None,controlled_environment={k:v for k,v in env.items() if k.startswith('THE_TREE_')})
result,capture=outer.run_phase(argv,folder,180 if phase=='import' else selected['timeout'],env,
now_fn=now,dump_fn=write,sha_fn=sha,live_engines_fn=outer.native.helper['live_engines'])
row.update(native_process=result,outer_actual_capture=capture,actual_pid=result.get('pid') if result else None,
engine_ended=capture['all_captured_ended'],exit_code=result.get('exit_code') if result else None)
stdout=(folder/'stdout.log').read_text(); stderr=(folder/'stderr.log').read_text(); raw=stdout+stderr
raw_file=out/(test.replace('/','__')+'.log') if test else folder/'combined-raw.log'; raw_file.write_text(raw)
row.update(log=str(raw_file.relative_to(out)),log_sha256=sha(raw_file),raw_native_log={'path':str(logfile),'bytes':logfile.stat().st_size,'sha256':sha(logfile)})
if test:
parsed=markers.parse_raw(test,raw,contracts,row['source_sha256']); row.update(completion=parsed,
reported_checks=parsed['checks'],reported_failures=parsed['failures'],completion_marker=parsed['marker'],collector_errors=parsed['errors'])
review=diagnostics.review_streams(outer.native,{'stdout':stdout,'stderr':stderr,'native_file':logfile.read_text()},test,stage/test if test else None)
row['finite_original_diagnostic_review']=review
require(result and diagnostics.diagnostic_result_gate(result,review) and process_gate(result,review['allowed_e31_error_lines']) and capture['passed'], 'strict native/ownership/logger/finite diagnostics')
if script: require(sha(script)==row['starter_sha256'], 'actual starter source remains exact')
if runtime: row['actual_private_profile']=profile_gate(runtime,runtime_report,name)
if phase=='parse':
matches=re.findall(r'(?m)^FULL_DEPENDENCY_PARSE checks=([0-9]+) failures=([0-9]+)\n',raw)
require(matches==[(str(len(paths)),'0')], 'unique actual all normal-context dependencies parsed')
row['actual_parse_checks']=int(matches[0][0])
if test:
require(parsed['passed'] and sha(stage/test)==row['source_sha256'], 'exact original suite source/marker schema')
if evidence: row['outside_report']=routed_report(evidence,route,test,row['actual_pid'],raw,stage,row['source_sha256'])
if p['ordinary_cleanup_prepared']: row['ordinary_owner_pairs']=owners.ordinary_gate(proof,row['actual_pid'],out,p['ordinary_control_labels'])
if p['old_bounded_teardown'] or test=='tests/ui/test_ui_audio_feedback.gd': row['old_bounded_proof']=audio.teardown_gate(proof,row['actual_pid'],out)
if test=='tests/ui/test_ui_audio_feedback.gd':
row['actual_command_evidence']=audio.command_gate(audio_dir/'actual-command-result-buffer-proof.json',audio_dir,row['actual_pid'])
row['actual_ogg_evidence']=audio.ogg_gate(audio_dir/'actual-ogg-decode-player-state.json',row['actual_pid'],selected['map'])
row['passed']=True; row['failed']=False
except BaseException as caught: row['validation_error']=type(caught).__name__+': '+str(caught)
finally:
# run_phase owns cleanup before any receipts/guards. No discovered PID signals.
(stage/'project.godot').write_bytes(public_project)
row['whole_startup_profile_after']={'path':str(startup),'files':inventory(startup),'fresh_before_engine':True}
if runtime is not None: row['whole_runtime_profile_after']={'path':str(runtime),'files':inventory(runtime)}
try:
requested=policy['fixed_generated_report_routes'].get(test,[]) if test else []
generated=declared_reports(stage,expected,previous,requested); previous.update(generated)
for name in generated:
if name.endswith('-comparison.json'):
value=read(stage/name)
require(type(value.get('checks')) is int and value['checks']==row['reported_checks'] and value.get('failures')==0,
'actual generated JSON report/marker agreement')
row['generated_reports']={n:dict(pin,actual_engine_pid=row.get('actual_pid'),producer_test=test,
producer_source_sha256=row['source_sha256'],payload_modified=False)for n,pin in generated.items()}
row['source_sha256_end']=sha(stage/test) if test else None
row['default_player_entire_profile_unchanged']=default_player(root)==protected_player
require(row['default_player_entire_profile_unchanged'] and source_refs(root)==selected['root_before'], 'entire protected source/default player including logs/locks unchanged')
except BaseException as caught:
row['passed']=False;row['failed']=True;row['guard_error']=type(caught).__name__+': '+str(caught)
write(out/(phase+'-receipt.json'),row)
return row
def main(argv=None):
parser=argparse.ArgumentParser(description=__doc__)
parser.add_argument('--project-root',type=Path,default=HERE.parent); parser.add_argument('--output',type=Path)
parser.add_argument('--godot',default='godot');parser.add_argument('--timeout',type=int,default=1200)
parser.add_argument('--marker-contracts',type=Path);parser.add_argument('--source-input-refs',type=Path)
parser.add_argument('--prepare-only',action='store_true');args=parser.parse_args(argv)
root=args.project_root.resolve();out=(args.output or root/'artifacts'/('all-tests-'+uuid.uuid4().hex)).resolve();require(args.timeout>0,'positive timeout')
root_before=source_refs(root); refs=read(args.source_input_refs) if args.source_input_refs else root_before
require(set(refs)>=set(root_before), 'prospective source must retain every canonical source key')
for n,pin in refs.items():
path=Path(pin['path']);require(not path.is_symlink() and path.is_file() and path.stat().st_size==pin['bytes'] and sha(path)==pin['sha256'], 'exact selected source input '+n)
require(refs['tools/run_all_tests.py']['sha256']==sha(Path(__file__)), 'actual public runner selected source exact')
for file in SUPPORT.iterdir():
if file.is_file(): require(refs['tools/regression/'+file.name]['sha256']==sha(file), 'actual support selected source exact '+file.name)
paths=['tests/run_tests.gd']+sorted(n for n in refs if n.startswith('tests/') and n.endswith('.gd') and Path(n).name.startswith('test') and 'export'not in Path(n).parts and n!='tests/s0/test_kernel_and_save.gd')
markers=module('standard_exact_markers',HERE/'strict_test_markers.py');contract_path=(args.marker_contracts or root/'docs/plans/original-completion-2026-10-02/execution/completion_marker_contracts.json').resolve()
contracts=markers.load_contracts(contract_path);require(len(set(paths))==len(paths) and all(n in contracts for n in paths),'unique reviewed roster/schema')
policy=read(SUPPORT/'suite-policy.json')
for test in set(policy['suite_policies'])&set(paths): phase_policy(test,Path(refs[test]['path']),policy['suite_policies'])
policy_coverage(paths,refs,policy)
parse_paths=['res://'+n for n in sorted(refs) if n.endswith('.gd')]
if args.prepare_only:
print(json.dumps({'source_files':len(refs),'suites':len(paths),'parse_paths':len(parse_paths),'future_native_phases':len(paths)+2,'actual_checks':None,'engines':0,'stages':0}));return 0
outer=module('standard_exact_owned_outer',SUPPORT/'owned_outer.py');require(not outer.native.helper['live_engines'](),'another Godot owns CPU')
engine=shutil.which(args.godot) if not Path(args.godot).is_absolute() else args.godot;require(engine,'native launcher exists')
engine=str(Path(engine).resolve());require(shutil.disk_usage(out.parent).free>=MIN_SPACE,'fresh run disk admission')
out.mkdir(parents=True,exist_ok=False);stage=out/'stage';stage.mkdir();(out/'starters').mkdir()
(out/'bootstrap').mkdir()
for name,pin in read(SUPPORT/'bootstrap-scripts.json').items():
data=pin['text'].encode('utf-8');require(hashlib.sha256(data).hexdigest()==pin['sha256'], 'unchanged external bootstrap bytes')
(out/'bootstrap'/name).write_bytes(data)
protected=default_player(root);expected={n:{'bytes':p['bytes'],'sha256':p['sha256']}for n,p in refs.items()}
selected={'root_before':root_before,'map':{n:p['sha256']for n,p in refs.items()},'timeout':args.timeout}
for n,pin in refs.items():
target=stage/n;target.parent.mkdir(parents=True,exist_ok=True)
copy_source(Path(pin['path']),target)
require(inventory(stage,stage=True)==expected,'one complete private CoW source copy')
public=(stage/'project.godot').read_bytes();write(out/'parse-paths.json',parse_paths)
run_id=uuid.uuid4().hex;input_receipt={'protocol':'guarded-standard-v1','run_id':run_id,'owner_runner_pid':os.getpid(),
'project_root':str(root),'stage':str(stage),'roster':paths,'selected_source_map':selected['map'],
'marker_contract_sha256':sha(contract_path),'runner_sha256':sha(Path(__file__)),'marker_module_sha256':sha(HERE/'strict_test_markers.py'),
'engine':engine,'engine_sha256':sha(engine),'default_player_before':protected,'performance_is_not_maximum_scene_acceptance':True}
write(out/'runner-input.json',input_receipt);write(out/'source-start.json',root_before)
phases=[];rows=[];previous={};error=None;cleanup=[];cleanup_errors=[]
diagnostics=module('standard_finite_diagnostics',SUPPORT/'diagnostic_gate.py')
owners=module('standard_ordinary_proof_gate',SUPPORT/'ordinary_proof_gate.py');audio=module('standard_audio_proof_gate',SUPPORT/'audio_evidence_gate.py')
try:
for phase,test in [('import',None),('parse',None)]+[(str(i).zfill(3),n)for i,n in enumerate(paths)]:
row=execute_phase(engine,stage,out,phase,test,selected,expected,previous,policy,contracts,parse_paths,
public,protected,root,outer,markers,diagnostics,owners,audio);phases.append(row)
if test:
row['sequence']=len(rows);row['godot']=engine;rows.append(row)
(out/'all-tests.json').write_text(json.dumps(rows,ensure_ascii=False,indent=2)+'\n')
print(('PASS ' if row['passed'] else 'FAIL ')+test,flush=True)
require(row['passed'],'first strict failed phase stops without retry: '+phase)
except BaseException as caught: error=type(caught).__name__+': '+str(caught)
finally:
# Sweep actual factory objects before all fragile final receipt/guard writes.
cleanup,cleanup_errors=outer.finish_captured(outer.OWNED)
context=totals(rows,paths);guard_errors=[]
try:
require(source_refs(root)==root_before,'full canonical source unchanged at end')
require(default_player(root)==protected,'full default player unchanged at end')
require(inventory(stage,stage=True)==dict(expected,**previous),'whole stage/UID/report set at end')
except BaseException as caught:guard_errors.append(type(caught).__name__+': '+str(caught))
ended=bool(outer.OWNED) and all(r['engine_ended']is True for r in cleanup)
passed=error is None and not cleanup_errors and not guard_errors and ended and len(phases)==len(paths)+2 and all(r['passed']for r in phases)
write(out/'runner-ended.json',dict(context,protocol='guarded-standard-v1',run_id=run_id,owner_runner_pid=os.getpid(),
passed=passed,engine_ended=ended,failed_suites=sum(r['failed']for r in rows),actual_captured_PIDs=[p.pid for p in outer.OWNED],
final_owned_cleanup=cleanup,cleanup_errors=cleanup_errors,guard_errors=guard_errors,error=error,
all_tests_sha256=sha(out/'all-tests.json') if (out/'all-tests.json').exists() else None,phases=phases,
default_player_after=default_player(root),formal_acceptance=False,native_Windows='pending'))
print(json.dumps(dict(context,passed=passed,error=error,cleanup_errors=cleanup_errors,guard_errors=guard_errors)),flush=True)
return 0 if passed else 1
if __name__=='__main__':raise SystemExit(main())