From 408f7fb8031179fff615eb40ed9d2ba60a25a255 Mon Sep 17 00:00:00 2001 From: firestar5683 <168790843+firestar5683@users.noreply.github.com> Date: Fri, 18 Sep 2026 22:43:17 -0700 Subject: [PATCH] Sequence private judging with frozen configuration and fault stops --- roadscore/tools/judging_batch.py | 47 ++++++++++++++++++++++++++++++++ roadscore/tools/judging_run.py | 8 +++++- 2 files changed, 54 insertions(+), 1 deletion(-) create mode 100644 roadscore/tools/judging_batch.py diff --git a/roadscore/tools/judging_batch.py b/roadscore/tools/judging_batch.py new file mode 100644 index 0000000000..d74d4ccd40 --- /dev/null +++ b/roadscore/tools/judging_batch.py @@ -0,0 +1,47 @@ +"""Sequential private batch; never resets hardware or repeats an existing attempt.""" +import argparse +import json +from pathlib import Path +import subprocess +import time + +ROOT = Path(__file__).resolve().parents[1] +PYTHON = '/usr/local/venv/bin/python' + + +def main(): + parser = argparse.ArgumentParser() + parser.add_argument('manifest', type=Path) + args = parser.parse_args() + out = ROOT / 'results/community_judging' + failures = 0 + for label in 'ABCDEFGHIJK': + ledger = out / f'official_{label}.json' + if ledger.exists(): + # The original A preparation/replay is already owned by its runner. + deadline = time.monotonic() + 7200 + while json.loads(ledger.read_text()).get('phase') in ('preparing', 'replaying'): + if time.monotonic() > deadline: + raise RuntimeError('Existing attempt did not finish; refusing to take it over') + time.sleep(5) + else: + with (out / f'orchestrator_{label}.log').open('wb') as log: + subprocess.run([PYTHON, str(ROOT / 'tools/judging_run.py'), str(args.manifest), '--label', label], stdout=log, stderr=subprocess.STDOUT) + record = json.loads(ledger.read_text()) if ledger.exists() else {'phase': 'failed'} + print(label, record['phase'], flush=True) + # Preserve failed route evidence and continue, but never grind through a known link fault. + link = ROOT / 'generated/ace_link.jsonl' + if link.exists(): + last = json.loads(link.read_text().splitlines()[-1]) + if last.get('healthy') is False: + raise RuntimeError('Chestnut link fault; batch paused without reset') + failures = failures+1 if record['phase'] == 'failed' else 0 + if failures >= 3: + raise RuntimeError('Three consecutive attempt failures; inspect pipeline before continuing') + subprocess.run(['/data/sa3-feasibility/venv/bin/python', str(ROOT / 'tools/judging_review.py'), str(out)], check=True) + subprocess.run([PYTHON, str(ROOT / 'prototype/worker_service.py'), 'stop', '--composer', 'ace'], check=True) + print('Batch finished; worker stopped. All runs requested muted output.', flush=True) + + +if __name__ == '__main__': + main() diff --git a/roadscore/tools/judging_run.py b/roadscore/tools/judging_run.py index f841e7b91d..db306e49d1 100644 --- a/roadscore/tools/judging_run.py +++ b/roadscore/tools/judging_run.py @@ -1,5 +1,6 @@ """Private single-attempt native judging orchestration. No musical selection or uploads.""" import argparse +import hashlib import json import os from pathlib import Path @@ -30,6 +31,11 @@ def main(): row.update(json.loads(ranges.read_text()).get(args.label, {})) if row.get('range_resolution') == 'pending metadata': raise SystemExit('Resolve submitted range before launching this entry') + frozen = ROOT / 'generated/judging_configuration.private.json' + if frozen.exists(): + for name, digest in json.loads(frozen.read_text()).items(): + if hashlib.sha256((ROOT/name).read_bytes()).hexdigest() != digest: + raise SystemExit('Frozen judging configuration changed: '+name) out = ROOT / 'results/community_judging' out.mkdir(exist_ok=True, mode=0o700) ledger = out / f'official_{args.label}.json' @@ -55,7 +61,7 @@ def main(): 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: + if (state.get('generation_seed') == row['seed'] and 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())