Skip to content

Hold a normal Send until usage-limit reset instead of sending immediately, and let it replace the generic auto-resume prompt #22851

Closed

Description

@EmanuelMedinaAraujo

What happened

When a thread is stopped on a usage limit and auto-resume is armed ("Resume at reset"), typing a new message and sending it (normal Send, not queue) does not wait for the reset. It goes to the provider immediately, fails on the same limit, and cancels the scheduled resume. After the reset nothing resumes; the message has to be sent again. The scheduled continuation also always uses the generic "Continue where you left off." text, so a message the user typed while limited can never take its place.

This is the remaining open part of #19327. #19583 fixed the queued-run case (a message already saved while a turn runs is held while the thread is usage-limited, including when compaction failed but was recorded as completed). A normal new Send still takes the immediate path.

Desired behavior

While a thread is usage-limited with a known future reset (thread.usageLimitResetAt / limitRecovery), a newly sent message should be held like a held queued run instead of being dispatched immediately, so it does not fail on the same limit and does not cancel the armed recovery. At reset it should be delivered in place of the generic "Continue where you left off." continuation (or run alongside it), so the user's actual message is what resumes the thread.

Diagnosis

In orchestration V2 a usage-limited thread is idle (latest run failed with class usage_limit), so a send resolves to start_immediately:

  • apps/server/src/orchestration-v2/CommandPolicy.ts (resolveMessageDispatchIntent): with no active run, returns { type: "start_immediately" }.
  • apps/server/src/orchestration-v2/Orchestrator.ts: a send is held/queued only behind an active run, or behind a failed run whose worktree is missing (worktreeMissingRun). There is no equivalent hold for a usage-limited run, so even queue_after_active starts immediately.
  • docs/user/thread-sidebar.md: "Sending a new message ... prevents a pending continuation from starting", so the send also cancels the armed recovery.

This is inconsistent with how the server already treats the existing queue on the same thread:

  • Orchestrator.ts (startNextQueuedRun): refuses to start while usageLimitBlockedRun(...) is non-null, because "Starting the queue would send every waiting message and drop it from the queue as each one fails."

A new message takes the very path that comment warns against. The server already knows the limit and reset time, so the send is predictably doomed and destroys the scheduled recovery as a side effect.

The scheduled continuation text is hard-coded in apps/server/src/orchestration-v2/UsageLimitRecoveryWorker.ts ("Continue where you left off.").

The worktree-missing hold at Orchestrator.ts (the worktreeMissingRun branch) is a precedent for holding a message behind a failed run instead of starting it.

Steps to reproduce

  1. Settings → General: enable "Auto-resume limited threads" (or use "Resume at reset" on the thread).
  2. Run a thread on a provider that reports a reset time (Claude or Codex) until it stops on a usage limit. The thread shows Limited and the banner offers "Cancel auto-resume".
  3. While still limited, type a new message in the composer and send it (normal Send; Mod+Enter queue behavior is already covered by fix(server): a /compact stopped by the Claude usage limit now fails #19583).
  4. Observe: the message starts a new turn immediately and fails on the same usage limit, and the scheduled auto-resume is cancelled.
  5. After the reset, nothing resumes; the message has to be sent again manually.

Version

0.0.46 (main)

Environment

Any provider that reports a reset time (Claude, Codex).

Related issues

Filed by

Claude (beam-free) via T3 Code, on behalf of the reporter of #19327

Activity

  1. locked and limited conversation to collaborators on Oct 21, 2026
  2. converted this issue into a discussion #22859 on Oct 21, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions