LearnNewsExamplesServices
Frontmatter
id15593
titlePrevent stale CI reruns from canceling the current PR head
stateClosed
labels
bugaitestingbuild
assigneesneo-gpt
createdAtJul 19, 2026, 11:00 PM
updatedAtJul 19, 2026, 11:40 PM
githubUrlhttps://github.com/neomjs/neo/issues/15593
authorneo-gpt
commentsCount3
parentIssuenull
subIssues[]
subIssuesCompleted0
subIssuesTotal0
contentTrust
projected
quarantined0
signals[]
blockedBy[]
blocking[]
closedAtJul 19, 2026, 11:40 PM

Prevent stale CI reruns from canceling the current PR head

Closed Backlog/active-chunk-8 bugaitestingbuild
neo-gpt
neo-gpt commented on Jul 19, 2026, 11:00 PM

Context

While unblocking PR #15588, the original head 65304b6e1b5f12268edc61ca9f618e8a0bbc12dd failed the full unit job. The author pushed the contract-test repair as current head 54a8ba339e5f9a77a46ffd0c409056d2331102bf, which started Tests run 29703195785. A failed-job rerun of the obsolete run 29702552420 then began at 2026-07-19T20:50:01Z. The newer head's unit and integration jobs were canceled at 20:49:56Z, leaving the current PR head red even though its focused contract suites pass 50/50 locally.

This is not an adapter defect. It is a CI arbitration defect exposed by a normal maintainer action.

Live latest-open sweep: checked the latest 20 open issues at 2026-07-19T20:59Z; no equivalent found. The recent all-state A2A claim sweep found no competing lane. KB ticket search and exact archive/discussion searches found no prior ticket for stale reruns canceling newer-head CI.

The Problem

.github/workflows/test.yml:9-11 groups every attempt for one pull-request ref together and sets cancel-in-progress: true:

concurrency:
  group: tests-${{ github.workflow }}-${{ github.ref }}
  cancel-in-progress: true

That correctly lets a newly pushed head supersede tests for the previous head. It does not distinguish a new head from a later rerun of an old workflow attempt.

GitHub's documented semantics make the collision deterministic:

Because the group contains only the stable PR ref, dispatch recency wins over commit freshness. An obsolete rerun dispatched later can therefore kill the only run for the current head. Sixteen workflows currently use cancel-in-progress: true; the same ref-only shape is present across the PR CI family.

The Architectural Reality

  • Workflow-run arbitration belongs in .github/workflows/*.yml, not in application code or the GitHub Workflow MCP service.
  • .github/workflows/test.yml already has a cheap changes classifier before the expensive unit and integration jobs; that is the natural current-head admission seam.
  • Normal first-attempt supersession is valuable and must remain: a new commit should cancel expensive work for the previous commit.
  • Rerun attempts expose github.run_attempt and preserve their original github.run_id; those are the available attempt-authority inputs at workflow-concurrency evaluation time.
  • The Agent OS structure map does not inventory .github/workflows; direct inspection found the owner plus fifteen sibling workflows with cancel-in-progress: true. This ticket owns that CI family rather than creating an ai/ service.
  • Exact-head review routing depends on current-head CI. A stale run may remain visible historically, but it must never cancel or substitute for the current head's checks.

The Fix

  1. Define a monotonic concurrency convention for PR workflows:
    • initial attempts for the same workflow/ref remain in one “head stream,” preserving new-head cancellation;
    • rerun attempts are isolated from that stream using attempt/run identity, so an obsolete rerun cannot cancel a newer initial run;
    • repeated attempts of the same original run may still supersede one another.
  2. Apply the convention to every PR workflow using the vulnerable ref-only + cancel-in-progress: true shape, or record an explicit, evidence-backed exclusion.
  3. In the high-cost Tests workflow, extend the cheap classifier/admission job to compare the event payload head SHA with the live PR head before expensive jobs start. A stale rerun exits cheaply with an explicit summary; a current-head rerun remains valid.
  4. Add mechanical regression coverage for the workflow family so later ref-only cancellation groups cannot silently reintroduce the asymmetry.
  5. Preserve push-to-dev behavior and current permissions; the freshness check needs only pull-request read access.

Contract Ledger

Target surface Source of authority Proposed behavior Fallback Docs Evidence
PR workflow concurrency group GitHub concurrency + rerun context semantics newest initial head supersedes older initial work; reruns cannot supersede a different head non-PR runs retain a unique/safe fallback group inline workflow rationale static contract test + controlled PR sequence
Tests current-head admission live PR head.sha via actions/github-script expensive jobs run only for the current PR head API failure fails closed before expensive work with visible reason job summary mocked classifier test + live run receipt
CI-green reviewer gate current-head check suite only current-head results decide routing stale historical attempts remain non-authoritative existing pull-request workflow docs remain authoritative gh pr checks on controlled heads

Decision Record impact

None. This is an operational GitHub Actions correctness rule and does not amend or challenge an accepted ADR.

Acceptance Criteria

  • Reproduce and retain a two-head receipt where rerunning the old head after the new head starts does not cancel any current-head job.
  • A normal new initial head still cancels the previous head's initial in-progress workflow, preserving the existing compute-saving behavior.
  • Rerun attempts are isolated from the current initial-head stream while repeated attempts of the same original run remain bounded.
  • The Tests classifier compares event-head to live PR-head before unit/integration execution; a stale rerun skips expensive jobs with an explicit reason.
  • A current-head job can be rerun successfully through the same job-level attempt path used by failed-job reruns, and the complete current-head check set remains green.
  • All PR workflows with ref-only cancel-in-progress: true are updated or carry an explicit exclusion justified by a falsifier.
  • Mechanical coverage fails when a protected PR workflow regresses to a concurrency key that lets a stale rerun collide with a different head.
  • push runs on dev, workflow permissions, path classification, and check names remain unchanged.

Out of Scope

  • Removing maintainers' permission to rerun workflows.
  • Treating an obsolete run as valid evidence for a newer head.
  • Replacing GitHub Actions or changing branch-protection policy.
  • Serializing all PR CI with queue: max; current-head latency must not regress.
  • Refactoring unrelated workflow steps while sweeping concurrency keys.

Avoided Traps

  • Append only the head SHA to every group: prevents the wrong cancellation but also prevents a new head from canceling expensive old-head work.
  • Disable cancellation or use a FIFO queue: preserves evidence but delays the current head behind obsolete work and still spends the compute.
  • Rely on maintainer discipline: “rerun only the latest run” is a social workaround, not a correctness boundary.
  • Accept local green as replacement: the current GitHub head still needs its own non-canceled required checks.

Related

PR #15588 (live reproducer) · .github/workflows/test.yml · GitHub Actions runs 29702552420 and 29703195785.

Origin Session ID: b4496dab-2fb9-4548-9293-78b4a3d78f60

Retrieval Hint: stale old-head GitHub Actions rerun cancels current PR head cancel-in-progress github.ref run_attempt run_id