This is an automated email from the ASF dual-hosted git repository.

potiuk pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/airflow-steward.git


The following commit(s) were added to refs/heads/main by this push:
     new 1445c52  fix(pr-management-triage): post-filter on conclusion to find 
action_required runs (#112)
1445c52 is described below

commit 1445c52bd2b0c39248b3e049df99f29714940a6f
Author: Jarek Potiuk <[email protected]>
AuthorDate: Mon May 11 04:00:46 2026 +0200

    fix(pr-management-triage): post-filter on conclusion to find 
action_required runs (#112)
    
    The `?status=action_required` query parameter on the Actions runs
    REST endpoint does not match runs awaiting maintainer approval.
    GitHub returns those runs as `status: "completed"` with
    `conclusion: "action_required"`, so `?status=action_required`
    silently returns an empty result.
    
    Replace the broken filter in all four places it was used
    (`fetch-and-batch.md` mandatory index + three recipes in
    `actions.md`) with `?per_page=N` and a jq post-filter on
    `conclusion`. Add explanatory text in fetch-and-batch.md and
    inline comments in the recipes so a future reader does not
    reintroduce the broken filter.
    
    The `gh api -X POST .../approve` mutation itself works fine
    against runs in this state — only the discovery queries were
    broken.
---
 .claude/skills/pr-management-triage/actions.md     | 29 ++++++++++++++--------
 .../skills/pr-management-triage/fetch-and-batch.md | 13 +++++++++-
 2 files changed, 30 insertions(+), 12 deletions(-)

diff --git a/.claude/skills/pr-management-triage/actions.md 
b/.claude/skills/pr-management-triage/actions.md
index 17cb3c7..fa94b23 100644
--- a/.claude/skills/pr-management-triage/actions.md
+++ b/.claude/skills/pr-management-triage/actions.md
@@ -189,10 +189,14 @@ the maintainer queue fills with PRs whose CI has not 
actually
 executed.
 
 ```bash
-# Pre-check: index action_required runs repo-wide, then look up head SHA
+# Pre-check: index action_required runs at the head SHA.
+# Note: runs awaiting approval are returned as `status: "completed"`
+# with `conclusion: "action_required"`. The query parameter
+# `?status=action_required` matches no runs and would silently
+# return an empty result — post-filter on `conclusion` instead.
 head_sha=$(gh api "repos/<owner>/<repo>/pulls/<N>" --jq '.head.sha')
-pending=$(gh api 
"repos/<owner>/<repo>/actions/runs?head_sha=${head_sha}&status=action_required&per_page=10"
 \
-  --jq '.workflow_runs | length')
+pending=$(gh api 
"repos/<owner>/<repo>/actions/runs?head_sha=${head_sha}&per_page=20" \
+  --jq '[.workflow_runs[] | select(.conclusion == "action_required")] | 
length')
 if [ "$pending" -gt 0 ]; then
   echo "refuse mark-ready: <N> has ${pending} workflow run(s) awaiting 
approval at ${head_sha}" >&2
   # Reclassify: this PR is really pending_workflow_approval, route accordingly.
@@ -242,9 +246,12 @@ mark-ready'ing without context.
 
 ```bash
 # 1. Pre-check: refuse if any workflow run is awaiting approval (same as 
mark-ready).
+#    Runs awaiting approval surface as `status: "completed"` +
+#    `conclusion: "action_required"` — `?status=action_required` matches
+#    none of them, so post-filter on `conclusion`.
 head_sha=$(gh api "repos/<owner>/<repo>/pulls/<N>" --jq '.head.sha')
-pending=$(gh api 
"repos/<owner>/<repo>/actions/runs?head_sha=${head_sha}&status=action_required&per_page=10"
 \
-  --jq '.workflow_runs | length')
+pending=$(gh api 
"repos/<owner>/<repo>/actions/runs?head_sha=${head_sha}&per_page=20" \
+  --jq '[.workflow_runs[] | select(.conclusion == "action_required")] | 
length')
 if [ "$pending" -gt 0 ]; then
   echo "refuse mark-ready-with-ping: <N> has ${pending} workflow run(s) 
awaiting approval at ${head_sha}" >&2
   # Reclassify: this PR is really pending_workflow_approval.
@@ -438,12 +445,12 @@ protocol. Only after the maintainer confirms the diff 
looks
 non-malicious, approve:
 
 ```bash
-# List pending workflow runs for this PR
-gh api repos/<owner>/<repo>/actions/runs \
-  -X GET \
-  -f head_sha=<head_sha> \
-  -f status=action_required \
-  --jq '.workflow_runs[].id' |
+# List pending workflow runs for this PR.
+# Runs awaiting approval are returned as `status: "completed"` with
+# `conclusion: "action_required"` — `?status=action_required` matches
+# none of them. Post-filter on `conclusion` to enumerate the real set.
+gh api "repos/<owner>/<repo>/actions/runs?head_sha=<head_sha>&per_page=20" \
+  --jq '.workflow_runs[] | select(.conclusion == "action_required") | .id' |
   while read run_id; do
     gh api -X POST "repos/<owner>/<repo>/actions/runs/${run_id}/approve"
   done
diff --git a/.claude/skills/pr-management-triage/fetch-and-batch.md 
b/.claude/skills/pr-management-triage/fetch-and-batch.md
index c4d7f91..8f8735a 100644
--- a/.claude/skills/pr-management-triage/fetch-and-batch.md
+++ b/.claude/skills/pr-management-triage/fetch-and-batch.md
@@ -247,7 +247,8 @@ is the anti-pattern this file exists to prevent.
 Before classification runs, fetch one REST call per page:
 
 ```bash
-gh api 
"repos/<owner>/<repo>/actions/runs?event=pull_request&status=action_required&per_page=100"
+gh api "repos/<owner>/<repo>/actions/runs?event=pull_request&per_page=100" \
+  --jq '.workflow_runs[] | select(.conclusion == "action_required")'
 ```
 
 This lists **every** workflow run across the repo that is
@@ -256,6 +257,16 @@ any PR on the current page whose head SHA appears in the 
index
 is `pending_workflow_approval` (see
 [`classify-and-act.md#decision-table`](classify-and-act.md), row 1).
 
+**Why the post-filter, not `?status=action_required`.** The
+GitHub Actions API returns runs awaiting approval as
+`status: "completed"` with `conclusion: "action_required"` (the
+run has *finished* the queueing phase and is now blocked
+pending approval). The query parameter `?status=action_required`
+matches no runs in this state and silently returns an empty
+result, which lets `pending_workflow_approval` PRs slip through
+classification as `passing`. The post-filter on `conclusion`
+is the only correct way to enumerate them.
+
 Why this is mandatory, not "fallback":
 
 - `statusCheckRollup.state` aggregates only **completed**

Reply via email to