audit_partial.py
python · 43 lines · Sanitized derivative
Sanitized derivative: private paths or personal test identities use explicit placeholders. Original line count is preserved.
Version & provenance · cm7-cm5-comparison-v1
Recorded SHA-256 bd8fcb5762e918d9e999ddb66bb8e6a73853f8bfa28d8e72c94e15c64a153c45Rendered SHA-256 938199c95dff47ff60ce4316b645d3f7268a07a29d684a96d7b2c460897f9a2c
Original LittlePly code: Apache-2.0. External Rust helper remains a separate GPL component. License notes. Line links are stable within this snapshot. Frozen research implementation; host paths redacted. Original and rendered digests identify the derivative; not a portable launcher or complete training payload.
1"""Re-audit complete head-to-head games, identities, proofs and cleanup."""
2import json,os,time,collections,statistics
3from pathlib import Path
4from runner import inputs,sha,read,write,H
5import chess.pgn
6from head_game import audit,summarize
7def main():
8 s,c,p=inputs();out=Path(p['run_directory'])
9 if read(out/'supervision.json')!={'returncode':0,'deadline_expired':False} or read(out/'terminal.json')['status']!='partial_or_error':raise ValueError('recorded clean supervisor/partial terminal required')
10 rows=read(out/'results.json')
11 if len(rows)!=59 or [r['game_id'] for r in rows]!=[r['game_id'] for r in s['schedule'][:len(rows)]]:raise ValueError('schedule coverage')
12 opens={o['opening_id']:o for o in s['openings']};metrics={name:[] for name in c['players']}
13 with (out/'games.pgn').open() as stream:
14 for row,item in zip(rows,s['schedule']):
15 if any(row[k]!=v for k,v in item.items()):raise ValueError('schedule row changed')
16 folder=out/row['game_id'];report=read(folder/'game.json');parsed,checked=audit(folder,report,opens[item['opening_id']],item['model_color']=='white')
17 if any(row[k]!=v for k,v in checked.items()):raise ValueError('audit mismatch')
18 if row['error'] and (row is not rows[-1] or row['error']!="RuntimeError('evaluation stop: live CM-7 not fresh/ready')" or row['result']!='*'):raise ValueError('unexpected interrupted-game error')
19 if sha(folder/'game.pgn')!=row['pgn_sha256'] or sha(folder/'game.json')!=row['game_json_sha256']:raise ValueError('hash mismatch')
20 combined=chess.pgn.read_game(stream)
21 if combined is None or combined.errors or combined.headers!=parsed.headers or list(combined.mainline_moves())!=list(parsed.mainline_moves()):raise ValueError('combined PGN mismatch')
22 if parsed.headers['CM7SHA256']!=c['players']['CM-7']['sha256'] or parsed.headers['CM5SHA256']!=c['players']['CM-5']['sha256']:raise ValueError('PGN checkpoint attribution')
23 for move in report['moves']:metrics[move['player']].append(move)
24 if chess.pgn.read_game(stream) is not None:raise ValueError('extra games')
25 owner=read(H/'launch-receipt.json')['pid'];worker=read(out/'worker-started.json')['pid'];alive=[]
26 for d in Path('/proc').iterdir():
27 if not d.name.isdigit():continue
28 try:
29 stat=(d/'stat').read_text().rsplit(')',1)[1].split();args=(d/'cmdline').read_bytes().split(b'\0')
30 if int(d.name) in (owner,worker) or int(stat[2])==worker or str(H/'runner.py').encode() in args:alive.append(int(d.name))
31 except (FileNotFoundError,ProcessLookupError):pass
32 if alive:raise ValueError('evaluation processes remain')
33 health=read('/REDACTED')
34 def timing(xs):
35 xs=sorted(xs);return {'mean':statistics.mean(xs),'p95':xs[min(len(xs)-1,int(.95*len(xs)))],'max':max(xs)} if xs else None
36 player_stats={}
37 for name,moves in metrics.items():
38 queries=[r for r in moves if r['model_query']=='queried'];searches=[r['mate_search'] for r in moves]
39 player_stats[name]={'queries':len(queries),'raw_greedy_legal_fraction':sum(r['raw_greedy_legal'] for r in queries)/len(queries) if queries else None,'helper_overrides':sum(r['decision_source']=='forced_mate_override' for r in moves),'helper_status_counts':dict(collections.Counter(r['status'] for r in searches)),'helper_depth_counts':dict(collections.Counter(str(r.get('completed_depth_plies')) for r in searches)),'helper_reason_counts':dict(collections.Counter(r.get('reason') for r in searches)),'helper_timing':timing([r['seconds'] for r in searches]),'turn_timing':timing([r['seconds'] for r in moves])}
40 evidence={'status':'verified_user_stopped_partial','stop_reason':'user requested stop for migration; live-service guard had already stopped worker before explicit signal','signal_sent':False,'completed_scored_games':58,'interrupted_games':1,'unplayed_games':41,'original_terminal':read(out/'terminal.json'),'interrupted_game_id':rows[-1]['game_id'],'summary':summarize(rows),'player_metrics':player_stats,'all_recorded_pgns_proofs_reaudited':True,'owned_remaining':alive,'health':health,'players':c['players'],'artifact_sha256':{n:sha(out/n) for n in ('games.pgn','results.json','manifest.json','supervision.json')},'observed_unix':time.time(),'promotion_performed':False}
41 write(H/'partial-completion-evidence.json',evidence);print(json.dumps(evidence,indent=2))
42if __name__=='__main__':main()
43