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:
shutdown(ctx) in cmd/__PROJECT_NAME__/main.go (deferred shutdown return value ignored).
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.:
(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.
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?
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.)
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?
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:
The two-line errcheck fix (cmd/__PROJECT_NAME__/main.go + pkg/litellm/model.go).
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.
Your acceptance criterion was the right one — "verify by generating a throwaway repo and confirming run #1 is green" — and it is now enforced on every run rather than done once by hand.
The cause was not in the code
The template's own task check has been green this whole time. That is not because the defect was absent; it is because Go ignores directories whose names begin with _ or . when expanding ./....
$ go list ./...
__MODULE_PATH__/internal/agent
__MODULE_PATH__/pkg/litellm <- no cmd package
cmd/__PROJECT_NAME__ starts with an underscore, so go build ./..., go vet ./..., go test ./...andgolangci-lint run ./... all silently skipped the one package every generated repo is built around. After substitution the directory has a real name, becomes visible, and the error appears.
Template green, children red, by construction — and invisible from either repo's CI, which is why this sat open since June.
Demonstrated rather than reasoned about:
template: golangci-lint run ./... -> 0 issues
child: cmd/childproj/main.go:25:16: Error return value is not
checked (errcheck) -> defer shutdown(ctx)
What changed
The live defect: defer shutdown(ctx) dropped an error (SetupTelemetry returns func(context.Context) error). Now wrapped, with the underscore-directory reason recorded inline so the next person does not delete the wrapper as noise.
scripts/tests/generated-child_test.sh, wired into task check: substitutes the tracked tree into a throwaway child and gates go list / build / vet / golangci-lint on the result. Four assertions, the first being that the entrypoint is visible at all — without it the other three would pass for the wrong reason.
Verified in CI, not just locally (run 7509):
task: [check] bash scripts/tests/generated-child_test.sh
ok the generated child's entrypoint is visible to go list
ok the generated child builds
ok the generated child passes go vet
ok the generated child passes golangci-lint
4 passed, 0 failed
That log check mattered: the harness skips when golangci-lint is absent, so a green job would otherwise not prove it had run.
Rejected alternative, recorded so it is not retried
golangci-lint run ./cmd/__PROJECT_NAME__does work — naming a path directly bypasses the pattern exclusion. But it reports a gofumpt violation on the import block that does not exist in any child: the formatter's import grouping depends on the module path, and the template's is a placeholder. Wiring that into check would turn the gate red over an artifact of being a template.
Proven to fail when it should
It went red twice before going green — once on the unfixed entrypoint, and once when it was still reading git archive HEAD and reported a stale pass against an unstaged fix. It now reads tracked files from the working tree, because a check inside task check has to gate what is about to be committed.
**Fixed in `2b3851a` (v0.1.0), verified 2026-09-17.**
Your acceptance criterion was the right one — *"verify by generating a throwaway repo and confirming run #1 is green"* — and it is now enforced on every run rather than done once by hand.
## The cause was not in the code
The template's own `task check` has been green this whole time. That is not because the defect was absent; it is because **Go ignores directories whose names begin with `_` or `.` when expanding `./...`**.
```
$ go list ./...
__MODULE_PATH__/internal/agent
__MODULE_PATH__/pkg/litellm <- no cmd package
```
`cmd/__PROJECT_NAME__` starts with an underscore, so `go build ./...`, `go vet ./...`, `go test ./...` **and** `golangci-lint run ./...` all silently skipped the one package every generated repo is built around. After substitution the directory has a real name, becomes visible, and the error appears.
Template green, children red, by construction — and invisible from either repo's CI, which is why this sat open since June.
Demonstrated rather than reasoned about:
```
template: golangci-lint run ./... -> 0 issues
child: cmd/childproj/main.go:25:16: Error return value is not
checked (errcheck) -> defer shutdown(ctx)
```
## What changed
The live defect: `defer shutdown(ctx)` dropped an error (`SetupTelemetry` returns `func(context.Context) error`). Now wrapped, with the underscore-directory reason recorded inline so the next person does not delete the wrapper as noise.
`scripts/tests/generated-child_test.sh`, wired into `task check`: substitutes the tracked tree into a throwaway child and gates `go list` / `build` / `vet` / `golangci-lint` on **the result**. Four assertions, the first being that the entrypoint is visible at all — without it the other three would pass for the wrong reason.
Verified in CI, not just locally (run 7509):
```
task: [check] bash scripts/tests/generated-child_test.sh
ok the generated child's entrypoint is visible to go list
ok the generated child builds
ok the generated child passes go vet
ok the generated child passes golangci-lint
4 passed, 0 failed
```
That log check mattered: the harness skips when `golangci-lint` is absent, so a green job would otherwise not prove it had run.
## Rejected alternative, recorded so it is not retried
`golangci-lint run ./cmd/__PROJECT_NAME__` *does* work — naming a path directly bypasses the pattern exclusion. But it reports a **gofumpt** violation on the import block that does not exist in any child: the formatter's import grouping depends on the module path, and the template's is a placeholder. Wiring that into `check` would turn the gate red over an artifact of being a template.
## Proven to fail when it should
It went red twice before going green — once on the unfixed entrypoint, and once when it was still reading `git archive HEAD` and reported a stale pass against an unstaged fix. It now reads tracked files from the working tree, because a check inside `task check` has to gate what is about to be committed.
---
_Created via git-mcp on behalf of @mathias_
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Problem
The template scaffolds lint-dirty — a fresh repo generated from it fails
task checkonerrcheckfrom 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 byerrcheck:shutdown(ctx)incmd/__PROJECT_NAME__/main.go(deferred shutdown return value ignored).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.:
(Applied downstream in brain-gardener
4ad4237; should be fixed at the template source so it doesn't recur.)Acceptance
task check(lint + vet +go test -race) green on its first CI run, with no hand-fixing.Impact
Low severity but high friction — every
create_project_from_templateconsumer (brain-gardener, and future Go agents) starts with a redmain+ red first PR, which masks real failures and erodes the "green main" signal during early dev.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):task checkfailederrcheckon exactly the two deferred calls named here.4ad4237:task checkgreen (lint 0 issues, vet clean,go test -racegreen), 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 nextcreate_project_from_templateconsumer starts green on run #1 — satisfying this issue's acceptance ("freshly generated repo passestask checkon its first CI run"). It's a trivial, surgical, behavior-preserving change.Two adjacent hardening ideas — want either?
errcheckto 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 owntask checktoday, which is why this shipped.).golangci.ymlcommitted 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?
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 checkgate.→ PR both together:
errcheckfix (cmd/__PROJECT_NAME__/main.go+pkg/litellm/model.go).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.
Fixed in
2b3851a(v0.1.0), verified 2026-09-17.Your acceptance criterion was the right one — "verify by generating a throwaway repo and confirming run #1 is green" — and it is now enforced on every run rather than done once by hand.
The cause was not in the code
The template's own
task checkhas been green this whole time. That is not because the defect was absent; it is because Go ignores directories whose names begin with_or.when expanding./....cmd/__PROJECT_NAME__starts with an underscore, sogo build ./...,go vet ./...,go test ./...andgolangci-lint run ./...all silently skipped the one package every generated repo is built around. After substitution the directory has a real name, becomes visible, and the error appears.Template green, children red, by construction — and invisible from either repo's CI, which is why this sat open since June.
Demonstrated rather than reasoned about:
What changed
The live defect:
defer shutdown(ctx)dropped an error (SetupTelemetryreturnsfunc(context.Context) error). Now wrapped, with the underscore-directory reason recorded inline so the next person does not delete the wrapper as noise.scripts/tests/generated-child_test.sh, wired intotask check: substitutes the tracked tree into a throwaway child and gatesgo list/build/vet/golangci-linton the result. Four assertions, the first being that the entrypoint is visible at all — without it the other three would pass for the wrong reason.Verified in CI, not just locally (run 7509):
That log check mattered: the harness skips when
golangci-lintis absent, so a green job would otherwise not prove it had run.Rejected alternative, recorded so it is not retried
golangci-lint run ./cmd/__PROJECT_NAME__does work — naming a path directly bypasses the pattern exclusion. But it reports a gofumpt violation on the import block that does not exist in any child: the formatter's import grouping depends on the module path, and the template's is a placeholder. Wiring that intocheckwould turn the gate red over an artifact of being a template.Proven to fail when it should
It went red twice before going green — once on the unfixed entrypoint, and once when it was still reading
git archive HEADand reported a stale pass against an unstaged fix. It now reads tracked files from the working tree, because a check insidetask checkhas to gate what is about to be committed.Created via git-mcp on behalf of @mathias