Skip to content

fix: make checkAndWaitForRateLimit context-cancellable#43848

Merged
pelikhan merged 12 commits into
mainfrom
copilot/deep-report-make-check-and-wait-context-cancellabl
Jul 7, 2026
Merged

fix: make checkAndWaitForRateLimit context-cancellable#43848
pelikhan merged 12 commits into
mainfrom
copilot/deep-report-make-check-and-wait-context-cancellabl

Conversation

Copilot AI commented Jul 6, 2026

Copy link
Copy Markdown
Contributor

checkAndWaitForRateLimit used bare time.Sleep calls that could block for up to ~60 minutes waiting for a GitHub API rate-limit reset, completely ignoring the context already held by DownloadWorkflowLogs. Ctrl-C had no effect during this wait.

Changes

  • pkg/cli/logs_rate_limit.go — Add ctx context.Context as first parameter to checkAndWaitForRateLimit. Extract a sleepWithContext helper that replaces all four time.Sleep calls:

    func sleepWithContext(ctx context.Context, d time.Duration) error {
        timer := time.NewTimer(d)
        defer timer.Stop()
        select {
        case <-timer.C:
            return nil
        case <-ctx.Done():
            return ctx.Err()
        }
    }
  • pkg/cli/logs_orchestrator.go — Pass activeCtx at the call site; treat returned context.Canceled/context.DeadlineExceeded errors as a continue so the existing top-of-loop activeCtx.Done() check handles graceful shutdown.

  • pkg/cli/logs_rate_limit_test.go — Add unit tests for sleepWithContext covering: already-cancelled context, deadline expiry, and normal timer fire.


Generated by 👨‍🍳 PR Sous Chef · 6.99 AIC · ⌖ 5.55 AIC · ⊞ 7.1K ·
Comment /souschef to run again

Copilot AI and others added 2 commits July 6, 2026 18:18
Replace the 4 bare time.Sleep calls in checkAndWaitForRateLimit with
a new sleepWithContext helper that selects on both the timer and
ctx.Done(). Add ctx context.Context as the first parameter and update
the call site in logs_orchestrator.go to pass activeCtx so that
Ctrl-C or a deadline wakes the rate-limit wait immediately.

Also propagate context errors from checkAndWaitForRateLimit in the
orchestrator loop so the top-of-loop cancellation check handles the
graceful shutdown path.

Closes #43812

Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
…ndling

Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
Copilot AI changed the title [WIP] Make checkAndWaitForRateLimit context-cancellable fix: make checkAndWaitForRateLimit context-cancellable Jul 6, 2026
Copilot AI requested a review from pelikhan July 6, 2026 18:20
@github-actions

github-actions Bot commented Jul 6, 2026

Copy link
Copy Markdown
Contributor

🤖 PR Triage

Field Value
Category bug
Risk 🟡 medium
Score 55 / 100 (impact 25 · urgency 15 · quality 15)
Action 🕐 defer

Summary: DRAFT. Makes checkAndWaitForRateLimit context-cancellable so Ctrl-C works during rate-limit waits (up to ~60 min). 3 files (implementation + test). No CI yet.

Merge when promoted from draft and CI passes.

Generated by 🔧 PR Triage Agent · 136.6 AIC · ⌖ 9.03 AIC · ⊞ 5.5K ·

@pelikhan pelikhan marked this pull request as ready for review July 6, 2026 20:33
Copilot AI review requested due to automatic review settings July 6, 2026 20:33
@github-actions

github-actions Bot commented Jul 6, 2026

Copy link
Copy Markdown
Contributor

Test Quality Sentinel completed test quality analysis.

@github-actions

github-actions Bot commented Jul 6, 2026

Copy link
Copy Markdown
Contributor

🧠 Matt Pocock Skills Reviewer has completed the skills-based review. ✅

@github-actions

github-actions Bot commented Jul 6, 2026

Copy link
Copy Markdown
Contributor

Design Decision Gate 🏗️ completed the design decision gate check.

No ADR enforcement needed: PR #43848 does not have the 'implementation' label and has only 68 new lines of code in business logic directories (≤100 threshold).

@github-actions

github-actions Bot commented Jul 6, 2026

Copy link
Copy Markdown
Contributor

PR Code Quality Reviewer completed the code quality review.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This pull request makes the log-download rate-limit waiting logic context-cancellable so Ctrl-C (or deadlines) can interrupt long waits instead of blocking on time.Sleep.

Changes:

  • Added sleepWithContext(ctx, d) and updated checkAndWaitForRateLimit to accept a context.Context and use context-aware sleeping.
  • Updated the logs orchestrator to pass activeCtx and treat context cancellation/timeouts during rate-limit waits as a normal loop continuation toward graceful shutdown.
  • Added unit tests covering sleepWithContext cancellation, deadline expiry, and normal timer completion.
Show a summary per file
File Description
pkg/cli/logs_rate_limit.go Introduces context-aware sleeping and threads ctx through rate-limit wait logic.
pkg/cli/logs_orchestrator.go Passes activeCtx into rate-limit waiting and handles ctx-cancellation/timeout outcomes gracefully.
pkg/cli/logs_rate_limit_test.go Adds unit tests validating sleepWithContext behavior under cancellation/deadlines and normal completion.

Review details

Tip

Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

  • Files reviewed: 3/3 changed files
  • Comments generated: 1
  • Review effort level: Low

Comment on lines +77 to +80
func sleepWithContext(ctx context.Context, d time.Duration) error {
timer := time.NewTimer(d)
defer timer.Stop()
select {
@github-actions github-actions Bot mentioned this pull request Jul 6, 2026
@github-actions

github-actions Bot commented Jul 6, 2026

Copy link
Copy Markdown
Contributor

🧪 Test Quality Sentinel Report

Test Quality Score: 85/100 — Excellent

Analyzed 6 test(s): 6 design, 0 implementation, 0 violation(s).

📊 Metrics (6 tests)
Metric Value
Analyzed 6 (Go: 6, JS: 0)
✅ Design 6 (100%)
⚠️ Implementation 0 (0%)
Edge/error coverage 3 (50%)
Duplicate clusters 0
Inflation No (1.48:1)
🚨 Violations 0
Test File Classification Issues
TestRateLimitResponseUnmarshal logs_rate_limit_test.go:18 design_test Happy-path only; malformed JSON not exercised
TestRateLimitThresholdConstants logs_rate_limit_test.go:48 design_test None
TestRateLimitResourceIsBelowThreshold logs_rate_limit_test.go:56 design_test Table-driven (4 rows, boundary + edge)
TestSleepWithContextCancellation logs_rate_limit_test.go:90 design_test None — core PR fix coverage
TestSleepWithContextDeadlineExceeded logs_rate_limit_test.go:104 design_test None — deadline edge case
TestSleepWithContextTimerFires logs_rate_limit_test.go:118 design_test Happy-path; completes the 3-way sleepWithContext coverage

Verdict

Passed. 0% implementation tests (threshold: 30%). No guideline violations. The three sleepWithContext tests directly target the PR's stated fix (context-cancellable sleep), and the boundary table-test for RateLimitThreshold provides genuine regression protection. Build tag //go:build !integration is present. No forbidden mocks detected.

References:

Warning

Firewall blocked 1 domain

The following domain was blocked by the firewall during workflow execution:

  • awmgmcpg

To allow these domains, add them to the network.allowed list in your workflow frontmatter:

network:
  allowed:
    - defaults
    - "awmgmcpg"

See Network Configuration for more information.

🧪 Test quality analysis by Test Quality Sentinel · 34.7 AIC · ⌖ 12.3 AIC · ⊞ 6.8K ·
Comment /review to run again

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ Test Quality Sentinel: 85/100. 0% implementation tests (threshold: 30%).

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The implementation is clean and correct.

  • sleepWithContext correctly uses time.NewTimer + select on ctx.Done() with defer timer.Stop() to prevent timer leaks.
  • All four time.Sleep call sites are replaced, so no sleep can block Ctrl-C.
  • The continue on context cancellation (orchestrator line 374) is safe: the top-of-loop select { case <-activeCtx.Done() } check immediately catches the cancelled context and exits gracefully, and iteration is not incremented, preserving loop invariants.
  • Unit tests cover the three relevant cases: already-cancelled context, deadline expiry, and normal timer fire.

No issues found.

🧵 Reviewed using Impeccable skills by Impeccable Skills Reviewer · 37.9 AIC · ⌖ 5.97 AIC · ⊞ 4.9K

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Non-blocking observation

The elapsed < time.Second timing threshold in TestSleepWithContextCancellation (and similarly in TestSleepWithContextDeadlineExceeded) is too loose to verify that cancellation is actually prompt — a 1-second bound only catches catastrophic regressions. Tightening to ~100ms would give the tests real signal. See inline comment for the suggested change.

Everything else is clean and correct: the sleepWithContext helper is idiomatic, all four former time.Sleep callsites are covered, the defer timer.Stop() is placed correctly, and the orchestrator continue path correctly routes through the existing top-of-loop activeCtx.Done() check.

🔎 Code quality review by PR Code Quality Reviewer · 55.7 AIC · ⌖ 6.16 AIC · ⊞ 5.5K
Comment /review to run again

Comments that could not be inline-anchored

pkg/cli/logs_rate_limit_test.go:129

Test timing threshold is too loose to verify promptness: elapsed &lt; time.Second would pass even if cancellation took 900ms, giving no signal about actual responsiveness.

<details>
<summary>💡 Suggested fix</summary>

Tighten both the Cancellation and DeadlineExceeded tests to assert promptness:

// In TestSleepWithContextCancellation
assert.Less(t, elapsed, 100*time.Millisecond, &quot;should return near-instantly on already-cancelled context&quot;)

// In TestSleepWithContextDeadlineExcee…

</details>

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Skills-Based Review 🧠

Applied /diagnosing-bugs and /tdd — overall a clean, well-motivated fix. Leaving as COMMENT rather than APPROVE due to a few actionable items.

📋 Key Themes & Highlights

Key Themes

  • Missing regression test for the root bug: sleepWithContext is well-tested in isolation, but there's no test proving checkAndWaitForRateLimit as a whole unblocks on cancellation. The bug (blocking 60 min through Ctrl-C) can't be regression-tested by the unit tests alone because fetchRateLimit is not injectable.
  • Silent error discard in the fetch-failure path: when fetchRateLimit fails and the context is also cancelled, the original fetch error is dropped; the caller only sees context.Canceled.
  • continue comment clarity: the continue in the orchestrator is correct but the reason (preserve Canceled vs DeadlineExceeded branching above) deserves a tighter comment to prevent a well-meaning simplification.
  • Potential flakiness in TestSleepWithContextTimerFires: 1 ms is at the edge of OS timer resolution on loaded CI machines.

Positive Highlights

  • sleepWithContext is a deep module: tiny interface (3 lines), handles all three cases (cancel, deadline, normal), documented clearly.
  • ✅ All four time.Sleep call sites updated consistently — no partial fix.
  • ✅ Three unit tests cover the three observable paths of the helper.
  • ✅ PR description is precise and accurate: root cause, fix, and tests all described correctly.
  • defer timer.Stop() is present — no timer leak on cancellation.

🧠 Reviewed using Matt Pocock's skills by Matt Pocock Skills Reviewer · 77.6 AIC · ⌖ 5.6 AIC · ⊞ 6.7K
Comment /matt to run again

if errors.Is(rlErr, context.Canceled) || errors.Is(rlErr, context.DeadlineExceeded) {
// Context was cancelled or timed out during the rate-limit wait;
// let the top-of-loop check handle the graceful exit path.
continue

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[/diagnosing-bugs] The continue here is correct — it routes through the top-of-loop activeCtx.Done() check, which preserves the Canceled vs DeadlineExceeded distinction. However, a future maintainer might replace it with break outerLoop, which would silently suppress the "Operation cancelled" message and treat Ctrl-C as a successful run. The comment should make this trade-off explicit.

💡 Suggested comment improvement
// Context was cancelled or timed out during the rate-limit wait.
// Use continue (not break outerLoop) so the top-of-loop activeCtx.Done()
// handler can distinguish context.Canceled (user Ctrl-C → return error) from
// context.DeadlineExceeded (our own timeout → set timeoutReached, break cleanly).
continue

@copilot please address this.

// Best-effort: fall back to static cooldown so the caller can continue.
logsRateLimitLog.Printf("Could not fetch rate limit, using static cooldown: %v", err)
time.Sleep(APICallCooldown)
if sleepErr := sleepWithContext(ctx, APICallCooldown); sleepErr != nil {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[/diagnosing-bugs] When fetchRateLimit fails, the function sleeps the cooldown and then returns the original fetchRateLimit error. If sleepWithContext also returns an error (context cancelled), the original rate-limit fetch error is silently swallowed — the caller only sees context.Canceled, losing the root cause.

💡 Suggested fix

Consider wrapping both errors:

if sleepErr := sleepWithContext(ctx, APICallCooldown); sleepErr != nil {
    return fmt.Errorf("%w (during cooldown after rate-limit fetch failure: %v)", sleepErr, err)
}
return err

Or at minimum document that err is dropped when the context is cancelled so future readers don't wonder.

@copilot please address this.

Comment thread pkg/cli/logs_rate_limit_test.go Outdated
// timer fires before context cancellation.
func TestSleepWithContextTimerFires(t *testing.T) {
ctx := context.Background()
err := sleepWithContext(ctx, time.Millisecond)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[/tdd] TestSleepWithContextTimerFires uses a 1 ms sleep — this can be flaky on a heavily loaded CI runner where timer resolution is coarser. The other two tests correctly avoid real waits by pre-cancelling; this test exercises the normal path, but a 1 ms timer fire is right on the edge of OS scheduler jitter.

💡 Suggestion

Use a slightly longer duration (e.g. 5*time.Millisecond) and set an upper-bound assertion to prevent future regressions from the test accidentally blocking:

start := time.Now()
err := sleepWithContext(ctx, 5*time.Millisecond)
elapsed := time.Since(start)
require.NoError(t, err)
assert.Less(t, elapsed, time.Second, "timer should have fired, not blocked")

Adding the upper-bound check also makes the test self-documenting — it proves the function returned because the timer fired, not because something else unblocked it.

@copilot please address this.

cancel() // cancel immediately

start := time.Now()
err := sleepWithContext(ctx, 10*time.Second)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[/tdd] Missing a test for the integration between checkAndWaitForRateLimit and cancellation. The three new sleepWithContext tests are solid unit tests, but there's no test verifying that checkAndWaitForRateLimit itself propagates a cancelled context correctly when the rate-limit fetch returns data indicating a long wait.

💡 Suggested test sketch
func TestCheckAndWaitForRateLimitContextCancelled(t *testing.T) {
    // Patch fetchRateLimit to return a future reset time requiring a long wait
    // (using a test double or by stubbing the HTTP call), then cancel the context.
    // Verify that checkAndWaitForRateLimit returns context.Canceled promptly.
}

This is the regression test /diagnosing-bugs recommends: prove the root bug (blocking for up to 60 min despite Ctrl-C) cannot reappear without the test catching it.

@copilot please address this.

@gh-aw-bot

Copy link
Copy Markdown
Collaborator

@copilot please run the pr-finisher skill, address unresolved review comments, and rerun checks once the branch is up to date.

Generated by 👨‍🍳 PR Sous Chef · 8.4 AIC · ⌖ 5.03 AIC · ⊞ 4.6K ·
Comment /souschef to run again

Copilot AI and others added 8 commits July 6, 2026 21:36
Co-authored-by: gh-aw-bot <259018956+gh-aw-bot@users.noreply.github.com>
Co-authored-by: gh-aw-bot <259018956+gh-aw-bot@users.noreply.github.com>
Co-authored-by: gh-aw-bot <259018956+gh-aw-bot@users.noreply.github.com>
Co-authored-by: gh-aw-bot <259018956+gh-aw-bot@users.noreply.github.com>
Co-authored-by: gh-aw-bot <259018956+gh-aw-bot@users.noreply.github.com>
Co-authored-by: gh-aw-bot <259018956+gh-aw-bot@users.noreply.github.com>
Co-authored-by: gh-aw-bot <259018956+gh-aw-bot@users.noreply.github.com>
Co-authored-by: gh-aw-bot <259018956+gh-aw-bot@users.noreply.github.com>

Copilot AI commented Jul 6, 2026

Copy link
Copy Markdown
Contributor Author

@copilot please run the pr-finisher skill, address unresolved review comments, and rerun checks once the branch is up to date.

...

Addressed the unresolved review feedback in commit abcf075 (plus follow-up test commits on this branch), including nil-context safety, early-cancel fast-path handling, joined error propagation, clearer orchestrator intent, and expanded cancellation tests.

Reran local checks on the updated branch:

  • make fmt
  • make lint
  • make test-unit
  • make test
  • make recompile

Current PR workflows for this head SHA are in action_required with no jobs started, so CI for this head is still unverified until a maintainer re-triggers workflows (close/reopen PR, workflow_dispatch, or maintainer push).

Copilot AI requested a review from gh-aw-bot July 6, 2026 22:03
@gh-aw-bot

Copy link
Copy Markdown
Collaborator

@copilot please run the pr-finisher skill, address unresolved review comments, and rerun checks once the branch is up to date.

Generated by 👨‍🍳 PR Sous Chef · 6.99 AIC · ⌖ 5.55 AIC · ⊞ 7.1K ·
Comment /souschef to run again

Copilot AI commented Jul 6, 2026

Copy link
Copy Markdown
Contributor Author

@copilot please run the pr-finisher skill, address unresolved review comments, and rerun checks once the branch is up to date.

...

Ran the pr-finisher pass on the current branch state.

  • In-scope unresolved review feedback is already reflected in the existing commits on this branch.
  • Reran local checks on the current head (3d34a2a):
    • make fmt
    • make lint
    • make test-unit
    • make test
    • make recompile
      All passed locally.
  • Re-triaged CI via workflow runs + job logs: latest PR runs are action_required with no jobs started (failed_jobs: 0 / total_jobs: 0), so CI for this head remains unverified.

Hand-off: a maintainer needs to re-trigger CI (close/reopen PR, workflow_dispatch, or maintainer push) before merge.

@pelikhan pelikhan merged commit 897baff into main Jul 7, 2026
30 checks passed
@pelikhan pelikhan deleted the copilot/deep-report-make-check-and-wait-context-cancellabl branch July 7, 2026 00:19
@github-actions

github-actions Bot commented Jul 7, 2026

Copy link
Copy Markdown
Contributor

🎉 This pull request is included in a new release.

Release: v0.82.3

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[deep-report] Make checkAndWaitForRateLimit context-cancellable (~60min uncancellable sleep)

4 participants