diff --git a/internal/gh/fetch.go b/internal/gh/fetch.go index a46465e..9a1ea0b 100644 --- a/internal/gh/fetch.go +++ b/internal/gh/fetch.go @@ -14,14 +14,16 @@ import ( ) const ( - maxFailingNames = 5 - runsPerWorkflow = 5 - maxWorkflows = 100 - eventPullRequest = "pull_request" - eventPullReqTgt = "pull_request_target" - statusStateFail = "failure" - statusStateError = "error" - statusStatePend = "pending" + maxFailingNames = 5 + runsPerWorkflow = 5 + // freshRunsWindowDays bounds the created filter of the confirming run query. + freshRunsWindowDays = 30 + maxWorkflows = 100 + eventPullRequest = "pull_request" + eventPullReqTgt = "pull_request_target" + statusStateFail = "failure" + statusStateError = "error" + statusStatePend = "pending" ) // backgroundEvents are triggers that run without a person pushing code. @@ -374,9 +376,11 @@ func (c *Client) workflowRuns(ctx context.Context, full string, id int64, branch path := fmt.Sprintf("/repos/%s/actions/workflows/%d/runs", full, id) get := c.getJSON if fresh { - // GitHub can keep answering one exact query from an old cached result, for weeks, whatever - // the connection. A different query string skips that entry and reads current runs. + // GitHub can keep answering a branch query from an old cached result, for weeks, whatever + // the connection, and page=1 alone does not skip it. A created filter changes the query + // every day and reads current runs. q.Set("page", "1") + q.Set("created", ">="+c.now().UTC().AddDate(0, 0, -freshRunsWindowDays).Format(time.DateOnly)) get = c.getJSONFresh } if err := get(ctx, full, path, q, &out); err != nil { diff --git a/internal/gh/gh_test.go b/internal/gh/gh_test.go index 3e2cf69..fa5671c 100644 --- a/internal/gh/gh_test.go +++ b/internal/gh/gh_test.go @@ -461,12 +461,12 @@ func TestAPIErrorMessageParsing(t *testing.T) { } // staleThenFresh models GitHub answering one exact query from an old cached result: the plain -// query returns an old failing run every time, and the same query with page=1 returns current runs. +// query returns an old failing run every time, and the same query with a created filter returns current runs. func staleThenFresh(f *fakeGitHub, path string) *atomic.Int64 { var calls atomic.Int64 f.routes[path] = func(w http.ResponseWriter, r *http.Request) { calls.Add(1) - if r.URL.Query().Get("page") == "" { + if r.URL.Query().Get("created") == "" { _, _ = w.Write([]byte(`{"workflow_runs":[ {"id":1,"html_url":"old","event":"schedule","head_branch":"main","status":"completed","conclusion":"failure","created_at":"2026-09-11T03:02:00Z"}]}`)) return