Repository navigation
Resume direct-spawn child threads after a usage limit #71
Description
What: A child thread started with spawn_thread that stops on a usage limit continues on its own as soon as the limit has lifted, and its parent is told.
Why: #63 resumes only top-level threads and deliberately skips every sub.* thread (for Prism jobs). A direct-spawn worker therefore waits until the user types "continue" to the planner, and the planner then relays it.
So what: a worker implements it and opens a PR; the user approves the merge.
Outcome
- A direct-spawn child (
spawn_thread, not a Prism job) whose turn ends in a usage-limit error gets exactly one automatic "Continue. (Sent automatically after the usage limit reset.)". - The parent thread gets one line saying the child was resumed.
- Prism-managed jobs keep today's behaviour. Model Router moves or blocks them, as in Resume threads after a usage limit resets #61.
Contributes to the Objective ("Model Router jobs run as T3 threads … messageable there, and continued"): workers continue without the user relaying "continue".
Evidence (2026-09-29)
- Child
sub.176330ed-8996-47b4-80a8-14bb1f41ae54.01b1a94bbcfd(claudeAgent, claude-opus-5-5, a directspawn_threadchild) failed at 13:10:29Z with "Claude usage limit reached. Send the message again once the limit resets." - The staleTurnMonitor (feat(server): detect stale worker turns and notify their parent #65) told the parent:
Reason: provider-error. - The UI showed "resets 5:40pm (Europe/Warsaw)", but the parent thread on the same account ran normally minutes later. The user typed "continue" twice; the second time, the parent messaged the child and it ran at once.
- So the displayed
resetsAtcan be hours later than when requests work again. Waiting forresetsAtalone would have idled the worker for about 2.5 hours.
Acceptance
- A direct-spawn child failing on a usage limit is resumed once, at the earliest of:
- (a)
resetsAtplus about 1 minute; - (b) the first successful turn on the same provider instance by any thread, which proves the limit has lifted.
- (a)
- Not resumed for Prism jobs, for non-limit errors, or when someone sent the child a new message first.
- The parent gets one notice per resume, not a flood.
- Tests use TestClock, with no sleeps. Run the targeted typecheck and lint, and
scripts/fork-check.sh.
Non-goals
Surviving a server restart, UI or settings, and Model Router changes.
Blockers
None.
Proof
- Focused tests for the decision, and for both resume triggers.
- One real or simulated limit event observed resuming a direct child.
Elon record
Requirements and who asked: The user asked (2026-09-29) never to have to relay "continue" to a worker after a limit, and chose T3 over an AgentsMD rule as the place to fix it.
Deleted: An AgentsMD instruction; the host sees the error and owns the thread, and an instruction depends on the model obeying it. Resuming Prism jobs, because the Router owns them (#61). New UI, settings and restart persistence.
Bottleneck: after a limit, a direct child idles until a human notices and relays "continue".
Checked myself: #61 (closed, delivered by #63) excludes sub.* threads. apps/server/src/prism/staleTurnMonitor.ts produced today's stale-child notice. The child's lastError read "Claude usage limit reached…", and it resumed immediately when messaged at about 13:14Z, long before the displayed 5:40pm reset.
Activity
feat(server): direct child threads resume on their own after a usage …
fix(server): interrupt_thread cancels a usage-limit auto-continue (#71)
fix(server): continue only usage-limit errors, and past resets keep w…
feat(server): direct child threads resume on their own after a usage …
fix(server): interrupt_thread cancels a usage-limit auto-continue (#71)
fix(server): continue only usage-limit errors, and past resets keep w…
fix(server): resume at a reset only once the window shows it lifted (#71
feat(server): direct child threads resume on their own after a usage …