Skip to content

Add a GAIA variant whose seed is an empty shell - #83

Merged
varunursekar merged 2 commits into
mainfrom
worktree-gaia-shell-seed
Aug 2, 2026
Merged

Add a GAIA variant whose seed is an empty shell#83
varunursekar merged 2 commits into
mainfrom
worktree-gaia-shell-seed

Conversation

@varunursekar

@varunursekar varunursekar commented Aug 2, 2026

Copy link
Copy Markdown
Collaborator

Every optimization task in the suite starts from a seed that already works, so what they measure is tuning — how much an optimizer adds to a competent program. This variant asks a different question: what an optimizer does when there is nothing to tune and it has to write the program first.

build.shell.yaml runs the same benchmark against target-shell/ instead of target/. The skeleton satisfies the Harbor interface, resolves the model and constructs a client, but its run writes an empty answer and returns. 72 lines against the seed's 359.

Design choices worth reviewing

The plumbing is deliberately kept. Model resolution, the removeprefix("openai/") the gateway allow-list requires, and the client construction are properties of this harness rather than of GAIA. Making an optimizer rediscover them by trial and error would spend budget on the wrong thing and add variance unrelated to the question being asked.

It writes an empty answer rather than doing nothing. A case that scores zero and a case that errors are different events here: errors count against error_rate_threshold, and a wholly erroring evaluation comes back invalid. Writing the file keeps every case scoreable, so the floor is a real 0.0 and a half-built candidate gets a number rather than nothing.

The reframing lives entirely in description. The shared instruction template opens with "Improve the program", which is the wrong framing here and cannot change without touching every benchmark. The description says the target is a skeleton, that the first job is to make it work at all and the second is to make it good, and leaves the approach open — no mention of tools, search or turn budgets, which would be an answer key.

Rationale lives in the baseline README, not the target. The optimizer mounts target-shell/ and would read anything placed there.

Needs confirming before use

baseline_reward: 0.0 is a claim, not a measurement — it follows from the skeleton writing an empty answer, but nothing has measured it. Worth one baseline round before quoting deltas against it. The config comment says so.

Test coverage

Nothing else in the suite would notice an implementation reappearing in the shell, so test_gaia_shell_variant_shares_the_measurement_substrate_and_stays_a_shell pins the variant to the seeded config on task source, partitions, target model and both gateway scopes — that is what makes the two runs comparable — and asserts the skeleton never issues a model request. Negative-controlled: adding a responses.create call fails it with the right message; reverting passes.

  • full vero suite 493 passed, 16 skipped
  • shell target's own tests 4 passed

One trap for anyone running these locally: uv run pytest picks Python 3.14 by default and litellm fails to build there. Use --python 3.12, which is what harbor_python_version already specifies.

Launching it

--config ../harness-engineering-bench/gaia/baseline/build.shell.yaml

🤖 Generated with Claude Code

Greptile Summary

Adds a GAIA benchmark variant that starts from a scoreable empty agent.

  • Adds the shell target, benchmark configuration, lockfile, tests, and documentation.
  • Adds benchmark-specific instruction templates with inheritance from the shared workflow.
  • Resolves custom template paths relative to build configurations and fails compilation when a template is missing.
  • Adds invariants keeping the shell and seeded GAIA measurement substrates aligned.

Confidence Score: 5/5

The PR appears safe to merge.

No blocking failure remains.

Important Files Changed

Filename Overview
vero/src/vero/harbor/build/compiler.py Adds custom instruction-template lookup and rendering while retaining built-in templates as the inheritance fallback.
vero/src/vero/harbor/build/config.py Adds the optional instruction_template build field.
vero/src/vero/harbor/build/loader.py Resolves relative instruction-template paths against the build configuration directory.
vero/src/vero/harbor/build/templates/instruction.md.j2 Wraps the existing opening and objective in an overridable framing block.
harness-engineering-bench/gaia/baseline/build.shell.yaml Defines the GAIA shell-seed variant while retaining the seeded benchmark's evaluation substrate.
harness-engineering-bench/gaia/baseline/instruction.shell.md.j2 Reframes the task as building before optimizing while inheriting the shared workflow.
harness-engineering-bench/gaia/baseline/target-shell/src/gaia_agent/agent.py Implements a Harbor-compatible skeleton that writes an empty answer without issuing a model request.
vero/tests/test_v05_harbor_build.py Tests custom-template inheritance, rendering composition, and missing-template failure.
vero/tests/test_v05_benchmark_configs.py Tests shell-seed invariants and alignment with the seeded GAIA benchmark.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
  A[build.shell.yaml] --> B[Resolve target-shell and instruction template]
  B --> C[Compile benchmark]
  C --> D[Render custom framing]
  C --> E[Inherit shared workflow and rules]
  C --> F[Package empty GaiaAgent seed]
  F --> G[Run GAIA case]
  G --> H[Write empty answer.txt]
  H --> I[Scoreable zero-result baseline]
Loading

Reviews (2): Last reviewed commit: "Let a build supply its own instruction t..." | Re-trigger Greptile

varunursekar and others added 2 commits August 1, 2026 19:53
Every optimization task in the suite starts from a seed that already works, so
what they measure is tuning: how much an optimizer adds to a competent program.
This variant asks a different question -- what an optimizer does when there is
nothing to tune and it has to write the program first.

`build.shell.yaml` runs the same benchmark against `target-shell/`. The skeleton
there satisfies the Harbor interface, resolves the model and constructs a
client, but its `run` writes an empty answer and returns. 72 lines against the
seed's 359.

The plumbing is deliberately kept. Model resolution, the
`removeprefix("openai/")` the gateway allow-list requires, and the client
construction are properties of this harness rather than of GAIA; making an
optimizer rediscover them by trial and error would spend budget on the wrong
thing and add variance unrelated to the question.

It writes an empty answer rather than doing nothing because a case that scores
zero and a case that errors are different events here -- errors count against
`error_rate_threshold`, and a wholly erroring evaluation comes back `invalid`.
Writing the file keeps every case scoreable, so the floor is a real 0.0 and a
half-built candidate gets a number rather than nothing.

`baseline_reward: 0.0` is a claim, not a measurement: it follows from the
skeleton writing an empty answer. Confirm it with one baseline round before
quoting deltas against it.

The shared instruction template opens with "Improve the program", which is the
wrong framing here and cannot change without touching every benchmark, so the
reframing lives entirely in `description`. It says the target is a skeleton, that
the first job is to make it work at all and the second is to make it good, and
leaves the approach open -- no mention of tools, search or turn budgets, which
would be an answer key.

The new invariant test pins the variant to the seeded config on task source,
partitions, target model and both gateway scopes, which is what makes the two
runs comparable, and asserts the skeleton never issues a model request. Nothing
else in the suite would notice an implementation reappearing in the shell, and a
shell that scores above zero is not a shell.

Rationale lives in the baseline README rather than the target, which the
optimizer mounts and would read.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
`description` was the only per-build lever on the optimizer's instruction, and
it is the wrong one when a task contradicts the shared framing. The built-in
template opens with "Improve the program in ...", which is the first line the
optimizer reads and is false for the shell-seed variant: there is nothing to
improve, and the objective further down says to build one. The two fought.

Adds `instruction_template`, an optional path resolved relative to the config
like `agent_repo` and `task_manifest` already are. The named template's own
directory is searched ahead of the built-in one, so a variant can
`{% extends "instruction.md.j2" %}` rather than restate what it does not change.

The built-in template gains a `framing` block around its opening and Objective
sections. That is inert: rendering the gaia baseline before and after the change
is byte-identical, verified directly rather than argued.

gaia's shell variant now overrides only that block. Its rendered instruction
opens with "Build the program, then optimize it" and everything from `## Workflow`
onward is byte-identical to the built-in render, so the variant cannot drift as
the shared instruction evolves. `description` is left saying only what the
program must do, which puts task shape and task content in separate places.

A missing template is a build-time FileNotFoundError rather than a silent
fallback: falling back would ship the wrong framing to a shell-seed run and score
it as though nothing were wrong.

Tests cover the inherited remainder being byte-identical, the framing actually
being replaced, `description` still composing, and the missing-file error. The
benchmark invariant additionally pins that the shell variant uses a template that
extends the built-in one, and that the seeded gaia config still uses the default.

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

Copy link
Copy Markdown
Collaborator Author

Follow-up: the framing now comes from a template, not description

Varun asked whether a benchmark could select its own instruction template. It could not — instruction.md.j2 was hardcoded at the call site and the Jinja loader only searched vero's package templates. So description was the only per-build lever, and it was the wrong one here: the built-in template opens with "Improve the program in ...", which is the first line the optimizer reads and is false for a shell seed. The objective further down said to build one. The two fought.

Added instruction_template (9d68d4d), an optional path resolved relative to the config exactly like agent_repo and task_manifest already are. The named template's directory is searched ahead of the built-in one, so a variant can extend rather than restate:

{% extends "instruction.md.j2" %}
{% block framing %}
# Build the program, then optimize it
...
{% endblock %}

The shared template change is inert. It gains a framing block around its opening and Objective sections and nothing else. I rendered the gaia baseline before and after and byte-compared: identical. Same check on the variant from ## Workflow onward — identical to the built-in render, so the workflow, budget, inspection and rules sections cannot drift as the shared instruction evolves.

A missing template is a build-time FileNotFoundError, not a silent fallback. Falling back would ship the wrong framing to a shell-seed run and score it as though nothing were wrong.

description now says only what the program must do, which puts task shape and task content in separate places.

What the optimizer now reads

# Build the program, then optimize it

The program in `/work/agent` is a skeleton. It satisfies the agent
interface and runs to completion, but it does not attempt the task: it produces an
empty answer and scores zero. There is no working implementation to tune.

Write one, then make it score as highly as possible on the hidden final evaluation.
...
Both halves count. An implementation that merely runs is worth little, and time
spent refining a design before anything scores is worth nothing. Reach something
that scores, measure it, and improve from there.

## Objective

The program must answer canonical GAIA tasks: ...

Suite is 495 passed, 16 skipped (+2 new). The benchmark invariant additionally pins that the shell variant uses a template extending the built-in one, and that the seeded gaia config still uses the default.

@varunursekar
varunursekar merged commit 82d4045 into main Aug 2, 2026
10 checks passed
@varunursekar
varunursekar deleted the worktree-gaia-shell-seed branch August 2, 2026 06:07
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