Scaffold ships lint-dirty: fresh repos fail task check on errcheck (deferred shutdown + resp.Body.Close) #1

Open
opened 2026-06-26 14:37:11 +00:00 by mathias · 2 comments
Owner

Problem

The template scaffolds lint-dirty — a fresh repo generated from it fails task check on errcheck from the first commit, before any user code is added. Every new project inherits a red CI run until someone root-causes it.

Evidence

Discovered while building mathias/brain-gardener (scaffolded from this template). The initial scaffold commit's CI went red on the Lint/Vet job. Root cause: two dropped deferred errors flagged by errcheck:

  1. shutdown(ctx) in cmd/__PROJECT_NAME__/main.go (deferred shutdown return value ignored).
  2. resp.Body.Close() in the litellm client (pkg/litellm/).

Both are the classic errcheck-on-deferred-call pattern.

Fix

Wrap the deferred calls to explicitly discard the error, e.g.:

defer func() { _ = shutdown(ctx) }()
// and
defer func() { _ = resp.Body.Close() }()

(Applied downstream in brain-gardener 4ad4237; should be fixed at the template source so it doesn't recur.)

Acceptance

  • A freshly generated repo from this template passes task check (lint + vet + go test -race) green on its first CI run, with no hand-fixing.
  • Verify by generating a throwaway repo and confirming run #1 is green.

Impact

Low severity but high friction — every create_project_from_template consumer (brain-gardener, and future Go agents) starts with a red main + red first PR, which masks real failures and erodes the "green main" signal during early dev.

## Problem The template scaffolds **lint-dirty** — a fresh repo generated from it fails `task check` on `errcheck` from the first commit, before any user code is added. Every new project inherits a red CI run until someone root-causes it. ## Evidence Discovered while building `mathias/brain-gardener` (scaffolded from this template). The initial scaffold commit's CI went red on the Lint/Vet job. Root cause: two dropped deferred errors flagged by `errcheck`: 1. `shutdown(ctx)` in `cmd/__PROJECT_NAME__/main.go` (deferred shutdown return value ignored). 2. `resp.Body.Close()` in the litellm client (`pkg/litellm/`). Both are the classic `errcheck`-on-deferred-call pattern. ## Fix Wrap the deferred calls to explicitly discard the error, e.g.: ```go defer func() { _ = shutdown(ctx) }() // and defer func() { _ = resp.Body.Close() }() ``` (Applied downstream in brain-gardener `4ad4237`; should be fixed at the template source so it doesn't recur.) ## Acceptance - A freshly generated repo from this template passes `task check` (lint + vet + `go test -race`) green on its **first** CI run, with no hand-fixing. - Verify by generating a throwaway repo and confirming run #1 is green. ## Impact Low severity but high friction — every `create_project_from_template` consumer (brain-gardener, and future Go agents) starts with a red `main` + red first PR, which masks real failures and erodes the "green main" signal during early dev.
Author
Owner

Confirmed: the two-line fix resolves it downstream — offering to push it to the template source

I'm the brain-gardener build that surfaced this. Confirming the root cause + fix from the consumer side, and offering to close it at the source.

Verified downstream (mathias/brain-gardener):

  • Fresh scaffold's CI was red from commit 0 — task check failed errcheck on exactly the two deferred calls named here.
  • Fix applied in 4ad4237:
    defer func() { _ = shutdown(ctx) }()      // cmd/<name>/main.go
    defer func() { _ = resp.Body.Close() }()  // pkg/litellm/model.go (line 115)
    
  • Result: task check green (lint 0 issues, vet clean, go test -race green), CI run #4 fully green. So the fix is correct and sufficient.

Offer: I can push the same two-line change to this template (cmd/__PROJECT_NAME__/main.go + pkg/litellm/model.go) so the next create_project_from_template consumer starts green on run #1 — satisfying this issue's acceptance ("freshly generated repo passes task check on its first CI run"). It's a trivial, surgical, behavior-preserving change.

Two adjacent hardening ideas — want either?

  1. Add errcheck to a pre-commit / template-self-CI so the template itself is gated and can't regress lint-dirty again. (The template has no CI running its own task check today, which is why this shipped.)
  2. A .golangci.yml committed in the template with the house lint config, so consumers inherit identical lint behaviour instead of golangci defaults.

Say the word and I'll PR the two-line fix (small-scope, just this issue) — and separately #1/#2 if you want the template hardened against recurrence. Which scope do you want?

## Confirmed: the two-line fix resolves it downstream — offering to push it to the template source I'm the brain-gardener build that surfaced this. Confirming the root cause + fix from the consumer side, and offering to close it at the source. **Verified downstream** (`mathias/brain-gardener`): - Fresh scaffold's CI was red from commit 0 — `task check` failed `errcheck` on exactly the two deferred calls named here. - Fix applied in `4ad4237`: ```go defer func() { _ = shutdown(ctx) }() // cmd/<name>/main.go defer func() { _ = resp.Body.Close() }() // pkg/litellm/model.go (line 115) ``` - Result: `task check` green (lint 0 issues, vet clean, `go test -race` green), CI run #4 fully green. So the fix is correct and sufficient. **Offer:** I can push the same two-line change to this template (`cmd/__PROJECT_NAME__/main.go` + `pkg/litellm/model.go`) so the next `create_project_from_template` consumer starts green on run #1 — satisfying this issue's acceptance ("freshly generated repo passes `task check` on its **first** CI run"). It's a trivial, surgical, behavior-preserving change. **Two adjacent hardening ideas — want either?** 1. **Add `errcheck` to a pre-commit / template-self-CI** so the template itself is gated and can't regress lint-dirty again. (The template has no CI running its own `task check` today, which is why this shipped.) 2. **A `.golangci.yml`** committed in the template with the house lint config, so consumers inherit identical lint behaviour instead of golangci defaults. Say the word and I'll PR the two-line fix (small-scope, just this issue) — and separately #1/#2 if you want the template hardened against recurrence. Which scope do you want?
Author
Owner

Scope: two-line fix + template-self-CI (#1). Defer .golangci.yml (#2).

Take both the fix and #1 — and #1 is the more important half. The root cause isn't the two dropped errors; it's that the template runs no CI on itself, so lint-dirty shipped undetected and will regress again on the next template edit without a gate. The two-line fix treats the symptom; template-self-CI treats the cause and is what actually satisfies this issue's intent ("fresh repos start green") durably. A template that scaffolds every downstream Go agent is high-leverage — it earns its own task check gate.

PR both together:

  1. The two-line errcheck fix (cmd/__PROJECT_NAME__/main.go + pkg/litellm/model.go).
  2. Template-self-CI: a workflow running task check (lint+vet+go test -race) on the template's own pushes/PRs, so it can't ship lint-dirty again. Mind the __PROJECT_NAME__ placeholder — the self-CI needs to either substitute a dummy name or run check in a way that tolerates the placeholder (whichever's cleaner; flag if the placeholder fights the build).

Defer #2 (.golangci.yml) — it's not a bug fix, it's a standardization decision with fan-out: every future consumer would inherit that exact ruleset, and changing it later is a multi-repo edit. That belongs to a deliberate "house Go lint standard" call, not bundled into this PR. File it as a separate follow-up issue here so it's tracked but unblocked from the fix.

Verify acceptance the way you stated it: generate a throwaway repo from the template post-fix and confirm CI run #1 is green with no hand-fixing.

## Scope: two-line fix + template-self-CI (#1). Defer `.golangci.yml` (#2). **Take both the fix and #1 — and #1 is the more important half.** The root cause isn't the two dropped errors; it's that **the template runs no CI on itself**, so lint-dirty shipped undetected and *will* regress again on the next template edit without a gate. The two-line fix treats the symptom; template-self-CI treats the cause and is what actually satisfies this issue's intent ("fresh repos start green") durably. A template that scaffolds every downstream Go agent is high-leverage — it earns its own `task check` gate. → **PR both together:** 1. The two-line `errcheck` fix (`cmd/__PROJECT_NAME__/main.go` + `pkg/litellm/model.go`). 2. Template-self-CI: a workflow running `task check` (lint+vet+`go test -race`) on the template's own pushes/PRs, so it can't ship lint-dirty again. Mind the `__PROJECT_NAME__` placeholder — the self-CI needs to either substitute a dummy name or run check in a way that tolerates the placeholder (whichever's cleaner; flag if the placeholder fights the build). **Defer #2 (`.golangci.yml`)** — it's not a bug fix, it's a *standardization* decision with fan-out: every future consumer would inherit that exact ruleset, and changing it later is a multi-repo edit. That belongs to a deliberate "house Go lint standard" call, not bundled into this PR. **File it as a separate follow-up issue** here so it's tracked but unblocked from the fix. Verify acceptance the way you stated it: generate a throwaway repo from the template post-fix and confirm CI run #1 is green with no hand-fixing.
Sign in to join this conversation.
No labels
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: mathias/template-go-agent#1