Skip to content

feat(terminal_bench_2_1): Terminal Bench 2.1 on the Harbor task shape (M8) - #3730

Merged
adil-a merged 1 commit into
adasif/harbor-m7-terminus-sessionsfrom
adasif/harbor-m8-tb21
Oct 21, 2026
Merged

adil-a merged 1 commit into
adasif/harbor-m7-terminus-sessionsfrom
adasif/harbor-m8-tb21

Conversation

@adil-a

@adil-a adil-a commented Sep 28, 2026 •

Copy link
Copy Markdown
Contributor

What does this PR do?

M8 of the Harbor tasks RFC (GitLab MR 340, MR 354): Terminal Bench 2.1 runs through the generic harbor environment server as published Harbor task folders, with no Gym-side patching, and gym dataset validate gets an output flag. Stacked on #3729 (M7).

  • gym dataset validate --output (-o). Rollouts can go to a chosen file and the summary reads from the effective output_jsonl_fpath, whether it came from the flag, the command line or a config file (_override_value in nemo_gym/tasks/harbor/cli.py). The flag is translated to a quoted Hydra override through a new _hydra_quote helper in nemo_gym/cli/main.py, which single-quotes the value and escapes only what Hydra itself un-escapes, so a filename with parentheses, commas, Unicode or backslashes reaches collection under its own name. gym search +query= uses the same helper.
  • The TB2.1 Harbor path. A new docs page, fern/versions/latest/pages/evaluation-tutorials/terminal-bench-2-1-harbor.mdx, clones harbor-framework/terminal-bench-2-1, pins the commit the legacy benchmark runs from (7131e43), and runs gym dataset validate terminal-bench-2-1/tasks and gym eval run terminal-bench-2-1/tasks --agent terminus_2_sandboxed_agent on that folder. It states that results compare with Harbor's own numbers rather than one to one with the terminal_bench_2_1 Resources Server, whose run-time fixes stay on its own path, and lists qemu-startup and qemu-alpine-ssh as known upstream noise (Bullseye security repository). The package-store form of the same dataset is added to this page in feat(harbor): fetch package-store datasets by content hash (M9) #3743, where it first resolves.
  • environments/harbor/terminal_bench_2_1/README.md is a short pointer to that page (the folder moved from benchmarks/harbor/ in the rebase). environment_servers/harbor/README.md gains the paragraph on what a registry fetch reuses: an existing folder on its recorded pin (full commit SHA, a task.toml, no symlink leaving the folder), with no content hashing, the recorded commit kept when the registry moves, and move the folder aside to fetch the current copy.

There is no preparation script and nothing is patched or checked in. Tasks come from the Harbor hub or the clone exactly as published.

Validation

  • pytest tests/unit_tests/test_cli_main.py tests/unit_tests/test_harbor_validation.py tests/unit_tests/test_harbor_tasks.py at 6ea3bf6f: 468 passed. TestDatasetValidateFlags::test_output_reaches_the_validation_run_quoted (test_cli_main.py) is parametrized over current(1).jsonl, current,failed.jsonl, résumé.jsonl, 日本語.jsonl, dir\name.jsonl, a Windows path, it's.jsonl, a\'b.jsonl and a trailing double backslash, and asserts the override parses back through Hydra's OverridesParser to the exact name. The new flag case of test_validate_reads_effective_output_and_preserves_evaluation_artifacts (test_harbor_validation.py) runs the real argparse and Hydra parse with --output résumé(1),back\slash.jsonl and checks that collection receives that path and that the summary reads it, next to the existing cli and file cases.
  • ruff check and ruff format --check (ruff 0.9.9) clean on the changed files at 6ea3bf6f.

Rollouts

All tables in this section were produced before the 2026-10-04 rebase onto main and before the preparation script was dropped, so the Harbor-path side ran the pinned clone with the legacy fixes applied. The documented commands now run the folders exactly as published, which is why qemu-startup and qemu-alpine-ssh are listed as upstream noise on the docs page rather than repaired. The tables have not been rerun since.

Parity rerun after the earlier rebase onto main and M15 (2026-10-01). Both paths ran 30 September's code. 89 tasks on both sides, 80 identical, 9 flips, each with a byte-identical prompt, divergence at the model's first reply, and a wrong answer in the losing side's verifier output. Two tasks are refused by the endpoint's content moderation on both sides and score 0 on both. Full per-task table below. The section below is the earlier, pre-rebase measurement.

Full per-task table (both sides on 30 September's code, latest rerun per task):

Per-task rewards, old path vs Harbor path
task old new verdict evidence
adaptive-rejection-sampler 1 1 same
bn-fit-modify 1 1 same
break-filter-js-from-html 0 0 same endpoint moderation refused the prompt on both sides
build-cython-ext 1 1 same
build-pmars 1 1 same
build-pov-ray 0 0 same
caffe-cifar-10 1 1 same
cancel-async-tasks 1 1 same
chess-best-move 1 1 same
circuit-fibsqrt 1 1 same
cobol-modernization 1 1 same
code-from-image 1 1 same
compile-compcert 1 1 same
configure-git-webserver 0 1 flip old: served page returned 404
constraints-scheduling 1 1 same
crack-7z-hash 1 1 same
custom-memory-heap-crash 1 1 same
db-wal-recovery 1 1 same
distribution-search 1 1 same
dna-assembly 0 1 flip old: reverse primer 46 nt, limit 45
dna-insert 1 0 flip new: primer Tm gap 8.9 C, limit 5
extract-elf 0 0 same
extract-moves-from-video 1 1 same
feal-differential-cryptanalysis 1 1 same
feal-linear-cryptanalysis 1 1 same
filter-js-from-html 0 0 same
financial-document-processor 1 1 same
fix-code-vulnerability 1 1 same
fix-git 1 1 same
fix-ocaml-gc 1 1 same
gcode-to-text 0 1 flip old: read 0 as O in the flag
git-leak-recovery 1 1 same
git-multibranch 1 1 same
gpt2-codegolf 1 0 flip new: binary segfaulted (exit -11)
headless-terminal 1 1 same
hf-model-inference 1 1 same
install-windows-3.11 0 0 same
kv-store-grpc 1 1 same
large-scale-text-editing 1 1 same
largest-eigenval 1 1 same
llm-inference-batching-scheduler 1 1 same
log-summary-date-ranges 1 1 same
mailman 1 1 same
make-doom-for-mips 0 0 same
make-mips-interpreter 1 1 same
mcmc-sampling-stan 1 1 same
merge-diff-arc-agi-task 1 1 same
model-extraction-relu-logits 1 1 same
modernize-scientific-stack 1 1 same
mteb-leaderboard 1 0 flip new: wrong model id
mteb-retrieve 0 0 same
multi-source-data-merger 1 1 same
nginx-request-logging 1 1 same
openssl-selfsigned-cert 1 1 same
overfull-hbox 1 1 same
path-tracing 1 1 same
path-tracing-reverse 1 1 same
polyglot-c-py 1 1 same
polyglot-rust-c 1 1 same
portfolio-optimization 1 1 same
protein-assembly 0 1 flip old: fusion component order wrong
prove-plus-comm 1 1 same
pypi-server 1 1 same
pytorch-model-cli 1 1 same
pytorch-model-recovery 1 1 same
qemu-alpine-ssh 1 1 same
qemu-startup 1 1 same
query-optimize 1 1 same prompt differs by a leading newline the new loader strips
raman-fitting 0 1 flip old: peak parameters out of tolerance
regex-chess 0 1 flip old: 1 of 4 test games failed
regex-log 1 1 same
reshard-c4-data 1 1 same
rstan-to-pystan 1 1 same
sam-cell-seg 1 1 same
sanitize-git-repo 1 1 same
schemelike-metacircular-eval 1 1 same
sparql-university 1 1 same
sqlite-db-truncate 1 1 same
sqlite-with-gcov 1 1 same
torch-pipeline-parallelism 0 0 same
torch-tensor-parallelism 1 1 same
train-fasttext 0 0 same
tune-mjcf 1 1 same
video-processing 0 0 same
vulnerable-secret 0 0 same endpoint moderation refused the prompt on both sides
winning-avg-corewars 1 1 same
write-compressor 1 1 same

Terminal Bench 2.1 parity: old server vs Harbor shape (x86, OpenSandbox, 1 rollout per task).

Reference solutions (old golden-patch mode vs gym dataset validate)

old server harbor server
tasks with a result 89 89
pass (reward 1.0) 85 83
same reward on both 87 of 89
task old harbor note
rstan-to-pystan 1 0 oracle failed
torch-tensor-parallelism 1 0 harbor:verifier_timeout

Terminus 2 with openai/openai/eccn-gpt-6-sol

old server harbor server
tasks with a result 89 88
pass (reward 1.0) 70 72
same reward on both 75 of 88
task old harbor note
build-cython-ext 0 1
dna-assembly 0 1
dna-insert 1 0
extract-elf 1 0
extract-moves-from-video 1 0
financial-document-processor 0 1 terminus did not finish
fix-git 0 1
gpt2-codegolf 1 no row harbor: dropped, 500, message='Internal Server Error', url='http://127.0.0.1:17472/ng-rollout/34-0/v1/respo
mteb-leaderboard 0 1
mteb-retrieve 1 0
protein-assembly 1 0
sam-cell-seg 0 1
sqlite-with-gcov 0 1
torch-tensor-parallelism 0 1

Notes on the differences:

  • torch-tensor-parallelism is a race in the task itself: every test reuses MASTER_PORT=12355 under mp.spawn, and when a test's rank 0 binds before the previous test's processes let go, torch waits out its 30-minute rendezvous timeout, past the task's 900 s verifier budget. It hit 2 of 4 runs on the new path and 0 of 1 on the old. A standalone run passed 13 of 13 tests in 82 s.
  • rstan-to-pystan failed once when the httpstan source build died mid-way. A standalone rerun passed.
  • Model runs: the provider's content filter rejected some prompts on both paths (a 400 upstream, retried by Terminus, then a harness error). One new-path episode was dropped because Terminus's in-sandbox tmux install failed on that image, which remote_tmux_binary_path avoids. The old run got a working install for the same task.
  • Old golden mode failed protein-assembly, mcmc-sampling-stan, build-pov-ray and caffe-cifar-10. The new path fails the same four for the same upstream reasons.

Run details: x86 OpenSandbox nodes, one rollout per task, both paths sized identically (4 CPU, 16 GiB, 30 GiB, requests 0.5 CPU, 512 MiB), overlay in results/tb21_parity/new_overlay.yaml on the run host. Old path: benchmarks/terminal_bench_2_1 config with num_repeats: 1 (golden mode with max_turns: 1 for the reference run). New path: gym dataset validate datasets/terminal-bench-2-1 and gym eval run datasets/terminal-bench-2-1 --agent terminus_2_sandboxed_agent.

Compatibility and benchmark impact

The existing terminal_bench_2_1 Resources Server, its benchmark folder under benchmarks/terminal_bench_2_1/ and its configs are unchanged. Nothing is checked in: the task folders come from the clone or the hub into the datasets folder, and the Harbor path applies none of the legacy server's run-time fixes. gym dataset validate without --output behaves as before.

Checklist

  • I have read the contributing guidelines.
  • The change is focused; unrelated "drive-by" edits are tracked as separate issues/PRs.
  • Tests added or updated and pass locally, or N/A for docs-only / non-code changes (so CI unit/server checks pass when applicable).
  • Documentation added or updated, or N/A with justification.
  • Pre-commit checks pass locally (pre-commit run --all-files) (so CI lint/format checks pass).
  • New source files include the required Apache-2.0 SPDX header.
  • All commits have DCO sign-off (git commit -s) (so the DCO check passes).

🤖 Generated with Claude Code

@adil-a
adil-a added this pull request to stack #3678 September 28, 2026 17:56
@copy-pr-bot

copy-pr-bot Bot commented Sep 28, 2026

Copy link
Copy Markdown

This pull request requires additional validation before any workflows can run on NVIDIA's runners.

Pull request vetters can view their responsibilities here.

Contributors can view more details about this message here.

@adil-a adil-a changed the title adasif/harbor m8 tb21 feat(terminal_bench_2_1): Terminal Bench 2.1 on the Harbor task shape (M8) Sep 28, 2026
@adil-a
adil-a force-pushed the adasif/harbor-m8-tb21 branch from a244db1 to a453d04 Compare September 28, 2026 19:35
@adil-a
adil-a removed this pull request from stack #3678 September 28, 2026 19:35
@adil-a
adil-a added this pull request to stack #3733 September 28, 2026 19:36
@adil-a
adil-a force-pushed the adasif/harbor-m8-tb21 branch from a453d04 to 20857c2 Compare September 28, 2026 20:10
@adil-a
adil-a force-pushed the adasif/harbor-m8-tb21 branch from 20857c2 to 81f0e76 Compare September 28, 2026 23:53
@adil-a
adil-a force-pushed the adasif/harbor-m8-tb21 branch from 81f0e76 to 359dc64 Compare September 30, 2026 03:48
@adil-a
adil-a removed this pull request from stack #3733 September 30, 2026 03:50
@adil-a
adil-a added this pull request to stack #3806 September 30, 2026 03:51
@adil-a
adil-a removed this pull request from stack #3806 September 30, 2026 04:12
@adil-a
adil-a added this pull request to stack #3808 September 30, 2026 04:12
@adil-a
adil-a force-pushed the adasif/harbor-m8-tb21 branch from 359dc64 to 07dba43 Compare October 4, 2026 23:19
khazic pushed a commit to khazic/Gym-lao that referenced this pull request Oct 7, 2026
NVIDIA-NeMo#4135)

## What does this PR do?

`benchmarks/terminal_bench_2_1/prepare.py` wrote rows in `glob()` order
(filesystem order) and without an id field. Gym's task identity
(`_trajectory_identity`, `materialize_task`) therefore fell back to the
row position, so "task 0" could name a different task on another machine
or after a re-clone.

This PR:
- adds `task_id` to every row, set to the task's folder name (e.g.
`path-tracing`). That is the id the Harbor task format uses for the same
task (NVIDIA-NeMo#3673 takes the folder name as `task_id`; NVIDIA-NeMo#3730 keeps TB2.1's
folder names), so the ids stay the same when TB2.1 moves to that format.
Folder names are unique by construction; `task_name`
(`terminal-bench/path-tracing`) is unchanged;
- writes rows in sorted order;
- checks that the 89 task ids are unique (`NUM_TASKS = 89` replaces the
literal in the existing count check).

Servers are unaffected: `TaskData` is `extra="allow"`, the Terminus and
OpenCode run requests accept extra fields, and nothing reads `task_id`
from TB2.1 rows today.

Motivation: stable ids are required by `gym eval plan` (NVIDIA-NeMo#4131), which
refuses position-based ids. With this change TB2.1 can be planned. No
separate issue.

## Validation

- Ran `prepare.py` via `gym eval plan --benchmark
terminal_bench_2_1/terminus_2` (with NVIDIA-NeMo#4131 applied locally): 89 tasks,
ids `adaptive-rejection-sampler`, `bn-fit-modify`, …; two runs produce
byte-identical plans. Without this change the same command is refused
with "89 of 89 tasks have no task id".
- `pre-commit run --files benchmarks/terminal_bench_2_1/prepare.py`:
pass.

## Rollouts

N/A: data-only change. The added field is ignored by the TB2.1 resources
server and both agents; the instruction, image and task folder of every
row are unchanged. Happy to run a smoke rollout if reviewers want one.

## Compatibility and benchmark impact

- Rollout outputs for TB2.1 now report the task's folder name as the
task id instead of the row index, so tooling that joins TB2.1 results
across runs by task id will see different keys for runs before and after
this change.
- Row order changes, so `_ng_task_index` values change. Don't resume
(`--resume`) a TB2.1 run started before this change after re-preparing
the data.
- Scores are unaffected.

## Checklist

- [x] I have read the [contributing
guidelines](https://github.com/NVIDIA-NeMo/Gym/blob/main/CONTRIBUTING.md).
- [x] The change is focused; unrelated "drive-by" edits are tracked as
separate issues/PRs.
- [x] Tests added or updated and pass locally, or N/A for docs-only /
non-code changes (so CI unit/server checks pass when applicable). N/A:
no existing tests cover this prepare script; validated by running it.
- [x] Documentation added or updated, or N/A with justification. N/A: no
documented behaviour changes.
- [x] Pre-commit checks pass locally (`pre-commit run --all-files`) (so
CI lint/format checks pass). Ran on the changed file.
- [x] New source files include the required Apache-2.0 SPDX header. N/A:
no new files.
- [x] All commits have DCO sign-off (`git commit -s`) (so the DCO check
passes).

Signed-off-by: Ingo Guehring <iguehring@nvidia.com>
@adil-a
adil-a force-pushed the adasif/harbor-m8-tb21 branch from 07dba43 to 6e7bcc3 Compare October 9, 2026 20:08
@adil-a
adil-a force-pushed the adasif/harbor-m8-tb21 branch from 6e7bcc3 to c5d7165 Compare October 9, 2026 20:20
Comment thread nemo_gym/tasks/harbor/cli.py Resolved
Comment thread benchmarks/terminal_bench_2_1/README.md Resolved
@adil-a
adil-a removed this pull request from stack #3808 October 14, 2026 00:54
@adil-a
adil-a added this pull request to stack #4583 October 14, 2026 00:55

@yuhezhang-ai yuhezhang-ai left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

One additional nonblocking filename case from the current-head verification; the previously reported comma/parenthesis cases are fixed.

Comment thread nemo_gym/cli/main.py Resolved
@yuhezhang-ai

Copy link
Copy Markdown
Contributor

Re-review status at c0041eb5:

Resolved: the comma/parenthesis output-path fix is verified; the preparation-test and legacy startup-repair requests are obsolete under the rewritten scope. Those threads are now closed. The earlier output-summary-path fix remains resolved.

Still needs fixing:

  • TB2.1 dataset target: provide a working source; both documented commands still fail before startup.
  • Comparison documentation, nonblocking: show the available command or state the M17 dependency.
  • Cache wording, nonblocking: describe reuse accurately; existing content is not compared against the published revision.
  • Unicode/backslash filenames, nonblocking: preserve the literal output filename. This additional case is separate from the fixed comma/parenthesis issue.

Please also refresh the PR description to reflect the removed preparation script and current scope. Existing findings were kept in their original threads; only the additional filename case received a new thread.

@adil-a
adil-a force-pushed the adasif/harbor-m8-tb21 branch from c0041eb to 5bc0c0b Compare October 20, 2026 00:20
@adil-a

adil-a commented Oct 20, 2026

Copy link
Copy Markdown
Contributor Author

/ok to test 5bc0c0b

@github-actions

github-actions Bot commented Oct 20, 2026 •

Copy link
Copy Markdown
Contributor

@adil-a

adil-a commented Oct 20, 2026

Copy link
Copy Markdown
Contributor Author

/ok to test 765622b

@adil-a

adil-a commented Oct 20, 2026

Copy link
Copy Markdown
Contributor Author

/ok to test fc9258b

@adil-a

adil-a commented Oct 20, 2026

Copy link
Copy Markdown
Contributor Author

/ok to test 6ea3bf6

@adil-a

adil-a commented Oct 20, 2026

Copy link
Copy Markdown
Contributor Author

Status at the new head 6ea3bf6. Your four remaining items:

  • TB2.1 dataset target: the commands moved from the README to fern/versions/latest/pages/evaluation-tutorials/terminal-bench-2-1-harbor.mdx and use the pinned local clone at this commit, which gym dataset validate and gym eval run accept. The package-store form, harbor:terminal-bench/terminal-bench-2-1, is added on the same page in feat(harbor): fetch package-store datasets by content hash (M9) #3743, where it first resolves.
  • comparison documentation: the README is a pointer now and names no compare command, so there is nothing left to relabel. gym dev compare arrives in feat(cli): gym dev compare, the per-task parity report (M17) #4257 and gym eval compare is the aggregate comparison until then.
  • cache wording: environment_servers/harbor/README.md now says what a fetch does, reuse an existing folder on its recorded pin without hashing its content, as Harbor's download does. On the stack tip prepare re-hashes every folder against manifest.toml and refuses an edited one (check_manifest, TestCheckManifestFailsClosed).
  • Unicode and backslash filenames: they reach Hydra literally through the new _hydra_quote helper, with the parametrized cases in test_cli_main.py and the real-CLI case in test_harbor_validation.py.

The benchmark folder moved from benchmarks/harbor/ to environments/harbor/ in the rebase. The description still describes the removed preparation script, I am refreshing it to the current scope.

yuhezhang-ai
yuhezhang-ai previously approved these changes Oct 20, 2026

@yuhezhang-ai yuhezhang-ai left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Rechecked 6ea3bf6f against M7. Previous findings are addressed or superseded by the documented scope change; no remaining blockers from this review. Required DCO, lint and CI checks pass. The optional harness-conformance check still reports unavailable or invalid probe evidence.

@adil-a

adil-a commented Oct 21, 2026

Copy link
Copy Markdown
Contributor Author

/ok to test f6a36db

@adil-a

adil-a commented Oct 21, 2026

Copy link
Copy Markdown
Contributor Author

/ok to test 24916f9

@adil-a

adil-a commented Oct 21, 2026

Copy link
Copy Markdown
Contributor Author

/ok to test 022ad8f

ffrujeri
ffrujeri previously approved these changes Oct 21, 2026

@ffrujeri ffrujeri left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Approving 022ad8f9. All threads are resolved and Yu has no remaining blockers. The --output quoting round-trips through Hydra in the tests, and the summary reads the effective output_jsonl_fpath. CLI, validation, Harbor task and Harbor environment-server suites pass locally (589 passed). CI is green apart from the non-blocking harness conformance probes. One non-blocking docs note inline.

@adil-a

adil-a commented Oct 21, 2026

Copy link
Copy Markdown
Contributor Author

/ok to test f41a064

ffrujeri
ffrujeri previously approved these changes Oct 21, 2026

@ffrujeri ffrujeri left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Re-approving f41a064b. Against the approved 022ad8f9, the patch adds the docs note on the expected non-zero exit for the two broken upstream tasks (thanks) and rebases the README context. Stack tests pass locally; CI was still running at approval time.

@adil-a

adil-a commented Oct 21, 2026

Copy link
Copy Markdown
Contributor Author

/ok to test eb9535e

…b path (M8)

`gym dataset validate` accepts `--output/-o` for its rollouts file, so a later `gym dev compare`
can find oracle runs where the caller put them. The Terminal Bench 2.1 README documents the
Harbor task shape: the 89 tasks fetched from the Harbor hub as published, through the `harbor`
environment server, with no Gym-side patching of `tests/test.sh` or `solution/solve.sh`.
Results on that path are compared with Harbor's own numbers for the dataset rather than one to
one with this benchmark's server.

The earlier `prepare_harbor_taskset.py`, which materialized the pinned fork with the server's
run-time fixes applied as plain files, is dropped: the Harbor path reuses Harbor's format and
loader semantics, and Harbor-side task fixes belong upstream.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Signed-off-by: Adil Asif <adasif@nvidia.com>
@adil-a

adil-a commented Oct 21, 2026

Copy link
Copy Markdown
Contributor Author

/ok to test f782ce9

@ffrujeri ffrujeri left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Re-approving f782ce93 after the restack. git range-diff shows the patch is identical to the f41a064b I approved.

@github-actions

Copy link
Copy Markdown
Contributor

🔄 Merge queue validation started! You can track the progress here: https://github.com/NVIDIA-NeMo/Gym/actions/runs/39680941039

This branch was previously deployed

1 inactive deployment
public — f782ce93 Deployed Oct 21, 2026 by copy-pr-bot[bot] via release / finalize / notify #6383
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area:cli Command-line interfaces and user-facing command behavior complexity:low Localized change in one scope with a small, straightforward review surface feature New capabilities, enhancements, or enablement work needs-review PR is ready for code review and waiting on a reviewer

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants