From 9ac6dc662c4a8aace953e449e36b9d4bf5d3f985 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 16 Jun 2026 17:39:20 +0000 Subject: [PATCH 1/3] Initial plan From a18d005b8e571db66df03b10e5c59b0b2f1b8262 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 16 Jun 2026 17:59:07 +0000 Subject: [PATCH 2/3] Improve github_api_calls provenance for snapshot-only runs Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com> --- pkg/cli/logs_github_rate_limit_usage.go | 47 +++++++++++++++----- pkg/cli/logs_github_rate_limit_usage_test.go | 22 +++++++++ 2 files changed, 57 insertions(+), 12 deletions(-) diff --git a/pkg/cli/logs_github_rate_limit_usage.go b/pkg/cli/logs_github_rate_limit_usage.go index 84fe753f8e5..6804235b383 100644 --- a/pkg/cli/logs_github_rate_limit_usage.go +++ b/pkg/cli/logs_github_rate_limit_usage.go @@ -55,11 +55,12 @@ type GitHubRateLimitResourceUsage struct { // by a single workflow run. It is populated by parsing the github_rate_limits.jsonl // artifact produced during the run. type GitHubRateLimitUsage struct { - TotalRequestsMade int `json:"total_requests_made" console:"header:Total GitHub API Calls,format:number"` - CoreConsumed int `json:"core_consumed" console:"header:Core Quota Consumed,format:number"` - CoreRemaining int `json:"core_remaining" console:"header:Core Remaining,format:number"` - CoreLimit int `json:"core_limit" console:"header:Core Limit,format:number"` - Resources []*GitHubRateLimitResourceUsage `json:"resources,omitempty"` + TotalRequestsMade int `json:"total_requests_made" console:"header:Total GitHub API Calls,format:number"` + CoreConsumed int `json:"core_consumed" console:"header:Core Quota Consumed,format:number"` + CoreConsumedSource string `json:"core_consumed_source,omitempty" console:"-"` + CoreRemaining int `json:"core_remaining" console:"header:Core Remaining,format:number"` + CoreLimit int `json:"core_limit" console:"header:Core Limit,format:number"` + Resources []*GitHubRateLimitResourceUsage `json:"resources,omitempty"` } // ResourceRows returns per-resource rows sorted by total requests made descending, @@ -130,13 +131,16 @@ func parseGitHubRateLimitsFile(filePath string) (*GitHubRateLimitUsage, error) { defer file.Close() type resourceState struct { - requestsMade int - firstRemaining int - lastRemaining int - firstUsed int - lastUsed int - limit int - firstEntrySet bool + requestsMade int + firstRemaining int + lastRemaining int + firstUsed int + lastUsed int + firstSnapshotUsed int + lastSnapshotUsed int + snapshotCount int + limit int + firstEntrySet bool } byResource := make(map[string]*resourceState) @@ -194,6 +198,11 @@ func parseGitHubRateLimitsFile(filePath string) (*GitHubRateLimitUsage, error) { state = &resourceState{} byResource[resource] = state } + state.snapshotCount++ + if state.snapshotCount == 1 { + state.firstSnapshotUsed = entry.Used + } + state.lastSnapshotUsed = entry.Used if entry.Limit > 0 && state.limit == 0 { state.limit = entry.Limit } @@ -226,6 +235,7 @@ func parseGitHubRateLimitsFile(filePath string) (*GitHubRateLimitUsage, error) { // If used values suggest a window reset occurred (lastUsed < firstUsed), // fall back to using the absolute lastUsed value as the consumption metric. var consumed int + consumedSource := "" if state.requestsMade > 0 { diff := state.lastUsed - state.firstUsed if diff >= 0 { @@ -234,6 +244,18 @@ func parseGitHubRateLimitsFile(filePath string) (*GitHubRateLimitUsage, error) { // Window reset mid-run; use lastUsed as a lower-bound estimate consumed = state.lastUsed } + consumedSource = "response_headers_delta" + } else if state.snapshotCount >= 2 { + diff := state.lastSnapshotUsed - state.firstSnapshotUsed + if diff >= 0 { + consumed = diff + } else { + // Window reset across snapshots; use last snapshot used as a lower-bound estimate + consumed = state.lastSnapshotUsed + } + consumedSource = "rate_limit_api_delta" + } else if state.snapshotCount == 1 { + consumedSource = "rate_limit_api_single_snapshot" } row := &GitHubRateLimitResourceUsage{ @@ -247,6 +269,7 @@ func parseGitHubRateLimitsFile(filePath string) (*GitHubRateLimitUsage, error) { if resource == "core" { usage.CoreConsumed = consumed + usage.CoreConsumedSource = consumedSource usage.CoreRemaining = state.lastRemaining usage.CoreLimit = state.limit } diff --git a/pkg/cli/logs_github_rate_limit_usage_test.go b/pkg/cli/logs_github_rate_limit_usage_test.go index a6a19a893b0..47234538913 100644 --- a/pkg/cli/logs_github_rate_limit_usage_test.go +++ b/pkg/cli/logs_github_rate_limit_usage_test.go @@ -31,6 +31,7 @@ func TestParseGitHubRateLimitsFileBasic(t *testing.T) { // Core resource: 2 calls, consumed = lastUsed(120) - firstUsed(110) = 10 assert.Equal(t, 10, usage.CoreConsumed, "core quota consumed should be 10") + assert.Equal(t, "response_headers_delta", usage.CoreConsumedSource, "core consumed source should come from response headers") assert.Equal(t, 4880, usage.CoreRemaining, "core remaining should match last entry") assert.Equal(t, 5000, usage.CoreLimit, "core limit should be 5000") @@ -103,6 +104,27 @@ func TestParseGitHubRateLimitsFileOnlyAPISnapshots(t *testing.T) { require.NotNil(t, usage, "usage should not be nil") assert.Equal(t, 0, usage.TotalRequestsMade, "should count 0 API calls from response_headers") + assert.Equal(t, 10, usage.CoreConsumed, "core consumed should be derived from rate_limit_api snapshot delta") + assert.Equal(t, "rate_limit_api_delta", usage.CoreConsumedSource, "core consumed source should come from rate_limit_api snapshots") +} + +// TestParseGitHubRateLimitsFileOnlyAPISnapshotsWindowReset verifies snapshot-only +// runs still produce a lower-bound consumption value when the window resets. +func TestParseGitHubRateLimitsFileOnlyAPISnapshotsWindowReset(t *testing.T) { + content := `{"timestamp":"2026-04-05T08:00:00.000Z","source":"rate_limit_api","operation":"startup","resource":"core","limit":5000,"remaining":100,"used":4900,"reset":"2026-04-05T09:00:00.000Z"} +{"timestamp":"2026-04-05T09:00:10.000Z","source":"rate_limit_api","operation":"shutdown","resource":"core","limit":5000,"remaining":4995,"used":5,"reset":"2026-04-05T10:00:00.000Z"} +` + dir := t.TempDir() + path := filepath.Join(dir, "github_rate_limits.jsonl") + require.NoError(t, os.WriteFile(path, []byte(content), 0600), "should write test JSONL file") + + usage, err := parseGitHubRateLimitsFile(path) + require.NoError(t, err, "should not return an error") + require.NotNil(t, usage, "usage should not be nil") + + assert.Equal(t, 0, usage.TotalRequestsMade, "should count 0 API calls from response_headers") + assert.Equal(t, 5, usage.CoreConsumed, "window reset should fall back to last snapshot used value") + assert.Equal(t, "rate_limit_api_delta", usage.CoreConsumedSource, "core consumed source should come from rate_limit_api snapshots") } // TestFindGitHubRateLimitsFileAbsent verifies that findGitHubRateLimitsFile returns From 0346fa7123dbe19a7a780bb83359eb1c20153df8 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 16 Jun 2026 20:20:08 +0000 Subject: [PATCH 3/3] fix: update snapshot comment and add single-snapshot regression test Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com> --- pkg/cli/logs_github_rate_limit_usage.go | 5 +++-- pkg/cli/logs_github_rate_limit_usage_test.go | 21 ++++++++++++++++++++ 2 files changed, 24 insertions(+), 2 deletions(-) diff --git a/pkg/cli/logs_github_rate_limit_usage.go b/pkg/cli/logs_github_rate_limit_usage.go index 6804235b383..c922851386a 100644 --- a/pkg/cli/logs_github_rate_limit_usage.go +++ b/pkg/cli/logs_github_rate_limit_usage.go @@ -191,8 +191,9 @@ func parseGitHubRateLimitsFile(filePath string) (*GitHubRateLimitUsage, error) { state.limit = entry.Limit } case "rate_limit_api": - // Use rate-limit API snapshots to fill in limit and remaining when we - // have no response-header entries for this resource yet. + // Use rate-limit API snapshots to fill in limit and remaining, and to + // derive core_consumed via firstSnapshotUsed/lastSnapshotUsed when no + // response-header entries are present for this resource. state, ok := byResource[resource] if !ok { state = &resourceState{} diff --git a/pkg/cli/logs_github_rate_limit_usage_test.go b/pkg/cli/logs_github_rate_limit_usage_test.go index 47234538913..b7862aab86d 100644 --- a/pkg/cli/logs_github_rate_limit_usage_test.go +++ b/pkg/cli/logs_github_rate_limit_usage_test.go @@ -127,6 +127,27 @@ func TestParseGitHubRateLimitsFileOnlyAPISnapshotsWindowReset(t *testing.T) { assert.Equal(t, "rate_limit_api_delta", usage.CoreConsumedSource, "core consumed source should come from rate_limit_api snapshots") } +// TestParseGitHubRateLimitsFileOnlyAPISnapshotSingle verifies that a file containing +// exactly one rate_limit_api snapshot (no response_headers) produces CoreConsumed==0 +// with provenance tagged as rate_limit_api_single_snapshot. +func TestParseGitHubRateLimitsFileOnlyAPISnapshotSingle(t *testing.T) { + content := `{"timestamp":"2026-04-05T08:00:00.000Z","source":"rate_limit_api","operation":"startup","resource":"core","limit":5000,"remaining":4850,"used":150,"reset":"2026-04-05T09:00:00.000Z"} +` + dir := t.TempDir() + path := filepath.Join(dir, "github_rate_limits.jsonl") + require.NoError(t, os.WriteFile(path, []byte(content), 0600), "should write test JSONL file") + + usage, err := parseGitHubRateLimitsFile(path) + require.NoError(t, err, "should not return an error") + require.NotNil(t, usage, "usage should not be nil") + + assert.Equal(t, 0, usage.TotalRequestsMade, "should count 0 API calls from response_headers") + assert.Equal(t, 0, usage.CoreConsumed, "single snapshot cannot compute a delta; consumed should be 0") + assert.Equal(t, "rate_limit_api_single_snapshot", usage.CoreConsumedSource, "provenance should be rate_limit_api_single_snapshot") + assert.Equal(t, 4850, usage.CoreRemaining, "core remaining should match snapshot value") + assert.Equal(t, 5000, usage.CoreLimit, "core limit should match snapshot value") +} + // TestFindGitHubRateLimitsFileAbsent verifies that findGitHubRateLimitsFile returns // an empty string when the file does not exist. func TestFindGitHubRateLimitsFileAbsent(t *testing.T) {