Skip to content

Resume direct-spawn child threads after a usage limit #71

Closed

Description

@lukemaj

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 direct spawn_thread child) 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 resetsAt can be hours later than when requests work again. Waiting for resetsAt alone 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) resetsAt plus about 1 minute;
    • (b) the first successful turn on the same provider instance by any thread, which proves the limit has lifted.
  • 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.

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