diff --git a/roadscore/EVENT.md b/roadscore/EVENT.md index 4682048fc4..0e38801948 100644 --- a/roadscore/EVENT.md +++ b/roadscore/EVENT.md @@ -107,3 +107,9 @@ Native muted replay `normal_1789795416` is running without continuous UI video e `normal_1789795416` passed the instrumented native gate through final-segment EOF: 254.1 seconds captured, six accepted fresh generation jobs, zero accepted-music holds, zero underflows, zero emergency fallbacks, no worker failure, zero output flags and all blocks muted. Camera accepted 5,088 frames; path/lane drawing, navigation and ten curve activations were observed. All request timestamps were causal. Maximum source-clock drift was 24.671 ms. Continuous UI video encoding was disabled; the overlay snapshot and UI audit remain available. This single pass does not prove that encoding caused the earlier timing failure. The local private evidence is `results/event_night_one/prism_power45/`: preparation metadata, native audit, overlay and captured score. The reusable post-run audit correctly rejects the earlier full-route failure and has three focused negative-evidence tests. It does not claim human musical approval, physical speaker/Bluetooth validation or modeld coexistence. Phase 2 has not yet begun; the native prerequisite is now met. No upload or push was performed. + +### Private judging batch setup + +After local checkpoint `f75a98181f`, all eleven unique submissions returned native route-file listings. Private complete-route caching has begun. The private manifest fixes anonymous labels, route-derived seeds, common Prism/45 W/muted settings and objective excerpt rules before any judging playback. Exact identities and acquisition logs remain ignored/private. + +Checkpoint `963d069778` adds optional deterministic sampling: separate preparation/continuation seed streams, request sequence independent of wall-clock job filenames, and rejection of a mismatched resident preparation. Legacy sampling remains unchanged unless explicitly configured. Three seed tests and six composer tests pass. Submission A's first attempt is preparing; the single-attempt runner waits for its matching preparation and complete ordinary route cache, then performs muted native replay. It refuses automatic reruns. No listening scores or winner exist. Range-disambiguation entries remain blocked from launch until their metadata decision is recorded. diff --git a/roadscore/tools/judging_run.py b/roadscore/tools/judging_run.py new file mode 100644 index 0000000000..10a98b36dc --- /dev/null +++ b/roadscore/tools/judging_run.py @@ -0,0 +1,93 @@ +"""Private single-attempt native judging orchestration. No musical selection or uploads.""" +import argparse +import json +import os +from pathlib import Path +import subprocess +import time + +ROOT = Path(__file__).resolve().parents[1] +PYTHON = '/usr/local/venv/bin/python' + + +def save(path, value): + temp = path.with_suffix('.tmp') + temp.write_text(json.dumps(value, indent=2)) + os.chmod(temp, 0o600) + temp.replace(path) + + +def main(): + parser = argparse.ArgumentParser() + parser.add_argument('manifest', type=Path) + parser.add_argument('--label', required=True, choices=list('ABCDEFGHIJK')) + parser.add_argument('--resume-preparation', action='store_true') + args = parser.parse_args() + manifest = json.loads(args.manifest.read_text()) + row = next(r for r in manifest['submissions'] if r['label'] == args.label) + if row.get('range_resolution') == 'pending metadata': + raise SystemExit('Resolve submitted range before launching this entry') + out = ROOT / 'results/community_judging' + out.mkdir(exist_ok=True, mode=0o700) + ledger = out / f'official_{args.label}.json' + if ledger.exists(): + record = json.loads(ledger.read_text()) + if not args.resume_preparation or record['phase'] != 'preparing' or record['seed'] != row['seed']: + raise SystemExit('Existing attempt preserved; refusing an automatic rerun') + else: + record = {'label': args.label, 'seed': row['seed'], 'attempt': 1, 'phase': 'preparing', + 'started_wall': time.time(), 'configuration': manifest['configuration'], 'manual_reruns': 0} + with ledger.open('x') as stream: + json.dump(record, stream, indent=2) + os.chmod(ledger, 0o600) + env = os.environ.copy() + env.update(ROADSCORE_GENERATION_SEED=str(row['seed']), AM_POWER_LIMIT='45') + service = ROOT / 'prototype/worker_service.py' + try: + if not args.resume_preparation: + subprocess.run([PYTHON, str(service), 'stop', '--composer', 'ace'], check=True) + subprocess.run([PYTHON, str(service), 'start', '--composer', 'ace', '--profile', 'prism'], env=env, check=True) + deadline = time.monotonic() + 1560 + while True: + state = json.loads((ROOT / 'generated/ace_worker_state.json').read_text()) + if state.get('generation_seed') == row['seed'] and state.get('phase') == 'READY': + break + if state.get('phase') == 'Stopped' or time.monotonic() > deadline: + raise RuntimeError('Preparation stopped or timed out; original attempt retained') + time.sleep(5) + initial = json.loads((ROOT / 'generated/ace_initial.json').read_text()) + if initial.get('generation_seed') != row['seed']: + raise RuntimeError('Prepared seed mismatch') + save(out / f'preparation_{args.label}.json', initial) + # Require the ordinary complete cache, without inspecting musical/road events. + import sys + sys.path.insert(0, str(ROOT / 'prototype')) + from route_library import cache_complete + deadline = time.monotonic() + 3600 + while not cache_complete(ROOT / 'routes' / row['route']): + if time.monotonic() > deadline: + raise RuntimeError('Cache not ready within one hour; no replay launched') + time.sleep(10) + before = set((ROOT / 'results').glob('normal_*')) + record.update(phase='replaying', replay_started_wall=time.time()) + save(ledger, record) + command = [str(ROOT.parent / 'onroad'), '--routeid', row['route'], '--roadscore', + '--composer', 'ace', '--profile', 'prism', '--muted', + '--start', str(row.get('start_seconds', 0)), '--duration', str(row.get('duration_seconds', 86400))] + with (out / f'console_{args.label}.log').open('wb') as log: + result = subprocess.run(command, env=env, cwd=ROOT.parent, stdout=log, stderr=subprocess.STDOUT) + runs = sorted(set((ROOT / 'results').glob('normal_*')) - before) + record.update(phase='finished' if result.returncode == 0 else 'failed', exit_code=result.returncode, + run_paths=[str(r) for r in runs], ended_wall=time.time()) + for run in runs: + subprocess.run([PYTHON, str(ROOT / 'tools/event_replay_audit.py'), str(run)], stdout=subprocess.DEVNULL, check=True) + except Exception as error: + record.update(phase='failed', error_type=type(error).__name__, error=str(error), ended_wall=time.time()) + raise + finally: + save(ledger, record) + print(args.label, record['phase'], flush=True) + + +if __name__ == '__main__': + main()