Skip to content

ci: cancel superseded review runs - #45

Merged
johnny4young merged 5 commits into
mainfrom
codex/maintenance-g1
Oct 8, 2026
Merged

johnny4young merged 5 commits into
mainfrom
codex/maintenance-g1

Conversation

@johnny4young

@johnny4young johnny4young commented Oct 5, 2026 •

Copy link
Copy Markdown
Owner

Summary

When a new commit replaces a PR revision, the obsolete CI jobs keep occupying hosted runners. This PR adds workflow-level concurrency to the ordinary CI workflow so that superseded pull request runs are cancelled, while every push to main keeps its own, never-cancelled run.

What changed

.github/workflows/ci.yml:

concurrency:
  group: ci-${{ github.workflow }}-${{ github.event_name == 'pull_request' && github.ref || github.sha }}
  cancel-in-progress: ${{ github.event_name == 'pull_request' }}
  • Pull requests: the group is the PR merge ref (refs/pull/N/merge), so a new revision cancels the previous run. Different PRs, including stacked PRs that target non-default bases, stay independent.
  • Pushes to main: the group is the commit SHA and cancellation is off, so a later merge can neither cancel nor replace the pending run of an earlier merge. Every merged commit keeps a complete OS/shell matrix result.
  • release.yml (group: release) and canary.yml (group: canary-live-feed) are untouched and keep cancel-in-progress: false.
  • tests/workflows.bash: the invariants now evaluate the configured group and cancel-in-progress expressions, using a small evaluator for GitHub's == / != / && / || value semantics. They are evaluated for representative events (two revisions of PR 45, PR 46, and two main pushes) and assert:
    • PR revisions share a group;
    • different PRs, and PRs versus main, do not;
    • each main commit has its own group;
    • PR runs cancel and main runs never do;
    • the workflow name is part of the group.
  • CHANGELOG.md and CONTRIBUTING.md describe the PR-only cancellation.

Why

Superseded PR feedback wastes runners. main results, however, are what bisecting, the status badge and picking a known-good commit to release rely on, so they must never be cancelled.

How it was tested

  • The workflow invariants were mutation-tested locally. Each of these made tests/workflows.bash fail with the matching message:
    • cancel-in-progress: true → "CI must never cancel runs for pushes to main";
    • the old github.ref group → "each main commit must keep its own group…";
    • a group without the workflow name → "…must include the workflow name".
  • Full suite on head a3d3a2c, as a non-root user: 31/31 passed.
  • actionlint 1.7.12 (the CI-pinned version), ShellCheck 0.11.0, shfmt 3.13.1, the YAML parse and git diff --check are clean.
  • Hosted CI on a3d3a2c: all 10 jobs and all 11 checks green, including native macOS Bash 3.2, Linux Fish and Windows Bash/PowerShell.
  • Previous qualification: run 37370497199 on 54fcb9a.

Review follow-ups

  • a3d3a2c ci: never cancel main runs; supersede only pull request revisions. This applies the review finding that the original ci-<workflow>-<ref> group with cancel-in-progress: true also cancelled the previous main push run on every merge. It also replaces the tautological ci_group lambda asserts, which compared a copied lambda with itself and could never fail, with assertions evaluated from the YAML.

Decisions taken

  • Owner decision: cancel only for pull_request events, and group main pushes by SHA. A conditional cancel-in-progress alone would not have been enough, because GitHub keeps only one pending run per group and replaces older pending runs even when cancel-in-progress is false.

Known limitations & follow-ups

  • Actual hosted cancellation of superseded revisions is not separately demonstrated; it is guarded by the evaluated invariants. No quantified runner saving is claimed.
  • Release pushes made with GITHUB_TOKEN do not trigger CI at all (GitHub behavior). That is unchanged.

Merge order

#46 → #47, then this PR (#45). It edits the same spots in CHANGELOG.md and CONTRIBUTING.md as #46 and #47. git merge-tree shows only those text conflicts (the ci.yml and tests/workflows.bash hunks auto-merge). Whichever lands later resolves them at merge time by keeping both entries. Independent of #44, #48, #49 and #41.

@johnny4young johnny4young left a comment

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review: CI cancels superseded runs

What it does: adds a workflow-level concurrency block to ci.yml (group ci-<workflow>-<ref>, cancel-in-progress: true), with docs, a changelog entry, and invariants in tests/workflows.bash.

Verdict: needs a decision before merge (one should-fix that depends on intent, nothing else).

Findings

  • 🟠 .github/workflows/ci.yml:9-12: the group also covers push to main (refs/heads/main), so a newer merge cancels the previous merge commit's CI. The title says "review runs". If main should keep a result for every commit, see the inline suggestion, which uses a per-SHA group for pushes and cancels only for pull_request. I did not push this because the inline comment in the workflow explicitly mentions main, so it is your call.
  • 🟡 tests/workflows.bash (the ci_group lambda): the three ci_group.call(...) asserts check a Ruby lambda against itself, not GitHub's evaluation. They can never fail, so they add no protection beyond the exact-string assert above them. Consider dropping them, or replacing them with a comment.
  • 🟡 This PR and #46 both append to the same spots in CHANGELOG.md / CONTRIBUTING.md (and touch ci.yml / tests/workflows.bash). git merge-tree shows text conflicts in the two docs, so whichever merges second needs a trivial rebase.

Checked and fine

  • pull_request has no branches: filter, so stacked PRs (base ≠ main) still run. The PR ref refs/pull/N/merge isolates PRs from each other and from main.
  • release.yml (group: release) and canary.yml (group: canary-live-feed) keep cancel-in-progress: false and live in different workflows, so they can never be cancelled by this group. Release's own pushes to main use GITHUB_TOKEN, so they don't trigger CI anyway.
  • Local: full suite passed except install-transaction.bash, which fails identically on main because this container runs as root (environment-only). Workflow invariants, ShellCheck 0.11.0, shfmt 3.13.1 and bash -n were clean. CI is green.

Generated by Claude Code

Comment thread .github/workflows/ci.yml Outdated
Comment on lines +9 to +12
# Superseded review/main feedback must not occupy the current head's runners.
concurrency:
group: ci-${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟠 This also cancels main push runs: CI runs on push: branches: [main], github.ref is refs/heads/main for every one of them, and cancel-in-progress: true applies regardless of event. If two PRs merge a few minutes apart, the first merge commit's CI is cancelled, so that commit never gets a full OS/shell matrix result. That hurts bisecting, the status badge, and choosing a known-good commit to release from. The PR title says "review runs". This comment says "review/main feedback", so please confirm main was meant to be included.

If only PR runs should be superseded, note that GitHub keeps at most one pending run per group even with cancel-in-progress: false. A conditional flag alone therefore isn't enough for main. Give push runs a per-commit group instead:

Suggested change
# Superseded review/main feedback must not occupy the current head's runners.
concurrency:
group: ci-${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
# Superseded PR feedback must not occupy the current head's runners. Pushes to
# main get a per-commit group so every merged commit keeps a full CI result.
concurrency:
group: ci-${{ github.workflow }}-${{ github.event_name == 'pull_request' && github.ref || github.sha }}
cancel-in-progress: ${{ github.event_name == 'pull_request' }}

(tests/workflows.bash asserts the exact group string and == true, so it would need the matching update.) Release and canary are unaffected either way: different workflow, own release / canary-live-feed groups with cancel-in-progress: false.


Generated by Claude Code

The shared ci-<workflow>-<ref> group with cancel-in-progress: true also
cancelled the previous main push run whenever another commit merged, so
that merged commit never got a complete CI result. Pull requests keep
their per-ref group and are superseded; pushes get one group per commit
and are never cancelled.

The workflow invariants now evaluate the configured group and
cancel-in-progress expressions for representative pull request and main
events instead of comparing a copied lambda with itself, so they fail if
main runs become cancellable or PRs share a group.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01ExmnfUngrDYjAheGvYwMnn
claude added 3 commits October 8, 2026 04:01
- compare strings case-insensitively as GitHub expressions do
- keep YAML booleans as booleans instead of stringifying them
- evaluate a lone expression once and interpolate multi-expression templates
- read the workflow name from ci.yml instead of hard-coding it
- reject pull_request branches-ignore filters as well as branches
- move the CI cancellation note under the pull request guidelines
- list CI concurrency groups among the workflow invariants

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01ExmnfUngrDYjAheGvYwMnn
@johnny4young
johnny4young merged commit 71b6346 into main Oct 8, 2026
11 checks passed
@johnny4young
johnny4young deleted the codex/maintenance-g1 branch October 8, 2026 04:50
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants