Skip to content

Daemon: classify Claude session/usage-limit exhaustion — stop claiming until reset instead of insta-failing runs#1798

Open
interactor-pm-service[bot] wants to merge 2 commits into
mainfrom
goal/daemon-classify-claude-session-usage-limit-exhaustion-stop-c
Open

Daemon: classify Claude session/usage-limit exhaustion — stop claiming until reset instead of insta-failing runs#1798
interactor-pm-service[bot] wants to merge 2 commits into
mainfrom
goal/daemon-classify-claude-session-usage-limit-exhaustion-stop-c

Conversation

@interactor-pm-service

Copy link
Copy Markdown
Contributor

Integration PR for this goal — its tasks commit onto this branch (one PR per goal). Opened automatically by the engine on first push; left as a draft until the goal completes.

psdjungpulzze and others added 2 commits July 4, 2026 20:15
…with no pause

Mirror the GitHub rate-limit handling (G#343/G#376) for a Claude account
usage/session-limit exhaustion.

Blocking reason (self-healing):
- Add a new `claude_rate_limited` BlockingReasonKind + `claudeRateLimitedUntil`
  signal. It fires on a FUTURE reset time, carries no fix command, sets
  `resolvesAt`, and OUTRANKS `claude_auth_missing` so an exhausted usage limit
  never masquerades as a sign-in loss.
- Thread `claudeRateLimitedUntil` through the sync layer, the register /
  heartbeat / detail ingestion routes, and Machine (+ migration).
- Fold into the goal block-state as a `self_healing` resolution (goal →
  progressing with the reset as eta), the machine card + fleet-sidebar
  renderers, and the board label map. Generalize the fleet-sidebar self-heal
  branch to any reason carrying `resolvesAt` (not just github_rate_limited).

No-penalty RELEASE outcome:
- New wire outcome `claude_limited` (field `claudeRateLimitedUntil`) on the
  engine report route: requeue the task via a new `requeueForClaudeLimit`
  helper (same requeue-state logic as tryRequeueForRetry) WITHOUT bumping
  transientRetryCount and WITHOUT escalateFailedRun/recordPause; set
  retryNotBefore from the reset so it isn't instantly re-claimed; finalize the
  run to `retried`. Persist the reported reset onto the run's Machine as a
  redundancy.

WIRE NAMES (for the daemon task): field `claudeRateLimitedUntil` (ISO-8601);
report outcome string `claude_limited`.

Tests: new-kind derivation (fires future / null past / never masquerades as
claude_auth_missing), report-route release (task→queued, no pause, retry count
unchanged, run→retried, machine stamped), goal-block-state fold to
progressing/self-healing, and machine-card + fleet-sidebar render specs.

Agent config: senior-pm updated to explain the new self-healing reason. No new
user tool registered, so no config-sync TOOL_NAMES change is needed.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
… claims until reset

Daemon half of G (mirror of the GitHub rate-limit pause, G#343) — pairs with the
platform task's `claude_limited` report outcome + `claudeRateLimitedUntil` field
(commit 5df526b).

Detection (llm-errors.ts): a DISTINCT `classifyClaudeAccountLimit` sibling of the
transient classifier — anchored to BOTH a 429 status AND a session/usage-limit phrase
("hit your session limit", "usage limit reached"), kept as tight as the envelope
regexes so a coding run's output tail (a bare 429, "rate limit" prose) is not
misclassified. Parses the reset ("resets 3am (UTC)" → next future 3am UTC; ISO; epoch)
into a limit-until instant, defaulting to the top of the next hour when unparseable so
the pause self-clears. Deliberately a sibling verdict — it never enters the bounded
RetryReason set llm-retry.ts validates.

Retry short-circuit (task.ts): runClaudeWithRetry classifies the account limit BEFORE
the transient classifier and STOPS — re-spawning claude against an exhausted account
only burns the in-place budget. Surfaces `accountLimit` to the caller.

Release + pause + report (container-loop.ts): a session-limited run reports the
no-penalty `claude_limited` outcome carrying the reset (via the extracted, unit-testable
`assembleEngineReport`, before the retryable/failed fallbacks) so the task requeues to
`queued` instead of a run_failure pause. A module-level `claudeRateLimitedUntil`,
REMEMBERED across ticks (no free Claude probe like gh's), gates the claim loop head with
one honest log line and clears automatically once past; it's serialized onto
register/heartbeat's `AuthStatus.claudeRateLimitedUntil` so the machine card + fleet
sidebar render the self-healing reason.

Bump DAEMON_VERSION 0.41.0 → 0.42.0 with a changelog entry. No agent-visible tool/data
change here (senior-pm config was updated in the paired platform commit), so no
config-sync update is needed.

Tests: llm-errors (real strings → parsed reset; unparseable → default window; negatives
for bare-429 / limit-in-prose; precedence over transient), task (short-circuit, no
retry; transient 429 still retries), container-loop (assembleEngineReport release
precedence + the remembered-block pause/heartbeat seam), api (version bump).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@psdjungpulzze psdjungpulzze marked this pull request as ready for review July 4, 2026 20:37
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant