7dd6c46156
The launcher now polls AgentControl.status(paneId) after starting the pane.
It returns the handle only once the worker reports an injectable state
(IDLE/BLOCKED/DONE). If the timeout elapses while still UNKNOWN, the
pane is self-reaped and a PeerUnreachableException is thrown — no orphan
left behind. The gate is disabled when spawnReadyTimeoutMs == 0 (legacy
non-blocking spawn, the default for the 6-arg constructor).
Key changes:
- PeerUnreachableException (new) in dev.ltms.bridged.peer
- BridgedConfig: spawnReadyTimeoutMs (default 20000), spawnReadyPollMs (default 300)
- ClaudeCodeLauncher: 3 constructor overloads:
(a) 6-arg backward-compat: gate disabled (timeout=0)
(b) 8-arg production: gate with config knobs + real clock/sleep
(c) 10-arg testability: full seam (LongSupplier clock + Runnable sleeper)
- waitUntilInjectableOrThrow() loop in spawn(SpawnRequest)
- sleepUninterruptibly() helper for the production sleeper
- BridgeMcp.spawn + BridgedApp.spawnWorker catch PeerUnreachableException
→ clean tool error / 502 response (not an uncaught 500)
- SessionManager.acquire inherently registers nothing on throw (both
worktree and non-worktree paths) — confirmed by new test
Tests:
- ClaudeCodeLauncherTest: 4 new tests
- unknown→idle: returns handle, no pane.close
- always-unknown: throws PeerUnreachableException, pane closed,
clock advanced past timeout
- timeout=0 (6-arg ctor): no agent.get calls, returns handle
- timeout=0 (10-arg ctor): no orphan pane close
- SessionManagerTest: 1 new test
- acquire → PeerUnreachableException: roster remains empty
Total: 188 tests, all pass (no existing test changed semantics)
19 lines
699 B
Java
19 lines
699 B
Java
package dev.ltms.bridged.peer;
|
|
|
|
/**
|
|
* Thrown when a {@link PeerLauncher} starts a peer process but the peer
|
|
* does not reach an injectable (ready-to-receive) state within the configured
|
|
* timeout. The launcher MUST clean up any resources it created (pane, tab)
|
|
* before throwing — no orphaned peer or pane is left behind.
|
|
*
|
|
* <p>This is a spawn-time failure, distinct from a post-spawn disconnect.
|
|
* Callers treat this as a clean spawn error (the peer never materialized
|
|
* into a usable session), not a mid-life session fault.
|
|
*/
|
|
public final class PeerUnreachableException extends RuntimeException {
|
|
|
|
public PeerUnreachableException(String message) {
|
|
super(message);
|
|
}
|
|
}
|