Conversation
📝 WalkthroughWalkthroughIntroduces a new ChangesRateLimitClock Extraction
Estimated code review effort🎯 2 (Simple) | ⏱️ ~12 minutes Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Codecov Report❌ Patch coverage is
📢 Thoughts on this report? Let us know! |
| // clock has a timestamp, and rateLimitSeconds=10 means the | ||
| // backoff should account for the rate limit window (10s) being | ||
| // larger than the base exponential delay (3s). | ||
| const retryMessages = saySpy.mock.calls.filter((call) => call[0] === "api_req_retry_delayed") |
There was a problem hiding this comment.
Nit (test strength): This test is named "should respect rate limit window in
retry backoff" and the inline comment explains that with rateLimitSeconds=10
and base backoff requestDelaySeconds=3, the 10s rate-limit window should
dominate the delay. But the assertions only check that a api_req_retry_delayed
message was emitted and that clock.getLastRequestTime() is defined — neither
confirms the delay actually reflected the rate-limit window. The test would
pass even if the backoff used the 3s base value (or any non-zero delay), so it
doesn't really prove the stated behavior.
Consider asserting on the computed delay so a regression in the
window-vs-backoff logic would fail here. Since delay is already mocked as
mockDelay, something like:
// The rate-limit window (10s) should dominate the base exponential backoff
(3s)
expect(mockDelay).toHaveBeenCalledWith(10000)
or assert the countdown ran for ~10 iterations / parse the seconds out of
the api_req_retry_delayed payload. That ties the test to its intent. Not
blocking — current coverage is adequate, just weaker than the name implies.
If you want it tighter/less wordy for an inline review thread, the one-liner
version:
Assertions here only check that a retry-delay message fired and the clock is
set — they don't verify the delay reflected the 10s window vs the 3s base
backoff, so the test would pass even if the window logic regressed. Since
delay is mocked, consider expect(mockDelay).toHaveBeenCalledWith(10000) (or
asserting the countdown length) to match the test's stated intent.
Non-blocking.
navedmerchant
left a comment
There was a problem hiding this comment.
A small nit comment, but non blocking, approved
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@src/core/task/Task.ts`:
- Around line 3853-3855: The condition checking `!lastRequestTime` uses a falsy
comparison which incorrectly treats a valid `0` timestamp as a missing value and
skips rate-limit enforcement. Replace the falsy check `!lastRequestTime` with an
explicit undefined check using `lastRequestTime === undefined` (or
`lastRequestTime == null` if you want to handle both null and undefined). Apply
this same fix to both the occurrence in the rate-limit check block around line
3853-3855 and the additional occurrence mentioned at lines 4282-4284 in the same
file.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro Plus
Run ID: ce77650f-1e4e-44b6-8899-d7b43d2648d0
📒 Files selected for processing (5)
src/core/task/RateLimitClock.tssrc/core/task/Task.tssrc/core/task/__tests__/RateLimitClock.spec.tssrc/core/task/__tests__/Task.spec.tssrc/core/webview/ClineProvider.ts
🚧 Files skipped from review as they are similar to previous changes (4)
- src/core/task/tests/RateLimitClock.spec.ts
- src/core/task/RateLimitClock.ts
- src/core/webview/ClineProvider.ts
- src/core/task/tests/Task.spec.ts
| const lastRequestTime = this.rateLimitClock.getLastRequestTime() | ||
| if (rateLimitSeconds <= 0 || !lastRequestTime) { | ||
| return |
There was a problem hiding this comment.
Use explicit undefined checks for last-request timestamps.
Truthy/falsy checks on lastRequestTime mis-handle a valid 0 timestamp and can incorrectly bypass rate-limit behavior.
Suggested fix
- const lastRequestTime = this.rateLimitClock.getLastRequestTime()
- if (rateLimitSeconds <= 0 || !lastRequestTime) {
+ const lastRequestTime = this.rateLimitClock.getLastRequestTime()
+ if (rateLimitSeconds <= 0 || lastRequestTime === undefined) {
return
}
...
- const lastRequestTime = this.rateLimitClock.getLastRequestTime()
- if (lastRequestTime && rateLimit > 0) {
+ const lastRequestTime = this.rateLimitClock.getLastRequestTime()
+ if (lastRequestTime !== undefined && rateLimit > 0) {
const elapsed = performance.now() - lastRequestTime
rateLimitDelay = Math.ceil(Math.min(rateLimit, Math.max(0, rateLimit * 1000 - elapsed) / 1000))
}Also applies to: 4282-4284
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@src/core/task/Task.ts` around lines 3853 - 3855, The condition checking
`!lastRequestTime` uses a falsy comparison which incorrectly treats a valid `0`
timestamp as a missing value and skips rate-limit enforcement. Replace the falsy
check `!lastRequestTime` with an explicit undefined check using `lastRequestTime
=== undefined` (or `lastRequestTime == null` if you want to handle both null and
undefined). Apply this same fix to both the occurrence in the rate-limit check
block around line 3853-3855 and the additional occurrence mentioned at lines
4282-4284 in the same file.
Related GitHub Issue
Closes: #361
Description
Replaces
Task.lastGlobalApiRequestTime(private static) with an injectableRateLimitClockinstance, scoped perClineProvider.Problem: The static field was shared across all
Taskinstances in the Node.js module. When two subtasks ran concurrently, they stomped on each other's rate-limit timestamps — causing both to see stale values and either over-delay or skip the delay entirely.Solution:
src/core/task/RateLimitClock.ts— small class withgetLastRequestTime()andrecordRequest()Taskaccepts an optionalrateLimitClockviaTaskOptions(defaults to a new instance if omitted)ClineProvidercreates oneRateLimitClockper provider instance and passes it to all tasks it creates, so parent and child tasks share rate-limit state within a providerTask.lastGlobalApiRequestTimeandTask.resetGlobalApiRequestTime()Behavioral change: Rate-limit state is now per-provider instead of global. Two providers with different API keys no longer share rate-limit timestamps. This is intentionally better — the old global sharing was overly broad.
Test Procedure
RateLimitClock.spec.ts— covers initial undefined state, recording, monotonic updates, and cross-instance isolationTask.spec.tsrate-limit tests to use explicit shared clocks instead of static resets — all passgrep -rn "lastGlobalApiRequestTime\|resetGlobalApiRequestTime" src/returns zero matchesShadowCheckpointService.spec.ts)Pre-Submission Checklist
Documentation Updates
Additional Notes
Files changed:
src/core/task/RateLimitClock.ts(new)src/core/task/__tests__/RateLimitClock.spec.ts(new)src/core/task/Task.ts(modified — 6 call sites updated)src/core/task/__tests__/Task.spec.ts(modified — tests use shared clocks)src/core/webview/ClineProvider.ts(modified — creates and injects clock)Summary by CodeRabbit