D3 · Pinned toolchain, bootstrap and doctor¶
Done 2026-10-27 · one pinned list, one install script, one doctor · doctor passes in a cloud session (sind and kind skipped: cgroup v1) and in CI with every layer · cold bootstrap 25 s here, 55 s in CI · 4 questions for Thomas

The timings and ticks come from a cloud session and from GitHub Actions run 40523940610, both on 2026-10-27. The later run 40526621317 gave the same result (bootstrap 55 s). The PNGs were rendered with the local Virgil font, which is not committed (inbox #1).
What changed¶
dev/versions.env: one exact pin per tool, each with its source. Every binary and tarball has a sha256, and every image the doctor runs has a digest. It also holds the on-demand pins (mermaid-cli, mkdocs, mkdocs-material, depictio), which bootstrap never installs. The values are listed in §1 and in the file.- Two hash locks:
dev/snakemake.lock.txt: Snakemake 9.27.1, the SLURM and Kubernetes executor plugins and their 72 packages, each with--hash=sha256:;dev/npm/package.jsonandpackage-lock.json: Playwright 1.63.0 with integrity hashes.
dev/bootstrap.sh [--ci]: installs the toolchain in four parallel jobs, each with its own log, and is idempotent. Downloads are checked before they are moved into place. With--ciit also startsdockerdwhen nothing else will, and pre-pulls the doctor's images by digest, frommirror.gcr.iofirst.-
dev/doctor.sh:- checks each tool's version against the pin;
- runs one smoke test per layer: docker, sind, kind, nextflow, snakemake and playwright;
- exits 1 on the first failure, printing
doctor: FAILED: <tool>.
sind and kind are skipped, with the reason, where Docker runs on cgroup v1.
SPECTIO_DOCTOR_STRICT=1turns a skip into a failure. -dev/lib.sh: the paths and the Docker helpers shared by both scripts. -.github/workflows/dev-env.yml: bootstrap timed, then the doctor in strict mode onubuntu-24.04; the output goes to the job summary. It runs on pushes that touchdev/, weekly, and by hand. - Specdev-environment-and-testing.md: - §1: the table, a new "Bootstrap" paragraph and the rewritten "Checks"; - §2: the cloud environment settings, with the network list, environment variables and setup script; - §4.2: answered; - §7.2: a note thatversions.envnow exists; - §11: thedev-env.ymlrow. - Other files: - decision log, "D3: pinned toolchain, bootstrap and doctor"; - review record; - inbox entries #41 to #44.
Why¶
The dev spec asked for one bootstrap script that runs in the cloud setup script, in CI and on a laptop, and for a doctor that proves each layer works. Spike D2 measured what a cloud session can run. Its findings set the design:
- sind and kind need cgroup v2, so they are proven in CI;
dockerdis not running at session start;- Docker Hub rate-limits anonymous pulls;
releases/latestURLs are refused.
Prompt: next-phase.md · D3. The step's own choices are in the decision log.
Evidence¶
In this cloud session (Docker on cgroup v1). This is the exit check, run after the last code change (51c759a):
$ bash dev/doctor.sh
spectio doctor: <repo>, pins from dev/versions.env
--- versions
ok docker 29.9.2 (cgroup v1)
ok java 21.0.12.1
ok uv 0.11.32
ok python 3.13.14
ok sqlite 3.53.1
ok node 24.22.1
ok playwright 1.63.0
ok nextflow 26.10.4
ok nextflow-25.10.9 25.10.9
ok nextflow-25.04.8 25.04.8
ok snakemake 9.27.1
ok smk-slurm 2.8.0
ok smk-kubernetes 0.5.1
ok sind 0.15.0
ok kind 0.33.0
ok kubectl 1.37.1
ok helm 4.3.1
--- smoke tests
ok docker docker run hello-world (0s)
skip sind Docker on cgroup v1; sind and kind need cgroup v2 (spike D2), so they run in CI
skip kind Docker on cgroup v1; sind and kind need cgroup v2 (spike D2), so they run in CI
ok nextflow synthetic pipeline, test,local (14s)
ok snakemake dry run of the synthetic Snakefile (2s)
ok playwright chromium 153.0.8010.12 opened about:blank (0s)
doctor: all layers passed (2 skipped)
$ echo $?
0
Bootstrap timings in this session:
| Run | Time |
|---|---|
first cold --ci run |
25 s (sind and kind images not pulled on cgroup v1) |
| cold again, with uv's and npm's caches warm | 8 s |
| second run, nothing to do | 1 s, bootstrap: done in 1s |
Deliberately broken layers (exit 1, naming the tool):
$ PLAYWRIGHT_BROWSERS_PATH=/nonexistent bash dev/doctor.sh # no Chromium
FAIL playwright Chromium did not open about:blank
doctor: FAILED: playwright # exit 1
$ SPECTIO_DOCTOR_STRICT=1 bash dev/doctor.sh # a skip is a failure
FAIL sind skipped, and SPECTIO_DOCTOR_STRICT=1: Docker on cgroup v1; sind and kind need cgroup v2 (spike D2), so they run in CI
doctor: FAILED: sind # exit 1
A tampered lock is refused. Two wrong hashes for annotated-types in a copy of dev/snakemake.lock.txt:
╰─▶ Hash mismatch for `annotated-types==0.8.0`
Expected:
sha256:0000000000000000000000000000000000000000000000000000000000000000
sha256:1111111111111111111111111111111111111111111111111111111111111111
Computed:
sha256:f072f4d804ea359e4eaf198b1af7a8b0943881a87f31bb764f8bf219bb9419e0
uv pip sync exit=1
In GitHub Actions, run 40526621317 on 51c759a (ubuntu-24.04, cgroup v2, strict). It passed.
bootstrap: done in 55s. Every job reported ok: the four installs and the five images.
spectio doctor: /home/runner/work/spectio/spectio, pins from dev/versions.env
--- versions
ok docker 28.0.4 (cgroup v2)
ok java 21.0.13
ok uv 0.11.32
ok python 3.13.14
ok sqlite 3.53.1
ok node 24.22.1
ok playwright 1.63.0
ok nextflow 26.10.4
ok nextflow-25.10.9 25.10.9
ok nextflow-25.04.8 25.04.8
ok snakemake 9.27.1
ok smk-slurm 2.8.0
ok smk-kubernetes 0.5.1
ok sind 0.15.0
ok kind 0.33.0
ok kubectl 1.37.1
ok helm 4.3.1
--- smoke tests
ok docker docker run hello-world (3s)
ok sind cluster up, sbatch --wait --wrap hostname ran on worker-0 (15s)
ok kind cluster up, Job completed (39s)
ok nextflow synthetic pipeline, test,local (25s)
ok snakemake dry run of the synthetic Snakefile (2s)
ok playwright chromium 153.0.8010.12 opened about:blank (2s)
doctor: all layers passed (0 skipped)
The first green run before the review fixes was 40523940610, with bootstrap at 56 s.
Static checks: shellcheck -x dev/bootstrap.sh dev/doctor.sh dev/lib.sh is clean.
review-code:
CODE step=D3 review=approve b/M/m=0/0/5 security=approve b/M/m=0/1/4 rounds=2 simplify=changed -1/+0 tests=pass
code-reviewer: approve (0/0/4), then approve (0/0/1).security-reviewer: changes (0/1/3), then approve (0/0/1). The major, SEC-D3-01, was that the dependency trees were not locked; both are now hash-locked.code-simplifier: removed one orphan export; tests pass.- Findings and their status:
reviews/code/D3.md.
Figures and media¶
- The
dev-toolchainsketch, light and dark, at the top of this page:python3 tools/diagrams/dev_toolchain.py. - The
dev-doctorcast, a real run of the scripts in this session:python3 tools/media/cast.py tools/media/casts/dev-doctor.yaml. It shows three steps:- a second bootstrap is a no-op;
- the doctor passes, with sind and kind skipped;
- a broken Playwright layer exits 1 naming the tool.
Findings and open questions¶
- For Thomas:
- #43: enter the §2 settings in the cloud environment (network list, environment variables, setup script). Until then each session runs
bash dev/bootstrap.sh --ciitself. - #41: switch the Codespaces
setup.shtodev/bootstrap.sh. - #42: move the synthetic matrix and the required check names to Nextflow 26.10.4.
- #44 (assumed): Linux x86_64 only, and Node 24 now ahead of the image's Node 22 on
PATH.
- #43: enter the §2 settings in the cloud environment (network list, environment variables, setup script). Until then each session runs
- Not hashed by us (§1): the uv-managed CPython [to be validated] and Playwright's Chromium where it has to be downloaded (CI).
- Accepted, not changed: the doctor starts checked images by tag, not by digest (SEC-D3-03), so that mirror pulls keep working.
- For the D2 re-probe:
- narrow the wildcard hosts of the §2 list (SEC-D3-04);
- check where Nextflow plugin downloads redirect;
- check whether the repository is cloned before the setup script runs.
- D5's request, partly done: uv, mkdocs, mkdocs-material and Playwright are now pinned in
versions.env. The dev-container image and its features are not; #41 covers them.