feat(harbor_agent): NeMo Gym sandbox execution backend + Terminal-Bench 2.1 config - #2031
Draft
hemildesai wants to merge 23 commits into
Draft
feat(harbor_agent): NeMo Gym sandbox execution backend + Terminal-Bench 2.1 config#2031hemildesai wants to merge 23 commits into
hemildesai wants to merge 23 commits into
Conversation
…ch 2.1 opensandbox config Adds NemoGymSandboxEnvironment, a harbor BaseEnvironment implementation backed by the nemo_gym.sandbox API (AsyncSandbox), selected purely via harbor_environment_import_path — no changes to the harbor_agent server code. Any Gym sandbox provider (opensandbox, docker, daytona, apptainer, ecs_fargate) becomes a Harbor execution backend through config. - task.toml resources map to SandboxSpec.resources; per-task Docker images are required (no Harbor-side builds); tar-based upload_dir/download_dir with per-file fallback; sandboxes are always terminated on stop() to avoid leaking remote resources; opt-in allow_unenforced_internet_isolation. - harbor_agent_opensandbox.yaml runs Terminal-Bench 2.1 (89 tasks, pinned harbor-framework/terminal-bench-2-1 checkout via TERMINAL_BENCH_2_1_TASKS_DIR) with the stock Terminus-2 harness through the opensandbox provider in server-proxy mode, mirroring the Qwen3.6 model-card harness settings (3h agent timeout, 32 CPU / 48 GB per task, 256K ctx / 80K max output). - prepare_terminal_bench_2_1.py regenerates the checked-in 89-row benchmark input JSONL from the pinned tasks repo. Note: signed off (DCO) but not GPG-signed — no interactive pinentry available in this session. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Signed-off-by: Hemil Desai <hemild@nvidia.com>
…40 compatibility openai 2.40+ made InputTokensDetails.cache_write_tokens required; extract_usage built the details dict without it, so every /run 500'd at HarborVerifyResponse validation when running against a live model server. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Signed-off-by: Hemil Desai <hemild@nvidia.com>
…r TB2.1 config Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Signed-off-by: Hemil Desai <hemild@nvidia.com>
… episodes The 120s default httpx timeout caused Terminus-2 to abandon and re-send long thinking-mode generations, piling duplicate in-flight requests onto the model server under concurrency. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Signed-off-by: Hemil Desai <hemild@nvidia.com>
…ral storage to 30GiB
- Adopt the mini_swe_agent_2 config style: the opensandbox provider is now
referenced via `sandbox_provider: ${sandbox}` and its definition comes from
the shipped provider config, included with `--config
nemo_gym/sandbox/providers/opensandbox/configs/opensandbox.yaml`. Swapping
providers is now swapping that --config path, with no edits to this file.
- Add `override_storage_mb: 30720` (30 GiB). Root cause of mid-run sandbox 500s:
disk-heavy TB tasks (image builds, dataset downloads) overran the 10 GiB
task.toml ephemeral-storage default and the kubelet evicted the pod, so the
server's proxy calls to that pod's execd 500'd. Raising disk_gib via the
sandbox API (SandboxSpec.resources.disk_gib -> ephemeral-storage limit)
fixes it.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Signed-off-by: Hemil Desai <hemild@nvidia.com>
Raise the agent request-semaphore concurrency to 89 so all Terminal-Bench 2.1 tasks run at once (paired with gym eval --concurrency 89 and vLLM --max-num-seqs 96). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Signed-off-by: Hemil Desai <hemild@nvidia.com>
…e full concurrency Each Harbor job is dispatched as a Ray task; Ray's default 1 CPU/task capped in-flight jobs at the driver's physical CPU count (e.g. 24), so --concurrency 89 only ran ~24 tasks at once. Add harbor_ray_task_num_cpus (default None = current behavior); jobs are I/O-bound (remote sandbox + model server), so a fractional value (0.25 in the TB2.1 config) packs all 89 onto a 24-CPU driver. Verified: 89 concurrent sandboxes, Ray available CPU ~1.75/24. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Signed-off-by: Hemil Desai <hemild@nvidia.com>
…del-card 59.3) Full Terminal-Bench 2.1 run complete: 53/89 passed (mean reward 0.5955), essentially matching the Qwen3.6-27B model card's Terminal-Bench 2.0 = 59.3. Traces for all 89 tasks captured on Lustre (rollouts + per-trial trajectory, asciinema recordings, verifier artifacts). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Signed-off-by: Hemil Desai <hemild@nvidia.com>
Confirmed all per-trial trajectories are ATIF-v1.5. Running TB2.1 5x sequentially on the 2-GPU deployment for the model-card 5-run average (after a 4-GPU num-repeats attempt hit GB300 scheduling limits). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Signed-off-by: Hemil Desai <hemild@nvidia.com>
Root causes of the wedges (user insight): reusing one driver + abrupt kills accumulate stuck state. Two mechanisms: (1) killing a run leaves its in-flight LLM requests queued on vLLM; (2) short-timeout health probes abandon connections that vLLM keeps processing, piling up until the engine stalls. Fix: each run is its own k8s Job (fresh Ray/servers) with a shared Lustre venv; runs complete without killing; no POST-probing of vLLM. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Signed-off-by: Hemil Desai <hemild@nvidia.com>
…am drops OpenSandbox exec streams a command's output over one long-lived SSE connection through the server proxy. When that connection crosses a load balancer with a TCP idle/stream-duration cap (e.g. an AWS NLB fronting an istio east-west gateway, default 350s), a command that runs a while gets its stream silently reaped and the client hangs (~4h) since the SSE read timeout is disabled. Add OpenSandboxOperationConfig.background_exec (default False): submit the command in the background, then issue short get_command_status / get_background_ command_logs polls instead of holding one stream open. No single request is long enough to be reaped, so it is robust across LB idle/duration limits. Also default command_retries 0->3 in the shipped config (recovers RST-family drops) and enable background_exec there since OpenSandbox exec always traverses the server proxy. Root cause confirmed via OpenSandbox issues #1277/#507/#96 and a mesh trace (sbx east-west NLB, 350s idle, no override). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Signed-off-by: Hemil Desai <hemild@nvidia.com>
Background exec polled at a fixed interval added that full interval of latency to every short command. Agents issue many sub-second commands (tmux keystrokes, pane captures), so a multi-second poll crawled the turn loop and left the GPU idle. Poll fast initially (background_poll_initial_s=0.25) and back off toward the cap, so short commands are detected almost immediately while long ones don't spam requests; frequent early polls also keep the connection warm. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Signed-off-by: Hemil Desai <hemild@nvidia.com>
… for cross-cluster LB paths - provider: disable_connection_pooling option — pass a no-keepalive httpx transport to the SDK so every request opens a fresh connection; an LB between client and server that silently reaps idle pooled connections (observed as CLOSE_WAIT zombies wedging exec) can never cause a reaped socket to be reused. Enabled in the shipped opensandbox.yaml. - provider: hard wall-clock cap around the whole exec dispatch (submit + poll + retries) as a backstop against pathological wedges. - config: foreground SSE exec is the default again (background_exec: false); background mode multiplies short cross-cluster requests and churns connections without fresh-connection transport. - harbor env: default_exec_timeout_s 14400 -> 300 (per-command bound, not a trial bound; a trial-length value lets one stuck exec hang the task until the trial deadline). TB2.1 config uses 600. - harbor config: nemo_model_server_timeout_sec 7200 -> 2400 so a serving stack that silently drops an in-flight request self-heals in <=40min. - harbor app: await the Ray ObjectRef directly instead of asyncio.to_thread(ray.get, ...) — the default executor's thread cap queued finished results behind long-blocking gets, delaying /run responses in waves. - tests: disable_connection_pooling transport, exec hard-cap timeout, uncapped-exec path. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Signed-off-by: Hemil Desai <hemild@nvidia.com>
Exec-path 500s with no server-side error are consistent with sandbox containers dying under load; roomier limits keep execd alive on memory/CPU-hungry tasks (also matches the resources used for the final benchmark runs). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Signed-off-by: Hemil Desai <hemild@nvidia.com>
Sandbox containers see the host's full core count (cgroup cpu limits don't shrink nproc), so build tools inside tasks fan out to e.g. 96 workers and get CFS-throttled. With cpu_pin_enabled, every Harbor exec is prefixed with a fail-open taskset pin to a random contiguous core block sized by the task's cpu count: nproc inside reports the real budget (tools right-size their parallelism) and co-resident sandboxes spread across different core blocks instead of stacking on cores 0..N-1. The tmux server Terminus-2 starts is launched via exec, so the whole agent session inherits the affinity; the verifier exec gets its own block. POSIX-sh compatible; unpinned when taskset is missing or the host has <= width cores. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Signed-off-by: Hemil Desai <hemild@nvidia.com>
hemildesai
marked this pull request as ready for review
July 27, 2026 14:40
…ers) Resolves the .gitignore conflict by keeping both sides' entries. SECURITY: EXPERIMENT_LOG.md was committed and pushed to this public repo in 6ac1c22, 369071e and 70cd889. It contains internal infrastructure identifiers (AWS account IDs, EKS cluster ARNs/names, internal mesh endpoints). This commit removes it from tracking and adds it to .gitignore alongside OPENSANDBOX_SSE_DROP_REPORT.md so it cannot be re-added. The local working copy is preserved. NOTE: removing the file at the branch tip does NOT purge it from git history; the blobs remain reachable from the three commits above and require a history rewrite to fully remove. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Signed-off-by: Hemil Desai <hemild@nvidia.com>
- Lint check: apply ruff format to the files this PR touches. - secrets-detector: PINNED_COMMIT is the git commit SHA the Terminal-Bench 2.1 dataset checkout is pinned to, not a credential. Marked with the detector's documented inline mitigation (pragma: allowlist secret). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Signed-off-by: Hemil Desai <hemild@nvidia.com>
run() now awaits the Ray ObjectRef directly instead of going through asyncio.to_thread(ray.get, ...), but _harbor_run_mocks still patched asyncio.to_thread, so the mocked trial dir was never returned: four TestApp::test_run_* cases saw an empty trajectory (reward 0.0 / empty output), and test_run_failed_execution passed only incidentally because awaiting a MagicMock raises. Mock .remote(...) to return a fresh awaitable per call instead, covering both the bare and .options(num_cpus=...) dispatch paths. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Signed-off-by: Hemil Desai <hemild@nvidia.com>
Reverts the direct `await future` on the Ray ObjectRef, restoring the original result-consumption path. app.py's remaining delta vs main is just the harbor_ray_task_num_cpus knob and its runner.options(...) dispatch. Also reverts the test mock that had been realigned to the awaitable path; _harbor_run_mocks patches asyncio.to_thread again, matching the code. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Signed-off-by: Hemil Desai <hemild@nvidia.com>
…cate verification
Harbor's verifier calls environment.exec() WITHOUT timeout_sec, so
default_exec_timeout_s -- not the task's declared [verifier] timeout_sec --
is what actually bounds verification. The previous defaults (300 in the
environment, 600 in the TB 2.1 config) were justified by reasoning only about
Terminus-2's short tmux plumbing, which missed the verifier (and the oracle
agent) entirely.
Impact: 87 of the 89 TB 2.1 tasks declare a [verifier] timeout_sec above 600
(median 900, max 12000), so verification was being killed mid-run and the task
scored 0 rather than erroring -- a silent correctness bug that understates
pass@1.
Measured with a gold-patch (oracle) run of all 89 tasks, which executes each
task's reference solution and should score 89/89:
600s -> 83/89 (compile-compcert killed at 602s; query-optimize's verifier
killed at 604s -> RewardFileNotFoundError)
1200s -> 83/89 (query-optimize fixed; compile-compcert still killed)
1800s -> 85/89 (both fixed)
12000s -> 85/89 (no further gain)
Longest single command observed: 1384s. 1800s is therefore the smallest value
that costs nothing, and is what both the env default and the TB config now use.
Adds a regression test asserting the default stays generous enough for
verifier budgets.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Signed-off-by: Hemil Desai <hemild@nvidia.com>
… runs
A gold-patch (oracle) run executes each task's reference solve.sh and should
score 89/89, which makes it the cleanest end-to-end check of the harness. Three
TB 2.1 tasks fail that check for reasons outside the harness -- the task images
are pinned in time while their unpinned dependencies and a dataset host are not:
build-cython-ext planarity 1.0.0 renamed the drawplanar attrs pyknotid 0.5.3
reads verbatim -> KeyError: 'pos'
mcmc-sampling-stan RcppParallel 6.0.0 switched to a CMake build; the image has
no cmake -> rstan never installs
caffe-cifar-10 the CIFAR-10 URL redirects to a host that TCP-RSTs some
networks; get_cifar10.sh has no error handling, so caffe
aborts on a missing file
Adds task_patches/ plus an opt-in --apply-task-patches flag. Off by default, so
the benchmark is prepared exactly as published.
Integrity: patches may touch '*/solution/solve.sh' and nothing else -- enforced
before git apply runs, since patching tests/ or task.toml would change what is
graded. solution/ is uploaded only by the oracle agent and never by a model
agent, so scored model runs are unaffected either way. A patch that does not
apply raises rather than being skipped: the checkout is pinned, so a failure
means the pin moved.
The CIFAR-10 mirror is md5-verified against the official tarball. README.md
records the evidence for each patch and when to drop it.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Signed-off-by: Hemil Desai <hemild@nvidia.com>
test_environment.py sat next to the module it covers; move it alongside the other harbor_agent suites in tests/. Collection is unchanged (114 tests) since the runner collects the whole server directory, and the test's imports are absolute, so this is purely a layout change. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Signed-off-by: Hemil Desai <hemild@nvidia.com>
Reverts 0a55fed. The patches only modified solution/solve.sh, which is uploaded by Harbor's OracleAgent and never by a model agent, so they could only ever affect gold-patch validation runs -- they cannot change a scored eval. That makes them dead weight in this PR. The underlying breakages are unaffected by this revert and still apply to model runs (a blocked dataset host on caffe-cifar-10; missing cmake on mcmc-sampling-stan; planarity drift on build-cython-ext); fixing those for scored runs would mean changing the task environment, which would change the benchmark rather than the reference solution. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Signed-off-by: Hemil Desai <hemild@nvidia.com>
hemildesai
marked this pull request as draft
July 28, 2026 19:36
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
Integrates the
nemo_gym.sandboxAPI into the harbor agent non-intrusively: a newNemoGymSandboxEnvironment(a HarborBaseEnvironment) executes Harbor trials inside sandboxes managed by any NeMo Gym sandbox provider (opensandbox, docker, daytona, apptainer, ecs_fargate). Selected purely via config — no changes to the harbor_agent execution path.custom_envs/nemo_gym_sandbox/environment.py— AsyncSandbox-backed backend: task.toml resources →SandboxSpec, tar-basedupload_dir/download_dirwith per-file fallback,bash -icexec wrapping (parity with Harbor's docker/daytona backends), always-terminate onstop(), opt-inallow_unenforced_internet_isolation,image_rewrites.configs/harbor_agent_opensandbox.yaml— Terminal-Bench 2.1 (89 tasks) with stock Terminus-2 through the opensandbox provider in server-proxy mode. Provider referenced viasandbox_provider: ${sandbox}(mini_swe_agent_2 style; include the shipped provider config with a second--config). Sampling/harness match the Qwen3.6-27B model card (temp 1.0 / top_p 0.95 / top_k 20, 3h agent timeout, 256K ctx / 80K max output). 30 GiB sandbox ephemeral storage to avoid kubelet evictions on disk-heavy tasks.prepare_terminal_bench_2_1.py+ checked-in 89-row benchmark input JSONL (pinnedharbor-framework/terminal-bench-2-1).harbor_ray_task_num_cpusconfig knob — Harbor jobs are I/O-bound Ray tasks; default 1 CPU/task caps concurrency at the driver CPU count, so a fractional value (0.25) enables true full-89 concurrency on a 24-CPU driver.extract_usagesetsinput_tokens_details.cache_write_tokens(required by openai ≥2.40; every live-model/run500'd without it).Result — Terminal-Bench 2.1, Qwen3.6-27B
Ran the full 89-task benchmark end-to-end: vLLM-served Qwen3.6-27B on 2 GPU replicas, gym/Ray driver, and sandboxes on a live OpenSandbox deployment reached over an istio ambient multi-cluster mesh (server-proxy mode, no public endpoint). Traces captured for all 89 tasks (rollouts + per-trial trajectory, asciinema recordings, verifier artifacts).
Reproduces the model-card Terminal-Bench number essentially exactly (within ~0.25 points). Caveats: TB 2.1 vs 2.0 (26 tasks fixed; same count/harness), single run vs 5-run average, and sandbox resources set to 4 vCPU / 16 GiB / 30 GiB.
Note: commits carry DCO sign-off; GPG signature to be added on amend (non-interactive session).
🤖 Generated with Claude Code
Update: cross-cluster hardening + 5-run measurement (in progress)
Follow-up commit hardens the opensandbox provider + harbor agent for sandboxes reached across a load-balanced/multi-cluster path, based on failure modes hit while running the benchmark end-to-end:
connection.disable_connection_pooling(provider, on by default in the shipped config): a load balancer between client and OpenSandbox server can silently reap idle pooled connections; the SDK then reuses a dead socket and exec hangs (observed as dozens of CLOSE_WAIT sockets wedging tasks, while fresh-connection requests completed in ~1s). With pooling disabled every request opens a fresh connection — no reaped socket is ever reused. Verified A/B on a live mesh: identical run with pooling on collapsed (8/89 tasks in 40 min, CLOSE_WAIT≈80); with the option on, 57/89 in 27 min with CLOSE_WAIT=0.TimeoutError(which Terminus-2 records as a command timeout) instead of hanging the task.— superseded: that reasoning accounted only for Terminus-2's tmux plumbing and missed Harbor's verifier, which also callsdefault_exec_timeout_s14400 → 300exec()withouttimeout_sec. The value is now 1800 in both the env default and the TB config — see the gold-patch section below for the measurement.nemo_model_server_timeout_sec7200 → 2400: lets a silently-dropped model call self-heal within 40 min instead of blocking a task for 2h.cpu_pin_enabled(opt-in, default false): pins every Harbor exec (tmux session + verifier) to a random contiguous core block sized by the task's cpu count, sonprocinside reports the real budget and co-resident sandboxes spread across core blocks. Fail-open (notaskset/small host → unpinned). Measured on a full TB 2.1 run under card conditions: no e2e-time or score delta vs baseline (58.4% / 3h13m / 12.4% timeouts, all within the 5-run baseline spread) — task wall time on this benchmark is dominated by model turns, not sandbox CPU contention. Kept for CPU-bound workloads.Serving guidance for Qwen3.6 (hybrid GDN attention) on vLLM, learned the hard way:
vllm/vllm-openai(v0.25.1 used here; multi-arch incl. arm64).Disable prefix caching (— superseded: the engine-core freezes initially attributed to prefix caching turned out to be the default (FlashInfer) GDN prefill kernel; with--no-enable-prefix-caching)--gdn-prefill-backend triton, prefix caching ON is stable (validated over a further 5-run cohort, zero freezes) and scores better — see the final card-parity section below.5-run pass@1 (final)
Two cohorts, differing only in the per-task agent timeout (the 4h budget compensates the per-turn re-prefill slowdown from running without prefix caching — see serving notes):
Serving notes for Qwen3.6 (hybrid GDN) on vLLM
Hard-won operational guidance from ~30h of serving under full-benchmark agentic load:
--gdn-prefill-backend triton— with the default (FlashInfer) GDN prefill kernel, the engine core froze roughly hourly under concurrent long-context load: requests parked, prompt/generation counters static, no errors, health endpoint green. Reproduced on two different vLLM builds, with prefix caching on and off, on fresh and long-lived engines. On the Triton/FLA kernel: zero freezes across ~16h of identical load. (An upstream issue with the full signature is worth filing.)--compilation-config '{"cudagraph_mode": "PIECEWISE"}'— attention ops run eagerly, outside graph replay.karpenter.sh/do-not-disrupt: "true"or equivalent) — multi-hour drivers are casualties of routine autoscaler bin-packing otherwise. The same applies to sandbox pods on autoscaled pools: one mid-eval node roll (triggered by an AMI-alias drift) killed ~12 in-flight sandboxes whose pods carried no disruption protection, surfacing as opaque proxy 500s.Follow-up: 4-replica scaling + SGLang A/B (5 runs each, 4h timeout, 8C/16G sandboxes)
--gdn-prefill-backend triton/healthgenerates a token and can exceed a 1s probe timeout, flapping healthy pods out of the Service; set probetimeoutSeconds≥ 15), vLLM with the Triton GDN kernel noted above. Scores are statistically indistinguishable; vLLM sustained ~40% more tokens/episode at lower timeout rates on this workload.Harness validation: gold-patch (oracle) run — 89/89
Ran all 89 TB 2.1 tasks with Harbor's
OracleAgent, which uploads each task's referencesolution/and runs it instead of generating with a model. This exercises the sandbox backend,upload_dir, exec and the verifier end-to-end with the model removed from the equation, so the expected score is a clean sweep.Result: 89/89 (reward 1.0 on every task), 22 min wall clock, zero agent timeouts.
Getting there surfaced one real bug in this integration, now fixed in this PR:
default_exec_timeout_swas silently truncating verification. Harbor's verifier callsenvironment.exec()withouttimeout_sec, so this default — not the task's declared[verifier] timeout_sec— is what bounds verification. The old values (300 in the environment, 600 in the TB config) were justified by reasoning only about Terminus-2's short tmux plumbing, which missed the verifier entirely. 87 of the 89 TB 2.1 tasks declare a verifier budget above 600s (median 900, max 12000), so verification was being killed mid-run and the task scored 0 instead of failing loudly.compile-compcertkilled at 602s;query-optimizeverifier killed at 604s →RewardFileNotFoundErrorquery-optimizefixed;compile-compcertstill killedLongest single command observed was 1384s, so 1800s is the smallest value that costs nothing — now the default in both the environment and this config. Because this bug suppressed slow-verifying tasks, previously reported pass@1 numbers are conservative.
The remaining 4 gold-patch failures were not harness issues and are deliberately not fixed here — three were upstream drift (a dataset host that refuses this network's egress;
RcppParallel 6.0.0requiringcmake, published 4 days before the run;planarity 1.0.0renaming attributespyknotidreads, published 4 weeks before) and one was macOS._*sidecar files in our local dataset copy. Patching those reference solutions would only change an oracle run —solution/is never uploaded for a model agent — so it cannot affect a scored eval, and fixing them for model runs would mean altering the task environment rather than the solution. Worth noting for anyone reproducing: sandbox resources were not implicated — the config's uniform 8 CPU / 16 GiB / 30 GiB is a strict superset of what all 89 task.toml files declare (max 4 CPU / 8 GiB / 10 GiB).Final: card-parity cohort (prefix cache ON + Triton GDN kernel, 3h agent timeout — model-card conditions)
🤖 Generated with Claude Code