Skip to content

CI: gc-ratchet's main runs are cancelled while queued, so the gate has executed zero times in three merges #7205

Description

@proggeramlug

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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions