Skip to content

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

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

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

Conversation

@shehabyasser-scale

@shehabyasser-scale shehabyasser-scale commented Jul 24, 2026

Copy link
Copy Markdown
Collaborator

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.

This is no longer a stub. The two blockers called out in the original draft (an unpinned task_source and empty partitions) are resolved, and the seed agent has been fixed to the point where it actually completes a trial.

Verification

A single-case smoke run against the real dataset, instance_ansible__ansible-0ea40e with a gpt-4o agent on Modal:

reward 1.0
required tests 16 / 16 PASSED
errored trials 0
retries 0

pytest tests/test_v05_benchmark_configs.py -k swe-bench-pro passes 3/3. The suite has 6 pre-existing failures on officeqa and browsecomp-plus (registry task_source must include an explicit version) that are present on this branch's base with or without this PR; they are fixed by newer commits on pr3-harness-bench that this branch has not absorbed yet.

Pinning the task source

SWE-bench-Pro is not a package dataset. It ships as swebenchpro in Harbor's default registry (731 instances over 11 projects), so the pin is a bare name@version, not an <org>/<name>@sha256:<digest>:

task_source: swebenchpro@1.0

Its tasks resolve to git-backed ids under laude-institute/harbor-datasets at commit c8e8f3fac7097accaacf261d74c3d6f441de45b1. Grading is fully offline (the task's own run_script.sh + parser.py write reward.txt), so no judge model and no verifier credentials are needed.

Consequences that shaped the config:

  • expose_case_resources: false on the development partition. VeRO materializes case resources through PackageDatasetClient, which only accepts <org>/<name> refs, so a registry dataset cannot be exposed that way.
  • scripts/partition_swe_bench_pro.py now reads the stratification key (repo) from each task's tests/config.json rather than [metadata].repository, because these task.toml files carry no [task].name and Harbor derives the canonical name from the task directory.
  • task_agent_timeout_seconds 3600 to 3000, mirroring the task package's own [agent] timeout_sec, which makes the runner's --agent-timeout-multiplier exactly 1800/3000 = 0.6.

Partitions

Regenerated and stratified by repo: 146 / 292 / 293 (development / validation / test), replacing three empty files and a TOTAL_TASKS = 0 stub guard. 731 splits 20/40/40 as 146.2/292.4/292.4; the leftover goes by largest remainder and the .4/.4 tie breaks toward test, matching how officeqa (246 to 49/98/99) and swe-atlas-qna (124 to 25/49/50) resolved the same tie.

Five seed-agent defects

Each of these silently biases the measurement rather than failing loudly, so the optimizer would have hill-climbed against a broken baseline.

# Defect Effect
1 REPO_DIR was /app/repo The images set WORKDIR /app and reset the checkout in place there. Every shell command ran in a directory that does not exist.
2 reasoning.effort sent unconditionally Hard 400 on gpt-4o at turn 1, so not one trial could complete. Now gated on capability, matching the sibling agents.
3 Absolute path in _write_file Path(logs_dir) / "staged" / "/app/x.py" discards the left side, so the write landed on the host root and killed the trial with OSError [Errno 30] Read-only file system: '/app'. Seen on 2 of the first 12 held-out trials. Paths are normalized repo-relative; traversal is refused.
4 raise RuntimeError on turn-budget exhaustion Reward comes from the hidden suite run against whatever the agent left in the repo, so raising discarded real edits, errored the trial, forced a full harbor re-run (9 of the first 49 held-out trials) and scored a hard 0 for a fix that may have been correct. Now recorded in metadata and left for the verifier to grade, exactly as the "model stopped calling tools" branch already did.
5 Unhandled UnicodeDecodeError Harbor decodes exec output as strict UTF-8. A model that cats or greps a binary killed the trial: 5 of the first 49 held-out trials. Now a tool error the model can react to.

Note on the base

This branch is behind pr3-harness-bench and GitHub currently reports it as conflicting. That predates this PR and is not addressed here; the branch needs a merge from its base before it can land.

How to run

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

Greptile Summary

This PR scaffolds a fully functional SWE-bench-Pro baseline under harness-engineering-bench/swe-bench-pro/, fixes five agent defects that silently biased measurements in the held-out run, and introduces a shared error taxonomy module (error_taxonomy.py) that gives all pipeline layers a single vocabulary for classifying failures.

  • Agent fixes: resolves five production defects — wrong REPO_DIR, unconditional reasoning.effort crashing gpt-4o, absolute-path staging landing on the host root, raise RuntimeError on turn-budget exhaustion discarding edits, and unhandled UnicodeDecodeError on binary output.
  • Partition & config: 731 tasks stratified by repo into 146/292/293 with a SHA-256-keyed deterministic split; task_source: swebenchpro@1.0 correctly pins the registry dataset; expose_case_resources: false justified by PackageDatasetClient limitation.
  • Infrastructure: error_taxonomy.py consolidates error classification with UPSTREAM_MODEL_UNAVAILABLE specifically added to prevent misclassifying Azure 404s as informative task failures; harbor/cli.py adds pre-flight model probing and a _redact_trace_text path for session export.

Confidence Score: 4/5

Safe to merge with two minor quality issues that do not affect correctness or reward integrity.

The agent's path-traversal guard and error taxonomy precedence table are both sound. The two findings are: full response.output_text written verbatim to the trace on every turn, and when both git apply passes fail the model only sees the C1 retry error. Neither breaks the reward pipeline or introduces data loss.

Files Needing Attention: swebench_pro_agent/agent.py for trace logging volume and apply-patch error-message retention; test_agent.py would benefit from parametrised tests for _repo_relative boundary conditions.

Important Files Changed

Filename Overview
harness-engineering-bench/swe-bench-pro/baseline/target/src/swebench_pro_agent/agent.py Core SWE-bench-Pro agent: fixes all 5 documented defects; two minor issues — original git-apply error lost on C1 retry, and full response.output_text logged per turn.
harness-engineering-bench/swe-bench-pro/baseline/build.yaml Build config correctly pins task_source as swebenchpro@1.0, sets expose_case_resources: false on both partitions with clear justification, and matches the task package's own agent timeout (3000s).
harness-engineering-bench/swe-bench-pro/scripts/partition_swe_bench_pro.py Deterministic SHA-256-keyed stratified split; reads stratification key from tests/config.json correctly; TARGET_COUNTS sum to TOTAL_TASKS; --check mode enables CI verification.
vero/src/vero/evaluation/scoring/error_taxonomy.py New single-source-of-truth error taxonomy with correct precedence ordering and a specific UPSTREAM_MODEL_UNAVAILABLE pattern that avoids false-positives on container load failures.
vero/src/vero/harbor/cli.py New Harbor CLI module; preflight probing distinguishes route-level 404s from model-level 404s; _redact_trace_text covers Bearer tokens, env var values, and sk- keys.
harness-engineering-bench/swe-bench-pro/baseline/target/tests/test_agent.py Tests cover happy-path submit, transient retry, and missing-model guard; missing coverage for _repo_relative path normalisation and traversal rejection.
vero/tests/test_v05_benchmark_configs.py Adds swe-bench-pro to parametrised benchmark config tests; verifies gateway scoping, credential isolation, and model allow-list invariants.
vero/tests/test_v05_error_taxonomy.py Comprehensive tests for the new error taxonomy: signal classification, case precedence, harness environment loss, and the swe-atlas-qna deployment-not-found regression case.
vero/src/vero/sandbox.py Cosmetic-only change: removes two section-header comment blocks. No functional changes.

Sequence Diagram

sequenceDiagram
    participant VeRO as VeRO Optimizer
    participant CLI as harbor CLI
    participant GW as Inference Gateway
    participant Agent as SweBenchProAgent
    participant Env as Harbor Environment (Modal)
    participant Verifier as Task Verifier

    VeRO->>CLI: harbor run --config build.yaml
    CLI->>CLI: _preflight_models()
    CLI->>GW: POST /responses (1 token probe)
    GW-->>CLI: 200 OK
    CLI->>Env: uvx harbor run compiled task

    loop Each turn (max 50)
        Agent->>GW: responses.create(model, tools, input)
        GW->>GW: enforce producer scope budget
        GW-->>Agent: response (function_calls)
        alt tool call
            Agent->>Env: "environment.exec(command, cwd=/app)"
            Env-->>Agent: ExecResult
            Agent->>Agent: _trace(turn, tool, result)
        else submit
            Agent->>Agent: record metadata, break
        end
    end

    Agent->>Verifier: repo state in /app
    Verifier->>Verifier: run hidden test suite
    Verifier-->>VeRO: reward.txt (0.0-1.0)
Loading

Fix All in Cursor Fix All in Claude Code Fix All in Codex

Prompt To Fix All With AI
Fix the following 3 code review issues. Work through them one at a time, proposing concise fixes.

---

### Issue 1 of 3
harness-engineering-bench/swe-bench-pro/baseline/target/src/swebench_pro_agent/agent.py:480-490
**Full response body logged per turn**

`response.output_text` is the model's complete text output and is serialised verbatim into the JSONL trace on every turn. Over a 50-turn session with a reasoning model this can easily run to hundreds of kilobytes per trial, which conflicts with the codebase's rule against logging full response bodies in production code. Consider logging a truncated summary (e.g. character count or first 200 chars) rather than the full text.

### Issue 2 of 3
harness-engineering-bench/swe-bench-pro/baseline/target/src/swebench_pro_agent/agent.py:415-427
**Original diagnostic discarded on C1 retry failure**

When both `git apply` attempts fail the function returns the `stderr` from the C1 retry only. The original attempt usually gives a more informative hunk-mismatch message (exact offsets, context lines), while the `-C1` output is often just "patch failed". A model trying to fix the patch only sees the less precise C1 error, making self-correction harder. Preserving the first attempt's `stderr` (e.g. under a separate key like `"original_stderr"`) would give the model the full picture.

### Issue 3 of 3
harness-engineering-bench/swe-bench-pro/baseline/target/tests/test_agent.py:1-10
**No tests for `_repo_relative` path-traversal guard**

`_repo_relative` is the sole barrier that prevents the model from writing files outside `/app` (defect #3 in the PR description). It handles absolute paths, `..` components, and traversal via `/app/../…`. The existing test suite exercises the happy path (submit, retry) but does not test any of these normalisation or rejection cases. A targeted parametrised test would be cheap to add and protects against regressions if the VeRO optimizer rewrites the tool dispatch loop.

Reviews (1): Last reviewed commit: "test: cover swe-bench-pro in the benchma..." | Re-trigger Greptile

Greptile also left 2 inline comments on this PR.

Context used:

  • Rule used - What: Never log full response bodies, request bodi... (source)

@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 4 commits July 25, 2026 22:00
…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
@varunursekar
varunursekar force-pushed the pr3-harness-bench branch 2 times, most recently from 9a963a3 to 5723247 Compare July 27, 2026 05:50
shehabyasser-scale and others added 3 commits July 27, 2026 10:31
The scaffold could not resolve a task: `task_source` was the literal
`scale-ai/swe-bench-pro@sha256:REPLACE_WITH_REAL_DIGEST`, the three
partition files were empty, and the split script refused to run behind a
`TOTAL_TASKS = 0` stub guard.

SWE-bench-Pro is not a package dataset. It ships as `swebenchpro` in
Harbor's default registry (731 instances over 11 projects), whose tasks
resolve to git-backed ids under laude-institute/harbor-datasets at commit
c8e8f3fac7097accaacf261d74c3d6f441de45b1, so the pin is a bare
`name@version` rather than a content digest.

That distinction drives the rest of the change:

- `_read_tasks` reads the stratification key from `tests/config.json`
  (`repo`), not `[metadata].repository`: these task.toml files carry no
  `[task].name`, and Harbor derives the canonical name from the task
  directory, which is what `-i` matches.
- `_fetch_registry_refs` goes through `RegistryClientFactory` instead of
  `PackageDatasetClient`, recording `<git_commit>:<path>` as the per-task
  ref, which identifies content the way a digest would.
- `expose_case_resources` is false on the development partition. VeRO
  materializes case resources through `PackageDatasetClient`, which only
  accepts `<org>/<name>` refs, so a registry dataset cannot be exposed
  that way.
- 731 splits 20/40/40 as 146.2/292.4/292.4; the leftover goes by largest
  remainder and the .4/.4 tie breaks toward test, matching how officeqa
  (246 -> 49/98/99) and swe-atlas-qna (124 -> 25/49/50) resolved it.
  Hence 146/292/293.
- `task_agent_timeout_seconds` drops 3600 -> 3000 to mirror the task
  package's own `[agent] timeout_sec`, making the runner's
  --agent-timeout-multiplier exactly 1800/3000 = 0.6.
- `requires-python` floors at 3.12 because harbor 0.20.0 requires it;
  ">=3.11" left the package's own `uv run pytest` unresolvable. The
  <3.14 cap stays (litellm/pyo3 fails to build on 3.14).

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

Each of these silently biases the measurement rather than failing loudly,
so the optimizer would have hill-climbed against a broken baseline.

1. REPO_DIR was `/app/repo`. The swebenchpro task images set `WORKDIR
   /app` and reset the checkout in place there, and the task instruction
   says so verbatim ("I've uploaded a code repository in the directory
   /app"). Every shell command ran in a directory that does not exist.

2. `reasoning: {effort: high}` was sent unconditionally. Non-reasoning
   models reject it with a hard 400 on the very first turn, so a gpt-4o
   run could not complete a single trial. Gated on capability, matching
   the sibling agents.

3. `_write_file` joined a model-supplied path onto `logs_dir`. Models
   routinely answer with the absolute path they just saw in shell output,
   and `Path(logs_dir) / "staged" / "/app/x.py"` discards the left side
   entirely, so the write landed on the HOST root and killed the trial
   with `OSError [Errno 30] Read-only file system: '/app'`. Seen on 2 of
   the first 12 held-out trials. Paths are now normalized repo-relative
   and traversal is refused.

4. Exhausting the turn budget raised RuntimeError. Reward comes from the
   task's hidden suite run against whatever the agent left in the
   repository, so raising discarded real edits, errored the trial, made
   harbor re-run the whole thing (9 of the first 49 held-out trials) and
   scored a hard 0 for a fix that may well have been correct. It now
   records the exhaustion in metadata and lets the verifier grade, which
   is exactly what the "model stopped calling tools" branch already did.

5. Harbor decodes exec output as strict UTF-8. These are real
   repositories, so a model that cats or greps a binary raised
   UnicodeDecodeError out of `exec` and killed the trial: 5 of the first
   49 held-out trials. A non-decodable stream is now a tool error the
   model can react to, with a hint to re-run through `strings`/`file`.

Verified end to end: a single-case smoke on
instance_ansible__ansible-0ea40e with a gpt-4o agent completed with
reward 1.0, all 16 required tests PASSED, 0 errored trials.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The config-invariant suite enumerates benchmarks explicitly, so
swe-bench-pro was silently unchecked. Now that its task_source resolves,
it can join the other five. All three parametrized cases pass.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@shehabyasser-scale
shehabyasser-scale marked this pull request as ready for review July 27, 2026 07:41
@shehabyasser-scale shehabyasser-scale changed the title feat: scaffold SWE-bench-Pro baseline for the vero optimizer feat: make the SWE-bench-Pro baseline runnable for the vero optimizer Jul 27, 2026
Comment on lines +415 to +427
)
if result.return_code != 0:
# Retry with a looser fuzz factor before giving up.
result = await environment.exec(
f"git apply --whitespace=nowarn -C1 {remote_path}",
cwd=REPO_DIR,
timeout_sec=120,
)
return {
"applied": result.return_code == 0,
"return_code": result.return_code,
"stderr": self._truncate(result.stderr or ""),
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Original diagnostic discarded on C1 retry failure

When both git apply attempts fail the function returns the stderr from the C1 retry only. The original attempt usually gives a more informative hunk-mismatch message (exact offsets, context lines), while the -C1 output is often just "patch failed". A model trying to fix the patch only sees the less precise C1 error, making self-correction harder. Preserving the first attempt's stderr (e.g. under a separate key like "original_stderr") would give the model the full picture.

Prompt To Fix With AI
This is a comment left during a code review.
Path: harness-engineering-bench/swe-bench-pro/baseline/target/src/swebench_pro_agent/agent.py
Line: 415-427

Comment:
**Original diagnostic discarded on C1 retry failure**

When both `git apply` attempts fail the function returns the `stderr` from the C1 retry only. The original attempt usually gives a more informative hunk-mismatch message (exact offsets, context lines), while the `-C1` output is often just "patch failed". A model trying to fix the patch only sees the less precise C1 error, making self-correction harder. Preserving the first attempt's `stderr` (e.g. under a separate key like `"original_stderr"`) would give the model the full picture.

How can I resolve this? If you propose a fix, please make it concise.

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

Fix in Cursor Fix in Claude Code Fix in Codex

Comment on lines +1 to +10
from types import SimpleNamespace

import pytest

from swebench_pro_agent import SweBenchProAgent


class FakeEnvironment:
def __init__(self):
self.commands: list[str] = []

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 No tests for _repo_relative path-traversal guard

_repo_relative is the sole barrier that prevents the model from writing files outside /app (defect #3 in the PR description). It handles absolute paths, .. components, and traversal via /app/../…. The existing test suite exercises the happy path (submit, retry) but does not test any of these normalisation or rejection cases. A targeted parametrised test would be cheap to add and protects against regressions if the VeRO optimizer rewrites the tool dispatch loop.

Prompt To Fix With AI
This is a comment left during a code review.
Path: harness-engineering-bench/swe-bench-pro/baseline/target/tests/test_agent.py
Line: 1-10

Comment:
**No tests for `_repo_relative` path-traversal guard**

`_repo_relative` is the sole barrier that prevents the model from writing files outside `/app` (defect #3 in the PR description). It handles absolute paths, `..` components, and traversal via `/app/../…`. The existing test suite exercises the happy path (submit, retry) but does not test any of these normalisation or rejection cases. A targeted parametrised test would be cheap to add and protects against regressions if the VeRO optimizer rewrites the tool dispatch loop.

How can I resolve this? If you propose a fix, please make it concise.

Fix in Cursor Fix in Claude Code Fix in Codex

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