Repository navigation
Conversation
A SWE-smith task branch stores the injected bug as a "Bug Patch" commit and the hidden tests' removal as "Remove F2P Tests". The image's /testbed is a full clone of the mirror, so the copied workspace also has every task branch as a remote ref (308 for Suor/funcy). An agent can run `git log -p` or `git show origin/<task>~1`, reverse the bug patch, and pass the hidden tests without solving anything; reverting it on a real task took tests/test_colls.py from 2 failed to 50 passed. After checkout, keep only the current tree as a single orphan commit, delete every other ref and the remotes, expire the reflog and prune unreachable objects. base_commit is taken after this, and the tree is unchanged, so the patch sent to grading is the same. Same class of fix as OpenHands#422 (commit0 shallow clone). Co-authored-by: openhands <openhands@all-hands.dev> Assisted-by: Claude (Anthropic)
Stripping the workspace copy is not enough: the image's /testbed is the mirror clone itself. Its working tree is the clean, pre-bug code, its .git holds every task branch, and the repo is installed in editable mode from it. Checked in swebench/swesmith.x86_64.suor_1776_funcy.207a7810 as uid 10001 (the agent-server user): /testbed is root:root 755 and readable, colls.py still has the original line, and after the safe.directory hint git lists 309 branches including the task's bug patch. The agent user cannot change /testbed, so this needs an image layer. - Dockerfile.testbed-access: chown /testbed's directories (not files) to the agent user, so it can unlink entries; the layer is 0 B. - build_images.py wraps every built agent-server image with it via post_build_fn, like the SWE-bench docutils wrapper; run_infer.py does the same for SKIP_BUILD=0 local builds. - run_infer.py replaces /testbed's contents with the stripped workspace after the history strip. The editable install then imports the task code. An image without the layer fails here loudly instead of leaking. Co-authored-by: openhands <openhands@all-hands.dev> Assisted-by: Claude (Anthropic)
midat-fx
marked this pull request as ready for review
October 24, 2026 15:30
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.
HUMAN:
I checked this by hand in the funcy image:
git branch -ashowed 309 taskbranches, and one
git showprinted the injected bug. I read the diffbefore opening this PR.
AGENT:
Code and description drafted with Claude Code.
Why
SWE-smith tasks are commits on a per-task branch of the mirror repo:
Bug Patchinjects the bug, andRemove F2P Testsdeletes the tests that grade it. The image's/testbedis a full clone of the mirror, checked out at the clean commit.run_infer.pycopies it into/workspace/<repo>with its.git, then fetches and checks out the task branch. Before writing any code, the agent can recover the fix three ways:git log -p,git show HEAD~1(the injected bug, often with comments like# Subtle swap: ...),git show HEAD(the removed hidden tests)./testbed:git branch -rlists every task branch of the repo (308 for Suor/funcy), each with its own bug patch./testbed, without git: the working tree is the original, pre-bug code. The repo is also installed in editable mode from/testbed, so tracebacks point there.Checked in
swebench/swesmith.x86_64.suor_1776_funcy.207a7810, running as uid 10001, the agent-server user:Reverting the bug patch passes the hidden tests:
tests/test_colls.pygoes from 2 failed / 48 passed to 50 passed. Results and training trajectories can come from copying history instead of fixing the bug. This is the same class of hole as #421 / #422 (commit0 git history).Summary
strip_git_history_command()runs after the task checkout. It turns the workspace tree into a single orphan commit, deletes every other ref and the remotes, dropsFETCH_HEAD/ORIG_HEAD, expires the reflog and runsgit gc --prune=now.base_commitis read afterwards, and the tree is unchanged, so the patch sent to grading is the same.replace_testbed_command()runs next:find /testbed -mindepth 1 -delete && cp -a <workspace>/. /testbed/./testbedbecomes the stripped task tree, and the editable install imports the task code instead of the clean copy.benchmarks/swesmith/Dockerfile.testbed-access(find /testbed -type d -exec chown openhands:openhands {} +). The agent user cannot change root-owned/testbed, and unlinking a file only needs write access to its directory. Chowning directories only keeps the layer at 0 B.build_images.pyapplies the layer to every built agent-server image throughpost_build_fn, the same pattern as the SWE-bench docutils wrapper.run_infer.pydoes the same forSKIP_BUILD=0local builds. With an image built without the layer, the/testbedstep fails with an explicit "rebuild the SWE-smith images" assertion instead of running a leaky task.The task commit is still fetched from the mirror URL at runtime, not from
/testbed, so the fetch path is unchanged.Issue Number
None. Related: #421 (same problem in commit0, fixed by #422).
How to Test
The tests build a temporary mirror with
mainand two task branches, a clean full clone standing in for/testbed, and a workspace prepared the wayrun_infer.pydoes. They check:Bug Patchobject gone;git diff <base> HEAD;/testbedmatches the workspace: buggy code, no removed tests, one commit, no remotes;wrap_testbed_accessbuildsDockerfile.testbed-accesson top of each built image tag withSDK_IMAGEset.In Docker, linux/amd64: the real funcy image, plus a stand-in for the agent-server layers (user 10001 owning
/workspace), plus the realDockerfile.testbed-access. The runtime commands ran as uid 10001 against the real mirror:docker historyshows the added layer as 0 B.Notes
build-swesmith-images.yml/build_images.py). Image tags are keyed by SDK SHA, so an image built before this change keeps its tag. Until it is rebuilt, the new/testbedstep fails it loudly./testbed; it only chowns/workspace. That is why a stand-in replaced those layers in the Docker check.git gcand the second copy add some seconds per instance on large repos.