Hard rules for any AI agent (Claude Code, Cursor, Aider, etc.) working in this repo. CLAUDE.md has the longer-form expectations; this file is the short list of things that get caught in CI and shouldn't.
Run the CI pipeline locally and fix every error before pushing. No exceptions. Not "looks fine to me", not "the change is small", not "my local cgo is broken so I'll skip it" — those are the exact reasons CI gets surprised. If the local environment can't run mage ci, fix that environment before pushing, or run the targets individually and document what was skipped and why in the commit message.
mage ci
That target runs download, lint, test, and build — the same sequence CI runs. If mage ci is too slow for an iteration loop, run the relevant subset:
mage lint # golangci-lint — errcheck and errorlint enabled
mage test # go test ./...
mage build # cross-compile for current OS
mage e2egithub # GitHub provider e2e against fake server
mage e2emodproxy # Go module proxy e2e
If any of those fail, fix the failure before pushing. A red CI run on a PR you opened is a process failure, not a discovery.
- errcheck — every fallible call must be checked. Closures over
http.ResponseWriterwrites,Close()in defers,io.Copyreturn values,fmt.Fprintfreturn values. The repo policy is to wrap them inif err := foo(); err != nil { log.Warn(...) }, never_ = foo(). - staticcheck SA9003 — empty
ifbranches. If the branch is empty because the comment is "either is acceptable", invert the condition andt.Errorf/return errfor the unexpected case. - typecheck failures on Windows —
miekg/pkcs11cgo preprocessing fails on Windows. This is a local problem, not a CI problem. Running lint on Linux works. If you can't run lint locally, you push at your own risk and the user's annoyance.
pkg/dind/TestPushHandlerEndToEndhas been flaky in CI in ways that aren't obviously reproducible locally. Don't paper over a flake with acs.Info()"warm-up" call or atime.Sleep— that's flake-masking, and the real bug will resurface in the next iteration. If a test is genuinely flaky, find the race or the missing lease/label and fix it; if you can't, mark itt.Skipwith an issue number, not a silent diagnostic.
Workflow YAML doesn't go through mage lint, but the consequences of a broken .github/workflows/*.yml are worse than Go lint errors — a bad workflow doesn't tell you anything when it fails on the next trigger. For non-trivial workflow changes:
- Use
actionlintif installed, or paste into rhysd.github.io/actionlint for a quick sanity check. - Sanity-check
runs-onlabels exist on registered self-hosted runners (ephemerd JIT-registers based on hostgoruntime.GOARCH; cross-arch runs require the matching host to be online). - Confirm any
secrets.*references exist in the repo/org secrets before pushing.
.github/workflows/release.yml only fires on push: tags: v*, so you cannot test it on a branch. Sequence:
- Push the workflow change in a regular PR. Get it merged.
- Push a release-candidate tag like
v0.0.1-rc1to validate the full pipeline end-to-end before pushing the real tag. - Only push
v0.0.1once the rc has produced a clean draft release.
Do not push the real tag as the first test of a changed release workflow.