Skip to content

feat: scaffold SWE-bench-Pro baseline for the vero optimizer#50

Draft
shehabyasser-scale wants to merge 42 commits into
pr3-harness-benchfrom
feat/swe-bench-pro-baseline-scaffold
Draft

feat: scaffold SWE-bench-Pro baseline for the vero optimizer#50
shehabyasser-scale wants to merge 42 commits into
pr3-harness-benchfrom
feat/swe-bench-pro-baseline-scaffold

Conversation

@shehabyasser-scale

Copy link
Copy Markdown
Collaborator

WIP scaffold (draft). Adds a SWE-bench-Pro baseline under harness-engineering-bench/swe-bench-pro/, mirroring the gaia baseline layout, so the vero optimizer can hill-climb a patch-style SWE agent. Not runnable yet: two pieces are stubbed (see below).

What's here (working skeleton)

  • baseline/target/src/swebench_pro_agent/agent.py: code-editing agent (SweBenchProAgent, Responses API, MAX_TURNS 50), tools run_shell / read_file / write_file / apply_patch / run_tests / submit. Edits the repo in place; the task-source verifier runs the test suite for reward (no self-grade, no answer.txt). The responses.create call is wrapped in a retry-with-backoff helper from the start (the gaia baseline scored 0.0 because that call was unguarded and transient errors crashed it, the optimizer's winning fix was exactly this retry).
  • baseline/build.yaml: agent_import_path, harbor[modal]==0.20.0, inference-gateway (producer gpt-5.3-codex, eval gpt-4o, both prefixed and bare forms), timeouts bumped for repo builds + test suites, W&B vero-swe-bench-pro.
  • baseline/target/pyproject.toml: requires-python = ">=3.11,<3.14" (avoids the litellm/pyo3 3.14 build wall).
  • tests, secrets.env.example, .gitignore, and scripts/partition_swe_bench_pro.py (deterministic sha256 stratified split + --check).

Stubbed (must finish before it runs)

  1. Pin the real task_source (placeholder scale-ai/swe-bench-pro@sha256:REPLACE_WITH_REAL_DIGEST). Get the SWE-bench-Pro Harbor task package (repos + failing tests + gold patches + grading) from the Harbor task registry / DEX-harness team.
  2. Regenerate partitions (currently empty) via scripts/partition_swe_bench_pro.py, fill agent_access.total_cases, and run uv lock in baseline/target/.

How to run (once the stubs are filled)

cd vero
uv run vero harbor build --config ../harness-engineering-bench/swe-bench-pro/baseline/build.yaml \
  --output ../harness-engineering-bench/swe-bench-pro/baseline/compiled
cp ../harness-engineering-bench/swe-bench-pro/baseline/secrets.env.example \
   ../harness-engineering-bench/swe-bench-pro/baseline/secrets.env   # fill Azure OPENAI_*, MODAL_*, WANDB_*
uv run vero harbor run --config ../harness-engineering-bench/swe-bench-pro/baseline/build.yaml \
  --env-file ../harness-engineering-bench/swe-bench-pro/baseline/secrets.env \
  --environment modal --agent codex --model gpt-5.3-codex --yes -o ../runs/swe-bench-pro/jobs

--environment modal needs a Modal-reachable model endpoint (e.g. the Azure …openai.azure.com/openai/v1 endpoint), not a VPN-internal one.

🤖 Generated with Claude Code

@varunursekar
varunursekar force-pushed the pr3-harness-bench branch 6 times, most recently from 51c152d to ced19f9 Compare July 26, 2026 04:26
varunursekar and others added 24 commits July 25, 2026 22:00
Bring the benchmark harness with each benchmark as a top-level sibling of
gaia/ (ale-bench, officeqa, swe-atlas-qna, tau3), flattening the former
candidates/ grouping.
The floor gates a ship on a validation comparison while gaia's reward is on
the test partition (different distributions), so keep it opt-in / off. Now
that the harbor default is false this is explicit-for-clarity.
Now that the sidecar image pre-warms the harness uv cache, the inner eval
runs as the unprivileged harness user again, so the candidate harness cannot
read the held-out records, budgets, or admin token.
Move each benchmark to harbor 0.20.0 in lockstep so the eval resolves:
build.yaml harbor_requirement (the evaluator's --with harbor[modal]) and
target/pyproject.toml (the candidate's own harbor dep) must match, else
`uv run --project <target> --with harbor[modal]==0.20.0` is unsatisfiable.
uv.lock regenerated with `uv lock` (not hand-edited). Covers gaia,
officeqa, swe-atlas-qna, tau3.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Follow-up to the pr2 rename: instructions, READMEs, and the ale-bench
reference solution now teach `evals run/status/result/submit` and the
.evals/tasks + .evals/results paths; baseline gitignores cover .evals/.
Compiled trees are build outputs (not committed) and pick this up on rebuild.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
All four vero-compiled benchmarks pass --ek app_name=harness-engineering-bench
so their Modal sandboxes are grouped under one app instead of __harbor__.
Docker inner environments ignore the kwarg.
The task's verifier runs inside the Modal task container and templates
EVAL_BASE_URL from OPENAI_API_BASE, unreachable-from-Modal gateway URLs
aside. Enable task_services_use_upstream so the judge gets the real
upstream on OPENAI_*, and point the baseline agent at the metered
gateway via VERO_AGENT_INFERENCE_* (falling back to OPENAI_* locally).
The Modal app-name commit added a second extra_harbor_args key; YAML
last-key-wins silently dropped --no-force-build, which would have
force-built the guard Dockerfile instead of pulling the prebuilt
corpus image.
Killing the outer harbor process leaves the compose topology and any
in-flight Modal sandboxes running (each layer's cleanup lives in the
layer above). Cap sandbox idle lifetime at 1h via --ek, and add
scripts/cleanup_orphans.sh to tear down task__* containers and
terminate sandboxes in the dedicated Modal app.
Across gaia/officeqa/swe-atlas-qna/tau3: 100-run budgets and 4x-pass
case budgets on both agent partitions, submit-mode selection,
baseline_floor off, 16k feedback, parameterized inner environment
(${inner_env:-modal}), and verifier_timeout = 2x timeout_seconds.
swe-atlas-qna and tau3 get explicit case/task-agent timeouts derived
from their datasets' declared agent timeouts (enforced budgets 1800s
and 900s, provisional until empirical wall_seconds data). harness_user
is explicit on gaia/officeqa; tau3 and swe-atlas-qna stay unisolated
pending off-env delivery of task-service credentials. Also fix the
vendor script's stale pre-restructure path in its usage comment.
Root cause of every swe-atlas trial dying at setup: the task images set
ENTRYPOINT ["/bin/bash"], Modal prepends it to the sandbox command, so
harbor's default keepalive ["sh","-c","sleep infinity"] ran as
'bash sh -c ...' - bash interprets the sh binary as a script and exits
126, killing the sandbox before the first exec. Pass bash's own args
via the JSON-decoded keepalive env kwarg instead. Validated end-to-end
on Modal (3 dev trials, 379-602s wall). CONFIGURATION.md records the
empirical probe timings behind the provisional case timeouts.
scripts/per_trial_tokens.py reads a session's gateway request log and
attributes token/latency to Harbor trials: by the gateway's stamped
thread_id when present (request_log.attribution builds → full coverage
incl. stateful chains), else a legacy fallback that recovers each
conversation's root turn (labelable via --tasks-dir) and residualizes
unrecoverable follow-ups. Reports gateway-total vs attributed vs
agent-reported per evaluation. Stdlib-only; tests cover both paths.
…r at turn cap

Chat Completions is the only universally-supported inference surface —
the OpenAI Responses API doesn't translate cleanly to non-OpenAI models
via litellm (parallel_tool_calls rejected, output_text crashes on a
null message part). Rewrite AtlasAgent's loop to stateless chat messages
+ tool_calls, validated cross-provider (gpt-5.4-mini and
fireworks_ai/gpt-oss-120b both run the multi-turn tool loop). Also: at
the turn cap, force one final tool-free answer instead of raising, so a
budget-exhausted trial scores best-effort rather than losing the case.
Same port as swe-atlas: stateless chat messages + tool_calls, works
across providers (validated cross-provider on gpt-5.4-mini and
fireworks_ai/gpt-oss-120b). officeqa drops the hosted web_search tool
(closed-corpus benchmark, and chat/completions has no hosted-tool
equivalent) and moves read_image feedback to chat image_url content.
tau3 keeps its host-side MCP loop; the stateless history also fixes the
previous_response_id reset that dropped context after plain-text turns,
and the MCP session id is now charset-validated before shell
interpolation (greptile injection finding).
Mirrors the swe-atlas fix: at MAX_TURNS force one tool-free completion for
the best answer and submit it, so a budget-exhausted case scores
best-effort rather than being lost with no answer recorded.
A fifth harness-engineering benchmark: optimize a retrieval + reasoning
agent over BrowseComp-Plus's fixed corpus and canonical BM25 index,
graded by a semantic answer judge (gpt-4.1). Task packages are generated
from the encrypted upstream dataset by scripts/build_tasks.py and are
gitignored (they contain decrypted questions/answers); the upstream repo
is pinned as a submodule and the 2.2 GB index is pulled at image-build
time.

Wired consistently with the normalized suite: 33/66/66 stratified split,
4-pass budgets, submit-mode selection, harness_user null +
task_services_use_upstream for the in-container judge. Baseline agent
uses Chat Completions (works across providers) and forces a best-effort
answer at the turn cap. The task image installs a full JDK with JAVA_HOME
resolved from it, so pyserini's Lucene BM25 searcher works (a headless
JRE with JAVA_HOME unset left jnius unable to locate the JVM).
varunursekar and others added 9 commits July 25, 2026 22:00
… succeeds

pyserini 1.2.0 constructs an openai.OpenAI() client at import time for its
unused OpenAI encoders; BM25/Lucene search never calls OpenAI, so set a
placeholder key in search.py before the import.
The task.toml had no verifier.env, so the gpt-4.1 answer judge ran without
OPENAI_API_KEY and scored every case 0 (Missing credentials). Template the
upstream creds and judge model into the verifier phase.
…las)

From a 10-trial per-benchmark probe of gpt-5.4-mini vs gpt-oss-120b vs
deepseek-v4-flash: deepseek matches/beats both on tau3 (0.875) and is ~2-3x
gpt-oss on the grounded-reasoning benchmarks (officeqa/browsecomp 0.60) at
roughly gpt-oss cost and far below mini. It is weaker only on swe-atlas
(0.30 vs gpt-oss 0.59 mean rubric), which is pinned to gpt-oss-120b — cheaper
and stronger there. Updates each benchmark's target `model` and the
evaluation-scope `allowed_models` (kept with the fireworks_ai/ prefix, since
agents strip only the openai/ prefix and the gateway matches the sent model
exactly). Producer scopes and tau3's user/assertion models are unchanged.

Also hardens the swe-atlas atlas agent: a shell command emitting non-UTF-8
(binary) output raised UnicodeDecodeError and killed the whole trial; it now
returns a tool error so the model can retry.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
… relock

- .gitignore: remove the paper-planning benchmark-scoping.md ignore
- README.md: correct candidate paths (candidates/ -> top level), add
  browsecomp-plus, drop the stale nested table
- vero/uv.lock: relock

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Fireworks' shared-org rate limit surfaces as a RateLimitError inside the
agent's OpenAI client; harbor only retries infra/env failures, not LLM 429s,
so a burst lost trials. The SDK's default 2 retries were exhausted under
sustained load. Bump to 8 so transient 429s back off in-place instead of
failing the trial (durable fix is gateway-level retry, tracked separately).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…line

Full held-out (test) pass of the seed harness, 3 independent rounds, under the
new targets (deepseek-v4-flash; gpt-oss-120b on swe-atlas):

  swe-atlas  0.097 ±0.011  (agg_score 0.632)   [gpt-oss-120b]
  tau3       0.611 ±0.021                       [deepseek-v4-flash]
  officeqa   0.360 ±0.042                       [deepseek-v4-flash]
  browsecomp 0.449 ±0.007                       [deepseek-v4-flash]

Each pinned into its target's baseline_reward with score_baseline: false, so
finalization uses the number instead of re-scoring the seed every run. The
three deepseek benchmarks logged zero exceptions over 945 trials (max_retries
absorbed the shared-quota 429s); swe-atlas lost 5/150 to gpt-oss 128k overflow.
CONFIGURATION.md documents the numbers, method, and the swe-atlas binary-vs-
agg_score note. gaia deferred.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
It was the suite's only Family B task and the only one that bypassed the
compiler: a hand-written task directory with no build.yaml, so it got none of
the build-time validation the other five benchmarks get. Its optimizer was also
unmetered -- no gateway container, raw upstream credentials in main, no model
allow-list -- which is a fair-play hole in a benchmark whose whole point is
controlled measurement. Its sidecar factory had additionally gone stale against
SidecarEvaluationPolicy and would have died at startup.

The nested-backend refactor now supports `evaluation_backend: command`, so a
replacement Family B task can be built through the compiler and get the gateway
for free. examples/harbor-circle-packing carries a worked version of exactly
that shape, verified end to end.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
These tests moved here from pr2-add-vero, where they could only ever skip. They
had been failing for a while against a pinned literal -- evaluation
allowed_models == ["gpt-5.4-mini-2026-03-17"] -- that stopped matching when the
benchmarks switched to fireworks-served targets.

Rewrite the assertions so they cannot drift that way again: the evaluation scope
must allow exactly [config.model], derived rather than pinned. That is the real
invariant, and it now matches the build-time validator that rejects a model
outside its scope's allow-list. Also drop the skip guard, so a missing
harness-engineering-bench fails rather than passing vacuously, and extend
coverage from three benchmarks to all five.

415 passed, 5 skipped -- the three long-standing failures are gone.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
shehabyasser-scale and others added 6 commits July 26, 2026 08:58
WIP scaffold: a working agent + build/config skeleton for a SWE-bench-Pro
optimization target, mirroring the gaia baseline layout. Two things are
stubbed and must be completed before it runs: (1) the task_source digest in
baseline/build.yaml is a clearly-marked placeholder that must be pinned to the
real SWE-bench-Pro Harbor task package, and (2) the partitions/ files are empty
placeholders that must be regenerated from the real task list via
scripts/partition_swe_bench_pro.py. The agent wraps responses.create in a
retry-with-backoff helper from the start (the unguarded call is what scored the
gaia baseline 0.0). See harness-engineering-bench/swe-bench-pro/README.md.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The agents strip only the `openai/` prefix before calling the gateway
(`removeprefix("openai/")`), and the gateway matches the allow-list exactly
(`model not in scope.allowed_models` -> 403 model_denied). So `model:
openai/gpt-4o` puts `gpt-4o` on the wire, and an allow-list carrying the
prefixed spelling never matches. Listing both spellings papered over that
and then fails the config invariant that the evaluation scope allow exactly
the target model.

Drop the prefix on both sides, and template the producer list the way every
other benchmark does so `--model` / `--param optimizer_model` keeps working
without a config edit.

This config still cannot be swept by tests/test_v05_benchmark_configs.py:
its partitions are deliberately empty pending real task ids, so it fails to
load. It should join BENCHMARKS there once the partitions and the task
source digest are pinned.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
A model that is configured but not deployed upstream is invisible until the
run is over. The gateway forwards the request, the upstream 404s, the agent
makes no progress, and every case is scored 0.0 and labelled `task_failure`
— a run that looks honest and says the candidate failed.

That is how swe-atlas-qna produced 469 zero-score cases across two runs
(#51): `gpt-5.4-mini-2026-03-17` is not provisioned on the configured Azure
resource, so 322/322 evaluation-scope and 79/79 finalization-scope requests
returned 404 DeploymentNotFound while the producer scope ran clean.

`vero harbor run` now sends one 16-token request per distinct allow-listed
model before compiling the task, and aborts if any comes back 404.

Only a definitive 404 is fatal. A timeout, a 429 or a 503 is inconclusive
and is reported without blocking, so a transient upstream blip cannot stop a
run that would have succeeded. Names are probed with the provider prefix
stripped, which is the form the agents actually send.

Verified against the live endpoint: gaia (which points at the undeployed
model) now exits 1 in about two seconds with the DeploymentNotFound body
quoted, instead of burning ~30 minutes and ~$2; a gpt-5.3-codex producer
with a gpt-4o evaluation scope passes.

Refs #51.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
#53 stopped a dead sandbox being scored as an informative zero. The same
masking survives one layer up, for a cause that is even easier to fix.

In the 2026-07-25 swe-atlas-qna run, 145 of 469 cases ended on:

    NotFoundError: Error code: 404 - {'error': {'type':
    'invalid_request_error', 'code': 'DeploymentNotFound', 'message': 'The
    API deployment for this resource does not exist. ...'}}

The configured eval model is not provisioned on the Azure resource. The
agent's every call 404s, it writes no answer, and `classify_case` — finding
nothing in the signal it recognises — returns TASK_FAILURE, whose policy is
`is_informative_sample=True`. So each case was recorded as a genuine score
of 0.0 and the harness reported that the candidate failed the task.

Add UPSTREAM_MODEL_UNAVAILABLE: never an informative sample, never retried,
terminating (every remaining case fails identically, so there is nothing
left to measure), and counted toward invalidity so the aggregate still comes
out invalid if the terminating path is ever bypassed. It is ranked above
TRANSIENT_INFRA, so a case that saw both a dead sandbox and a missing
deployment reports the deterministic, operator-fixable cause.

The pattern deliberately matches the provider error codes and the exact
Azure sentence rather than a bare "does not exist": 102 cases in that same
run failed with `FetchSpec failed: loading container: file does not exist`,
which is genuine infrastructure and must stay TRANSIENT_INFRA. There is a
test pinning both sides.

Refs #51.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The harbor backend's error taxonomy records any unrecognized case failure as
TASK_FAILURE: an informative, scoreable sample at the failure value with status
SUCCESS. That is right for a candidate that crashes on its own, but Modal
sandbox lifecycle failures and a missing held-out tests fixture are
infrastructure the candidate did not cause.

Seen on a swe-atlas-qna run (2026-07-24): all 396 cases returned status=success,
error_category=task_failure, score=0.0. Terminal exceptions were AddTestsDirError
x144, Modal sandbox NotFound x125, RuntimeError x67, BadRequestError x60.
Classified as task failures, the aggregate reported error_rate 0.0 and objective
feasible at 0.0, so a fully broken environment looked like a real, shippable
zero and the optimizer ran with no gradient (baseline and every candidate 0.0).

Add the harness/Modal environment-loss signals (sandbox lifecycle,
AddTestsDirError / "failed to add tests directory", container fetch failures,
StreamTerminatedError) to the TRANSIENT_INFRA patterns. Those cases are now
excluded from the aggregate and counted toward invalidity, so a collapsed
environment surfaces as an invalid run instead of a silent zero. A candidate's
own bug (ValueError, KeyError, no answer) is still a task failure, unchanged.
Covered by two new tests; existing behavior asserted unchanged.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The probe only spoke the Responses API and treated any 404 as proof the
deployment was absent. A route-level 404 (the upstream does not implement
that path) and a model-level 404 (the deployment does not exist) share a
status code and mean opposite things, so the preflight would hard-block a
run against a Chat-Completions-only upstream that would have succeeded.
That is now a live risk in this repo: officeqa, tau3, swe-atlas-qna and
browsecomp-plus were ported to Chat Completions, leaving gaia as the only
agent still on Responses.

Try both routes, and only call a model missing when the 404 body names the
model (`DeploymentNotFound`, `model_not_found`, or "model ... does not
exist") rather than the route. If every route 404s on the route itself, the
result is inconclusive and the run proceeds.

Same fix for the model name: the provider prefix routes on a proxy and is
meaningless to a single-provider endpoint, so probe the configured spelling
first and fall back to the bare one before reporting anything missing.
Previously the prefix was stripped unconditionally, which is wrong for
`fireworks_ai/...` names.

Verified against the live Azure endpoint: `gpt-4o` 200 on both routes;
`openai/gpt-5.3-codex` 404s prefixed and passes preflight via the bare
fallback; `fireworks_ai/gpt-oss-120b` is still correctly blocked; and a
model 404 body is distinguished from a bare route 404.

Refs #51.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@shehabyasser-scale

Copy link
Copy Markdown
Collaborator Author

Rebased onto the current pr3-harness-bench. This is now a one-commit PR: 12 of the 13 commits were already patch-equivalent upstream (0072a5c -> 87d7fd9, badc42c -> 5766013, and so on), so git rebase dropped them as already-applied and replayed only the scaffold itself.

Added one commit on top, fixing a real defect the new invariants exposed. The config had:

model: openai/gpt-4o
producer:   allowed_models: ["gpt-5.3-codex", "openai/gpt-5.3-codex"]
evaluation: allowed_models: ["gpt-4o", "openai/gpt-4o"]

The dual-spelling lists were a workaround for a mismatch that should not exist. Agents strip only openai/ (removeprefix("openai/")) and the gateway matches exactly (model not in scope.allowed_models -> 403 model_denied), so model: openai/gpt-4o puts gpt-4o on the wire and the prefixed entry can never match. It also fails 20dd166's invariant that the evaluation scope allow exactly the target model, and hard-coding the producer list fails the params-override test. Now unprefixed on both sides with the producer templated as ["${optimizer_model:-gpt-5.3-codex}"], matching every other benchmark.

Two things a reviewer should know:

  1. This config still cannot be swept by tests/test_v05_benchmark_configs.py. Its partitions are deliberately empty pending real task ids, so load_harbor_build_config rejects it before any invariant is checked. It should join BENCHMARKS there once the partitions and the task_source digest are pinned; I did not add it now because it would fail on load.

  2. That test currently fails 6 of 16 on a clean checkout, independent of this PR. officeqa and browsecomp-plus both set task_source: ../tasks and tasks/ is gitignored in both, so validate_references raises registry task_source must include an explicit version. Verified on pr3-harness-bench at 20dd166: 10 passed, 6 failed. Dropping the skip guard in d131412 was the right call, but those two are now green only on a machine that has already run officeqa/scripts/vendor_tasks.sh and browsecomp-plus/scripts/build_tasks.py.

Full suite matches pr3-harness-bench exactly: 11 pre-existing failures, none new.

fix: a missing upstream deployment is not a task failure
@varunursekar
varunursekar force-pushed the pr3-harness-bench branch 2 times, most recently from 5397fe4 to ec0b444 Compare July 26, 2026 16:52
feat: preflight configured models before launching an optimizer trial
fix: classify Modal sandbox / tests-dir loss as transient_infra
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants