114 lines
6.3 KiB
YAML
114 lines
6.3 KiB
YAML
# golangci-lint configuration for the backend module.
|
|
#
|
|
# Every enable/disable is justified by a MEASURED count from the report-only sweep of 2026-08-03
|
|
# (golangci-lint 2.12.2, 205 .go files, 115 of them tests). Raw sweep = 435 findings:
|
|
# errcheck 413, staticcheck 17, misspell 4, unused 1; govet / ineffassign / nolintlint 0.
|
|
# Those counts are the PROVENANCE of each decision below, not the state of the tree: everything they
|
|
# name has since been fixed or excluded, so a clean run today reports 0.
|
|
#
|
|
# Owner's constraint (D39.92) outranks coverage: standards must not slow development or push a
|
|
# model into hacks. A rule that produces noise is switched OFF here with its reason on one line —
|
|
# never worked around in the code — and nolintlint refuses a bare //nolint.
|
|
version: "2"
|
|
|
|
linters:
|
|
default: none
|
|
enable:
|
|
# --- the standard set ---
|
|
- errcheck # 413 raw; the exclusions below account for 401 of them, the rest were fixed
|
|
- govet # 0 — already in the manual battery; enabling pins it
|
|
- ineffassign # 0
|
|
- staticcheck # 17 raw; 6 excluded as QF1001, 11 fixed
|
|
- unused # 1 — a real dead method (pipeline.applyBanknote), deleted by this pack
|
|
# --- zero-cost bug classes: 0 findings today, so they cost nothing and cannot regress ---
|
|
- bodyclose # a leaked provider response body is a transport leak
|
|
- copyloopvar
|
|
- durationcheck
|
|
- makezero
|
|
# --- small real tails ---
|
|
- errorlint # 3 — wrapped-error comparison is a live correctness class here
|
|
- misspell # 4, all one word (see exclusions)
|
|
- nilerr # 3
|
|
- rowserrcheck # 2
|
|
- sqlclosecheck # 1
|
|
- nolintlint # 0 today — enforces the anti-hack rule: no silent suppressions
|
|
|
|
# DISABLED ON PURPOSE (measured, not skipped):
|
|
# exhaustruct — 988 findings module-wide. Scoped to the two types it was proposed for
|
|
# (config.Stage, pipeline.Request) it is 17, but 16 are test literals and the 17th is the SEAM
|
|
# ITSELF (config/internal_call.go Stage()) — the one construction site that is allowed. The
|
|
# invariant "Stage is built only through the seam" needs a rule that tells seam from bypass;
|
|
# exhaustruct cannot, the analyzer can. Off so it does not compete with that guard.
|
|
|
|
settings:
|
|
staticcheck:
|
|
checks:
|
|
- all
|
|
- -ST1000 # default-off in golangci-lint: package comment form
|
|
- -ST1003 # default-off: naming conventions
|
|
- -ST1016 # default-off: receiver name consistency
|
|
- -ST1020 # default-off: comment form on exported methods
|
|
- -ST1021 # default-off: comment form on exported types
|
|
- -ST1022 # default-off: comment form on exported vars
|
|
- -QF1001 # 6 findings: "apply De Morgan's law". The current shape mirrors the prose above
|
|
# each condition ("neither X nor Y"); rewriting for the linter reads worse
|
|
# than the rule it encodes. A style opinion, not a defect class.
|
|
|
|
misspell:
|
|
ignore-rules:
|
|
# `initals` is a DELIBERATE typo: the langpack tests write it into a fixture to prove the
|
|
# parser reports unknown keys instead of silently dropping them. Correcting it deletes the
|
|
# test. 1 comment + 3 test sites. NOTE: `locale` is left unset on purpose — setting it to US
|
|
# makes misspell rewrite en-GB spellings in the English prose (behaviour, honoured, dialogue):
|
|
# measured 0 findings unset against 311 with `locale: US`, none of them real errors.
|
|
- initals
|
|
|
|
nolintlint:
|
|
require-explanation: true # a suppression without a reason is the hack this rule exists to stop
|
|
require-specific: true # //nolint must name the linter, never blanket-silence a file
|
|
allow-unused: false
|
|
|
|
exclusions:
|
|
# Written out instead of the std-error-handling preset: that preset also swallows os.Setenv and
|
|
# os.Remove, and one of the two real production findings in this repo is an unchecked os.Setenv.
|
|
rules:
|
|
# Close/Flush: 294 sites. All 33 production ones are readers or DB handles (listed and
|
|
# checked one by one). The repo has exactly ONE flush-on-Close writer — chunktest/epub.go's
|
|
# zip.Writer — and its Close IS checked, so this cannot hide a lost write.
|
|
- linters: [errcheck]
|
|
text: 'Error return value of `[^`]*\.(Close|Flush)` is not checked'
|
|
# CLI rendering to stdout: 91 sites. A broken pipe on `tmctl status | head` is not something
|
|
# the CLI can act on, and threading it out would rewrite every renderer for nothing.
|
|
- linters: [errcheck]
|
|
text: 'Error return value of `fmt\.Fprint(f|ln)?` is not checked'
|
|
# hash.Hash.Write is documented never to return an error (2 sites, the membank digest). Keyed on
|
|
# the METHOD, not on a receiver spelled `h`: a rule that fires for `hasher.Write` but not `h.Write`
|
|
# pushes the next contributor toward renaming a variable to appease the linter.
|
|
- linters: [errcheck]
|
|
text: 'Error return value of `[^`]*\.Write` is not checked'
|
|
path: internal/membank/
|
|
# `defer tx.Rollback()` — 5 sites. The idiom is "roll back unless committed", so on the happy
|
|
# path the error is sql.ErrTxDone BY DESIGN. Wrapping each in a discard closure would add five
|
|
# blocks of noise to say nothing. Excluded in config rather than worked around in the code.
|
|
- linters: [errcheck]
|
|
text: 'Error return value of `[^`]*\.Rollback` is not checked'
|
|
# In tests, a response-writer/fixture write that fails cannot be acted on, and a t.Fatal on
|
|
# each would bury the assertion the test is actually about.
|
|
- linters: [errcheck]
|
|
path: _test\.go
|
|
text: 'Error return value of `(w\.Write|os\.WriteFile|fmt\.Sscanf)` is not checked'
|
|
# runToSignatureStop returns *WaveSignatureStop, which implements error because the stop travels
|
|
# the pipeline AS an error — so errcheck sees 12 discarded "errors". The helper has already
|
|
# t.Fatal'd on every wrong outcome; the return is there for the tests that want to inspect it.
|
|
- linters: [errcheck]
|
|
path: internal/pipeline/miningstop_join_test\.go
|
|
text: 'Error return value is not checked'
|
|
|
|
formatters:
|
|
enable:
|
|
- gofmt # `make fmt` checks this too; the duplicate is deliberate, so a bare
|
|
# `golangci-lint run` is still a complete gate on its own
|
|
|
|
issues:
|
|
max-issues-per-linter: 0 # never truncate: a hidden tail reads as "clean"
|
|
max-same-issues: 0
|