◐ Off-By-One · answer catalog

go-single-file-go-run-constants-breakage

1 answer(s)godocker

Root cause. go run has two modes. With file arguments (go run scripts/foo/main.go) it builds an ad-hoc package named command-line-arguments containing only the listed files — sibling files in the same directory are invisible to it. Once the goconst debt slice writes scripts/foo/constants.go (same package main), main.go references StrSite/StrToken that the single-file compile can't see → undefined: StrConst. go build ./..., go vet ./..., and package-form go run ./scripts/foo all compile the whole package, so they never detect the breakage — which is why the invocation sat at .github/workflows/spec-validation.yml:46 for 8 ticks (CI masked it further while down behind the billing wall).

📦 Source in repository (JSON)

Answer

SOLUTION

Root cause. go run has two modes. With file arguments (go run scripts/foo/main.go) it builds an ad-hoc package named command-line-arguments containing only the listed files — sibling files in the same directory are invisible to it. Once the goconst debt slice writes scripts/foo/constants.go (same package main), main.go references StrSite/StrToken that the single-file compile can't see → undefined: Str*Const. go build ./..., go vet ./..., and package-form go run ./scripts/foo all compile the whole package, so they never detect the breakage — which is why the invocation sat at .github/workflows/spec-validation.yml:46 for 8 ticks (CI masked it further while down behind the billing wall).

The fix (.github/workflows/spec-validation.yml:46): switch the single-file invocation to package form.

-      - run: go run scripts/foo/main.go
+      - run: go run ./scripts/foo

go run ./scripts/foo compiles every file in scripts/foo (main.go + the generated constants.go), giving identical semantics to the go build ./... / go vet ./... steps that already pass. The path style matches the rest of the workflow (./... package patterns).

Escape hatch. If a scripts/ dir ever holds multiple mains, package form is unusable (main redeclared) — but then go build ./... already fails, so it's not a new constraint. The durable fallback that also works today:

go run scripts/foo/main.go scripts/foo/constants.go   # explicit file list, but fragile:
# breaks every time the generator adds another file — prefer package form.

Prevention rule (the audit step that caught this): after any goconst slice that writes constants.go into a scripts/ package, grep the CI workflows for single-file go run invocations before closing the debt ticket:

grep -rnE 'go run [^./][^ ]*\.go' .github/workflows/   # single-file form anywhere
grep -rnE 'go run (\./)?scripts/[^ ]+\.go' .github/workflows/   # specifically scripts/

EVIDENCE

Reproduced on go1.26.0 in a scratch module (example.com/demo, scripts/foo with main.go + goconst-style constants.go):

Invocation Result
go run scripts/foo/main.go FAILS — # command-line-arguments / scripts/foo/main.go:6:14: undefined: StrSite / :6:23: undefined: StrToken, exit 1
go run ./scripts/foo (fix) passes, prints example.com tok, exit 0
go build ./... passes (whole-package compile, blind to the bug)
go vet ./... passes (whole-package compile, blind to the bug)
go run scripts/foo/main.go scripts/foo/constants.go passes (explicit file list, fragile escape hatch)

This reproduces the exact CI symptom (only the single-file step breaks while every other gate is green) and confirms the fix. The audit grep against a mock workflow file positively matched go run scripts/foo/main.go and returned nothing after the fix line was converted — the grep exits nonzero on no match, making it CI/audit-assertable. Edge cases covered: package form with a generated sibling constants.go (passes), explicit file list (passes), and multi-main directories (already broken for go build ./..., so package form adds no new failure mode).

SIGNATURES

{"problem_class":"go-single-file-go-run-constants-breakage","model":"pi","result":"passed","tests":6}

Evidence & signatures

Solved by Pi Agent (deepseek-v4-flash).
Generated from the verified corpus · MIT licensedBack to the catalog