Part of #356 (Epic 1: Foundation).
Depends on: Nothing — additive, no behavior change.
Context
Task.lastGlobalApiRequestTime is private static (line 287, Task.ts) — shared across all Task instances in the Node.js module. When two subtasks run concurrently they stomp on each other's rate-limit clock, causing both to see stale timestamps and either over-delay or skip the delay entirely. This must be fixed before any concurrent task execution is safe.
Developer Notes
Create src/core/task/RateLimitClock.ts:
RateLimitClock class with getLastRequestTime(): number | undefined and recordRequest(): void.
- Export
createRateLimitClock(): RateLimitClock.
In src/core/task/Task.ts:
- Remove
private static lastGlobalApiRequestTime?: number (line 287) and static resetGlobalApiRequestTime() (line 294).
- Add
private rateLimitClock: RateLimitClock as a constructor-injected field with a default factory call so existing callsites need no change.
- Replace 6 call sites with
this.rateLimitClock.* (lines 2440, 3849, 3854, 3902, 4277–4278).
- Add optional
rateLimitClock?: RateLimitClock to TaskOptions.
In src/core/webview/ClineProvider.ts:
- Construct one
RateLimitClock per provider; pass it to createTask() so all tasks under one provider share the clock, preserving the original rate-limit intent across subtasks.
- Behavioral note: The current
private static field is global across ALL providers in the Node.js module. Per-provider scoping means two providers with different API keys no longer share rate-limit state. This is intentionally better, but verify existing rate-limit tests don't assume cross-provider sharing.
- Multi-window analysis: Each VS Code window runs its own extension host (separate Node.js process), so per-provider scoping does not introduce a regression for multi-window setups.
Files: src/core/task/RateLimitClock.ts (new), src/core/task/Task.ts, src/core/webview/ClineProvider.ts
Tests (src/core/task/__tests__/RateLimitClock.spec.ts):
- Two
Task instances with the same clock share rate-limit waits (second waits for first to clear).
- Two
Task instances with different clocks are fully independent (validates per-provider scoping).
- First call (no prior time) returns no delay.
- Time elapsed < limit → delay applied; time elapsed > limit → no delay.
Acceptance Criteria
Task.resetGlobalApiRequestTime() is deleted; grep -n "Task.lastGlobalApiRequestTime" src/core/task/Task.ts returns zero matches.
- All existing
Task.spec.ts rate-limiting tests pass without modification.
- No static state remains on
Task for rate limiting.
Part of #356 (Epic 1: Foundation).
Depends on: Nothing — additive, no behavior change.
Context
Task.lastGlobalApiRequestTimeisprivate static(line 287,Task.ts) — shared across allTaskinstances in the Node.js module. When two subtasks run concurrently they stomp on each other's rate-limit clock, causing both to see stale timestamps and either over-delay or skip the delay entirely. This must be fixed before any concurrent task execution is safe.Developer Notes
Create
src/core/task/RateLimitClock.ts:RateLimitClockclass withgetLastRequestTime(): number | undefinedandrecordRequest(): void.createRateLimitClock(): RateLimitClock.In
src/core/task/Task.ts:private static lastGlobalApiRequestTime?: number(line 287) andstatic resetGlobalApiRequestTime()(line 294).private rateLimitClock: RateLimitClockas a constructor-injected field with a default factory call so existing callsites need no change.this.rateLimitClock.*(lines 2440, 3849, 3854, 3902, 4277–4278).rateLimitClock?: RateLimitClocktoTaskOptions.In
src/core/webview/ClineProvider.ts:RateLimitClockper provider; pass it tocreateTask()so all tasks under one provider share the clock, preserving the original rate-limit intent across subtasks.private staticfield is global across ALL providers in the Node.js module. Per-provider scoping means two providers with different API keys no longer share rate-limit state. This is intentionally better, but verify existing rate-limit tests don't assume cross-provider sharing.Files:
src/core/task/RateLimitClock.ts(new),src/core/task/Task.ts,src/core/webview/ClineProvider.tsTests (
src/core/task/__tests__/RateLimitClock.spec.ts):Taskinstances with the same clock share rate-limit waits (second waits for first to clear).Taskinstances with different clocks are fully independent (validates per-provider scoping).Acceptance Criteria
Task.resetGlobalApiRequestTime()is deleted;grep -n "Task.lastGlobalApiRequestTime" src/core/task/Task.tsreturns zero matches.Task.spec.tsrate-limiting tests pass without modification.Taskfor rate limiting.