feat: configurable TUI ready-gate timeout and optional spawn concurrency cap#1
Merged
Merged
Conversation
…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>
Merged
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
WHAT
Two backward-compatible
ClaudePooloptions (plus matchingserveflags) for hosts where worker cold starts are slow:tui_ready_timeout(default30.0, unchanged): seconds a TUI worker may take to signal readiness through its SessionStart hook before spawn fails withWorkerStartError.spawn_concurrency(defaultNone= 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_readydeadline. The pool then raisesWorkerStartError("TUI worker did not become ready")whosestderr_tailis 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_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_workernow 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.servegains--tui-ready-timeoutand--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/test_tui_worker.py: shortready_timeoutfails fast withWorkerStartErrorand no leaked reader threads; aready_timeoutlonger than a slow start still completes a turn.tests/test_backend_parity.py: pool-leveltui_ready_timeoutplumbing;spawn_concurrency=1serializes two concurrent cold starts (elapsed >= 2x slow-start); default overlaps them.tests/test_cli.py:serveaccepts the new flags and still round-trips an ask.RESULTS
On a Raspberry Pi 5 (Python 3.13.5,
/tmp/pool-test-venvper AGENTS.md):RISK
Low. Defaults reproduce the previous behavior exactly (30s deadline, unbounded spawns). The only structural change is the
_spawn_workersplit; withspawn_concurrency=Noneit is a straight passthrough. The semaphore is released on cancellation viaasync with.🤖 Generated with Claude Code