ESLint, pinned by package.json and yarn.lock, runs in a new js-lint
stage of the Dockerfile on the pinned node 24 LTS image. It starts from
a js-deps stage that installs ESLint and stays cached until those two
files change. script/lint builds js-lint after the Go lint, and the
build stage depends on it, so make check and the image build both fail
on a violation. eslint.config.mjs turns on the styleguide's checkable
rules: no-var and prefer-const.
Model: opus-5-5
golangci-lint no longer runs on the host. script/lint builds
Dockerfile.lint, which copies the repo into the digest-pinned linter
image, so the container holds only this repo and the cross-worktree
cache contamination of #106 becomes structurally impossible rather than
filtered after the fact. Five workers hit that contamination in one
evening, in both directions.
Three properties are load-bearing. --no-cache-filter=lint forces the
lint stage to re-execute while deps keeps its cache, because a cached
build lints nothing in 0.27s and exits 0. script/lint does not trust
that flag, since Docker silently ignores an unmatched stage name: it
asserts golangci-lint's own summary line appears, so no summary means no
lint whatever the exit code says. And both lint steps run
--network=none, which enforces rather than assumes that config verify
does not fetch its schema — verify is kept, because golangci-lint run
silently ignores unrecognized config keys and it is the only thing that
catches a typo that disables a setting.
Independently reviewed four times. Three passed the behaviour; the
remaining rounds were a README merge against #151, whose premise was
that the file contains no false statements. Six statements this change
falsified were found and corrected across those rounds — the last
reviewer re-derived every countable claim against the tree rather than
reading for plausibility, and found no seventh.
Supersedes #106.