From 8a27ead91c20f72de6ede76e39a1e33b9f6562ff Mon Sep 17 00:00:00 2001 From: Mathias Date: Sat, 5 Sep 2026 14:42:59 +0200 Subject: [PATCH] chore(lint): adopt the canonical .golangci.yml from infra/ci-templates Canonical lives at infra ci-templates/golangci.yml, distributed by the infra#309 pattern: byte-identical copies, checksum drift gate in infra. Do not edit this file here -- edit the canonical and run `task ci:sync`. Verified locally before pushing, not pushed-and-hoped: golangci-lint v2.11.4 against a Go 1.26.1 toolchain (matching CI) reports 0 issues here. Also applies gofumpt, which this config enables and the golangci-lint v2 default set does not check at all. That gap is why the drift was invisible: earlier passes ran gofmt, which is weaker. Mechanical, deterministic, build+vet verified. --- .golangci.yml | 52 +++++++++++++++++++ .../internal/capturehttp/handler_test.go | 3 ++ ingestion/internal/mcp/server_test.go | 2 + ingestion/internal/mcp/tools_capture_test.go | 2 + .../startupretry/startupretry_test.go | 2 - internal/config/models_test.go | 2 +- internal/registry/registry_test.go | 1 + 7 files changed, 61 insertions(+), 3 deletions(-) create mode 100644 .golangci.yml diff --git a/.golangci.yml b/.golangci.yml new file mode 100644 index 0000000..2790f24 --- /dev/null +++ b/.golangci.yml @@ -0,0 +1,52 @@ +# Canonical golangci-lint configuration for the estate's Go repos (infra#309 +# pattern: one canonical file here, a byte-identical copy in each adopter, +# and the drift gate in THIS repo comparing sha256). +# +# Why this exists: 26 Go repos, 3 had a lint config, and all three were +# different. The 23 without one ran golangci-lint's v2 default set, which +# does NOT include a formatter -- so gofmt/gofumpt drift went uncaught until +# it was found by hand (dma-cli#12: two files drifted, nothing flagged them). +# +# The two configs this replaces (cobalt-dingo, coo-agent) were near-identical +# copies of each other AND both invalid under v2: they carried v1-style +# `linters-settings` and `issues.exclude-rules` keys, which `golangci-lint +# config verify` rejects outright. Their revive settings and test exclusions +# were therefore not in effect. Consolidating fixes that too. +# +# Per-repo variation does NOT belong here -- this file must stay byte-identical +# across adopters so the gate is a checksum, not a diff a human has to judge. +# A repo with a genuine exception (agentsquad excludes its seeded-bug fixture +# tree, which exists precisely to contain broken code) keeps its own config and +# is not listed as an adopter. + +version: "2" + +linters: + default: none + enable: + - errcheck # unchecked errors + - govet # suspicious constructs + - staticcheck # broad static analysis + - unused # unused code + - misspell # spelling + - revive # idiomatic Go + - exhaustive # exhaustive switch on enums + + settings: + revive: + rules: + - name: var-naming + severity: warning + + exclusions: + rules: + # Test files: table-driven cases legitimately carry unused struct fields + # and long functions. + - path: _test\.go + linters: + - revive + - unused + +formatters: + enable: + - gofumpt diff --git a/ingestion/internal/capturehttp/handler_test.go b/ingestion/internal/capturehttp/handler_test.go index 7402301..ad8597a 100644 --- a/ingestion/internal/capturehttp/handler_test.go +++ b/ingestion/internal/capturehttp/handler_test.go @@ -38,9 +38,11 @@ func (f fakeTracker) CreateIssue(context.Context, string, string, string) (captu } return capture.IssueRef{Repo: "hyperguild", Number: 1, URL: "https://git/1"}, nil } + func (fakeTracker) CloseIssue(context.Context, string, int, string) (capture.IssueRef, error) { return capture.IssueRef{}, nil } + func (fakeTracker) CommentIssue(context.Context, string, int, string) (capture.IssueRef, error) { return capture.IssueRef{}, nil } @@ -183,6 +185,7 @@ type refusingAudit struct{} func (refusingAudit) Reserve(context.Context, classification.Level) (capture.AuditOutcome, error) { return 0, errors.New("central audit sink unreachable") } + func (refusingAudit) Record(context.Context, capture.AuditEntry, capture.AuditOutcome) error { return nil } diff --git a/ingestion/internal/mcp/server_test.go b/ingestion/internal/mcp/server_test.go index 6637197..328ada7 100644 --- a/ingestion/internal/mcp/server_test.go +++ b/ingestion/internal/mcp/server_test.go @@ -134,9 +134,11 @@ type stubTracker struct{} func (stubTracker) CreateIssue(context.Context, string, string, string) (capture.IssueRef, error) { return capture.IssueRef{}, nil } + func (stubTracker) CloseIssue(context.Context, string, int, string) (capture.IssueRef, error) { return capture.IssueRef{}, nil } + func (stubTracker) CommentIssue(context.Context, string, int, string) (capture.IssueRef, error) { return capture.IssueRef{}, nil } diff --git a/ingestion/internal/mcp/tools_capture_test.go b/ingestion/internal/mcp/tools_capture_test.go index 0b72a4d..469651e 100644 --- a/ingestion/internal/mcp/tools_capture_test.go +++ b/ingestion/internal/mcp/tools_capture_test.go @@ -28,9 +28,11 @@ type capFakeTracker struct{} func (capFakeTracker) CreateIssue(context.Context, string, string, string) (capture.IssueRef, error) { return capture.IssueRef{Repo: "hyperguild", Number: 1, URL: "https://git/1"}, nil } + func (capFakeTracker) CloseIssue(context.Context, string, int, string) (capture.IssueRef, error) { return capture.IssueRef{}, nil } + func (capFakeTracker) CommentIssue(context.Context, string, int, string) (capture.IssueRef, error) { return capture.IssueRef{}, nil } diff --git a/ingestion/internal/startupretry/startupretry_test.go b/ingestion/internal/startupretry/startupretry_test.go index a96a017..796c34b 100644 --- a/ingestion/internal/startupretry/startupretry_test.go +++ b/ingestion/internal/startupretry/startupretry_test.go @@ -18,7 +18,6 @@ func TestDo_ReturnsImmediatelyOnFirstSuccess(t *testing.T) { calls++ return "ready", nil }) - if err != nil { t.Fatalf("Do: %v", err) } @@ -41,7 +40,6 @@ func TestDo_SurvivesTransientFailuresThenSucceeds(t *testing.T) { } return "ready", nil }) - if err != nil { t.Fatalf("Do returned %v — a transient 502 must not be fatal", err) } diff --git a/internal/config/models_test.go b/internal/config/models_test.go index 910c02f..7772daf 100644 --- a/internal/config/models_test.go +++ b/internal/config/models_test.go @@ -30,7 +30,7 @@ skills: func writeModels(t *testing.T, content string) string { t.Helper() f := filepath.Join(t.TempDir(), "models.yaml") - require.NoError(t, os.WriteFile(f, []byte(content), 0644)) + require.NoError(t, os.WriteFile(f, []byte(content), 0o644)) return f } diff --git a/internal/registry/registry_test.go b/internal/registry/registry_test.go index d570f0e..80c626e 100644 --- a/internal/registry/registry_test.go +++ b/internal/registry/registry_test.go @@ -22,6 +22,7 @@ func (s stubSkill) Tools() []registry.ToolDef { InputSchema: json.RawMessage(`{"type":"object","properties":{}}`), }} } + func (s stubSkill) Handle(ctx context.Context, tool string, args json.RawMessage) (json.RawMessage, error) { return json.RawMessage(`{"ok":true}`), nil }