Summary
gc-ratchet on main is being cancelled while queued, before any job runs. Three consecutive main runs, zero executions. This is CLAUDE.md's hazard #3 — "concurrency with unconditional cancel-in-progress" — in the variant cancel-in-progress: false does not protect against.
gc-ratchet.yml L25-40 carries a long comment explaining that this exact hazard was observed and defused. It is not defused.
Evidence
| run |
head |
created |
ended |
jobs executed |
30707891646 |
eeb8ce421 (#7192) |
16:19:52Z |
16:32:45Z cancelled |
0 |
30708370282 |
ebcdb6618 (#7198) |
— |
cancelled |
0 |
30707189162 |
5fdf6ffb6 (#7195) |
— |
cancelled |
0 |
$ gh run view 30707891646 --json createdAt,updatedAt,conclusion,jobs
{"conclusion":"cancelled","created":"2026-08-01T16:19:52Z","jobs":[],"updated":"2026-08-01T16:32:45Z"}
jobs: [] — it never reached a runner. It was created when #7192 merged and cancelled 13 minutes later, at the exact moment #7198 merged.
Why cancel-in-progress: false does not help
concurrency:
group: gc-ratchet-${{ github.event_name }}-${{ github.ref }}
cancel-in-progress: ${{ github.event_name == 'pull_request' }}
For a push to main, cancel-in-progress evaluates to false, so an in-progress run is protected. But GitHub's documented behaviour is that at most one run may be pending in a group: when a new run enters, "any previously pending job or workflow in the concurrency group will be cancelled" — regardless of cancel-in-progress. On macOS runners with a deep queue, a main run sits pending for tens of minutes, so a merge cadence faster than the queue depth cancels every one of them.
The comment at L28-36 describes precisely this observation ("three consecutive main runs cancelled, zero executed") and concludes that scoping cancellation to pull_request fixes it. It does not — it only protects a run that already started.
Fix
Key the group on the commit for push events, so main runs never share one:
concurrency:
group: gc-ratchet-${{ github.event_name }}-${{ github.event_name == 'push' && github.sha || github.ref }}
cancel-in-progress: ${{ github.event_name == 'pull_request' }}
Every main commit then gets its own group and queues independently. PR runs keep superseding themselves, which is still correct.
Scope
.github/workflows/gc-root-dominance.yml (added in #7198) copies the same group/cancel-in-progress shape and inherits the same hole; it needs the same fix. Worth auditing any other workflow with a push: branches: [main] trigger and a github.ref-keyed group.
Separately: the ratchet's pinned baseline no longer describes the shipped configuration
Measured locally on main, every probe reports minor_cycles 80 → 0, copied_objects 18,294 → 0, freed_bytes 97,546,128 → 0 as REGRESSIONS. That is the signature of "no moving minor ran", which is the shipped default since #7161 flipped PERRY_GC_MOVING_LOOP_POLLS off. So even when the job does run, it currently cannot distinguish a real retention regression from the stopgap. Either the baseline is re-pinned under the shipped default, or the ratchet sets the flag explicitly so it keeps measuring the collector it was written for.
Refs #7161, #7192, #7198.
Summary
gc-ratchetonmainis being cancelled while queued, before any job runs. Three consecutivemainruns, zero executions. This is CLAUDE.md's hazard #3 — "concurrencywith unconditionalcancel-in-progress" — in the variantcancel-in-progress: falsedoes not protect against.gc-ratchet.ymlL25-40 carries a long comment explaining that this exact hazard was observed and defused. It is not defused.Evidence
30707891646eeb8ce421(#7192)30708370282ebcdb6618(#7198)307071891625fdf6ffb6(#7195)jobs: []— it never reached a runner. It was created when #7192 merged and cancelled 13 minutes later, at the exact moment #7198 merged.Why
cancel-in-progress: falsedoes not helpFor a push to
main,cancel-in-progressevaluates tofalse, so an in-progress run is protected. But GitHub's documented behaviour is that at most one run may be pending in a group: when a new run enters, "any previously pending job or workflow in the concurrency group will be cancelled" — regardless ofcancel-in-progress. On macOS runners with a deep queue, amainrun sits pending for tens of minutes, so a merge cadence faster than the queue depth cancels every one of them.The comment at L28-36 describes precisely this observation ("three consecutive main runs cancelled, zero executed") and concludes that scoping cancellation to
pull_requestfixes it. It does not — it only protects a run that already started.Fix
Key the group on the commit for push events, so
mainruns never share one:Every
maincommit then gets its own group and queues independently. PR runs keep superseding themselves, which is still correct.Scope
.github/workflows/gc-root-dominance.yml(added in #7198) copies the samegroup/cancel-in-progressshape and inherits the same hole; it needs the same fix. Worth auditing any other workflow with apush: branches: [main]trigger and agithub.ref-keyed group.Separately: the ratchet's pinned baseline no longer describes the shipped configuration
Measured locally on
main, every probe reportsminor_cycles 80 → 0,copied_objects 18,294 → 0,freed_bytes 97,546,128 → 0as REGRESSIONS. That is the signature of "no moving minor ran", which is the shipped default since #7161 flippedPERRY_GC_MOVING_LOOP_POLLSoff. So even when the job does run, it currently cannot distinguish a real retention regression from the stopgap. Either the baseline is re-pinned under the shipped default, or the ratchet sets the flag explicitly so it keeps measuring the collector it was written for.Refs #7161, #7192, #7198.