Development record
ci.yml's three go-version pins are coupled to nothing: TestSelfScaffoldParity gates release.yml and auto-release.yml against scaffold substitutions but no test reads ci.yml, so the d594511 drift where CI scanned green on a newer toolchain than the release build recurs silently on the next bump
ci.yml's three go-version pins are coupled to nothing: TestSelfScaffoldParity gates release.yml and auto-release.yml against scaffold substitutions but no test reads ci.yml, so the d594511 drift where CI scanned green on a newer toolchain than the release build recurs silently on the next bump
Evidence
TestSelfScaffoldParitybuilds its case table fromReleaseYMLPath/AutoReleaseYMLPathonly (internal/core/launch/scaffold/scaffold_test.go:31-54); nothing reads.github/workflows/ci.yml, whose threego-versionpins (ci.yml:217,355,390) float free ofAbcdSubstitutions().GoVersion(substitutions.go:27).- The drift is not hypothetical: between d594511 and 8b70d70 (2026-08-19) CI scanned green on 1.25.13 while release binaries built on 1.25.12 with four unpatched stdlib CVEs. iss-289 (resolved) records that incident and its substitutions fix; this residual — no coupling, so the identical divergence recurs silently — survived it. iss-329 (open) wants the 1.26 move and assumes lockstep exists.
- Refuter verdict: CONFIRMED substantive. Fix: a test in the scaffold package asserting every
go-version:in.github/workflows/*.ymlequalsAbcdSubstitutions().GoVersion.
Correction, 2026-09-29
The resolution was true when it was written, and the test it names is not in
the tree. df377ee6 ("fix: the format gate is pinned everywhere it runs, not
only in ci.yml") removed TestWorkflowGoVersionsMatchSubstitutions together
with the literal go-version: pins it coupled: every setup-go step reads
go-version-file: go.mod, which TestEverySetupGoResolvesTheToolchainFromGoMod
in internal/core/launch/scaffold/goversion_lockstep_test.go pins
(iss-2609090951291799). The drift this record names cannot recur in that form,
since no workflow carries a Go version of its own.
Frontmatter
| id | iss-354 |
|---|---|
| type | issue |
| lifecycle | resolved |
| severity | minor |
.abcd/work/issues/resolved/iss-354-ci-yml-s-three-go-version-pins-are.md
open on GitHub ↗ · commit history ↗
mentions5
- dcn-26082000000000022026-08-20 — bughunt round 2 (branch bughunt-b/round-2): captured the round-2 records (iss-346..iss-357, with the two colliding ids re-minted as iss-358/iss-359), fixed
- iss-289The release workflow builds on an unpinned Go minor: scaffold substitutions set GoVersion 1.25, which resolved to 1.25.12 on runners today — the toolchain with four stdlib vulnerabilities fixed in 1.25.13 that govulncheck flagged in ci. ci.yml was pinned to 1.25.13 the same day but release.yml renders from the template, so shipped binaries build on whatever 1.25.x the runner resolves. The v0.6.0 tag predates any fix and cannot be retro-patched (anti-tag-move); the pin lands for the next cut
- iss-329Go 1.25 has left the support window: go.dev lists 1.27.0 and 1.26.7 as the stable lines, so 1.25 receives no further security patches and the pinned 1.25.13 is the line's end. The toolchain (go.mod go directive, ci and release pins, scaffold substitutions) should move to the older supported line, 1.26.x, as its own change with the parity test in lockstep
- iss-2609090951291799The format gate exists because two gofmt versions judge a tree differently, and it goes to some trouble to derive the toolchain version from the go directive in go.mod so that one declaration governs. The workflows then declare it again: nine setup-go steps across four workflow files pin the version as a literal string, four of them in the CI workflow and the rest in the release, site and screenshot workflows. The action already accepts a reference to the go.mod file in place of a literal, which is the form that removes the second spelling. Verified by grepping the workflow tree. It matters in exactly the direction the format gate was built for: bump the go directive and forget one of the nine, and CI compiles and tests on the old toolchain while the format gate resolves and fetches the new one, so the gate and the lane disagree about which gofmt is correct, reintroducing at the CI boundary the skew the gate refuses to allow locally. Fix direction: replace every literal pin with the go.mod file reference so the go directive is the only place the version is written. Detector: a change to the go directive in go.mod must change which toolchain every workflow job runs, with no workflow edit.
- iss-2609261034583909Eight resolved records name, in their resolution notes, a test no longer in the tree; each was true when written, and RS006 reads only records entering a terminal folder, so none is refused, but a reader following the note finds nothing. Verified at b7646ff1, with the commit that removed each test: iss-184 names TestTokenizeRejectsUnterminatedHeredoc (16d50feb renamed it TestTokenizeFlagsUnterminatedHeredoc and changed the behaviour: an unterminated heredoc is a verdict, not ErrUnparsableCommand, so the note's part (2) is stale too); iss-2608301808193750 names TestIsAbsentValueIsASpellingTestNotANullTest (3eb4b549 replaced the spelling predicate with a class-based one, TestAbsenceIsDecidedByClassNotBySpelling, so the note's 'deliberately NOT widened' is superseded); iss-2608311632382737 names TestPreflightRunsBothEvalLanes (the test is TestPreflightRunsEveryTaggedEvalLane, which derives the lanes from the Makefile; the one clean rename of the eight); iss-2608311632439831 names TestComparativeRefusesToAssemble (3b62c967 made the comparative position assemble from the widening run's items, so the refusal the note describes no longer exists); iss-2609012039117381 names TestBinaryHooksFallBackToAPathBinary and iss-275 names TestGuardShimFallsBackToPATH (c637a734 removed the PATH rung those tests pinned: the shims run only the abcd the machine recorded); iss-354 names TestWorkflowGoVersionsMatchSubstitutions (removed in df377ee6, replaced by TestEverySetupGoResolvesTheToolchainFromGoMod under iss-2609090951291799); iss-2609090951291799 names TestWorkflowGoVersionsMatchSubstitutions only to say it was replaced, which stays accurate. Six of the eight need an amendment note stating what superseded the test and the claim, not a rename, so the set is not a mechanical fix.