Skip to content

Reference-harness grid: scoring convention, installed-harness support, producer fixes - #85

Open
varunursekar wants to merge 5 commits into
mainfrom
reference-harness-grid
Open

Reference-harness grid: scoring convention, installed-harness support, producer fixes#85
varunursekar wants to merge 5 commits into
mainfrom
reference-harness-grid

Conversation

@varunursekar

@varunursekar varunursekar commented Aug 3, 2026

Copy link
Copy Markdown
Collaborator

Four changes the reference-harness grid needed. Everything here is code and config;
the run artefacts live in the gitignored runs/ and are not included.

1. Score a trial the harness killed as zero, not as absent

trial_rewards dropped every rewardless trial, so a harness that crashed was scored
only on the trials that survived. Now it follows runs/recompute.py — the convention
that produced the pinned baselines: a harness kill scores 0, a platform kill is
dropped, and an unrecognised exception is a hard error instead of a quiet omission.

This changes published numbers. It recovers 73 zeroes across the grid; the largest
single move is swe-atlas × mini-swe-agent, 0.0656 → 0.0533, where 28 of 150 trials were
being ignored.

2. Let rescore run an installed harness in place of the seed

--agent swaps the seed program for a harness a practitioner could install and changes
nothing else — same dataset ref, partition, rounds, aggregation and pinned target model
— so the harness is the only variable and the number stays comparable to
baseline_reward. Four supporting flags, each added because a specific harness needed
it: --setup-timeout-multiplier (opencode's nvm → node 22 → npm -g install overruns
harbor's default), --agent-env (goose needs the proxy pointed at explicitly),
--with-requirement (harbor-native harnesses import their framework in the
orchestrator, which harbor does not declare), and --harbor-requirement.

3. Pin gpt-5.x producers to one upstream deployment

An unqualified model group load-balances across deployments, and Responses-API
encrypted reasoning is decryptable only by the deployment that produced it, so every
turn after the first fails invalid_encrypted_content. Measured on swe-atlas: 8 of 8
replays failed against the bare group, 0 of 5 on azure_ai. It killed both gpt-5.6-sol
cells at 2m58s under opencode as well as codex, so it is not the codex-only fault
terminal-bench's instance suggested.

Aliases apply after the allow-list check, so allowed_models still governs what an
optimizer may ask for and a cell's label still means what it says.

4. Give the producer the credential surfaces goose and openhands-sdk read

openhands-sdk reads its own LLM_* pair and goose reads OPENAI_HOST +
OPENAI_BASE_PATH, which it joins — so the host must not already carry /v1 or the
request lands on /v1/v1/chat/completions and the proxy 403s. The producer token is
minted per run, so neither can come from build.yaml agent_env.

Verification

  • tests/test_v05_cli.py: 21 passed
  • all three modified build.yamls parse, with model_aliases landing under
    inference_gateway.producer
  • rescore_candidate.py compiles; the commit split was verified byte-identical to the
    working version

🤖 Generated with Claude Code

Greptile Summary

This PR updates reference-harness rescoring and producer configuration.

  • Classifies rewardless harness failures as zero and drops recognized infrastructure failures.
  • Adds installed-agent, environment, dependency, Harbor-version, and setup-timeout options to the rescoring script.
  • Pins GPT-5.x producer aliases to specific upstream deployments.
  • Exposes additional producer credentials and endpoint variables for OpenHands SDK and Goose.

Confidence Score: 4/5

The PR is not yet safe to merge because qualified infrastructure exceptions can still abort otherwise valid rescoring runs.

The current classifier compares persisted exception types directly against bare names, while existing Harbor result fixtures use qualified values such as openai.RateLimitError; those failures therefore reach the unclassified-exception exit instead of being omitted from scoring.

Files Needing Attention: harness-engineering-bench/scripts/rescore_candidate.py

Important Files Changed

Filename Overview
harness-engineering-bench/scripts/rescore_candidate.py Adds installed-harness execution and explicit rewardless-trial classification; the previously reported qualified-exception mismatch remains.
vero/src/vero/harbor/cli.py Adds producer credential and endpoint environment surfaces used by OpenHands SDK and Goose.
harness-engineering-bench/gaia/baseline/build.shell.yaml Pins GPT-5.6 producer aliases to Azure AI deployments.
harness-engineering-bench/officeqa/baseline/build.yaml Pins GPT-5.1 through GPT-5.5 producer aliases to Azure AI deployments.
harness-engineering-bench/swe-atlas-qna/baseline/build.yaml Pins GPT-5.6 producer aliases to Azure AI deployments.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A[Load benchmark build] --> B{Seed or installed agent?}
    B -->|Seed| C[Copy seed workspace]
    B -->|Installed| D[Configure Harbor agent]
    C --> E[Run fixed rounds and tasks]
    D --> E
    E --> F[Read trial results]
    F --> G{Reward present?}
    G -->|Yes| H[Include reward]
    G -->|No| I{Failure classification}
    I -->|Harness| J[Include zero]
    I -->|Platform| K[Drop trial]
    I -->|Unrecognized| L[Abort rescore]
    H --> M[Aggregate score]
    J --> M
    K --> M
Loading

Reviews (2): Last reviewed commit: "Classify the five failure types a rescor..." | Re-trigger Greptile

varunursekar and others added 4 commits August 3, 2026 08:50
Both harnesses ignore OPENAI_*/ANTHROPIC_*: openhands-sdk reads its own LLM_*
pair, and goose reads OPENAI_HOST plus OPENAI_BASE_PATH, which it joins -- so the
host must not already carry /v1 or the request lands on /v1/v1/chat/completions
and the proxy 403s. The producer token is minted per run, so neither can be
supplied through build.yaml agent_env; it has to be injected here. A harness that
does not read these ignores them, so the rule stays unconditional.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
An unqualified model group load-balances across deployments, and Responses-API
encrypted reasoning is decryptable only by the deployment that produced it, so
every turn after the first fails invalid_encrypted_content. Measured on
swe-atlas: 8 of 8 replays failed against the bare group, 0 of 5 on azure_ai. It
killed both gpt-5.6-sol cells at 2m58s under opencode as well as codex, so it is
not the codex-only fault terminal-bench's instance suggested.

Aliases apply after the allow-list check, so allowed_models still governs what an
optimizer may ask for and a cell's label still means what it says.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
trial_rewards dropped every rewardless trial, which silently scored only the
trials that survived and inflated any harness that crashed. Adopt runs/recompute.py's
convention, the one that produced the pinned baselines: a harness kill scores 0,
because the harness owns its install, its context management and its step budget
and must pay for them exactly as a candidate does at finalization; a platform kill
is dropped, because a retry cannot score a trial that never ran and zero-filling
infra bakes outage luck into the number. An unrecognised exception is now a hard
error rather than a quiet omission.

Recovers 73 zeroes across the reference grid, worst on swe-atlas x mini-swe-agent
where 28 of 150 trials were being ignored (0.0656 -> 0.0533).

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
A reference run swaps the seed program for a harness a practitioner could install
-- claude-code, opencode, goose, mini-swe-agent, openhands-sdk, terminus-2 -- and
changes nothing else: same dataset ref, same partition, same rounds, same
aggregation, same pinned target model. That makes the harness the only variable, so
the number stays comparable to baseline_reward and to every contestant.

Four flags the grid turned out to need:
  --agent                      the harness to run instead of agent_import_path
  --setup-timeout-multiplier   opencode's nvm -> node 22 -> npm -g install is far
                               heavier than the seed's zero setup and overruns
                               harbor's default
  --agent-env KEY=VALUE        goose needs the proxy pointed at explicitly
  --with-requirement SPEC      harbor-native harnesses import their framework in
                               the orchestrator and harbor does not declare it, so
                               dspy-rlm dies on ModuleNotFoundError before the
                               agent starts
plus --harbor-requirement, which also relaxes the copied workspace's own harbor pin
so an override can resolve.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Comment thread harness-engineering-bench/scripts/rescore_candidate.py Outdated
Greptile flagged that a rewardless trial with an unclassified exception exits the
script. The mechanism it proposed -- a qualified name like openai.RateLimitError
missing an exact match -- cannot happen: every write path goes through harbor's
ExceptionInfo.from_exception, which stores type(e).__name__, and none of the 16
distinct exception_type values across 4,586 trials on disk is dotted. The
qualified spelling it had in mind belongs to the retry config's
retry_exception_names, a different field.

The underlying risk is real, though, just via real types. Four rewardless trials
on disk carry a type in neither set, so scanning one of those rounds would abort.
Classified from harbor's own class hierarchy:

  AgentAuthenticationError  HARNESS -- subclasses NonZeroAgentExitCodeError; the CLI
                            reports no login, i.e. the harness never read a credential
                            surface we set, which is the defect this branch also fixes
  AdapterParseError         HARNESS -- dspy's own output parser gave up
  UnknownApiError           INFRA   -- harbor's ApiError subclass for a provider error
                            it could not classify, like the two RateLimits
  RewardFileNotFoundError   INFRA   -- the verifier ran and produced no reward file
  CancelledError            INFRA   -- harbor cancelled the trial

ValueError stays in neither set on purpose: both instances on disk are harbor's
"ContextVar was created in a different Context" bug, which is platform-side, but the
name is generic enough that an agent-side ValueError would land there too. The abort
now prints the exception message so whoever hits one can classify it in a single look.

No published number moves: all 23 completed reference cells rescore byte-identical.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
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.

1 participant