Build SWE-bench instance images on aarch64 for Isambard - #37
Conversation
SWE-bench grades a patch by running a repository's own tests inside a
per-instance image, and the published images are x86_64 only: 4,503 repos under
the `swebench` Docker Hub namespace, all tagged `sweb.eval.x86_64.*`, none for
arm64. Isambard-AI is aarch64 and its login node has no qemu handler registered
-- /proc/sys/fs/binfmt_misc holds only `register` and `status`, and an amd64
image dies with `Exec format error: '/sbin/ldconfig'` -- so an instance image
has to be built natively or not at all.
Four things stop a task Dockerfile from SWE-bench/swe-bench-tasks building here.
The platform flag and the Miniconda installer URL are the obvious two. The third
is the conda pins in the embedded environment.yml: every dependency carries a
build string, `python=3.11.10=he870216_0`, and those hashes exist only for
linux-64. Keeping the version and dropping the build string lets conda solve the
same versions for linux-aarch64; six packages whose linux-64 names have no
aarch64 build at all are dropped, and conda pulls the aarch64 equivalents in as
dependencies. The pip section is left alone, since its pins are `==` and PyPI
resolves them per platform.
The fourth was not expected. The heredoc RUN blocks are where the environment is
created and the repository cloned, and buildah here cannot run them:
STEP 11/14: RUN <<EOF_a88cf31974aa
invalid mount ... /dev/pipes/buildahheredoc...: bind mounts cannot have any
filesystem-specific options applied
A heredoc whose body opens with a shebang is mounted as a file rather than piped
to the shell, and runc refuses the SELinux options buildah puts on that mount.
Two cheaper fixes failed: --isolation=chroot exits 127, and a SHELL directive is
ignored for heredocs, so `set -euo pipefail` still reaches /bin/sh and dies on
`Illegal option -o pipefail`. A heredoc with no shebang builds, so those are left
where they are. The rest are extracted to scripts named after their own
delimiter and replaced with a COPY and a RUN bash; a nested heredoc inside the
body survives because the body is written out unchanged.
Two smaller things the build taught us are in the script: podman-hpc resolves -f
inside the build context, so the generated Dockerfile is written there, and the
task directory is copied to a context of its own because a task Dockerfile may
COPY from it.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
A login-node build cannot be detached. `loginctl enable-linger` is denied for user accounts, so logind kills every user process when the last session closes: a `setsid --fork` build died mid-apt, and so did a tmux session started from the brics/tmux module. Compute nodes turned out to have outbound network and podman-hpc, and `migrate` writes to $SCRATCH, so a batch job does the build and survives whoever submitted it. django__django-16485 built in 6m23s and is 2.56 GB. The doc leads with the check rather than the build, because the aarch64 conda solve gives the same package versions and not the same builds, so an instance is only usable once its own tests are shown to agree with the benchmark: FAIL_TO_PASS failing before the gold patch and passing after, PASS_TO_PASS passing throughout. On this instance the unpatched run ends in the ValueError the issue describes and the patched run ends OK, with all nine PASS_TO_PASS tests passing both times. Also recorded: the login node resets ssh connections opened every 30 seconds, so a poll loop needs one multiplexed connection and minutes between checks. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Four of five repositories build: django, sympy, pytest and requests, about four minutes and 2.3-2.6 GB each. pydata__xarray-7229 does not, and will not - its environment pins cdms2, cdtime and libcdms, the CDAT stack, which has no linux-aarch64 build in any channel. django alone is 231 of the 500 Verified instances, so the hit rate is not the constraint it looks like. The other thing the checks turned up: eval.sh runs whole test files, so its summary line is not the verdict. psf__requests-2931 reports 81 errors in both runs - tests that want a live httpbin - and none of them are in FAIL_TO_PASS or PASS_TO_PASS. Read against tests.json, all 84 PASS_TO_PASS pass and the one FAIL_TO_PASS test flips, so the instance is faithful despite a summary that reads like a failure. The env's log parser has to compare test names, not totals. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Five repositories tried: four build, and all four are faithful instancesEach built as an sbatch job, in about four minutes, 2.3–2.6 GB, all migrated
In every case the unpatched failure is the issue the instance is about, not a
That is the failure shape to expect — a dependency never built for this One finding that changes the env's log parser
Now in |
What this is
The first piece of adding SWE-bench: getting an instance image onto Isambard at
all, and knowing it is a faithful instance. Nothing in
mas/ortasks/envs/yet — the environment, loader and prompts follow, and the plan for them is at
the end.
Why the published images cannot be used
SWE-bench grades a patch by running a repository's own tests inside a
per-instance image. Docker Hub carries 4,503 of those under the
swebenchnamespace, every one tagged
sweb.eval.x86_64.*; a search forarm64returnsnothing. Isambard-AI is aarch64 and has no qemu handler registered:
So the images are built natively, and the task Dockerfiles published in
SWE-bench/swe-bench-tasksdo not build on aarch64 as they stand.Four things had to change, and one was not expected
FROM --platform=linux/amd64— the flag.The Miniconda installer,
Linux-x86_64.shtoLinux-aarch64.sh.The conda pins in the embedded
environment.yml. Every dependency carries abuild string —
python=3.11.10=he870216_0— and those hashes exist onlyfor
linux-64. The version is kept and the build string dropped so condasolves the same versions for
linux-aarch64; six packages whose linux-64names have no aarch64 build at all are dropped, and conda pulls the aarch64
equivalents in as dependencies. The
pip:section is left alone, since itspins are
==and PyPI resolves them per platform.The heredoc
RUNblocks — where the conda environment is created and therepository cloned. buildah here cannot run them:
A heredoc whose body opens with a shebang is mounted as a file rather than
piped to the shell, and runc refuses the SELinux options buildah puts on that
mount. Two cheaper fixes failed:
--isolation=chrootexits 127, and aSHELL ["/bin/bash", "-c"]directive is ignored for heredocs, soset -euo pipefailstill reaches/bin/shand dies onIllegal option -o pipefail. A heredoc with no shebang builds fine, so those are left alone.The rest are extracted to scripts named after their own delimiter and
replaced with a
COPYand aRUN bash; a nested heredoc inside the body —the
cat <<'EOF' > environment.ymlcarrying the whole conda environment —survives because the body is written out unchanged.
The build has to be a Slurm job
loginctl enable-lingeris denied for user accounts, so logind kills every userprocess when the last session closes. A
setsid --forkbuild died mid-apt, andso did a tmux session from the
brics/tmuxmodule. Compute nodes have outboundnetwork (
pypi:200fromnid010975) andpodman-hpc, andmigratewrites to$SCRATCH, soswebench_build.sbatchdoes the whole thing and outlives thesession that submitted it.
Verified
django__django-16485built in 6m23s, is 2.56 GB, and showsR/O = trueso compute nodes can read it. The instance reproduces the benchmark's verdicts —
which matters because the aarch64 solve gives the same package versions, not
the same builds:
test_zero_valuesERROR: ValueError: valid range for prec is [1, MAX_PREC]ok—Ran 10 tests … OKThat ValueError is the issue the instance is about, so the environment is
reproducing the bug rather than a build artefact of its own.
Files
containers/swebench_arm64.py— the rewriter, andRewritten.write, whichlays out a build context.
containers/build_swebench_image.sh— copies the task directory to its owncontext, rewrites the Dockerfile into it, builds with
podman-hpc --layers,migrates the image.
containers/swebench_build.sbatch— that, as a job.containers/isambard.md— setup, the build, the faithful-instance check, andthe limits.
tasks/tests/test_swebench_arm64.py— ten tests over the rewriter, eachmutation-checked.
Two smaller things the build taught us, both now in the script and the doc:
podman-hpcresolves-finside the build context, so the generated Dockerfileis written there; and the login node's system python is 3.6, so the rewriter
needs a uv-provided 3.12.
What comes next in this PR
The dataset itself, in dependency order: a
data/swebench/verified.jsonlmanifest naming instances (the task repo clone holds the statements and patches,
as
rom_dirholds Jericho's roms), a loader,SWEBenchEnv/SWEBenchRecorderregistered in
ENVS/RECORDERS, a config block, a prompt module and anintrinsicmemory-swebenchtemplate.Two findings already shape that work:
clean_action_line. Measured against the currentcleaner,
ls tests/*.pybecomesls tests/.py,python -c "print(1)" > /tmp/outloses its redirection, a fenced./tests/runtests.py xloses itsleading dot,
sed -i "s/#comment//"loses the#, and a heredoc becomescat EOF /tmp/p.py._undecoratetreats<,>,*,#, backticks andleading dots as decoration, which is right for the other datasets and fatal
for a bash action space. It needs its own
process_action, with a test perform above.
instance has one FAIL_TO_PASS test (mean 3.0, max 438), so a fraction would be
binary in practice. A non-empty diff, the test command running, the
FAIL_TO_PASS fraction, PASS_TO_PASS preserved as a gate,
resolvedfor all ofit — with
donereserved for resolved, as Jericho reserves it for victory.The experiment the dataset is for is not a leaderboard number: the same
instances run repo-major, where a cross-task memory can accumulate knowledge of
one repository, against interleaved, where no two consecutive tasks share a
repository. The empty-memory arm is flat across both orders by construction,
which makes it the control.