From cef203ded670d824d06f66895b5dacd7dd4bd22d Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 9 Jun 2026 22:01:20 +0000 Subject: [PATCH 1/2] Serialize workflow runs per PR MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Two runs can act on the same PR concurrently: the push that finishes a conflict resolution fires a synchronize event while the conflict label is still attached, so the follow-up run races the one that triggered it (the label is its gate and only comes off at the end). A per-PR concurrency group queues the follow-up instead; cancel-in-progress stays off so a running update is never killed mid-mutation. The group is keyed by PR number rather than repo-wide because GitHub keeps only the newest pending run in a group - a repo-wide group could silently drop the update for one merge when several land in quick succession. 🤖 Generated with [Claude Code](https://claude.com/claude-code) https://claude.ai/code/session_01JHvKryT4QUpHYdNq9YEQxX --- .github/workflows/update-pr-stack.yml | 7 +++++++ README.md | 8 ++++++++ 2 files changed, 15 insertions(+) diff --git a/.github/workflows/update-pr-stack.yml b/.github/workflows/update-pr-stack.yml index 87cf4ea..86f030c 100644 --- a/.github/workflows/update-pr-stack.yml +++ b/.github/workflows/update-pr-stack.yml @@ -9,6 +9,13 @@ permissions: contents: write pull-requests: write +# Serialize runs per PR: the push that finishes a conflict resolution fires a +# synchronize event while the conflict label is still attached, so without a +# group the follow-up run races the one that triggered it. +concurrency: + group: update-pr-stack-${{ github.event.pull_request.number }} + cancel-in-progress: false + jobs: update-pr-stack: runs-on: ubuntu-latest diff --git a/README.md b/README.md index 6e19752..f0737ae 100644 --- a/README.md +++ b/README.md @@ -79,6 +79,10 @@ on: pull_request: types: [closed, synchronize] +concurrency: + group: update-pr-stack-${{ github.event.pull_request.number }} + cancel-in-progress: false + jobs: update-pr-stack: runs-on: ubuntu-latest @@ -110,6 +114,10 @@ permissions: contents: write pull-requests: write +concurrency: + group: update-pr-stack-${{ github.event.pull_request.number }} + cancel-in-progress: false + jobs: update-pr-stack: runs-on: ubuntu-latest From 0f7a4b26a03334c1aac7db1d5ba06342b834523c Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 9 Jun 2026 22:14:12 +0000 Subject: [PATCH 2/2] Tolerate pending workflow runs in the e2e monitor MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A run waiting on its concurrency-group slot reports status "pending", which the wait loop treated as fatal instead of sleeping. Surfaced as soon as the per-PR concurrency group queued a merge-triggered run behind a still-running synchronize run. 🤖 Generated with [Claude Code](https://claude.com/claude-code) https://claude.ai/code/session_01JHvKryT4QUpHYdNq9YEQxX --- tests/test_e2e.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/test_e2e.sh b/tests/test_e2e.sh index 420f2cd..b6e9f19 100755 --- a/tests/test_e2e.sh +++ b/tests/test_e2e.sh @@ -558,7 +558,7 @@ wait_for_synchronize_workflow() { log_cmd gh run view "$target_run_id" --repo "$REPO_FULL_NAME" --log || echo >&2 "Could not fetch logs for run $target_run_id" return 1 fi - elif [[ "$run_status" == "queued" || "$run_status" == "in_progress" || "$run_status" == "waiting" ]]; then + elif [[ "$run_status" == "queued" || "$run_status" == "in_progress" || "$run_status" == "waiting" || "$run_status" == "pending" ]]; then echo >&2 "Workflow $target_run_id is $run_status. Sleeping $sleep_time seconds." else echo >&2 "Workflow $target_run_id has unexpected status: $run_status. Conclusion: $run_conclusion" @@ -665,7 +665,7 @@ wait_for_workflow() { log_cmd gh run view "$target_run_id" --repo "$REPO_FULL_NAME" --log || echo >&2 "Could not fetch logs for run $target_run_id" return 1 fi - elif [[ "$run_status" == "queued" || "$run_status" == "in_progress" || "$run_status" == "waiting" ]]; then + elif [[ "$run_status" == "queued" || "$run_status" == "in_progress" || "$run_status" == "waiting" || "$run_status" == "pending" ]]; then echo >&2 "Workflow $target_run_id is $run_status. Sleeping $sleep_time seconds." else echo >&2 "Workflow $target_run_id has unexpected status: $run_status. Conclusion: $run_conclusion"