Found by an adversarial review of the same harness in template-go-web
(template-go-web#13). This copy had the identical defect.
The harness matched golangci-lint's human-readable summary to decide pass or
fail. Any failure that is not "found issues" -- bad config, unwritable TMPDIR,
OOM, or simply a future change to the output format -- produces non-matching
text and was scored as a pass.
That is the exact defect class this harness exists to end. It is the only gate
covering cmd/__PROJECT_NAME__, so a linter that dies leaves the entrypoint
unchecked with CI green.
golangci-lint exits 0 clean / 1 issues found / >1 the linter itself failed.
Now graded on that, with the >1 case reported as UNCHECKED rather than clean.
Also: a missing tool now fails instead of `exit 0` with zero assertions, which
was indistinguishable from a real pass to anything reading the exit code. And
Dockerfile joins the substituted file set -- it references the entrypoint path,
which is the one thing a rename has historically broken here.
Verified with a shimmed linter exiting 3: 3 passed, 1 failed. Normal run 4/4.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Closes#1. Closes#2.
A repo generated from this template failed `task check` on errcheck from its
first commit, while the template's own `task check` was green. The reason is
not in the code:
Go ignores directories whose names begin with "_" or "." when expanding
package patterns like ./...
The entrypoint lives in cmd/__PROJECT_NAME__. So `go build ./...`,
`go vet ./...`, `go test ./...` and `golangci-lint run ./...` ALL silently
skip the one package every generated repo is built around:
$ go list ./...
__MODULE_PATH__/internal/agent
__MODULE_PATH__/pkg/litellm <- no cmd package
After substitution the directory has a real name, becomes visible, and the
defect appears. Template green, children red, by construction -- and invisible
to anyone reading either repo's CI.
Fixes the one live defect: `defer shutdown(ctx)` dropped an error, and
SetupTelemetry returns func(context.Context) error.
Two of the three defects these issues describe were already fixed and neither
issue knew: go.sum has been committed for some time, and resp.Body.Close is
already wrapped. Verified before touching anything rather than trusted from
the issue text.
Adds scripts/tests/generated-child_test.sh, wired into `task check`: it
substitutes the tracked tree into a throwaway child and gates go list / build
/ vet / golangci-lint on the RESULT. Without it the next entrypoint defect
rots exactly the same way.
Not done by adding ./cmd/__PROJECT_NAME__ to the lint command, which does work
-- naming a path directly bypasses the pattern exclusion -- because 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, so that route turns the gate red over an artifact of being a
template.
The harness reads tracked files from the working tree rather than
`git archive HEAD`: it runs inside `task check`, so it must gate what is about
to be committed. Archiving HEAD reported a stale pass against an unstaged fix
on first use here.
Proven to fail when it should: red on the unfixed entrypoint, and red again on
the stale-HEAD read, before going green.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>