From 63ff0252bc8a999c2f3e048115722109a1eddfe1 Mon Sep 17 00:00:00 2001 From: Johnathan Falk Date: Wed, 12 Aug 2026 00:36:48 -0400 Subject: [PATCH 1/2] ci(minimal): raise Go Tests job cap above its own go test timeout The job capped at 20 minutes while the test step runs go test with -timeout 30m. The runner therefore killed the job ten minutes before Go's own timeout could fire, and Go's timeout is the only thing that prints a goroutine dump naming the stuck test. Every slow run produced conclusion=cancelled with no diagnostic output at all, and cancelled renders as a test failure on the PR even though nothing actually failed. Observed repeatedly in falkcorp/audiobook-organizer (#2311, #2315, and #2319, the last of which was cancelled at exactly 20m16s). Raises the cap to 35 and documents the invariant: the job cap must stay strictly greater than the longest -timeout passed to any go test invocation in the job, plus setup. Co-Authored-By: Claude Opus 5 (1M context) Claude-Session: https://claude.ai/code/session_01JnjSNo2WizbcnrW4pXT2xp --- .github/workflows/reusable-ci-minimal.yml | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/.github/workflows/reusable-ci-minimal.yml b/.github/workflows/reusable-ci-minimal.yml index 5ad8d5ef..ec680582 100644 --- a/.github/workflows/reusable-ci-minimal.yml +++ b/.github/workflows/reusable-ci-minimal.yml @@ -1,7 +1,7 @@ # file: .github/workflows/reusable-ci-minimal.yml -# version: 1.0.0 +# version: 1.1.0 # guid: c9d8e7f6-a5b4-3c2d-1e0f-9a8b7c6d5e4f -# last-edited: 2026-05-01 +# last-edited: 2026-08-12 # Fast parallel CI for PRs and pushes. Runs short tests only. # Uses manual actions/cache — NOT setup-go's built-in cache, which is unreliable. @@ -99,7 +99,20 @@ jobs: go-test-short: name: Go Tests (short, race) runs-on: ubuntu-latest - timeout-minutes: 20 + # 35, not 20. The test step below runs `go test` with `-timeout 30m`, so a + # job cap of 20 killed the runner TEN MINUTES BEFORE Go's own timeout could + # fire — and Go's timeout is the only thing that prints a goroutine dump + # naming the stuck test. Every such run produced `conclusion=cancelled` + # with no evidence whatsoever, and `cancelled` reads as a test failure on + # the PR even though nothing failed. Observed repeatedly in + # falkcorp/audiobook-organizer (#2311, #2315, #2319 — the last cancelled at + # exactly 20m16s). + # + # INVARIANT: this cap must stay strictly greater than the longest -timeout + # passed to any `go test` invocation in this job, plus setup time. If you + # raise the test timeout, raise this too — otherwise you silently make + # every slow run undiagnosable again. + timeout-minutes: 35 env: GOEXPERIMENT: ${{ inputs.go-experiment }} steps: From 6b0413c508171a88acfbeb091640668f27cb0258 Mon Sep 17 00:00:00 2001 From: Johnathan Falk Date: Wed, 12 Aug 2026 00:41:32 -0400 Subject: [PATCH 2/2] docs(changelog): fragment for the Go Tests timeout cap fix Co-Authored-By: Claude Opus 5 (1M context) Claude-Session: https://claude.ai/code/session_01JnjSNo2WizbcnrW4pXT2xp --- ...0260812-go-tests-cap-above-test-timeout.md | 24 +++++++++++++++++++ 1 file changed, 24 insertions(+) create mode 100644 changelog.d/20260812-go-tests-cap-above-test-timeout.md diff --git a/changelog.d/20260812-go-tests-cap-above-test-timeout.md b/changelog.d/20260812-go-tests-cap-above-test-timeout.md new file mode 100644 index 00000000..edbdd675 --- /dev/null +++ b/changelog.d/20260812-go-tests-cap-above-test-timeout.md @@ -0,0 +1,24 @@ +### Fixed + +#### `Go Tests (short, race)` can now reach its own `go test` timeout + +The job capped at 20 minutes while its test step ran `go test -short -race +-timeout 30m ./...`. The runner therefore killed the job ten minutes before +Go's own timeout could fire — and Go's `-timeout` is the only thing that prints +a goroutine dump naming the stuck test. Every slow run produced +`conclusion=cancelled` with no diagnostic output at all, and `cancelled` +renders on a PR as a test failure even though nothing failed. + +Observed repeatedly in `falkcorp/audiobook-organizer` (#2311, #2315, #2319 — +the last cancelled at exactly 20m16s on a docs-only PR, with every other job in +the run green). Consuming repos could not work around it: this workflow exposes +no timeout input, so the cap is only settable here. + +The cap is now 35, and the invariant is stated in a comment beside it: **the +job cap must stay strictly greater than the longest `-timeout` passed to any +`go test` invocation in the job, plus setup.** Raising the test timeout without +raising the cap silently makes every slow run undiagnosable again. + +Raising a job cap cannot turn a passing run into a failing one — it only lets a +genuinely slow or hung run survive long enough to emit the dump that identifies +the culprit. Suites finishing well under 20 minutes are unaffected.