Skip to content

feat: configurable TUI ready-gate timeout and optional spawn concurrency cap#1

Merged
plotarmordev merged 1 commit into
mainfrom
feat/tui-ready-timeout-spawn-concurrency
Jul 18, 2026
Merged

feat: configurable TUI ready-gate timeout and optional spawn concurrency cap#1
plotarmordev merged 1 commit into
mainfrom
feat/tui-ready-timeout-spawn-concurrency

Conversation

@plotarmordev

Copy link
Copy Markdown
Owner

WHAT

Two backward-compatible ClaudePool options (plus matching serve flags) for hosts where worker cold starts are slow:

  • tui_ready_timeout (default 30.0, unchanged): seconds a TUI worker may take to signal readiness through its SessionStart hook before spawn fails with WorkerStartError.
  • spawn_concurrency (default None = unbounded, unchanged): optional cap on simultaneous worker cold starts, enforced at the pool's spawn layer so warm pre-warm, checkout misses, and cold retries all go through it.

WHY

On a Raspberry Pi 5, when several heavy TUI workers cold-start at the same time (e.g. a process restart re-warming multiple pools), CPU contention pushes individual workers past the fixed 30s _wait_ready deadline. The pool then raises WorkerStartError("TUI worker did not become ready") whose stderr_tail is raw pty escape-sequence output — each worker would have started fine alone. Consumers currently have to work around this with wrapper-level ready gates and spawn throttles; a downstream consumer (smithyx) validated exactly these semantics in production today with a 75s ready gate and a spawn-concurrency cap of 2 at its adapter layer. This PR upstreams the capability so all consumers get it natively.

CHANGES

  • claude_pool.py
    • New module constant _TUI_READY_TIMEOUT = 30.0.
    • _TuiWorker.spawn(..., *, ready_timeout=_TUI_READY_TIMEOUT); _wait_ready(timeout) uses it instead of a hardcoded 30.0.
    • ClaudePool(..., tui_ready_timeout=_TUI_READY_TIMEOUT, spawn_concurrency=None); docstring documents both.
    • _spawn_worker now optionally gates on a spawn semaphore and delegates to _spawn_worker_now (previous body). The semaphore is held across process exec plus the TUI ready wait, which is the cold-start window that matters.
    • serve gains --tui-ready-timeout and --spawn-concurrency, plumbed through _pool_kwargs_from_args.
  • README.md: new "Startup Tuning" section with both knobs and the slow-host guidance.
  • CHANGELOG.md: Unreleased entries.
  • Tests (fake TUI only, no network):
    • tests/test_tui_worker.py: short ready_timeout fails fast with WorkerStartError and no leaked reader threads; a ready_timeout longer than a slow start still completes a turn.
    • tests/test_backend_parity.py: pool-level tui_ready_timeout plumbing; spawn_concurrency=1 serializes two concurrent cold starts (elapsed >= 2x slow-start); default overlaps them.
    • tests/test_cli.py: serve accepts the new flags and still round-trips an ask.

RESULTS

On a Raspberry Pi 5 (Python 3.13.5, /tmp/pool-test-venv per AGENTS.md):

$ ruff check .        -> All checks passed!
$ ruff format --check . -> 16 files already formatted
$ pytest -q           -> 126 passed in 82.62s

RISK

Low. Defaults reproduce the previous behavior exactly (30s deadline, unbounded spawns). The only structural change is the _spawn_worker split; with spawn_concurrency=None it is a straight passthrough. The semaphore is released on cancellation via async with.

🤖 Generated with Claude Code

…ncy cap

Two backward-compatible pool options for slow hosts where simultaneous
worker cold starts push TUI workers past the fixed 30s readiness deadline:

- ClaudePool(tui_ready_timeout=...) / serve --tui-ready-timeout: seconds a
  TUI worker may take to signal readiness via its SessionStart hook before
  spawn fails with WorkerStartError. Default unchanged at 30.0.
- ClaudePool(spawn_concurrency=...) / serve --spawn-concurrency: optional
  cap on simultaneous worker cold starts at the pool spawn layer. Default
  unchanged: unbounded.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@plotarmordev
plotarmordev merged commit a841a89 into main Jul 18, 2026
8 checks passed
@plotarmordev plotarmordev mentioned this pull request Jul 19, 2026
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