From 21b61f2e9147d8ef235bd4e3b98541e54d9994e0 Mon Sep 17 00:00:00 2001 From: Matthew Smith Date: Wed, 14 Oct 2026 17:31:59 -0700 Subject: [PATCH] fix(ci): rerun the newest failed run for a sha, not the newest run rerunFailedChecks asked for the newest workflow run on the head commit. That is often a skipped or green workflow, and rerunning one is a 403 ("This workflow run cannot be retried"). The 403 threw before the ledger line was written, so the daemon retried it every tick with backoff and never reached the builder fix round. Filter the query on status=failure. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01DL7BBw37FuBwVdPDMFT4MD --- src/github.test.ts | 6 +++--- src/github.ts | 9 ++++++--- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/src/github.test.ts b/src/github.test.ts index 5268d45..4dc24a0 100644 --- a/src/github.test.ts +++ b/src/github.test.ts @@ -337,7 +337,7 @@ describe("GitHub writes", () => { "--delete-branch", ]); }); - it("rerunFailedChecks posts to the newest run for the sha", async () => { + it("rerunFailedChecks posts to the newest failed run for the sha", async () => { const calls: string[][] = []; const gh = new GitHub(cfg, fakeExec(calls), false); const sha = "aaaa1111bbbb2222cccc3333dddd4444eeee5555"; @@ -345,7 +345,7 @@ describe("GitHub writes", () => { expect(calls[0]).toEqual([ "gh", "api", - `repos/${cfg.repo}/actions/runs?head_sha=${sha}&per_page=1`, + `repos/${cfg.repo}/actions/runs?head_sha=${sha}&status=failure&per_page=1`, "--jq", ".workflow_runs[0].id // empty", ]); @@ -357,7 +357,7 @@ describe("GitHub writes", () => { `repos/${cfg.repo}/actions/runs/77/rerun-failed-jobs`, ]); }); - it("rerunFailedChecks answers null when the commit has no run, and reruns nothing", async () => { + it("rerunFailedChecks answers null when the commit has no failed run, and reruns nothing", async () => { const calls: string[][] = []; const exec: Exec = async (cmd, args) => { calls.push([cmd, ...args]); diff --git a/src/github.ts b/src/github.ts index ef6650d..5564bf1 100644 --- a/src/github.ts +++ b/src/github.ts @@ -345,15 +345,18 @@ export class GitHub { } /** - * Reruns the failed jobs of the newest workflow run for `sha`, and answers with its run id (or - * null when GitHub has no run for that commit). REST rather than GraphQL on purpose: the + * Reruns the failed jobs of the newest FAILED workflow run for `sha`, and answers with its run id + * (or null when GitHub has no failed run for that commit). Filtered on `status=failure`, because + * the newest run for a sha is often a skipped or green workflow, and rerunning one of those is a + * 403 ("This workflow run cannot be retried") that threw before the ledger line was written, so + * the daemon retried it every tick. REST rather than GraphQL on purpose: the * tick's GraphQL budget is for the board reads, and this only fires on a red PR. */ async rerunFailedChecks(sha: string): Promise { const found = ( await this.gh([ "api", - `repos/${this.cfg.repo}/actions/runs?head_sha=${sha}&per_page=1`, + `repos/${this.cfg.repo}/actions/runs?head_sha=${sha}&status=failure&per_page=1`, "--jq", ".workflow_runs[0].id // empty", ])