"""Row 97 independent verification. Do not run before its assigned test slot.""" import os, subprocess, signal, pathlib, json, hashlib, sys, time root=pathlib.Path.home()/'filbert-scratch/r97-review/wt' out=root.parent/'verification';out.mkdir(exist_ok=True) env=dict(os.environ,TMPDIR=str(pathlib.Path.home()/'filbert-scratch/tmp'),DOCKER_HOST='unix:///nonexistent.sock') seats=['darkwing','dewey','rocko','researcher','sage'] other=[str(pathlib.Path.home()/f'{seat}-scratch') for seat in seats]+[str(pathlib.Path('/mnt/storage/scratch')/seat) for seat in seats] def quiet(): busy=[] for p in pathlib.Path('/proc').iterdir(): if not p.name.isdigit():continue try: cwd=str((p/'cwd').resolve(strict=True)) if any(cwd==x or cwd.startswith(x+'/') for x in other):busy.append((p.name,cwd)) except (OSError,RuntimeError):pass if busy:raise SystemExit('Other scratch processes active: '+repr(busy)) def matches(): m=pathlib.Path('/mnt/storage/src/mosaic-stack/agents/researcher/work/queue-97/candidate-manifest.sha256') for line in m.read_text().splitlines(): sha,path=line.split();assert hashlib.sha256((root/path).read_bytes()).hexdigest()==sha,path quiet();matches() def active_group(group): found=[] for d in pathlib.Path('/proc').iterdir(): if not d.name.isdigit():continue try: if os.getpgid(int(d.name))==group: stat=(d/'stat').read_text().split(') ',1)[1].split()[0] if stat!='Z':found.append((d.name,(d/'comm').read_text().strip())) except OSError:pass return found def run(cmd,cwd,log,limit=180): quiet();start=time.monotonic() with log.open('w') as f: p=subprocess.Popen(cmd,cwd=cwd,env=env,stdout=f,stderr=subprocess.STDOUT,start_new_session=True) try:p.wait(timeout=limit);rc=p.returncode except subprocess.TimeoutExpired: rc='timeout' try:os.killpg(p.pid,signal.SIGKILL) except ProcessLookupError:pass p.wait() # Record normal cleanup BEFORE safety cleanup. Do not conceal leftover browser processes. leftovers=active_group(p.pid) if leftovers: try:os.killpg(p.pid,signal.SIGKILL) except ProcessLookupError:pass return {'rc':rc,'seconds':round(time.monotonic()-start,2),'leftovers_before_safety_cleanup':leftovers} rows=[];stage=sys.argv[1] if stage in ['webui','control-board']: cwd=root/'packages'/stage;r=run(['node','--test','tests/'],cwd,out/f'{stage}.log') r.update(suite=stage,stats=[s for s in (out/f'{stage}.log').read_text().splitlines() if s.startswith(('ℹ tests ','ℹ pass ','ℹ fail ','# tests ','# pass ','# fail '))]) (out/f'{stage}-summary.json').write_text(json.dumps(r,indent=2)+'\n');print(json.dumps(r),flush=True) raise SystemExit(r['rc']!=0 or bool(r['leftovers_before_safety_cleanup'])) if stage=='shell': for script in sorted((root/'scripts').glob('test-*.sh')): r=run(['bash',str(script)],root,out/f'{script.stem}.log');r.update(suite=script.stem,last=(out/f'{script.stem}.log').read_text().splitlines()[-1:]) rows.append(r);print(json.dumps(r),flush=True) (out/'shell-summary.json').write_text(json.dumps(rows,indent=2)+'\n') raise SystemExit(any(r['rc']!=0 or r['leftovers_before_safety_cleanup'] for r in rows)) if stage!='mutants':raise SystemExit('usage: verify.py webui|control-board|shell|mutants') A='packages/webui/src/public/app.js';B='packages/webui/src/public/bus.js' M=[ ('13 toggle-off schedule',A,'if (!paused && machine && !out()) timer','if (!paused && !out()) timer',['tests/board-held.test.mjs'],120), ('27 signed-out bus schedule',B,'if (!paused() && !out()) timer','if (!paused()) timer',['tests/board-held.test.mjs'],120), ('19 toggle on reads nothing',A,'fresh(); refresh(); } else hideMachine();','fresh(); } else hideMachine();',['tests/browser-edge.test.mjs'],30), ] start={path:hashlib.sha256((root/path).read_bytes()).hexdigest() for path in [A,B]} for name,path,old,new,tests,limit in M: quiet();p=root/path;orig=p.read_bytes();src=orig.decode();assert src.count(old)==1,(name,src.count(old)) try: p.write_text(src.replace(old,new));log=out/f'mutant-{name.split()[0]}.log' r=run(['node','--test',*tests],root/'packages/webui',log,limit) r.update(mutant=name,result='survived' if r['rc']==0 else 'stalled' if r['rc']=='timeout' else 'killed') if name.startswith('19 '):r['five_second_message_seen']='Show machine sessions made no /api/board request in 5 s' in log.read_text() rows.append(r);print(json.dumps(r),flush=True) finally:p.write_bytes(orig) assert all(hashlib.sha256((root/path).read_bytes()).hexdigest()==sha for path,sha in start.items()) matches() r=run(['node','--test','tests/board-held.test.mjs','tests/browser-edge.test.mjs'],root/'packages/webui',out/'restored-tests.log') r.update(restored_tests=True,manifest_matches=True,product_sources_restored=True);rows.append(r);print(json.dumps(r),flush=True) (out/'mutant-summary.json').write_text(json.dumps(rows,indent=2)+'\n') raise SystemExit(r['rc']!=0 or any(x.get('result') in ['survived','stalled'] or x['leftovers_before_safety_cleanup'] or x.get('five_second_message_seen') is False for x in rows))