Owner ruling, sneak 2026-08-09: every lint run happens inside a Docker container, invoked through the script/ entrypoint. Docker is always available. Linting runs independently and does not need a cache. He has directed a PR for every repo not already set up this way.
Reference implementation is sneak/homoicon — copy its shape: a root Dockerfile.lint built FROM golangci/golangci-lint:v2.12.2@sha256:5cceeef04e53efe1470638d4b4b4f5ceefd574955ab3941b2d9a68a8c9ad5240, which COPYs the repo in and runs golangci-lint run --config .golangci.yml ./... as a build step, with script/lint reduced to building it. Linting as a build step means a successful build IS a clean lint.
This also answers the open pinning question in this repo. The reason there was no org-blessed place to pin the linter is that it was being installed on the host at all; pinning the lint image by digest in Dockerfile.lint is the answer, and it removes the host install entirely.
It also resolves the false green observed here, where an implementer reported 0 issues on a branch that was genuinely red with a goconst finding — the shared host cache served another tree's clean result. A container per run has its own cache and lock.
Two things to get right, both of which would otherwise ship a false green:
A cached build lints nothing. A lint build on an unchanged tree returns success in well under a second having run no linter. Caching is explicitly waived here, so force the lint layers to execute.
golangci-lint config verify fetches its JSON schema over an unpinned live HTTPS call. Decide deliberately whether to include it.
Definition of done
script/lint runs the linter only in Docker; no host golangci-lint path remains.
Two consecutive script/lint runs on an unchanged tree both demonstrably execute the linter.
Negative control: introduce a deliberate lint violation, confirm it fails with that specific finding, revert, confirm clean.
Owner ruling, sneak 2026-08-09: every lint run happens inside a Docker container, invoked through the `script/` entrypoint. Docker is always available. Linting runs independently and does not need a cache. He has directed a PR for every repo not already set up this way.
Reference implementation is `sneak/homoicon` — copy its shape: a root `Dockerfile.lint` built `FROM golangci/golangci-lint:v2.12.2@sha256:5cceeef04e53efe1470638d4b4b4f5ceefd574955ab3941b2d9a68a8c9ad5240`, which COPYs the repo in and runs `golangci-lint run --config .golangci.yml ./...` as a build step, with `script/lint` reduced to building it. Linting as a build step means a successful build IS a clean lint.
**This also answers the open pinning question in this repo.** The reason there was no org-blessed place to pin the linter is that it was being installed on the host at all; pinning the lint image by digest in `Dockerfile.lint` is the answer, and it removes the host install entirely.
It also resolves the false green observed here, where an implementer reported `0 issues` on a branch that was genuinely red with a `goconst` finding — the shared host cache served another tree's clean result. A container per run has its own cache and lock.
Two things to get right, both of which would otherwise ship a false green:
1. **A cached build lints nothing.** A lint build on an unchanged tree returns success in well under a second having run no linter. Caching is explicitly waived here, so force the lint layers to execute.
2. **`golangci-lint config verify` fetches its JSON schema over an unpinned live HTTPS call.** Decide deliberately whether to include it.
## Definition of done
- `script/lint` runs the linter only in Docker; no host golangci-lint path remains.
- Two consecutive `script/lint` runs on an unchanged tree both demonstrably execute the linter.
- Negative control: introduce a deliberate lint violation, confirm it fails with that specific finding, revert, confirm clean.
- `make check` still green.
Canonical tracking issue: https://git.eeqj.de/sneak/prompts/issues/40
Implementation requirements (manager), settling the three repo-specific points before dispatch.
1. This overrides the scaffold exemption, narrowly.TODO.md Future Steps note 3 records sneak's 2026-07-07 ruling that this repo takes no Dockerfile, CI config, or REPO_POLICIES.md, and that exemption is what I cited in #4 when I ruled against a lint container. sneak's ruling here is later and explicit ("he has directed a PR for every repo not already set up this way"), so it wins for Dockerfile.lint and script/lint — and only for those. No CI config, no REPO_POLICIES.md, and no other script/ entrypoints in this change. Amend note 3 in the same commit to say exactly what is now permitted, so the next reader does not re-derive the old answer.
2. Force the lint layers to execute — two stages, not one. A single-stage image with --no-cache would re-run go mod download over the network on every lint. Split it: a cached deps stage (COPY go.mod go.sum + go mod download), then FROM deps AS lint carrying COPY . . and the lint run. script/lint then builds with --no-cache-filter=lint, which busts only the stage that lints. Do not use a bare docker build as the reference repo does — caching is explicitly waived here, and an unchanged tree must still lint.
3. Leave golangci-lint config verify out, and say so in a comment in the Dockerfile. It resolves its JSON schema over a live unpinned HTTPS call, which is a network dependency and an unpinned input inside a step whose whole purpose is a pinned, reproducible gate; it would also turn a schema-host outage into a red build. golangci-lint run already fails on a malformed config, so the coverage lost is small and the config here is the shared canonical one, verified where it is maintained. This is a deliberate divergence from sneak/homoicon, which includes the step.
Do not touch .golangci.yml — the gomodguard deprecation is #29 and is sneak's to decide.
Evidence required in the PR body, since a green docker build is the classic false green: paste the tail of two consecutive script/lint runs on an unchanged tree showing the lint step actually ran both times (not CACHED), plus the negative control — a deliberate violation failing with that specific finding, then clean after revert.
Implementation requirements (manager), settling the three repo-specific points before dispatch.
**1. This overrides the scaffold exemption, narrowly.** `TODO.md` Future Steps note 3 records sneak's 2026-07-07 ruling that this repo takes no Dockerfile, CI config, or `REPO_POLICIES.md`, and that exemption is what I cited in https://git.eeqj.de/sneak/rgoue/issues/4 when I ruled against a lint container. sneak's ruling here is later and explicit ("he has directed a PR for every repo not already set up this way"), so it wins for `Dockerfile.lint` and `script/lint` — and only for those. No CI config, no `REPO_POLICIES.md`, and no other `script/` entrypoints in this change. Amend note 3 in the same commit to say exactly what is now permitted, so the next reader does not re-derive the old answer.
**2. Force the lint layers to execute — two stages, not one.** A single-stage image with `--no-cache` would re-run `go mod download` over the network on every lint. Split it: a cached `deps` stage (`COPY go.mod go.sum` + `go mod download`), then `FROM deps AS lint` carrying `COPY . .` and the lint run. `script/lint` then builds with `--no-cache-filter=lint`, which busts only the stage that lints. Do not use a bare `docker build` as the reference repo does — caching is explicitly waived here, and an unchanged tree must still lint.
**3. Leave `golangci-lint config verify` out, and say so in a comment in the Dockerfile.** It resolves its JSON schema over a live unpinned HTTPS call, which is a network dependency and an unpinned input inside a step whose whole purpose is a pinned, reproducible gate; it would also turn a schema-host outage into a red build. `golangci-lint run` already fails on a malformed config, so the coverage lost is small and the config here is the shared canonical one, verified where it is maintained. This is a deliberate divergence from `sneak/homoicon`, which includes the step.
Do not touch `.golangci.yml` — the `gomodguard` deprecation is https://git.eeqj.de/sneak/rgoue/issues/29 and is sneak's to decide.
**Evidence required in the PR body**, since a green docker build is the classic false green: paste the tail of two consecutive `script/lint` runs on an unchanged tree showing the lint step actually ran both times (not `CACHED`), plus the negative control — a deliberate violation failing with that specific finding, then clean after revert.
Implementation plan, per the requirements comment above. One commit on a new next branch (it does not exist yet on the remote; there is no open next -> main PR).
1. Dockerfile.lint (new, repo root). Two stages, so that busting the lint layer does not re-download modules:
FROM golangci/golangci-lint:v2.12.2@sha256:5cceeef04e53efe1470638d4b4b4f5ceefd574955ab3941b2d9a68a8c9ad5240 AS deps — WORKDIR /src, COPY go.mod go.sum ./, RUN go mod download.
FROM deps AS lint — COPY . ., RUN golangci-lint run --config .golangci.yml ./....
No golangci-lint config verify step, with a comment in the file recording why: it resolves its JSON schema over a live unpinned HTTPS call, which is an unpinned network input inside a step whose purpose is a pinned reproducible gate, and a schema-host outage would turn into a red build. This is the deliberate divergence from sneak/homoicon.
2. script/lint (new). POSIX sh, set -eu, mode 100755, resolves its own repo root the same way homoicon's does. Builds with --no-cache-filter=lint so the lint stage always re-executes on an unchanged tree while deps stays cached. Caching of the lint result is explicitly waived here.
3. Makefile.lint: becomes a thin shim calling ./script/lint; no host golangci-lint invocation remains anywhere in the repo. The Makefile's header comment currently asserts "no Dockerfile" as part of the exemption, so it gets corrected to match the narrowed exemption.
4. .dockerignore. Excluding .git only, after confirming nothing the lint reads lives there — golangci-lint run needs the Go sources, go.mod/go.sum and .golangci.yml, none of which come from .git.
5. README.md. The one sentence describing make lint as "(golangci-lint)" is corrected to say it runs in Docker; that description is made wrong by this change.
6. TODO.md. Future Steps note 2 (the 2026-07-07 scaffold exemption) is amended to state the new narrower exemption — Dockerfile.lint and script/lint are permitted, CI config, REPO_POLICIES.md and other script/ entrypoints still are not — plus a Completed Steps entry in the existing style. Same commit as the work.
.golangci.yml is not touched.
Verification I will run and paste into the PR body: two consecutive script/lint runs on an unchanged tree with the golangci-lint run layer shown executing (not CACHED) both times, with wall-clock for each; the negative control (deliberate violation, failure naming that specific finding, revert, clean again); and make check in full.
Implementation plan, per the requirements comment above. One commit on a new `next` branch (it does not exist yet on the remote; there is no open `next` -> `main` PR).
**1. `Dockerfile.lint` (new, repo root).** Two stages, so that busting the lint layer does not re-download modules:
- `FROM golangci/golangci-lint:v2.12.2@sha256:5cceeef04e53efe1470638d4b4b4f5ceefd574955ab3941b2d9a68a8c9ad5240 AS deps` — `WORKDIR /src`, `COPY go.mod go.sum ./`, `RUN go mod download`.
- `FROM deps AS lint` — `COPY . .`, `RUN golangci-lint run --config .golangci.yml ./...`.
No `golangci-lint config verify` step, with a comment in the file recording why: it resolves its JSON schema over a live unpinned HTTPS call, which is an unpinned network input inside a step whose purpose is a pinned reproducible gate, and a schema-host outage would turn into a red build. This is the deliberate divergence from `sneak/homoicon`.
**2. `script/lint` (new).** POSIX `sh`, `set -eu`, mode 100755, resolves its own repo root the same way homoicon's does. Builds with `--no-cache-filter=lint` so the lint stage always re-executes on an unchanged tree while `deps` stays cached. Caching of the lint result is explicitly waived here.
**3. `Makefile`.** `lint:` becomes a thin shim calling `./script/lint`; no host `golangci-lint` invocation remains anywhere in the repo. The Makefile's header comment currently asserts "no Dockerfile" as part of the exemption, so it gets corrected to match the narrowed exemption.
**4. `.dockerignore`.** Excluding `.git` only, after confirming nothing the lint reads lives there — `golangci-lint run` needs the Go sources, `go.mod`/`go.sum` and `.golangci.yml`, none of which come from `.git`.
**5. `README.md`.** The one sentence describing `make lint` as "(golangci-lint)" is corrected to say it runs in Docker; that description is made wrong by this change.
**6. `TODO.md`.** Future Steps note 2 (the 2026-07-07 scaffold exemption) is amended to state the new narrower exemption — `Dockerfile.lint` and `script/lint` are permitted, CI config, `REPO_POLICIES.md` and other `script/` entrypoints still are not — plus a Completed Steps entry in the existing style. Same commit as the work.
`.golangci.yml` is not touched.
**Verification I will run and paste into the PR body:** two consecutive `script/lint` runs on an unchanged tree with the `golangci-lint run` layer shown executing (not `CACHED`) both times, with wall-clock for each; the negative control (deliberate violation, failure naming that specific finding, revert, clean again); and `make check` in full.
Built and pushed as 599286a on next, in #43 (the single release-cycle PR — next did not exist and there was no open PR, so this opens the cycle).
What landed.Dockerfile.lint at the root, pinned by digest to golangci/golangci-lint:v2.12.2, two stages: a deps stage (WORKDIR /src, COPY go.mod go.sum ./, RUN go mod download) and FROM deps AS lint carrying COPY . . and RUN golangci-lint run --config .golangci.yml ./.... script/lint (POSIX sh, set -eu, mode 100755, resolves its own repo root) builds it with --no-cache-filter=lint, so the lint stage re-executes every run while the module download stays cached. make lint is now a shim over script/lint; no host golangci-lint invocation remains anywhere in the repo. .dockerignore excludes .git only, after confirming the lint reads only the Go sources, go.mod/go.sum and .golangci.yml.
golangci-lint config verify is deliberately absent, with the reason recorded in a comment in Dockerfile.lint: it resolves its JSON schema over a live unpinned HTTPS call, which is an unpinned network input inside a step whose purpose is a pinned reproducible gate, and a schema-host outage would read as a red build.
.golangci.yml untouched — the gomodguard deprecation warning is #29. No CI config, no REPO_POLICIES.md, no other script/ entrypoint. TODO.md's scaffold-exemption note now states the narrowed exemption, plus a Completed Steps entry, in the same commit; make fmt run and committed.
How I verified it, against the definition of done:
Two consecutive runs on an unchanged tree both execute the linter. Run A real 0m18.802s, run B real 0m12.044s, nothing modified between them. In both, #2 WORKDIR, #7 COPY go.mod go.sum and #8 RUN go mod download report CACHED while #10 [lint 2/2] RUN golangci-lint run ... reports DONE 9.8s and DONE 7.9s respectively, each printing 0 issues. after ~6.7s of real work. Never CACHED.
Negative control. An indent-error-flow violation added to game/dice.go failed the build at Dockerfile.lint:32 with exit 1, naming exactly that finding (game/dice.go:11:9: indent-error-flow: if block ends with a return statement ... (revive)) plus func negativeControlForLint is unused (unused), 2 issues: * revive: 1 * unused: 1. After git checkout -- game/dice.go, exit 0 again.
make check green.fmt-check, then the container lint (DONE 6.7s, 0 issues., not CACHED), then ok cmd/rogue 1.017s / ok game 2.379s under -race -cover — real durations, not (cached). EXIT=0.
Full captured output is in the PR body.
One thing I did not decide unilaterally, raised for review rather than changed: each run exports an untagged image, so repeated linting leaves dangling images behind. The reference shape does the same and it is small (only the per-run COPY/RUN layers differ), but it accumulates on a busy host. Tagging the build or --output=type=cacheonly would fix it and save ~3.5s per run; both are further divergences from sneak/homoicon, so they were left out.
Built and pushed as `599286a` on `next`, in https://git.eeqj.de/sneak/rgoue/pulls/43 (the single release-cycle PR — `next` did not exist and there was no open PR, so this opens the cycle).
**What landed.** `Dockerfile.lint` at the root, pinned by digest to `golangci/golangci-lint:v2.12.2`, two stages: a `deps` stage (`WORKDIR /src`, `COPY go.mod go.sum ./`, `RUN go mod download`) and `FROM deps AS lint` carrying `COPY . .` and `RUN golangci-lint run --config .golangci.yml ./...`. `script/lint` (POSIX `sh`, `set -eu`, mode `100755`, resolves its own repo root) builds it with `--no-cache-filter=lint`, so the lint stage re-executes every run while the module download stays cached. `make lint` is now a shim over `script/lint`; no host `golangci-lint` invocation remains anywhere in the repo. `.dockerignore` excludes `.git` only, after confirming the lint reads only the Go sources, `go.mod`/`go.sum` and `.golangci.yml`.
`golangci-lint config verify` is deliberately absent, with the reason recorded in a comment in `Dockerfile.lint`: it resolves its JSON schema over a live unpinned HTTPS call, which is an unpinned network input inside a step whose purpose is a pinned reproducible gate, and a schema-host outage would read as a red build.
`.golangci.yml` untouched — the `gomodguard` deprecation warning is https://git.eeqj.de/sneak/rgoue/issues/29. No CI config, no `REPO_POLICIES.md`, no other `script/` entrypoint. `TODO.md`'s scaffold-exemption note now states the narrowed exemption, plus a Completed Steps entry, in the same commit; `make fmt` run and committed.
**How I verified it**, against the definition of done:
1. _Two consecutive runs on an unchanged tree both execute the linter._ Run A `real 0m18.802s`, run B `real 0m12.044s`, nothing modified between them. In both, `#2 WORKDIR`, `#7 COPY go.mod go.sum` and `#8 RUN go mod download` report `CACHED` while `#10 [lint 2/2] RUN golangci-lint run ...` reports `DONE 9.8s` and `DONE 7.9s` respectively, each printing `0 issues.` after ~6.7s of real work. Never `CACHED`.
2. _Negative control._ An `indent-error-flow` violation added to `game/dice.go` failed the build at `Dockerfile.lint:32` with exit 1, naming exactly that finding (`game/dice.go:11:9: indent-error-flow: if block ends with a return statement ... (revive)`) plus `func negativeControlForLint is unused (unused)`, `2 issues: * revive: 1 * unused: 1`. After `git checkout -- game/dice.go`, exit 0 again.
3. _`make check` green._ `fmt-check`, then the container lint (`DONE 6.7s`, `0 issues.`, not `CACHED`), then `ok cmd/rogue 1.017s` / `ok game 2.379s` under `-race -cover` — real durations, not `(cached)`. `EXIT=0`.
Full captured output is in the PR body.
**One thing I did not decide unilaterally**, raised for review rather than changed: each run exports an untagged image, so repeated linting leaves dangling images behind. The reference shape does the same and it is small (only the per-run `COPY`/`RUN` layers differ), but it accumulates on a busy host. Tagging the build or `--output=type=cacheonly` would fix it and save ~3.5s per run; both are further divergences from `sneak/homoicon`, so they were left out.
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Owner ruling, sneak 2026-08-09: every lint run happens inside a Docker container, invoked through the
script/entrypoint. Docker is always available. Linting runs independently and does not need a cache. He has directed a PR for every repo not already set up this way.Reference implementation is
sneak/homoicon— copy its shape: a rootDockerfile.lintbuiltFROM golangci/golangci-lint:v2.12.2@sha256:5cceeef04e53efe1470638d4b4b4f5ceefd574955ab3941b2d9a68a8c9ad5240, which COPYs the repo in and runsgolangci-lint run --config .golangci.yml ./...as a build step, withscript/lintreduced to building it. Linting as a build step means a successful build IS a clean lint.This also answers the open pinning question in this repo. The reason there was no org-blessed place to pin the linter is that it was being installed on the host at all; pinning the lint image by digest in
Dockerfile.lintis the answer, and it removes the host install entirely.It also resolves the false green observed here, where an implementer reported
0 issueson a branch that was genuinely red with agoconstfinding — the shared host cache served another tree's clean result. A container per run has its own cache and lock.Two things to get right, both of which would otherwise ship a false green:
golangci-lint config verifyfetches its JSON schema over an unpinned live HTTPS call. Decide deliberately whether to include it.Definition of done
script/lintruns the linter only in Docker; no host golangci-lint path remains.script/lintruns on an unchanged tree both demonstrably execute the linter.make checkstill green.Canonical tracking issue: sneak/prompts#40
Implementation requirements (manager), settling the three repo-specific points before dispatch.
1. This overrides the scaffold exemption, narrowly.
TODO.mdFuture Steps note 3 records sneak's 2026-07-07 ruling that this repo takes no Dockerfile, CI config, orREPO_POLICIES.md, and that exemption is what I cited in #4 when I ruled against a lint container. sneak's ruling here is later and explicit ("he has directed a PR for every repo not already set up this way"), so it wins forDockerfile.lintandscript/lint— and only for those. No CI config, noREPO_POLICIES.md, and no otherscript/entrypoints in this change. Amend note 3 in the same commit to say exactly what is now permitted, so the next reader does not re-derive the old answer.2. Force the lint layers to execute — two stages, not one. A single-stage image with
--no-cachewould re-rungo mod downloadover the network on every lint. Split it: a cacheddepsstage (COPY go.mod go.sum+go mod download), thenFROM deps AS lintcarryingCOPY . .and the lint run.script/lintthen builds with--no-cache-filter=lint, which busts only the stage that lints. Do not use a baredocker buildas the reference repo does — caching is explicitly waived here, and an unchanged tree must still lint.3. Leave
golangci-lint config verifyout, and say so in a comment in the Dockerfile. It resolves its JSON schema over a live unpinned HTTPS call, which is a network dependency and an unpinned input inside a step whose whole purpose is a pinned, reproducible gate; it would also turn a schema-host outage into a red build.golangci-lint runalready fails on a malformed config, so the coverage lost is small and the config here is the shared canonical one, verified where it is maintained. This is a deliberate divergence fromsneak/homoicon, which includes the step.Do not touch
.golangci.yml— thegomodguarddeprecation is #29 and is sneak's to decide.Evidence required in the PR body, since a green docker build is the classic false green: paste the tail of two consecutive
script/lintruns on an unchanged tree showing the lint step actually ran both times (notCACHED), plus the negative control — a deliberate violation failing with that specific finding, then clean after revert.Implementation plan, per the requirements comment above. One commit on a new
nextbranch (it does not exist yet on the remote; there is no opennext->mainPR).1.
Dockerfile.lint(new, repo root). Two stages, so that busting the lint layer does not re-download modules:FROM golangci/golangci-lint:v2.12.2@sha256:5cceeef04e53efe1470638d4b4b4f5ceefd574955ab3941b2d9a68a8c9ad5240 AS deps—WORKDIR /src,COPY go.mod go.sum ./,RUN go mod download.FROM deps AS lint—COPY . .,RUN golangci-lint run --config .golangci.yml ./....No
golangci-lint config verifystep, with a comment in the file recording why: it resolves its JSON schema over a live unpinned HTTPS call, which is an unpinned network input inside a step whose purpose is a pinned reproducible gate, and a schema-host outage would turn into a red build. This is the deliberate divergence fromsneak/homoicon.2.
script/lint(new). POSIXsh,set -eu, mode 100755, resolves its own repo root the same way homoicon's does. Builds with--no-cache-filter=lintso the lint stage always re-executes on an unchanged tree whiledepsstays cached. Caching of the lint result is explicitly waived here.3.
Makefile.lint:becomes a thin shim calling./script/lint; no hostgolangci-lintinvocation remains anywhere in the repo. The Makefile's header comment currently asserts "no Dockerfile" as part of the exemption, so it gets corrected to match the narrowed exemption.4.
.dockerignore. Excluding.gitonly, after confirming nothing the lint reads lives there —golangci-lint runneeds the Go sources,go.mod/go.sumand.golangci.yml, none of which come from.git.5.
README.md. The one sentence describingmake lintas "(golangci-lint)" is corrected to say it runs in Docker; that description is made wrong by this change.6.
TODO.md. Future Steps note 2 (the 2026-07-07 scaffold exemption) is amended to state the new narrower exemption —Dockerfile.lintandscript/lintare permitted, CI config,REPO_POLICIES.mdand otherscript/entrypoints still are not — plus a Completed Steps entry in the existing style. Same commit as the work..golangci.ymlis not touched.Verification I will run and paste into the PR body: two consecutive
script/lintruns on an unchanged tree with thegolangci-lint runlayer shown executing (notCACHED) both times, with wall-clock for each; the negative control (deliberate violation, failure naming that specific finding, revert, clean again); andmake checkin full.Built and pushed as
599286aonnext, in #43 (the single release-cycle PR —nextdid not exist and there was no open PR, so this opens the cycle).What landed.
Dockerfile.lintat the root, pinned by digest togolangci/golangci-lint:v2.12.2, two stages: adepsstage (WORKDIR /src,COPY go.mod go.sum ./,RUN go mod download) andFROM deps AS lintcarryingCOPY . .andRUN golangci-lint run --config .golangci.yml ./....script/lint(POSIXsh,set -eu, mode100755, resolves its own repo root) builds it with--no-cache-filter=lint, so the lint stage re-executes every run while the module download stays cached.make lintis now a shim overscript/lint; no hostgolangci-lintinvocation remains anywhere in the repo..dockerignoreexcludes.gitonly, after confirming the lint reads only the Go sources,go.mod/go.sumand.golangci.yml.golangci-lint config verifyis deliberately absent, with the reason recorded in a comment inDockerfile.lint: it resolves its JSON schema over a live unpinned HTTPS call, which is an unpinned network input inside a step whose purpose is a pinned reproducible gate, and a schema-host outage would read as a red build..golangci.ymluntouched — thegomodguarddeprecation warning is #29. No CI config, noREPO_POLICIES.md, no otherscript/entrypoint.TODO.md's scaffold-exemption note now states the narrowed exemption, plus a Completed Steps entry, in the same commit;make fmtrun and committed.How I verified it, against the definition of done:
real 0m18.802s, run Breal 0m12.044s, nothing modified between them. In both,#2 WORKDIR,#7 COPY go.mod go.sumand#8 RUN go mod downloadreportCACHEDwhile#10 [lint 2/2] RUN golangci-lint run ...reportsDONE 9.8sandDONE 7.9srespectively, each printing0 issues.after ~6.7s of real work. NeverCACHED.indent-error-flowviolation added togame/dice.gofailed the build atDockerfile.lint:32with exit 1, naming exactly that finding (game/dice.go:11:9: indent-error-flow: if block ends with a return statement ... (revive)) plusfunc negativeControlForLint is unused (unused),2 issues: * revive: 1 * unused: 1. Aftergit checkout -- game/dice.go, exit 0 again.make checkgreen.fmt-check, then the container lint (DONE 6.7s,0 issues., notCACHED), thenok cmd/rogue 1.017s/ok game 2.379sunder-race -cover— real durations, not(cached).EXIT=0.Full captured output is in the PR body.
One thing I did not decide unilaterally, raised for review rather than changed: each run exports an untagged image, so repeated linting leaves dangling images behind. The reference shape does the same and it is small (only the per-run
COPY/RUNlayers differ), but it accumulates on a busy host. Tagging the build or--output=type=cacheonlywould fix it and save ~3.5s per run; both are further divergences fromsneak/homoicon, so they were left out.