ouroboros/devtools/benchmarks/programbench/run_programbench_e2e.py
Anton Razzhigaev a5bdf5e010 v6.81.0: benchmark artefacts must not carry or claim what did not happen
Two provenance fixes in the same class as the release itself, plus the
round-11/12 review findings that land in the same files.

FIX A — an isolated benchmark container carries only the providers the run
declared. `build_isolated_settings` copied EVERY provider credential present
in the live settings file regardless of which providers the run's model slots
declared, so a run pinned to OpenRouter still received direct ANTHROPIC /
OPENAI / Cloud.ru / GigaChat keys. Two consequences: a routing fallback could
spend outside the declared bucket while the manifest said otherwise, and the
reachable provider set was a function of whatever happened to be in the live
file at launch — a pinned seed that pins the code but not the environment is
not reproducible. Provider credentials are now gated on the run's declared
slots, derived from the routing SSOT (`provider_models.PROVIDER_PREFIXES` /
`provider_for_model`, the same registry `llm._resolve_remote_target` routes
on), and travel in whole groups so a key never arrives without the endpoint or
auth fields it is useless without. Owner/control secrets were never copied and
still are not. Ambiguity fails OPEN (carry a spare, disclose it) — never
closed. `benchmark_run_manifest` now records `provider_credentials`: which
credentials the container actually received, by fingerprint, never by value.

FIX B — a task the cost rail truncated says so. `usage_accounting.reserve_-
attempt` refuses on a worst-case reservation bound that reached a $6.00 rail at
$0.45 of actual spend in the v6.81.0 OSWorld smoke, stopping two of three tasks
at 13 and 22 rounds; the artefacts published `status=completed`,
`reason_code=official_evaluate` and the string `budget_exhausted` appeared
nowhere. `task_result_row` gains an always-present `runtime_outcome` projected
by the new shared `runtime_terminal_disclosure`, and every writer holding a
runtime task result now publishes it: OSWorld cu_bridge, SWE-bench and
ProgramBench success rows (the failure rows already did), the Terminal-Bench
in-container summary and disclosure ledger, the GAIA solver, the harness-bench
wrapper, and the CL-Bench per-question writer. Reward, `official_eval_status`
and adapter-stage `status` are untouched: disclosure ADDED, fact not
subtracted. TB's ledger gains a `cost_truncated` bucket — `genuine` asserts a
fair shot, which a rail-truncated trial did not get.

Review findings folded in:
- `_amend_manifest` emitted `output_paths.task_outcome` unconditionally, so the
  finalized attempt manifest kept pointing at an outcome whose write failed.
  The previous round fixed the ledger row and left the manifest lying; both
  sides now follow the same rule.
- `ADAPTER_PATCH_MARKERS` keyed two of three detections on bare env-var names,
  which the unpatched adapter may mention in a comment or a `-e` passthrough
  list. That false positive OVERSTATES enforcement. Markers are now
  patch-unique tokens and the uniqueness requirement is recorded beside them.
  (One marker legitimately covers all three env knobs: they arrive in one loop
  in one hunk.)
- CLB fidelity overstated enforcement on the DEFAULT `--path standard`:
  `_docker_launcher.submit()` hardcodes `disabled_tools: []` and never imports
  the patched bridge module, so the evidence is now entrypoint-specific.
- `runtime_attested` renamed to `runtime_attestation_available`: it is a tree
  probe, and a definition existing is not evidence that it ran.
- README version badge alt text said 6.80.0 while the URL said 6.81.0, which
  `version_carrier_desyncs` flags and the advisory preflight blocks on.

Bug-pinning tests inverted, and said so in the test docstrings:
- `test_dry_run_claims_attestation_only_when_the_patch_is_in_the_execution_-
  clone` asserted `runtime_attested is True` for a DRY RUN against a clone that
  merely contained an `_attest_runtime` definition — it demanded the false
  positive as the contract.
- the CLB fidelity fixtures wrote bare env-var names as "the patch", which is
  precisely the marker weakness above.

Docs corrected where the code falsified them: OSWorld METHODOLOGY §7.4 claimed
`OUROBOROS_MAX_ROUNDS` plus the timeout were the only per-task caps (the USD
rail binds first), §6 now says scoring reads `official_eval_status` /
`details.outcome_status` rather than filtering on `status == "completed"` and
that `output_paths.task_outcome` may be absent; CLB METHODOLOGY §3 documents
when an exported runtime mode overrides the adapter's hard-set `advanced`, and
§6 distinguishes the `--runner-path` adapter checkout from the
`--ouroboros-clone` execution seed and points at the field the code actually
writes.

Known ordering debt noted in place for the v6.82 backlog, deliberately not
restructured here: `_auto_sync_release_metadata_if_needed` runs ~87 lines after
the `_release_metadata_preflight` gate it would satisfy.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-26 05:04:14 +00:00

654 lines
31 KiB
Python

#!/usr/bin/env python3
"""End-to-end ProgramBench runner for Ouroboros.
Orchestrates docker cleanroom setup, Ouroboros task submission via the gateway API,
submission packaging, and optional official ``programbench eval``.
"""
from __future__ import annotations
import argparse
import dataclasses
import json
import os
import pathlib
import sys
import traceback
import urllib.parse
from typing import Any
if __package__ in {None, ""}:
sys.path.insert(0, str(pathlib.Path(__file__).resolve().parents[3]))
from devtools.benchmarks.common.manifests import (
MODEL_SLOT_KEYS,
admit_benchmark_run,
finalize_run_manifest,
runtime_attestation,
write_json,
)
from devtools.benchmarks.common.official_commands import programbench_command_for_manifest
from devtools.benchmarks.common.result_index import append_result_index, task_result_row
from devtools.benchmarks.common.run_roots import (
default_settings_path,
assert_outside_repo,
run_root,
safe_join_under,
)
from devtools.benchmarks.programbench.programbench_adapter import (
_checkpoint_task_id,
build_instruction,
build_ouroboros_task_body,
classify_infra_failure,
container_name_for_instance,
create_submission_tarball,
default_protected_backend_paths,
ouroboros_api_request,
pull_cleanroom_image,
run_official_eval,
seed_workspace_from_image,
start_cleanroom_container,
stop_cleanroom_container,
submit_and_wait,
terminal_task_status,
)
from devtools.benchmarks.programbench.schemas import PROGRAMBENCH_TIMEOUT_SEC
from ouroboros.provider_models import migrate_model_value
# Model-carrying slots only; the OUROBOROS_EFFORT_* entries in MODEL_SLOT_KEYS are
# effort levels, not model ids.
_MODEL_ID_SLOT_KEYS = tuple(key for key in MODEL_SLOT_KEYS if not key.startswith("OUROBOROS_EFFORT_"))
TASK_CHECKPOINT_BASENAME = "ouroboros_task_checkpoint.json"
# A resume-skipped instance (prior submission already on disk) is successful
# prior work, not a failure — the run exit code and failed_count agree on this.
_SUCCESS_STATUSES = ("completed", "skipped")
def _row_successful(row: dict[str, Any]) -> bool:
return row.get("status") in _SUCCESS_STATUSES
def _ensure_docker_host() -> None:
if os.environ.get("DOCKER_HOST"):
return
colima_sock = pathlib.Path.home() / ".colima" / "default" / "docker.sock"
if colima_sock.exists():
os.environ["DOCKER_HOST"] = f"unix://{colima_sock}"
def _setting_or_env(settings: dict[str, Any], key: str) -> str:
"""Settings value with env fallback — the server treats settings.json as SSOT
and falls back to the launch environment for keys it does not carry."""
raw = settings.get(key)
if raw is None or str(raw).strip() == "":
raw = os.environ.get(key, "")
return str(raw or "").strip()
def _active_direct_provider(settings: dict[str, Any]) -> str:
"""Mirror config._exclusive_direct_remote_provider_env over settings+env.
'' means the OpenRouter-style route (or an ambiguous key set), where
``provider/model`` ids are canonical and must NOT be rewritten.
"""
if any(
_setting_or_env(settings, key)
for key in ("OPENROUTER_API_KEY", "OPENAI_BASE_URL", "OPENAI_COMPATIBLE_BASE_URL")
):
return ""
direct = [
provider
for provider, key in (
("openai", "OPENAI_API_KEY"),
("anthropic", "ANTHROPIC_API_KEY"),
("cloudru", "CLOUDRU_FOUNDATION_MODELS_API_KEY"),
)
if _setting_or_env(settings, key)
]
if _setting_or_env(settings, "GIGACHAT_CREDENTIALS") or (
_setting_or_env(settings, "GIGACHAT_USER") and _setting_or_env(settings, "GIGACHAT_PASSWORD")
):
direct.append("gigachat")
return direct[0] if len(direct) == 1 else ""
def preflight_model_slots(settings_path: pathlib.Path, *, solve_model: str = "") -> dict[str, str]:
"""Validate configured model ids the way the runtime routes them.
Direct-provider routes reject legacy ``provider/model`` ids (they need
``provider::model``); a prior debug run burned 5/5 tasks on exactly that
400. The runner cannot patch the externally started server's environment, so
a legacy-form id (or a --solve-model that disagrees with settings) is a
launch blocker, not a warning. Returns the normalized slot snapshot.
"""
settings: dict[str, Any] = {}
try:
loaded = json.loads(pathlib.Path(settings_path).read_text(encoding="utf-8"))
if isinstance(loaded, dict):
settings = loaded
except (OSError, json.JSONDecodeError):
settings = {}
provider = _active_direct_provider(settings)
problems: list[str] = []
slots: dict[str, str] = {}
for key in _MODEL_ID_SLOT_KEYS:
raw = _setting_or_env(settings, key)
if not raw:
continue
parts = [part.strip() for part in raw.split(",") if part.strip()]
migrated = [migrate_model_value(provider, part) if provider else part for part in parts]
if migrated != parts:
problems.append(f"{key}: {raw!r} -> {','.join(migrated)!r}")
slots[key] = ",".join(migrated)
expected_solve = ""
if solve_model:
expected_solve = migrate_model_value(provider, solve_model) if provider else solve_model
if expected_solve != solve_model:
problems.append(f"--solve-model: {solve_model!r} -> {expected_solve!r}")
configured = slots.get("OUROBOROS_MODEL", "")
if configured and configured != expected_solve:
problems.append(
f"--solve-model {expected_solve!r} does not match settings OUROBOROS_MODEL "
f"{configured!r}; the server would solve on the wrong model"
)
if problems:
raise SystemExit(
"model slot preflight failed for the "
+ (f"direct {provider!r}" if provider else "configured")
+ " route — fix the settings the server was started with (see "
f"{settings_path}):\n " + "\n ".join(problems)
)
return slots
def _load_instances(
*,
difficulty: str,
filter_spec: str,
slice_spec: str,
instance_id: str,
shuffle: bool,
) -> list[dict[str, Any]]:
from programbench.utils.instance_filters import filter_instances
from programbench.utils.load_data import load_all_instances
instances = load_all_instances(include_tests=False)
if difficulty:
instances = [item for item in instances if str(item.get("difficulty") or "") == difficulty]
if instance_id:
instances = [item for item in instances if str(item.get("instance_id") or "") == instance_id]
return filter_instances(instances, filter_spec=filter_spec, slice_spec=slice_spec, shuffle=shuffle)
@dataclasses.dataclass(frozen=True)
class InstanceRunConfig:
"""Per-run knobs shared by every instance (keeps `_process_instance` within
the DEVELOPMENT.md parameter budget)."""
out_root: pathlib.Path
ouroboros_url: str
timeout_sec: float
cpus: str
memory: str
protected_paths: list[str]
dry_run: bool
skip_pull: bool
redo_existing: bool
def _process_instance(instance: dict[str, Any], cfg: InstanceRunConfig) -> dict[str, Any]:
instance_id = str(instance["instance_id"])
image_name = str(instance["image_name"])
instance_dir = safe_join_under(cfg.out_root, instance_id)
workspace = instance_dir / "workspace"
container_name = container_name_for_instance(instance_id)
instruction = build_instruction(instance)
instruction_path = instance_dir / "instruction.txt"
instance_dir.mkdir(parents=True, exist_ok=True)
instruction_path.write_text(instruction, encoding="utf-8")
checkpoint_path = instance_dir / TASK_CHECKPOINT_BASENAME
if cfg.redo_existing:
# An explicit redo means a fresh solve; a surviving checkpoint would
# re-attach to the previous task instead. Cancel a still-live server-side
# task FIRST — otherwise it keeps running and its docker-exec calls land
# in the NEW same-named container, contaminating the fresh solve
# (adversarial review r1).
stale_task_id = _checkpoint_task_id(checkpoint_path)
if stale_task_id:
try:
ouroboros_api_request(
cfg.ouroboros_url, "POST",
f"/api/tasks/{urllib.parse.quote(stale_task_id)}/cancel", timeout=30,
)
except Exception:
pass # best-effort: server may not know it / be unreachable
checkpoint_path.unlink(missing_ok=True)
harness: dict[str, Any] = {"container_name": container_name, "image_name": image_name}
output_paths: dict[str, str] = {"instruction": str(instruction_path)}
keep_container_for_reattach = False
# Reattach honor-check BEFORE any cleanroom work: a fresh seed/start would
# reset the workspace and (start_cleanroom_container stops any namesake
# first) kill the very container a prior client_poll_timeout_reattachable
# row deliberately left running. If the checkpoint names a task this server
# still knows, we preserve the container/workspace and let submit_and_wait
# reattach (a settled task returns immediately and is packaged as-is); only
# a missing/stale checkpoint (or --redo-existing, which unlinked it above)
# gets a fresh cleanroom.
reattach_task_id = _checkpoint_task_id(checkpoint_path)
if reattach_task_id:
try:
_reattach_payload = ouroboros_api_request(
cfg.ouroboros_url, "GET",
f"/api/tasks/{urllib.parse.quote(reattach_task_id)}", timeout=60,
)
except Exception:
# ANY failure (RuntimeError from an HTTPError, or a connection-level
# URLError/OSError the adapter does not map) means we cannot confirm
# the task is live — fall to a fresh cleanroom rather than aborting
# the whole run before the top-level ledger is written (r1 #14).
reattach_task_id = ""
else:
# Reattach ONLY to an in-flight or COMPLETED task. A task that already
# SETTLED as a failure/cancellation must NOT be reattached — replaying
# it is zero work and defeats the resume-RETRY contract (r1 #12: a
# failed instance gets NO tarball precisely so a resume run re-solves
# it). Drop the stale checkpoint and fall to a fresh cleanroom solve
# (adversarial review r2 #5). An in-flight task (status not settled)
# returns "" from terminal_task_status and is reattached as a genuine
# crash-resume; a settled "completed" is reattached and packaged as-is.
_settled = terminal_task_status(_reattach_payload)
if _settled and _settled != "completed":
reattach_task_id = ""
checkpoint_path.unlink(missing_ok=True)
harness["reattached_task_id"] = reattach_task_id
# While a reattachable task is live, NOTHING may stop its container — not a
# dry-run early return, not a mid-poll transport error. Only a settled
# terminal result (below) re-arms the normal teardown.
keep_container_for_reattach = bool(reattach_task_id)
try:
if not reattach_task_id:
if not cfg.skip_pull:
harness["image"] = pull_cleanroom_image(image_name)
harness["seed"] = seed_workspace_from_image(image_name, workspace)
harness["container"] = start_cleanroom_container(
container_name,
image_name,
workspace,
cpus=cfg.cpus,
memory=cfg.memory,
)
body = build_ouroboros_task_body(
instruction=instruction,
workspace_host_path=workspace,
container_name=container_name,
protected_backend_paths=cfg.protected_paths,
)
body.setdefault("metadata", {})["programbench_instance_id"] = instance_id
body.setdefault("metadata", {})["cleanroom_preflight"] = (harness.get("container") or {}).get("preflight") or {}
body["timeout_sec"] = float(cfg.timeout_sec)
write_json(instance_dir / "ouroboros_task_body.json", body)
output_paths["task_body"] = str(instance_dir / "ouroboros_task_body.json")
output_paths["workspace"] = str(workspace)
if cfg.dry_run:
return task_result_row(
benchmark="programbench",
instance_id=instance_id,
status="completed",
reason_code="dry_run",
official_eval_status="not_run",
output_paths=output_paths,
details=harness,
)
output_paths["task_checkpoint"] = str(checkpoint_path)
try:
task_result = submit_and_wait(
cfg.ouroboros_url,
body,
timeout_sec=cfg.timeout_sec,
checkpoint_path=checkpoint_path,
)
except Exception as exc:
# A client-side poll failure (timeout OR a transient mid-poll transport
# error) after a task was submitted leaves it RUNNING server-side with
# its task_id in the checkpoint. Tearing the container down here would
# make the next run reattach to a task whose Docker backend is gone —
# keep the executor alive for reattach (r1 #10, generalized from the
# timeout-only guard). If NO task_id was ever recorded (creation itself
# failed), there is nothing to reattach: re-raise to the normal
# teardown+failed-row path.
if not _checkpoint_task_id(checkpoint_path):
raise
keep_container_for_reattach = True
reason = "client_poll_timeout_reattachable" if isinstance(exc, TimeoutError) else "client_poll_error_reattachable"
return task_result_row(
benchmark="programbench",
instance_id=instance_id,
status="failed",
reason_code=reason,
official_eval_status="not_run",
output_paths=output_paths,
error=str(exc),
details={"harness": harness, "container_left_running": True,
"reattach_checkpoint": str(checkpoint_path)},
)
keep_container_for_reattach = False # settled: normal teardown applies
write_json(instance_dir / "ouroboros_task_result.json", task_result)
output_paths["task_result"] = str(instance_dir / "ouroboros_task_result.json")
# Classification comes from the payload's EXPLICIT terminal status; a
# stale provider reason_code on a completed task must never demote it.
status = terminal_task_status(task_result)
infra_failed = classify_infra_failure(task_result)
if status != "completed":
# A non-completed settled task produces NO submission tarball — else the
# resume gate would see submission.tar.gz and skip a FAILED instance as a
# skipped-success, never retrying it (r1 #12). Denominator stays honest
# via the failed row below.
return task_result_row(
benchmark="programbench",
instance_id=instance_id,
status="failed",
reason_code=str(task_result.get("reason_code") or "") or "task_not_completed",
runtime_result=task_result,
prediction_written=False,
official_eval_status="not_run",
output_paths=output_paths,
error=str(task_result.get("result") or task_result.get("error") or status),
details={"harness": harness, "task_status": status, "infra_failed": infra_failed},
)
# Only a COMPLETED task produces the source submission — its presence is
# what the resume gate keys on, so it must mean "solved", not "attempted".
submission = create_submission_tarball(
workspace,
instance_dir / "submission.tar.gz",
protected_paths=cfg.protected_paths,
)
output_paths["submission"] = str(submission)
return task_result_row(
benchmark="programbench",
instance_id=instance_id,
status="completed",
reason_code="submission_prepared",
# The non-completed branch above already publishes the runtime `reason_code`; the
# success branch dropped it, so a submission prepared by a cost-truncated run read
# exactly like one from a run that finished. `infra_failed` collapses the runtime's
# terminal reason to one bool and cannot carry `budget_exhausted` at all.
runtime_result=task_result,
prediction_written=True,
official_eval_status="not_run",
output_paths=output_paths,
details={"harness": harness, "task_status": status, "infra_failed": infra_failed},
)
except Exception as exc:
return task_result_row(
benchmark="programbench",
instance_id=instance_id,
status="failed",
reason_code=type(exc).__name__,
official_eval_status="not_run",
output_paths=output_paths,
error=str(exc),
details={"harness": harness, "traceback": traceback.format_exc()},
)
finally:
if not keep_container_for_reattach:
stop_cleanroom_container(container_name)
def main() -> int:
parser = argparse.ArgumentParser(description="Run ProgramBench instances through Ouroboros")
parser.add_argument("--repo-dir", default=str(pathlib.Path(__file__).resolve().parents[3]))
parser.add_argument("--ouroboros-url", default="http://127.0.0.1:8770", help="Ouroboros source server URL (desktop sandbox on 8765 cannot see bench_runs)")
parser.add_argument("--solve-model", default="", help="expected OUROBOROS_MODEL; validated/normalized against --settings-path before any task burns")
parser.add_argument("--difficulty", default="", help="filter by task.yaml difficulty (default: all)")
parser.add_argument("--filter", default="", dest="filter_spec", help="regex filter on instance_id")
parser.add_argument("--slice", default="", dest="slice_spec", help="slice spec, e.g. 0:3 (default: all)")
parser.add_argument(
"--shuffle",
action="store_true",
help="shuffle instances (programbench uses seed 42 after sorting by instance_id)",
)
parser.add_argument("--instance-id", default="", help="run a single instance id")
parser.add_argument("--run-id", default="")
parser.add_argument("--timeout-sec", type=float, default=PROGRAMBENCH_TIMEOUT_SEC)
parser.add_argument("--cpus", default="4")
parser.add_argument("--memory", default="16g")
parser.add_argument("--protected-path", action="append", default=[])
parser.add_argument("--settings-path", default="")
parser.add_argument("--redo-existing", action="store_true")
parser.add_argument("--skip-pull", action="store_true")
parser.add_argument("--dry-run", action="store_true", help="docker setup + task body only")
parser.add_argument("--eval", action="store_true", help="run official programbench eval/info after all instances")
parser.add_argument("--no-eval", action="store_true", help=argparse.SUPPRESS)
parser.add_argument(
"--allow-dirty-seed",
action="store_true",
help="record and proceed with an unclean/unidentifiable seed checkout instead of refusing",
)
args = parser.parse_args()
if args.no_eval:
args.eval = False
_ensure_docker_host()
repo_dir = pathlib.Path(args.repo_dir).expanduser().resolve(strict=False)
settings_path = pathlib.Path(args.settings_path).expanduser() if args.settings_path else default_settings_path()
out_root = assert_outside_repo(run_root("programbench", args.run_id), repo_dir)
protected_paths = args.protected_path or default_protected_backend_paths()
# The DECLARED selector, which is pure argv, is what admission can honestly record: the
# RESOLVED ids require loading the ProgramBench dataset, and that load (plus the model-slot
# preflight's settings read) can fail on the state of the world — a failure that used to
# take the process down before any manifest existed, leaving the run invisible rather than
# merely footprint-free. Both moved behind admission; `requested_task_ids` is amended with
# the resolved ids there, and the selector below records what was ASKED FOR either way.
selector = {
"difficulty": str(args.difficulty or ""),
"filter_spec": str(args.filter_spec or ""),
"slice_spec": str(args.slice_spec or ""),
"instance_id": str(args.instance_id or ""),
"shuffle": bool(args.shuffle),
}
declared_ids = [str(args.instance_id)] if args.instance_id else []
manifest_path = out_root / "run_manifest.json"
ledger_path = out_root / "result_index.jsonl"
# ADMISSION, before the first instance burns anything: the seed-provenance gate (built into
# the manifest) plus the runtime identity of the server we are about to submit to. Until
# v6.75.0 this manifest was written at the very END of the run — after every instance and
# the official eval — so the gate physically could not stop an unreproducible run.
# `admit_benchmark_run` persists the complete gate/refusal payload BEFORE enforcement raises.
manifest = admit_benchmark_run(
manifest_path,
benchmark="programbench",
run_root=out_root,
repo_dir=repo_dir,
requested_task_ids=declared_ids,
require_clean=not args.allow_dirty_seed,
argv=sys.argv,
output_paths={
"run_root": str(out_root),
"ledger": str(ledger_path),
"manifest": str(manifest_path),
},
dataset="programbench",
harness={
"ouroboros_url": str(args.ouroboros_url),
"difficulty": str(args.difficulty or ""),
"dry_run": bool(args.dry_run),
"solve_model": str(args.solve_model or ""),
"selector": selector,
"model_slots_normalized": {"pending": "preflight_runs_after_admission"},
},
official_command=programbench_command_for_manifest(out_root, eval_requested=bool(args.eval)),
isolated_data_root="",
settings_path=settings_path,
extra={
"eval_requested": bool(args.eval),
"official_eval_status": "not_run",
"protected_paths": protected_paths,
"shuffle": bool(args.shuffle),
"shuffle_seed": 42 if args.shuffle else None,
"instance_order": str(out_root / "instance_order.json"),
"runtime_attestation": {"pending": "not_attested_yet"},
},
)
with finalize_run_manifest(manifest_path, manifest, outcome="completed") as final:
# PREFLIGHT + DISCOVERY, now inside the admitted run: every refusal below is recorded by
# the finalization seam instead of vanishing with the process.
model_slots = preflight_model_slots(settings_path, solve_model=str(args.solve_model or ""))
manifest["harness"]["model_slots_normalized"] = model_slots
instances = _load_instances(**selector)
if not instances:
final.update({"outcome": "refused", "exit_code": 1,
"refusal": {"stage": "instance_selection", "exit_code": 1,
"reason": "no_instances_matched"}})
raise SystemExit("no ProgramBench instances matched the selection")
selected_ids = [str(item["instance_id"]) for item in instances]
# AMEND the retained manifest with the ids the selector actually resolved to. The
# declared selector stays in `harness`, so the record says both what was asked for and
# what it meant.
manifest["requested_task_ids"] = selected_ids
manifest["requested_count"] = len(selected_ids)
# Written AFTER persisted admission: a refused run must not leave run artefacts behind, and
# nothing before the admission call may touch the filesystem.
write_json(
out_root / "instance_order.json",
{
"count": len(instances),
"shuffle": bool(args.shuffle),
"shuffle_seed": 42 if args.shuffle else None,
"instance_ids": [str(item["instance_id"]) for item in instances],
},
)
# Attestation runs INSIDE the finalization block, never in the admission call's argument
# list: Python evaluates arguments BEFORE entering the callee, so an attestation refusal
# there meant `admit_benchmark_run` never ran and no manifest ever reached disk — the
# durable-refusal contract defeated by evaluation order. The refusal still propagates
# (unchanged behaviour); it is now recorded as a typed refusal first.
if args.dry_run:
manifest["extra"]["runtime_attestation"] = {"skipped": "dry_run"}
else:
try:
attested = runtime_attestation(str(args.ouroboros_url), repo_dir)
except Exception as exc:
# A refusal CARRIES the record it built (`RuntimeAttestationRefused.attestation`),
# so the durable manifest keeps the exact typed reason plus the runtime_version /
# repo_head / repo_version facts instead of a generic message. The fallback reason
# covers a failure that carries no record at all.
refused = getattr(exc, "attestation", None)
if isinstance(refused, dict):
manifest["extra"]["runtime_attestation"] = refused
final.update({
"outcome": "refused",
"exit_code": 3,
"refusal": {
"stage": "runtime_attestation",
"exit_code": 3,
"reason": str((refused or {}).get("reason") or "")
or "runtime_attestation_failed",
},
})
# RETURN the recorded code, never re-raise it. An escaping RuntimeError exits the
# process with status 1 (`raise SystemExit(main())` is never reached), so a bare
# re-raise made the durable record say 3 while the process said 1 — the record and
# reality disagreeing is the one thing this manifest exists to prevent. The message
# the exception used to surface is logged here instead.
print(f"[pb-e2e] FATAL: {exc}", file=sys.stderr)
return 3
manifest["extra"]["runtime_attestation"] = attested
skipped_rows: list[dict[str, Any]] = []
if not args.redo_existing:
# Resume narrows the WORK, never the ledger denominator: instances with a
# prior submission are recorded as explicit skipped rows, not dropped.
pending = []
for instance in instances:
submission = safe_join_under(out_root, str(instance["instance_id"])) / "submission.tar.gz"
if submission.is_file() and submission.stat().st_size > 0:
skipped_rows.append(task_result_row(
benchmark="programbench",
instance_id=str(instance["instance_id"]),
status="skipped",
reason_code="skipped_existing_submission",
prediction_written=True,
official_eval_status="not_run",
output_paths={"submission": str(submission)},
))
continue
pending.append(instance)
instances = pending
rows: list[dict[str, Any]] = list(skipped_rows)
cfg = InstanceRunConfig(
out_root=out_root,
ouroboros_url=str(args.ouroboros_url),
timeout_sec=float(args.timeout_sec),
cpus=str(args.cpus),
memory=str(args.memory),
protected_paths=protected_paths,
dry_run=bool(args.dry_run),
skip_pull=bool(args.skip_pull),
redo_existing=bool(args.redo_existing),
)
# APPEND-ONLY run ledger: one line per row, written the moment the row exists. The old
# whole-file rewrite at the end of the run discarded every completed instance if the
# process died mid-run, and a resumed run silently replaced the previous run's history.
# Readers dedup by instance_id, LAST row wins (documented in programbench/README.md).
# BOTH locations for EVERY row, skips included: a resumed instance whose skip event only
# reached the run root has an instance-level history that silently omits the resume, and the
# README states the two-location contract without qualification.
for row in skipped_rows:
append_result_index(safe_join_under(out_root, str(row["instance_id"])), row)
append_result_index(out_root, row)
for instance in instances:
row = _process_instance(instance, cfg)
rows.append(row)
append_result_index(safe_join_under(out_root, str(instance["instance_id"])), row)
append_result_index(out_root, row)
eval_result = None
official_eval_status = "not_run"
if args.eval and not args.dry_run:
try:
eval_result = run_official_eval(out_root)
# returncode != 0 = the eval RAN and reported test failures (a valid
# partial-pass benchmark result, not a run error).
official_eval_status = "completed" if eval_result.get("eval", {}).get("returncode") == 0 else "failed"
except Exception as exc:
# The eval could not RUN at all (harness/infra error) — distinct from a
# ran-but-some-tests-failed result; this is a run error (r2 fable #3).
official_eval_status = "error"
write_json(
out_root / "programbench_eval_result.json",
{"error": str(exc), "traceback": traceback.format_exc()},
)
manifest["extra"].update(
{
"official_eval_status": official_eval_status,
"completed_count": sum(1 for row in rows if row.get("status") == "completed"),
"failed_count": sum(1 for row in rows if not _row_successful(row)),
}
)
print(out_root)
rows_ok = all(_row_successful(row) for row in rows)
# A REQUESTED official eval that could NOT run (harness error) is a run failure
# even when every solve completed — otherwise `run_...e2e --eval && ...` reads a
# broken eval as success. A "failed" eval that merely reported test failures is
# a valid partial-pass result and does NOT fail the run (r2 fable #3).
eval_errored = bool(args.eval) and not args.dry_run and official_eval_status == "error"
code = 0 if (rows_ok and not eval_errored) else 1
if code:
# A typed terminal outcome, not a bare exit code: the two reasons a completed run
# still fails are distinguishable in its own record.
final["outcome"] = "official_eval_error" if eval_errored else "instances_failed"
final["exit_code"] = code
return code
if __name__ == "__main__":
raise SystemExit(main())