Conversation
The acceptance suite ran on every PR to main/next, so docs-only and repo-furniture PRs spent three API-key-backed matrix jobs (12m timeout each) plus the telemetry job to test a binary nobody had changed. Add an allowlist `paths:` filter covering everything the suite actually builds and asserts on: main.go, go.mod, go.sum, pkg/**, test/**, tools/** and .github/workflows/**. An allowlist rather than paths-ignore: a directory nobody thought about fails to trigger the suite visibly, instead of a denylist silently running it forever until someone adds an exclusion. Safe to skip because `unit-test` is the only required status check on main's ruleset, so a skipped acceptance run leaves no PR pending. workflow_dispatch deliberately keeps no filter -- it is the escape hatch for the case this filter creates (see #395). Closes #240 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BnrKWZQASV7bFJ4oGWwmo9
The suite has only ever triggered on pull_request -- zero runs in history with headBranch main -- so main's head is strictly a commit nothing ran acceptance against. The PR gate is inherited on merge, and that inheritance is only as current as the PR's last pull_request event. That is not hypothetical. Merging a PR into another PR's head branch fires no pull_request event, so #378 sat with 20 commits and 75 changed files against a stale run, and the code about to ship had never been through its own gate. test.yml already runs on push to main for exactly this reason, with the comment "so the merged result is tested, not just each PR branch in isolation. A PR based on stale main can merge into a combination that no PR run exercised." The argument was never extended to the stronger suite. Uses the same paths filter as the pull_request trigger, so a docs merge does not spend a full suite run -- which is what makes this affordable and is why it belongs in this PR rather than a separate one. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BnrKWZQASV7bFJ4oGWwmo9
|
Folded in a second change: acceptance now also runs on merge to The suite has only ever triggered on Not hypothetical: merging a PR into another PR's head branch fires no
That argument applies at least as strongly to acceptance as to the unit and build jobs; it was just never extended. It reuses the same paths filter as the
(Added by Claude on Phil's behalf.) |
Every caller of this reusable workflow -- pull requests, merges to main, manual dispatch, and the release gate -- exercises the same three Hookdeck projects with the same three API keys. The API allows 240 requests/minute and a full slice makes thousands of calls, so two overlapping runs exhaust it and both degrade. It has happened twice in two days. Once as all four jobs timing out with zero assertion failures. Once as a single named test failing -- a config that came back empty -- while a release ran concurrently against the same projects; that one looks exactly like a regression and sends you hunting through the auth code for a bug that is not there. Both times the same commit passed on a re-run with nothing else in flight. A note in the acceptance README tells a human how to recognise this after the fact. It does not prevent it, and writing it down did not stop the second occurrence. cancel-in-progress is deliberately false: release.yml calls this as its publish gate, and cancelling that to make room for a pull request is the wrong trade. Queueing costs wall-clock and nothing else. Placed on the reusable workflow rather than each caller so no future caller can be added outside the group. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BnrKWZQASV7bFJ4oGWwmo9
|
Third change folded in: acceptance runs are now serialised repo-wide. concurrency:
group: acceptance-suite
cancel-in-progress: falsePlaced on the reusable This has now bitten twice in two days. First as all four jobs timing out with zero assertion failures. Then yesterday on this very PR: run The second shape is the dangerous one. A clean 429 wipeout is recognisable; a single named assertion failure is indistinguishable from a real bug and sends you into the wrong code.
Worth noting: I documented this hazard in #418 the same morning and then walked into it while cutting the release. A README note tells you how to recognise it afterwards; it does not prevent it.
(Added by Claude on Phil's behalf.) |
Closes #240.
The acceptance suite ran on every PR to
main/next. A README-only PR spent three API-key-backed matrix jobs (12m timeout each) plus the telemetry job testing a binary nobody had touched. This adds apaths:allowlist to.github/workflows/test-acceptance.yml.What triggers the suite
Derived from what the suite actually builds, not from a guess:
main.go,go.mod,go.sum,pkg/**—test/acceptance/helpers.gorunsgo build -o <tmp> .andgo run main.go. That build resolves the root package, everypkg/package and the module graph.test/**— the tests and their helpers.test/contains onlytest/acceptance, sotest/**is exact, not over-broad.tools/**—tools/generate-referencederives docs from Cobra--helpmetadata; a change there can move output tests assert on..github/workflows/**— the run is a two-file chain (test-acceptance.yml→acceptance.yml). Matching the directory means adding another reusable workflow to that chain does not require remembering to extend this list. Workflow edits are rare, so the extra runs are cheap.Note the issue proposed
cmd/**; there is no top-levelcmd/, the commands live inpkg/cmd/, whichpkg/**covers.What does not trigger it
README.md,CHANGELOG.md,KNOWN_ISSUES.md,LICENSE,docs/,plans/,.plans/,.gitignore,.tool-versions,.cursor/,.agents/,.claude/,.github/dependabot.yml.Also deliberately excluded, because they feed packaging, not the acceptance binary — and each already has its own workflow:
.goreleaser/**,scripts/completions.sh→test.ymlbuild jobs,release.ymlpackage.json,bin/hookdeck.js,Dockerfile,test-scripts/**,.github/actions/test-npm-install/→test-npm-build.yml,test-npm-install.yml,test-homebrew-build.ymlThe two judgement calls
REFERENCE.md— excluded. It is generated from--helptext, so a change to it alone is either generator output or a hand edit the generator will overwrite; either way it cannot change CLI behaviour. No acceptance test reads it. The guard that does exist —pkg/cmd/reference_metrics_doc_test.go, which asserts the documentedgateway metricssubcommands are real — is a unit test underpkg/, and it is not-shortgated. It runs intest.yml, which has no path filters at all. So aREFERENCE.md-only PR is still checked, by the job that checks it today.AGENTS.md/CLAUDE.md— excluded. Instructions to coding agents. They are not compiled, not embedded (nogo:embedanywhere in the repo) and not read by any test. They cannot change CLI behaviour.Why an allowlist rather than
paths-ignoreAn allowlist fails safe. A new top-level directory nobody thought about does not trigger the suite, and that omission is visible — someone notices the run is missing. A denylist fails open in the opposite direction: it keeps running the full matrix on the new directory forever until somebody remembers to add an exclusion, which is the cost this issue is about.
Skipping is safe here specifically because
unit-testis the only required status check onmain's ruleset (confirmed viagh api repos/hookdeck/hookdeck-cli/rulesets), so a PR that skips acceptance is not left permanently pending on a check that will never report.workflow_dispatchis untouchedIt stays a bare
workflow_dispatch: {}sibling ofpull_request, carrying nopaths. It is the escape hatch for exactly the case this filter creates, and the only way to exercise a stacked release branch (#395). A comment in the file says so, to stop anyone "tidying" a filter onto it later.Verification
actionlint .github/workflows/test-acceptance.ymlpasses (exit 0), and the file parses to the expected structure —workflow_dispatchis a sibling ofpull_request, so no filter applies to it.Scenarios walked against the pattern set:
pkg/cmd/listen.gopkg/**test/acceptance/connection_test.gotest/**README.mdonlyAGENTS.mdonlypkg/listen/listen.go+README.mdpkg/**(any match wins).github/workflows/test.yml.github/workflows/**REFERENCE.mdonlyunit-test)go.mod+go.sum(dependabot)go.mod,go.sumtools/generate-reference/main.gotools/**main.gomain.go.github/dependabot.ymltest-scripts/,.goreleaser/,package.json,Dockerfileworkflow_dispatchThis PR itself touches only
.github/workflows/, so it exercises the new filter: the acceptance suite should run on it.🤖 Generated with Claude Code
https://claude.ai/code/session_01BnrKWZQASV7bFJ4oGWwmo9