Lineage: #59627 (filed R61) -> #61518 (R69 refile) -> #63342 (R77 refile, auto-expired closed/not_planned on 2026-10-02) -> this issue (R85 refile). Fourth time this exact bug has been filed; the code has never changed across any of the three prior closures.
Bug
Four library-scope linters classify a package as library code (vs. an executable main package) using only a path-substring heuristic, with zero check of the actual package declaration:
- pkg/linters/osexitinlibrary/osexitinlibrary.go:23
- pkg/linters/rawloginlib/rawloginlib.go:30
- pkg/linters/logfatallibrary/logfatallibrary.go:35
- pkg/linters/panic-in-library-code/panic-in-library-code.go:36
All four use the identical guard:
if strings.HasSuffix(pkgPath, "/main") || strings.Contains(pkgPath, "/cmd/") {
Compare the sibling linter pkg/linters/osgetenvlibrary/osgetenvlibrary.go:22, which does this correctly:
if pass.Pkg.Name() == "main" || strings.HasSuffix(pkgPath, "/main") || strings.Contains(pkgPath, "/cmd/") {
Impact
Any package whose import path does not end in /main or contain /cmd/, but whose package clause is package main, is misclassified as library code by all four linters. Two live examples of package main outside a /main or /cmd/ path: internal/tools/actions-build/main.go (3 os.Exit(1) sites) and internal/tools/generate-action-metadata/main.go (2 os.Exit(1) sites).
Status: latent today. Makefile:868 sets the default LINTER_PACKAGES ?= ./cmd/... ./pkg/..., which excludes ./internal/..., so these specific sites are not scanned by default. This is a structural correctness gap rather than a deliberate scope limit, and would misfire the moment LINTER_PACKAGES is widened or a new package-main-without-main-ish-dirname is added under ./pkg/... or ./cmd/....
Re-verified 2026-10-03: all four files re-read in full; zero occurrences of pass.Pkg.Name() outside osgetenvlibrary.go and ossetenvlibrary.go.
Recommendation
Add pass.Pkg.Name() == "main" || to the front of the OR-chain in all four files, matching the osgetenvlibrary/ossetenvlibrary reference implementation exactly.
Validation checklist
Effort: small -- one-line guard addition x4 files.
Generated by 🤖 Sergo - Serena Go Expert · claude · agent · 317.8 AIC · ⌖ 7.26 AIC · ⊞ 5.3K · ◷
Lineage: #59627 (filed R61) -> #61518 (R69 refile) -> #63342 (R77 refile, auto-expired closed/not_planned on 2026-10-02) -> this issue (R85 refile). Fourth time this exact bug has been filed; the code has never changed across any of the three prior closures.
Bug
Four library-scope linters classify a package as library code (vs. an executable main package) using only a path-substring heuristic, with zero check of the actual package declaration:
All four use the identical guard:
Compare the sibling linter pkg/linters/osgetenvlibrary/osgetenvlibrary.go:22, which does this correctly:
Impact
Any package whose import path does not end in /main or contain /cmd/, but whose package clause is
package main, is misclassified as library code by all four linters. Two live examples ofpackage mainoutside a /main or /cmd/ path: internal/tools/actions-build/main.go (3 os.Exit(1) sites) and internal/tools/generate-action-metadata/main.go (2 os.Exit(1) sites).Status: latent today. Makefile:868 sets the default
LINTER_PACKAGES ?= ./cmd/... ./pkg/..., which excludes ./internal/..., so these specific sites are not scanned by default. This is a structural correctness gap rather than a deliberate scope limit, and would misfire the moment LINTER_PACKAGES is widened or a new package-main-without-main-ish-dirname is added under ./pkg/... or ./cmd/....Re-verified 2026-10-03: all four files re-read in full; zero occurrences of
pass.Pkg.Name()outside osgetenvlibrary.go and ossetenvlibrary.go.Recommendation
Add
pass.Pkg.Name() == "main" ||to the front of the OR-chain in all four files, matching the osgetenvlibrary/ossetenvlibrary reference implementation exactly.Validation checklist
package mainunder a directory name that does not end in /main or contain /cmd/Effort: small -- one-line guard addition x4 files.