golangci-lint is no longer invoked on the host anywhere in the repo.
Dockerfile.lint pins golangci/golangci-lint:v2.12.2 by digest and runs
the linter as a build step, so a successful build IS a clean lint, and
`make lint` becomes a thin shim over script/lint. This removes the host
linter install that produced a false green here, where a branch that was
genuinely red with a goconst finding reported "0 issues" off the shared
host cache; a container per run has its own cache and lock.
Two deliberate divergences from the sneak/homoicon reference shape:
- Two stages rather than one. A cached `deps` stage holds
`go mod download`, then `FROM deps AS lint` carries the source copy
and the lint run, and script/lint builds with
`--no-cache-filter=lint`. Caching of the lint result is explicitly
waived (a cached build lints nothing), and splitting the stages means
busting the lint layer does not re-fetch the module cache over the
network on every run.
- No `golangci-lint config verify` step. It resolves its JSON schema
over a live, unpinned HTTPS call: an unpinned network input inside
the one step whose purpose is a pinned, reproducible gate, and a
schema-host outage would surface as a red build. `golangci-lint run`
already fails on a malformed config. The reason is recorded in a
comment in Dockerfile.lint.
.dockerignore excludes .git only; the lint reads the Go sources,
go.mod/go.sum and .golangci.yml, none of which come from there.
The TODO.md scaffold-exemption note is narrowed rather than dropped:
Dockerfile.lint and script/lint are now permitted and required, while CI
config, REPO_POLICIES.md, an application Dockerfile and any other
script/ entrypoint still are not.
Verified, since a green docker build is the classic false green: two
consecutive script/lint runs on an unchanged tree each showed the
`golangci-lint run` layer executing (9.8s and 7.9s, both "0 issues.")
while the deps layers reported CACHED; a deliberate indent-error-flow
violation failed the build naming that finding and the unused one, and a
revert went clean again. `make check` green.
The test: target was a bare `go test $(GO_PKGS)`, diverging from the
mandated shape in four ways: no -timeout 30s, no -race, no -cover, and no
conditional verbose rerun. It now runs
go test -timeout 30s -race -cover $(GO_PKGS)
and, on failure, reruns with -v and then exits 1 — so the build still
fails even if a flaky test happens to pass on the second attempt. The
repo's existing $(GO_PKGS) variable is kept rather than hardcoding ./...,
and the recipe is @-prefixed so the rerun banner is the only noise.
The substance here is -race, not the Makefile edit: this is the first
time the suite has run under the race detector. It is clean, across five
consecutive uncached runs, including the tcell terminal layer and the
os.Exit-path playthrough tests that were the suspected risk.
Timing against the 20-second budget: 5.1s cold (including the race
build), ~2.3s warm. The failure path was exercised with a throwaway
failing test to confirm the verbose rerun fires and make exits non-zero.
Build tooling only; no game behavior change. .golangci.yml is untouched.
Adds a minimal Makefile wrapping the toolchain the way sneak's other
repos expose it:
- fmt gofmt -w plus prettier (4-space tabs, proseWrap: always)
- fmt-check fail if any Go or Markdown file is unformatted
- lint golangci-lint run ./...
- test go test ./...
- check fmt-check + lint + test (the local pre-commit gate)
Running make fmt normalizes the four existing Markdown docs to the
shared prettier style (80-column proseWrap: always, aligned tables) —
a one-time reflow with no content change. The repo remains exempt from
the rest of the policy scaffold (no Dockerfile, CI, or REPO_POLICIES).