Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions .github/workflows/strix.yml
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,22 @@ jobs:

cancel-superseded-pr-runs:
if: github.event_name == 'pull_request_target' && (github.event.action == 'synchronize' || github.event.action == 'closed')
# Idempotent per PR: a fresh sweep re-verifies live state (live_target_matches
# below) before selecting or cancelling anything, so it fully subsumes
# whatever an older, not-yet-run instance would have done. cancel-in-progress
# true is the right shape here (current-head-run-coalescer.yml instead uses
# its own admission-order queueing, since each of its queued instances
# carries a DIFFERENT specific expected-head only it can act on): it caps
# this job to one running + one queued per PR instead of letting a push
# burst pile up N independent, mutually-non-deduped sweeps that each cost a
# full admission slot under the shared 60-job ceiling. Matches
# codeql-pr.yml's established group-key style (PR-number scoped).
concurrency:
group: >-
cancel-superseded-pr-runs-${{
github.event.pull_request.base.repo.full_name || github.repository }}-${{
github.event.pull_request.number || github.run_id }}
cancel-in-progress: true

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🔴 Late events cancel current cleanup

When an older webhook arrives late, cancel-in-progress: true cancels newer cleanup before either job verifies live pull-request state. The stale replacement exits, leaving obsolete scans active.

Prompt for agents
Redesign cancel-superseded-pr-runs concurrency so a delayed older pull_request_target event cannot preempt the worker for newer live state and then no-op. The current PR-scoped cancel-in-progress group cancels by admission order, while live_target_matches binds each replacement to its event payload. Consider making every cleanup instance operate idempotently from freshly fetched live PR state, including the live head or closed state, so even a stale event fully subsumes an interrupted worker. Preserve the bounded admission objective and add a regression for an old synchronize event arriving while a current synchronize or close cleanup is running.
Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

runs-on: ubuntu-24.04
# Bound this gh-api-only cleanup job so a stuck call (rate limit, hung
# `gh api --paginate`) cannot silently occupy a runner for GitHub's
Expand Down
7 changes: 6 additions & 1 deletion tests/test_required_workflow_queue_contract.py
Original file line number Diff line number Diff line change
Expand Up @@ -365,7 +365,12 @@ def test_strix_serializes_provider_evidence_per_repository_and_pr() -> None:
scans for *other* PRs to be blocked by it.
"""
workflow = workflow_text("strix.yml")
concurrency_contract = workflow.split("concurrency:", 1)[1].split(
# Isolate the strix: job's own text first: cancel-superseded-pr-runs above
# it now carries its own (PR-scoped, dedup-only) concurrency: block, so a
# naive first-match split on the bare "concurrency:" literal would grab
# that job's block instead of this one.
strix_job = workflow.split("\n strix:\n", 1)[1]
concurrency_contract = strix_job.split("concurrency:", 1)[1].split(
"permissions:", 1
)[0]

Expand Down
Loading