fix: feature-ideation reusable honours AGENTS_PAUSED, so a fleet pause stops it
Problem
AGENTS_PAUSED=true is the switch that pauses the agent fleet. feature-ideation-reusable.yml calls the model (the Claude action, in job analyze) and never reads the variable, so a fleet pause does not stop it. Its callers are frozen stubs, so the gate has to live in the reusable.
Design
- Both working jobs carry
if: vars.AGENTS_PAUSED != 'true'.analyzeis the model call.gather-signalscalls no model, but its only product is the artifactanalyzereads; left running during a pause it would start a runner, upload an orphan artifact, and could turn a paused run red on an unrelated API error. - A new
fleet-pausedjob runs on the complementary condition and prints one notice namingAGENTS_PAUSE_SOURCEandAGENTS_PAUSE_UNTIL. It has noneeds:, no secret, no token, no action andpermissions: {}. - The two values reach the script through
env:and have line breaks flattened to spaces, so a multi-line value cannot start a second workflow command. (The notice jobs inpetry-projects/.github-privatedo not flatten; filed there as petry-projects/.github-private#2282.) - Matching rule, the same as
petry-projects/.github-private: only the wordtruepauses, in any letter case, because GitHub compares strings without regard to case. Unset,false,1,yesand a padded value run the workflow as before. varsis read in the calling repository's context, so an organisation variable pauses every consumer.- No caller stub, channel tag or CI workflow is touched.
Scope item 3 of the issue (other model calls in this repository). Of the 14 workflow_call reusables, this is the only one that calls a model. One non-reusable workflow also does and is not gated: compliance-audit-and-improvement.yml, job analyze. Filed as #1309; not changed here.
Which channel tags have to move (acceptance criterion 3). Consumers get the gate only when the tag they pin reaches a commit that contains this change:
| Tag | Now | Pinned by |
|---|---|---|
feature-ideation/v1-next | 4bb3e7aa (v1.20.0) | .github-private |
feature-ideation/v1-ring0 | 2e3d523b (v1.16.1) | this repository's own stub |
feature-ideation/v1-ring1 | cd0b1675 (v1.11.0) | TalkTerm, bmad-bgreat-suite |
feature-ideation/v1-stable | cd0b1675 (v1.11.0) | every other consumer, and the template stub |
Most consumers are on v1-stable, which is nine releases behind next, so the pause does not reach them until the last promotion.
Risk
- A paused run ends green and the canary gate counts it as a healthy sample. The stub's
prepjob always succeeds, so a paused run endssuccesswith or without the notice job, andring1 -> stableneeds only one sample. Filed as #1310. It becomes reachable when this gate reaches a channel; it should be fixed before a pause is relied on. - The tests are static. Only a live run can prove that GitHub resolves
vars.AGENTS_PAUSEDfrom the caller inside thisworkflow_call(the jobs-ifcontext allowsvars, anddev-lead-reusable.ymlin.github-privaterelies on the same thing across repositories), that the organisation variable's visibility covers the consumers, and that the paused run shows the one notice. - "feat: Switch feature-ideation-reusable.yml model default to the opus family" (#1245) edits the same file at lines 53 to 56. The hunks here are elsewhere;
git merge-treeagainst its head is clean and the new suite passes on the merged tree.
Test plan
test/workflows/feature-ideation/fleet-pause.bats(new, 12 tests), picked up by the existingfeature-ideation-tests.ymlpath filters. It parses the committed workflow and evaluates each job'sif:with a small model of GitHub's expression rules for eight values of the variable; the model refuses any expression form it does not know, so a rewritten gate fails until the test is updated. It also executes the notice script.- Mutation-checked, 15 mutations, each turns named tests red: gate removed from
analyze, condition inverted, notice job dropped, a variable interpolated intorun:, an||bypass after the gate, a misspelt variable name, a second ungated model job, the notice job given a credential, and others. I re-ran the first independently after rebasing (6 of 12 red, then 12 of 12 green restored). actionlintwith CI's flags: clean. Other suites unchanged before and after.- Not runnable locally: 14 existing tests in this directory need
jsonschemaoryq. CI is the authority.
Rollback
Revert the squash commit. Channels that already carry the change keep it until they are re-cut or rolled back; the gate is inert while the variable is unset.
Monitoring
AGENTS_PAUSEDis unset today, so the first runs after each promotion should look exactly as before:fleet-pausedskipped, the two working jobs running.- The first run during a real pause should show
gather-signalsandanalyzeskipped and one "Agent fleet paused" notice. That run is the live proof the tests cannot give; record it on the issue.
Closes #1295
š¤ Generated with Claude Code