Seed-complexity blocks: shell stub and opencode full-harness seeds for Terminal-Bench and GAIA - #97
varunursekar wants to merge 17 commits into
Conversation
…efore passing it to uv Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…outed builds Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…routed build for transfer passes Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…yaml) mirroring the GAIA stub Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
… test parametrized over gaia and terminal-bench Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
- target-opencode/: opencode vendored as a git submodule pinned to e03db9bc (v1.18.31 dev), plus a thin wrapper that subclasses harbor's opencode runner, compiles the vendored tree on the evaluation host (Bun, cached by source hash) and uploads the binary into each task container; GAIA wrapper tells opencode to write /app/answer.txt - build.opencode.yaml for both benchmarks (same tasks/model/scopes as build.yaml; baseline_reward unpinned until measured) - compiler: materialise submodule contents when snapshotting agent_repo (git archive leaves gitlinks as empty dirs) - config test for the opencode variants; READMEs describe the seed ladder Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…e first add was clobbered by a concurrent clone Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…o (opencode links web assets across packages) Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
… instead of the install script (no curl/unzip dependency on the evaluation host) Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…ontainer The e2e smoke showed two things: the evaluation gateway is compose-internal and unreachable from task containers (opencode: 'Cannot connect to API'), and the sidecar runs harbor 0.20.0, whose opencode runner lacks ensure_system_dependencies. - builds set task_services_use_upstream (and harness_user: null, as browsecomp-plus does); the wrapper takes OPENAI_BASE_URL only, never the gateway address - install() installs coreutils by hand and stages the binary via /tmp before moving it into /usr/local/bin as root - config test asserts the upstream flag on the opencode variants Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…ation of an opencode candidate outlives the 1 h idle window and the main sandbox is gone before harbor downloads the verifier dir Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
The vendored opencode image takes 10-23 min to start on Modal and the 1800 s default (build_timeout_seconds -> harbor build_timeout_sec) timed out about ten launches with EnvironmentStartTimeoutError. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…rok-build-0.1) The seed registered the bare harbor model name under opencode's built-in openai provider and, under task_services_use_upstream, sent it straight to the litellm proxy, which only resolves xai/grok-build-0.1: every request 401'd (key not allowed to access model) and the seed scored 0.0 on every Terminal-Bench evaluation. Register the model under a plain openai-compatible provider id with models.<key>.id = the vendor-prefixed wire id (overridable via VERO_OPENCODE_WIRE_MODEL_ID). This is the exact change the claude-sonnet-5 claude-code r2 optimizer shipped (0.3426 on test, 0/36 errors). GAIA seed untouched: its bare gpt-5.4-mini is an upstream model group. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…the seed tree (opencode targets carry a 2.8 GB install that filled the disk) Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…e binary uploads from the host) Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
| # opencode runs inside the task container, which cannot reach the compose-internal | ||
| # evaluation gateway. This hands the container the public upstream (OPENAI_*), as | ||
| # task-owned services already get; the metered gateway stays on VERO_AGENT_INFERENCE_* | ||
| # for in-process agents. Consequence: the target model is fixed by the wrapper, not by | ||
| # the gateway allow-list, and its use is audited from the upstream per-key request log. | ||
| task_services_use_upstream: true |
There was a problem hiding this comment.
task_services_use_upstream puts the real OPENAI_API_KEY in the task environment. This target reads that key from optimizer-editable code instead of using the scoped gateway token. A candidate can send direct requests or print the key, while request logs only catch this later. Keep editable code on a reachable scoped gateway instead of giving it the provider key.
The GAIA opencode builds use the same setting.
How this was verified: HarborBackend copies the host provider key into the task environment, and the editable wrapper reads that exact variable.
Prompt To Fix With AI
This is a comment left during a code review.
Path: harness-opt-bench/terminal-bench/baseline/build.opencode.yaml
Line: 118-123
Comment:
`task_services_use_upstream` puts the real `OPENAI_API_KEY` in the task environment. This target reads that key from optimizer-editable code instead of using the scoped gateway token. A candidate can send direct requests or print the key, while request logs only catch this later. Keep editable code on a reachable scoped gateway instead of giving it the provider key.
The GAIA opencode builds use the same setting.
**How this was verified:** `HarborBackend` copies the host provider key into the task environment, and the editable wrapper reads that exact variable.
---
For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.| env = { | ||
| **os.environ, | ||
| "BUN_INSTALL_CACHE_DIR": str(cache_root() / "bun-cache"), | ||
| # husky's `prepare` hook and interactive prompts have no place here | ||
| "HUSKY": "0", "CI": "1", | ||
| } | ||
| _log(f"tree {key}: bun install") | ||
| subprocess.run([bun, "install", "--frozen-lockfile"], cwd=root, check=True, | ||
| capture_output=True, text=True, env=env) | ||
| _log(f"tree {key}: compile for {platform.system().lower()}-{platform.machine()}") | ||
| subprocess.run([bun, "run", "script/build.ts", "--single", "--skip-embed-web-ui", "--skip-install"], | ||
| cwd=root / "packages" / "opencode", check=True, capture_output=True, text=True, env=env) |
There was a problem hiding this comment.
build_binary runs optimizer-edited install hooks and script/build.ts on the evaluation host. Both commands inherit all of os.environ. This happens before the binary enters the task container, so task isolation does not protect the host. Build candidate source inside a sandbox and pass only the values it needs.
The GAIA copy has the same code.
How this was verified: Agent setup invokes both candidate-controlled Bun commands on the host with the full parent environment before uploading the binary.
Prompt To Fix With AI
This is a comment left during a code review.
Path: harness-opt-bench/terminal-bench/baseline/target-opencode/src/terminal_bench_agent/_build.py
Line: 104-115
Comment:
`build_binary` runs optimizer-edited install hooks and `script/build.ts` on the evaluation host. Both commands inherit all of `os.environ`. This happens before the binary enters the task container, so task isolation does not protect the host. Build candidate source inside a sandbox and pass only the values it needs.
The GAIA copy has the same code.
**How this was verified:** Agent setup invokes both candidate-controlled Bun commands on the host with the full parent environment before uploading the binary.
---
For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.| for entry in listing.stdout.split(b"\0"): | ||
| if not entry.startswith(b"160000 "): | ||
| continue | ||
| sub_path = entry.split(b"\t", 1)[1].decode("utf-8") | ||
| checkout = root / relative / sub_path if str(relative) != "." else root / sub_path | ||
| if not (checkout / ".git").exists(): | ||
| raise RuntimeError( | ||
| f"agent_repo contains submodule {sub_path!r} that is not checked out; " | ||
| "run `git submodule update --init --recursive` before compiling" | ||
| ) | ||
| archived = subprocess.run( | ||
| ["git", "-C", str(checkout), "archive", "--format=tar", "HEAD"], | ||
| capture_output=True, |
There was a problem hiding this comment.
ls-tree returns the gitlink SHA, but this code keeps only the path and archives the checkout's current HEAD. If an initialized submodule is stale or was moved by a developer, the build silently snapshots that other commit. Archive the gitlink SHA or reject a checkout whose HEAD does not match it.
Prompt To Fix With AI
This is a comment left during a code review.
Path: vero/src/vero/harbor/build/compiler.py
Line: 187-199
Comment:
`ls-tree` returns the gitlink SHA, but this code keeps only the path and archives the checkout's current `HEAD`. If an initialized submodule is stale or was moved by a developer, the build silently snapshots that other commit. Archive the gitlink SHA or reject a checkout whose `HEAD` does not match it.
---
For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.| def tree_hash(root: Path) -> str: | ||
| """Content hash of the source tree, ignoring build outputs and dependencies.""" | ||
| digest = hashlib.sha256() | ||
| for dirpath, dirnames, filenames in os.walk(root): | ||
| dirnames[:] = sorted(d for d in dirnames if d not in EXCLUDED_DIRS) | ||
| for name in sorted(filenames): | ||
| path = Path(dirpath) / name | ||
| if path.is_symlink(): | ||
| continue | ||
| digest.update(path.relative_to(root).as_posix().encode()) | ||
| digest.update(b"\0") | ||
| with path.open("rb") as handle: | ||
| for chunk in iter(lambda: handle.read(1 << 20), b""): | ||
| digest.update(chunk) | ||
| digest.update(b"\0") | ||
| return digest.hexdigest()[:16] |
There was a problem hiding this comment.
tree_hash skips each symlink, including its path and target. The compiler now keeps safe symlinks because opencode uses them as build inputs. A candidate can change such a link without changing the cache key, and build_binary can return the previous binary. Hash the link path and os.readlink(path) value.
Prompt To Fix With AI
This is a comment left during a code review.
Path: harness-opt-bench/terminal-bench/baseline/target-opencode/src/terminal_bench_agent/_build.py
Line: 34-49
Comment:
`tree_hash` skips each symlink, including its path and target. The compiler now keeps safe symlinks because opencode uses them as build inputs. A candidate can change such a link without changing the cache key, and `build_binary` can return the previous binary. Hash the link path and `os.readlink(path)` value.
---
For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.| def _bun() -> str: | ||
| """A Bun of the pinned version: on PATH, in ~/.bun, or fetched from GitHub. | ||
|
|
||
| The official install script needs curl and unzip on the host; the evaluation | ||
| host is not guaranteed either, so fetch the release zip with the standard | ||
| library and unpack it ourselves. | ||
| """ | ||
| found = shutil.which("bun") or str(Path.home() / ".bun" / "bin" / "bun") | ||
| if Path(found).exists(): | ||
| return found |
There was a problem hiding this comment.
BUN_VERSION affects only the fallback download URL. If bun is already on PATH or under ~/.bun, _bun returns it without checking the version. Check bun --version and fetch the pinned release when it differs.
The GAIA copy has the same code.
Prompt To Fix With AI
This is a comment left during a code review.
Path: harness-opt-bench/terminal-bench/baseline/target-opencode/src/terminal_bench_agent/_build.py
Line: 52-61
Comment:
`BUN_VERSION` affects only the fallback download URL. If `bun` is already on `PATH` or under `~/.bun`, `_bun` returns it without checking the version. Check `bun --version` and fetch the pinned release when it differs.
The GAIA copy has the same code.
---
For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.…serts the gitlink pin and skips the file checks on an unpopulated tree Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Summary
Adds the two new seed families the EACL rebuttal grid runs on, plus the compiler and rescore changes they needed. Every cell in the TB-stub, TB-opencode and GAIA-opencode blocks was launched from this branch (
VERO_SRC_OVERRIDEto this worktree'svero/src).Seeds
terminal-bench/baseline/target-shell+build.shell.yaml: non-functional shell stub mirroring the GAIA stub (measured baseline 0.0).terminal-bench/baseline/target-opencode,gaia/baseline/target-opencode+build.opencode.yaml: opencode vendored as a pinned submodule (anomalyco/opencode @ e03db9bc), thin harborOpenCodesubclass, host-side Bun build cached per tree hash,VERO_OPENCODE_BINARYoverride. GAIA e2e smoke variant.xai/grok-build-0.1) through a customopencode-targetprovider; the bare id was rejected upstream with 401 and scored 0.0.VERO_OPENCODE_WIRE_MODEL_IDoverrides.task_services_use_upstream).Compiler
agent_repo(they came out as empty dirs fromgit archive).Build timeouts (opencode builds)
build_timeout_seconds5400: the image build/start regularly took 45+ min.rescore_candidate
--build-filefor routed builds,--agent-env(harbor--ae) passthrough,--agent-setup-timeout-multiplier(the 143 MB opencode binary upload from the host exceeded harbor's 360 s setup budget at concurrency 24), resolve theharbor_requirementplaceholder, skipnode_modules/dist/.turbowhen copying the seed tree.Notes for review
secrets.env.examplefiles are tracked; added lines were scanned for key patterns.Test plan
vero/testsshell-variant config test parametrized over gaia and terminal-benchbuild.opencode.e2e.yaml) on Modal🤖 Generated with Claude Code
The PR is not safe to merge. Four open issues can expose credentials, run edited code on the host, or build the wrong source.
Fix with agent prompt
Summary
VeRO adds shell and full-opencode starting points for Terminal-Bench and GAIA, along with compiler and rescoring support for running them. The new seeds compare building an agent from an empty shell with improving a complete coding harness on the same benchmark setup.
Diagram
sequenceDiagram participant Compiler participant Git as Git checkout participant Host as Evaluation host participant Task as Task container participant API as Public upstream Compiler->>Git: Archive the seed at HEAD Compiler->>Git: Find submodule gitlinks Git-->>Compiler: Archive each submodule at its checked-out HEAD Compiler-->>Host: Return baseline with vendored opencode Host->>Host: Hash the opencode tree alt Cached binary exists Host->>Host: Reuse cached binary else No cached binary Host->>Host: Run bun install and build.ts Host->>Host: Cache the binary end Host->>Task: Upload the opencode binary Host->>Task: Pass upstream API settings Task->>API: Send model requests directly API-->>Task: Return model responsesReviews (2) · Last reviewed commit: "CI: check out the vendored opencode subm..."