From 3aa69a9e32159bf6fad15f7385a3319c520b2de1 Mon Sep 17 00:00:00 2001 From: Dai Ha Date: Sat, 18 Jul 2026 14:39:56 +0200 Subject: [PATCH] CB-401 Stage A follow-up: rename WorkerService -> ClaudeCodeLauncher Name the first-class Claude Code adapter explicitly, per the Peer Launcher SPI: WorkerService was the de-facto Claude-Code launcher; as an in-tree PeerLauncher impl it should say so. Pure IDE rename (class + file + WorkerServiceTest) plus stale Javadoc/comment mentions swept to the new name. No behaviour change. Gate: IDE diagnostics 0/0 on touched files; mvn clean install BUILD SUCCESS, MVN_EXIT=0, 183 tests pass. Deferral #1 from issue #3 cleared. --- .../main/java/dev/ltms/bridged/Bridged.java | 12 ++--- .../dev/ltms/bridged/herdr/PaneLocator.java | 2 +- .../java/dev/ltms/bridged/mcp/BridgeMcp.java | 14 ++--- .../dev/ltms/bridged/peer/PeerLauncher.java | 2 +- .../dev/ltms/bridged/rest/BridgedApp.java | 6 +-- ...erService.java => ClaudeCodeLauncher.java} | 10 ++-- .../dev/ltms/bridged/mcp/BridgeMcpTest.java | 6 +-- .../dev/ltms/bridged/rest/BridgedAppTest.java | 4 +- .../bridged/session/SessionManagerTest.java | 6 +-- .../session/WorktreeSessionManagerTest.java | 6 +-- ...eTest.java => ClaudeCodeLauncherTest.java} | 52 +++++++++---------- 11 files changed, 60 insertions(+), 60 deletions(-) rename bridged/src/main/java/dev/ltms/bridged/worker/{WorkerService.java => ClaudeCodeLauncher.java} (98%) rename bridged/src/test/java/dev/ltms/bridged/worker/{WorkerServiceTest.java => ClaudeCodeLauncherTest.java} (85%) diff --git a/bridged/src/main/java/dev/ltms/bridged/Bridged.java b/bridged/src/main/java/dev/ltms/bridged/Bridged.java index 18b23ff..2de5307 100644 --- a/bridged/src/main/java/dev/ltms/bridged/Bridged.java +++ b/bridged/src/main/java/dev/ltms/bridged/Bridged.java @@ -22,7 +22,7 @@ import dev.ltms.bridged.session.GitWorktrees; import dev.ltms.bridged.session.SessionManager; import dev.ltms.bridged.peer.PeerLauncher; import dev.ltms.bridged.session.SessionReaper; -import dev.ltms.bridged.worker.WorkerService; +import dev.ltms.bridged.worker.ClaudeCodeLauncher; import io.javalin.Javalin; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -57,13 +57,13 @@ public final class Bridged { AgentControl agents = new AgentControl(herdr); WorkspaceControl spaces = new WorkspaceControl(herdr); - PeerLauncher workers = new WorkerService(agents, spaces, guard, + PeerLauncher workers = new ClaudeCodeLauncher(agents, spaces, guard, cfg.workerProfiles(), cfg.defaultProfile(), System::getenv); // CB-117: herdr keeps worker panes alive across a daemon restart, and their ids died with // the previous process — reap those leaked orphans now, before we start serving. workers.reapOrphanWorkers(); - // CB-301: authoritative session registry + lifecycle FSM on top of WorkerService. + // CB-301: authoritative session registry + lifecycle FSM on top of ClaudeCodeLauncher. // CB-301-ext: worktree provisioning seam, optionally rooted at a configured directory. // CB-303 part 2: context cap is opt-in and disabled (0) when absent/null. int contextCap = 0; @@ -121,8 +121,8 @@ public final class Bridged { // Caller identity is resolved from the connection (peer PID → herdr pane), not arguments. ConnectionIdentity identity = new ConnectionIdentity( new PaneLocator(herdr), new LsofPeerPidLookup(), new LsofProcessCwdLookup()); - // Cast to WorkerService: BridgeMcp is not yet migrated to PeerLauncher (Stage A scope). - BridgeMcp mcp = new BridgeMcp(messages, rendezvous, (WorkerService) workers, sessions, identity, presence); + // Cast to ClaudeCodeLauncher: BridgeMcp is not yet migrated to PeerLauncher (Stage A scope). + BridgeMcp mcp = new BridgeMcp(messages, rendezvous, (ClaudeCodeLauncher) workers, sessions, identity, presence); // CB-303 part 3: single ordered shutdown hook. Drain sessions first while herdr is still // open (so releases reach the daemon), then stop poller/message/mcp/reaper, and close herdr @@ -136,7 +136,7 @@ public final class Bridged { herdr.close(); })); - Javalin app = new BridgedApp(herdr, (WorkerService) workers, sessions, messages, rendezvous, presence, mcp.servlet()).build(); + Javalin app = new BridgedApp(herdr, (ClaudeCodeLauncher) workers, sessions, messages, rendezvous, presence, mcp.servlet()).build(); app.start(cfg.bind().host(), cfg.bind().port()); log.info("bridged listening on {}:{}, herdr socket {}", cfg.bind().host(), cfg.bind().port(), socket); diff --git a/bridged/src/main/java/dev/ltms/bridged/herdr/PaneLocator.java b/bridged/src/main/java/dev/ltms/bridged/herdr/PaneLocator.java index 46d7fb3..4e30b73 100644 --- a/bridged/src/main/java/dev/ltms/bridged/herdr/PaneLocator.java +++ b/bridged/src/main/java/dev/ltms/bridged/herdr/PaneLocator.java @@ -12,7 +12,7 @@ import java.util.Map; * *

herdr owns the PID→pane truth: {@code pane.process_info} reports each pane's {@code shell_pid} * and foreground process PIDs. This scans agent panes; a spawn-time {@code pid→terminal} cache is - * the obvious optimization once wired into {@code WorkerService}. + * the obvious optimization once wired into {@code ClaudeCodeLauncher}. */ public final class PaneLocator { diff --git a/bridged/src/main/java/dev/ltms/bridged/mcp/BridgeMcp.java b/bridged/src/main/java/dev/ltms/bridged/mcp/BridgeMcp.java index 56df99b..b04aa4a 100644 --- a/bridged/src/main/java/dev/ltms/bridged/mcp/BridgeMcp.java +++ b/bridged/src/main/java/dev/ltms/bridged/mcp/BridgeMcp.java @@ -9,7 +9,7 @@ import dev.ltms.bridged.msg.Rendezvous; import dev.ltms.bridged.session.SessionManager; import dev.ltms.bridged.session.WorkerSession; import dev.ltms.bridged.session.WorktreeRequest; -import dev.ltms.bridged.worker.WorkerService; +import dev.ltms.bridged.worker.ClaudeCodeLauncher; import io.modelcontextprotocol.common.McpTransportContext; import io.modelcontextprotocol.json.McpJsonMapper; import io.modelcontextprotocol.json.jackson3.JacksonMcpJsonMapperSupplier; @@ -34,8 +34,8 @@ import java.util.stream.Collectors; * / {@code bridge_status}; the worker calls {@code bridge_reply}. * *

Beyond delegation the primary also manages the fleet here (CB-108): {@code bridge_spawn} / - * {@code bridge_list} / {@code bridge_stop} adapt {@link WorkerService} so a worker's whole lifecycle - * is driven through MCP, with the subscription boundary still enforced inside {@code WorkerService}. + * {@code bridge_list} / {@code bridge_stop} adapt {@link ClaudeCodeLauncher} so a worker's whole lifecycle + * is driven through MCP, with the subscription boundary still enforced inside {@code ClaudeCodeLauncher}. * *

The tool logic lives in package-private static methods returning a * {@link McpSchema.CallToolResult}, so it is unit-testable without standing up the HTTP transport; @@ -59,7 +59,7 @@ public final class BridgeMcp { private final HttpServletStreamableServerTransportProvider transport; private final McpSyncServer server; - public BridgeMcp(MessageService messages, Rendezvous rendezvous, WorkerService workers, + public BridgeMcp(MessageService messages, Rendezvous rendezvous, ClaudeCodeLauncher workers, SessionManager sessions, ConnectionIdentity identity, WorkerPresence presence) { McpJsonMapper json = new JacksonMcpJsonMapperSupplier().get(); this.transport = HttpServletStreamableServerTransportProvider.builder() @@ -103,7 +103,7 @@ public final class BridgeMcp { status(messages, str(req.arguments(), "sessionId"))) .toolCall(pollTool(), (_, req) -> poll(messages, str(req.arguments(), "ticket"))) - // Fleet management (CB-108): spawn/list/stop over WorkerService. + // Fleet management (CB-108): spawn/list/stop over ClaudeCodeLauncher. .toolCall(spawnTool(), (exchange, req) -> { Map a = req.arguments(); // CB-112: worker inherits the primary's cwd unless the call pins one. @@ -342,14 +342,14 @@ public final class BridgeMcp { } /** {@code bridge_profiles}: the configured worker profiles and the default. */ - static McpSchema.CallToolResult profiles(WorkerService workers) { + static McpSchema.CallToolResult profiles(ClaudeCodeLauncher workers) { return text(json(Map.of( "profiles", workers.profiles(), "default", workers.defaultProfile() == null ? "" : workers.defaultProfile()))); } /** {@code bridge_list}: bridge-owned roster merged with live herdr status by paneId. */ - static McpSchema.CallToolResult listWorkers(WorkerService workers, SessionManager sessions) { + static McpSchema.CallToolResult listWorkers(ClaudeCodeLauncher workers, SessionManager sessions) { try { Map live = workers.list().stream() .filter(a -> a.paneId() != null) diff --git a/bridged/src/main/java/dev/ltms/bridged/peer/PeerLauncher.java b/bridged/src/main/java/dev/ltms/bridged/peer/PeerLauncher.java index c987ab4..5e86005 100644 --- a/bridged/src/main/java/dev/ltms/bridged/peer/PeerLauncher.java +++ b/bridged/src/main/java/dev/ltms/bridged/peer/PeerLauncher.java @@ -6,7 +6,7 @@ import java.util.Set; /** * SPI for materializing a connected peer — the only way the bridge core creates or tears down * a peer process. Every launcher is a first-party, in-tree adapter selected by (future) profile - * config; today's single adapter is the {@code WorkerService} / Claude Code over herdr. + * config; today's single adapter is the {@code ClaudeCodeLauncher} / Claude Code over herdr. * *

The core delegates spawn and teardown to this interface without knowing how the peer is set * up. Environment variables, CLI flags, subscription guards, transport (herdr tab/pane) layout, diff --git a/bridged/src/main/java/dev/ltms/bridged/rest/BridgedApp.java b/bridged/src/main/java/dev/ltms/bridged/rest/BridgedApp.java index 2ebf9f1..be03338 100644 --- a/bridged/src/main/java/dev/ltms/bridged/rest/BridgedApp.java +++ b/bridged/src/main/java/dev/ltms/bridged/rest/BridgedApp.java @@ -12,7 +12,7 @@ import dev.ltms.bridged.msg.Rendezvous; import dev.ltms.bridged.session.SessionManager; import dev.ltms.bridged.session.WorkerSession; import dev.ltms.bridged.session.WorktreeRequest; -import dev.ltms.bridged.worker.WorkerService; +import dev.ltms.bridged.worker.ClaudeCodeLauncher; import io.javalin.Javalin; import io.javalin.http.Context; import jakarta.servlet.http.HttpServlet; @@ -44,7 +44,7 @@ public final class BridgedApp { private static final long MAX_ASK_TIMEOUT_MS = 115_000; private final HerdrClient herdr; - private final WorkerService workers; + private final ClaudeCodeLauncher workers; private final SessionManager sessions; // CB-301: authoritative session registry private final MessageService messages; private final Rendezvous rendezvous; @@ -52,7 +52,7 @@ public final class BridgedApp { private final HttpServlet mcpServlet; // MCP Streamable-HTTP endpoint, mounted at /mcp (nullable) private final ObjectMapper mapper = new ObjectMapper(); - public BridgedApp(HerdrClient herdr, WorkerService workers, SessionManager sessions, + public BridgedApp(HerdrClient herdr, ClaudeCodeLauncher workers, SessionManager sessions, MessageService messages, Rendezvous rendezvous, WorkerPresence presence, HttpServlet mcpServlet) { this.herdr = herdr; diff --git a/bridged/src/main/java/dev/ltms/bridged/worker/WorkerService.java b/bridged/src/main/java/dev/ltms/bridged/worker/ClaudeCodeLauncher.java similarity index 98% rename from bridged/src/main/java/dev/ltms/bridged/worker/WorkerService.java rename to bridged/src/main/java/dev/ltms/bridged/worker/ClaudeCodeLauncher.java index 7b7fc50..dff6c7c 100644 --- a/bridged/src/main/java/dev/ltms/bridged/worker/WorkerService.java +++ b/bridged/src/main/java/dev/ltms/bridged/worker/ClaudeCodeLauncher.java @@ -42,9 +42,9 @@ import java.util.regex.Pattern; * clutter the user's real work spaces. Teardown removes the worker's pane and its * now-empty tab, tolerating an already-gone worker so a repeated DELETE is harmless. */ -public final class WorkerService implements PeerLauncher { +public final class ClaudeCodeLauncher implements PeerLauncher { - private static final Logger log = LoggerFactory.getLogger(WorkerService.class); + private static final Logger log = LoggerFactory.getLogger(ClaudeCodeLauncher.class); /** herdr rejects a duplicate agent {@code name}; we retry a bumped name this many times. */ private static final int NAME_RETRIES = 8; @@ -86,9 +86,9 @@ public final class WorkerService implements PeerLauncher { // cannot collide with same-profile workers that outlived a restart. See startUniquelyNamed. private final String nameNonce = String.format("%06x", new SecureRandom().nextInt(1 << 24)); - public WorkerService(AgentControl agents, WorkspaceControl spaces, SubscriptionGuard guard, - Map profiles, String defaultProfile, - Function env) { + public ClaudeCodeLauncher(AgentControl agents, WorkspaceControl spaces, SubscriptionGuard guard, + Map profiles, String defaultProfile, + Function env) { this.agents = agents; this.spaces = spaces; this.guard = guard; diff --git a/bridged/src/test/java/dev/ltms/bridged/mcp/BridgeMcpTest.java b/bridged/src/test/java/dev/ltms/bridged/mcp/BridgeMcpTest.java index c305402..27b5abe 100644 --- a/bridged/src/test/java/dev/ltms/bridged/mcp/BridgeMcpTest.java +++ b/bridged/src/test/java/dev/ltms/bridged/mcp/BridgeMcpTest.java @@ -12,7 +12,7 @@ import dev.ltms.bridged.session.FakeWorktrees; import dev.ltms.bridged.session.SessionManager; import dev.ltms.bridged.session.WorkerSession; import dev.ltms.bridged.session.WorktreeRequest; -import dev.ltms.bridged.worker.WorkerService; +import dev.ltms.bridged.worker.ClaudeCodeLauncher; import io.modelcontextprotocol.spec.McpSchema; import org.junit.jupiter.api.Test; @@ -39,11 +39,11 @@ class BridgeMcpTest { return ((McpSchema.TextContent) r.content().getFirst()).text(); } - private static WorkerService workerService(FakeHerdr h, String baseUrl, Set allow) { + private static ClaudeCodeLauncher workerService(FakeHerdr h, String baseUrl, Set allow) { BridgedConfig.Worker cfg = new BridgedConfig.Worker( "ltms-local", baseUrl, "coder", null, "BRIDGED_WORKER_TOKEN", null, "tab", "bridged-workers", "worker: {profile} #{n}", null, null, null); - return new WorkerService(new AgentControl(h), new WorkspaceControl(h), + return new ClaudeCodeLauncher(new AgentControl(h), new WorkspaceControl(h), new SubscriptionGuard(allow), Map.of(cfg.profile(), cfg), cfg.profile(), _ -> "tok"); } diff --git a/bridged/src/test/java/dev/ltms/bridged/rest/BridgedAppTest.java b/bridged/src/test/java/dev/ltms/bridged/rest/BridgedAppTest.java index 6ca1134..1c482c6 100644 --- a/bridged/src/test/java/dev/ltms/bridged/rest/BridgedAppTest.java +++ b/bridged/src/test/java/dev/ltms/bridged/rest/BridgedAppTest.java @@ -16,7 +16,7 @@ import dev.ltms.bridged.session.FakeWorktrees; import dev.ltms.bridged.session.GitWorktrees; import dev.ltms.bridged.session.SessionManager; import dev.ltms.bridged.session.Worktrees; -import dev.ltms.bridged.worker.WorkerService; +import dev.ltms.bridged.worker.ClaudeCodeLauncher; import io.javalin.Javalin; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Test; @@ -64,7 +64,7 @@ class BridgedAppTest { "ltms-local", workerBaseUrl, "coder", null, "BRIDGED_WORKER_TOKEN", null, placement, "bridged-workers", "worker: {profile} #{n}", null, null, null); AgentControl agents = new AgentControl(herdr); - WorkerService workers = new WorkerService( + ClaudeCodeLauncher workers = new ClaudeCodeLauncher( agents, new WorkspaceControl(herdr), new SubscriptionGuard(allow), Map.of(wcfg.profile(), wcfg), wcfg.profile(), k -> "BRIDGED_WORKER_TOKEN".equals(k) ? "tok-abc" : null); diff --git a/bridged/src/test/java/dev/ltms/bridged/session/SessionManagerTest.java b/bridged/src/test/java/dev/ltms/bridged/session/SessionManagerTest.java index b2fff3f..0a53406 100644 --- a/bridged/src/test/java/dev/ltms/bridged/session/SessionManagerTest.java +++ b/bridged/src/test/java/dev/ltms/bridged/session/SessionManagerTest.java @@ -5,7 +5,7 @@ import dev.ltms.bridged.guard.SubscriptionGuard; import dev.ltms.bridged.herdr.AgentControl; import dev.ltms.bridged.herdr.FakeHerdr; import dev.ltms.bridged.herdr.WorkspaceControl; -import dev.ltms.bridged.worker.WorkerService; +import dev.ltms.bridged.worker.ClaudeCodeLauncher; import org.junit.jupiter.api.Test; import java.util.List; @@ -28,7 +28,7 @@ class SessionManagerTest { "ltms-local", "http://gx00.gw:8000", "coder", null, "BRIDGED_WORKER_TOKEN", List.of("ccs", "ltms-local"), "tab", "bridged-workers", "worker: {profile} #{n}", null, null, null); - WorkerService workers = new WorkerService(new AgentControl(herdr), new WorkspaceControl(herdr), + ClaudeCodeLauncher workers = new ClaudeCodeLauncher(new AgentControl(herdr), new WorkspaceControl(herdr), new SubscriptionGuard(Set.of("gx00.gw")), Map.of(cfg.profile(), cfg), cfg.profile(), _ -> null); return new SessionManager(workers); } @@ -42,7 +42,7 @@ class SessionManagerTest { "ltms-local", "http://gx00.gw:8000", "coder", null, "BRIDGED_WORKER_TOKEN", List.of("ccs", "ltms-local"), "tab", "bridged-workers", "worker: {profile} #{n}", null, null, null); - WorkerService workers = new WorkerService(new AgentControl(herdr), new WorkspaceControl(herdr), + ClaudeCodeLauncher workers = new ClaudeCodeLauncher(new AgentControl(herdr), new WorkspaceControl(herdr), new SubscriptionGuard(Set.of("gx00.gw")), Map.of(cfg.profile(), cfg), cfg.profile(), _ -> null); return new SessionManager(workers, new GitWorktrees(), clock, contextCap); } diff --git a/bridged/src/test/java/dev/ltms/bridged/session/WorktreeSessionManagerTest.java b/bridged/src/test/java/dev/ltms/bridged/session/WorktreeSessionManagerTest.java index b928407..774dc6d 100644 --- a/bridged/src/test/java/dev/ltms/bridged/session/WorktreeSessionManagerTest.java +++ b/bridged/src/test/java/dev/ltms/bridged/session/WorktreeSessionManagerTest.java @@ -5,7 +5,7 @@ import dev.ltms.bridged.guard.SubscriptionGuard; import dev.ltms.bridged.herdr.AgentControl; import dev.ltms.bridged.herdr.FakeHerdr; import dev.ltms.bridged.herdr.WorkspaceControl; -import dev.ltms.bridged.worker.WorkerService; +import dev.ltms.bridged.worker.ClaudeCodeLauncher; import org.junit.jupiter.api.Test; import java.util.List; @@ -21,12 +21,12 @@ import static org.junit.jupiter.api.Assertions.*; */ class WorktreeSessionManagerTest { - private static WorkerService workerService(FakeHerdr herdr) { + private static ClaudeCodeLauncher workerService(FakeHerdr herdr) { BridgedConfig.Worker cfg = new BridgedConfig.Worker( "ltms-local", "http://gx00.gw:8000", "coder", null, "BRIDGED_WORKER_TOKEN", List.of("ccs", "ltms-local"), "tab", "bridged-workers", "worker: {profile} #{n}", null, null, null); - return new WorkerService(new AgentControl(herdr), new WorkspaceControl(herdr), + return new ClaudeCodeLauncher(new AgentControl(herdr), new WorkspaceControl(herdr), new SubscriptionGuard(Set.of("gx00.gw")), Map.of(cfg.profile(), cfg), cfg.profile(), _ -> null); } diff --git a/bridged/src/test/java/dev/ltms/bridged/worker/WorkerServiceTest.java b/bridged/src/test/java/dev/ltms/bridged/worker/ClaudeCodeLauncherTest.java similarity index 85% rename from bridged/src/test/java/dev/ltms/bridged/worker/WorkerServiceTest.java rename to bridged/src/test/java/dev/ltms/bridged/worker/ClaudeCodeLauncherTest.java index 6cd5a4e..bc67a5f 100644 --- a/bridged/src/test/java/dev/ltms/bridged/worker/WorkerServiceTest.java +++ b/bridged/src/test/java/dev/ltms/bridged/worker/ClaudeCodeLauncherTest.java @@ -18,13 +18,13 @@ import java.util.function.Function; import static org.junit.jupiter.api.Assertions.*; /** The step-4 launch-flag injection: the bridge MCP + reply charter are appended to the argv. */ -class WorkerServiceTest { +class ClaudeCodeLauncherTest { - private WorkerService service(FakeHerdr herdr, List argv, String mcpUrl) { + private ClaudeCodeLauncher service(FakeHerdr herdr, List argv, String mcpUrl) { BridgedConfig.Worker cfg = new BridgedConfig.Worker( "ltms-local", "http://gx00.gw:8000", "coder", null, "BRIDGED_WORKER_TOKEN", argv, "tab", "bridged-workers", "worker: {profile} #{n}", mcpUrl, null, null); - return new WorkerService(new AgentControl(herdr), new WorkspaceControl(herdr), + return new ClaudeCodeLauncher(new AgentControl(herdr), new WorkspaceControl(herdr), new SubscriptionGuard(Set.of("gx00.gw")), Map.of(cfg.profile(), cfg), cfg.profile(), _ -> null); } @@ -54,12 +54,12 @@ class WorkerServiceTest { assertEquals(List.of("bash", "-c", "sleep 1"), spawnedArgv(herdr), "argv untouched without mcpUrl"); } - private WorkerService multiProfile(FakeHerdr herdr) { + private ClaudeCodeLauncher multiProfile(FakeHerdr herdr) { BridgedConfig.Worker gx10 = new BridgedConfig.Worker("gx10", "http://gx10.gw:8000", "coder", null, "BRIDGED_WORKER_TOKEN", List.of("ccs", "gx10"), "tab", "bridged-workers", "w #{n}", null, null, null); BridgedConfig.Worker ollama = new BridgedConfig.Worker("ollama", "http://ollama.ltms.dev", null, null, "BRIDGED_WORKER_TOKEN", List.of("ccs", "ollama"), "tab", "bridged-workers", "w #{n}", null, null, null); - return new WorkerService(new AgentControl(herdr), new WorkspaceControl(herdr), + return new ClaudeCodeLauncher(new AgentControl(herdr), new WorkspaceControl(herdr), new SubscriptionGuard(Set.of("gx10.gw", "ollama.ltms.dev")), Map.of("gx10", gx10, "ollama", ollama), "gx10", _ -> "tok"); } @@ -103,7 +103,7 @@ class WorkerServiceTest { BridgedConfig.Worker cfg = new BridgedConfig.Worker("ltms-local", "http://gx00.gw:8000", "coder", null, "BRIDGED_WORKER_TOKEN", List.of("ccs", "ltms-local"), "tab", "bridged-workers", "w #{n}", null, "/pinned/dir", null); - WorkerService svc = new WorkerService(new AgentControl(herdr), new WorkspaceControl(herdr), + ClaudeCodeLauncher svc = new ClaudeCodeLauncher(new AgentControl(herdr), new WorkspaceControl(herdr), new SubscriptionGuard(Set.of("gx00.gw")), Map.of("ltms-local", cfg), "ltms-local", _ -> null); svc.spawn("ltms-local", null, "/caller/home"); assertEquals("/pinned/dir", startCwd(herdr), "a profile-pinned cwd overrides the caller's"); @@ -135,7 +135,7 @@ class WorkerServiceTest { case "GITEA_HOST" -> "git.ltms.dev"; default -> null; }; - new WorkerService(new AgentControl(herdr), new WorkspaceControl(herdr), + new ClaudeCodeLauncher(new AgentControl(herdr), new WorkspaceControl(herdr), new SubscriptionGuard(Set.of("gx00.gw")), Map.of("impl", cfg), "impl", host).spawn(); Map env = startEnv(herdr); @@ -151,7 +151,7 @@ class WorkerServiceTest { BridgedConfig.Worker cfg = new BridgedConfig.Worker("ltms-local", "http://gx00.gw:8000", "coder", null, "BRIDGED_WORKER_TOKEN", List.of("ccs"), "tab", "bridged-workers", "w #{n}", null, null, null); - new WorkerService(new AgentControl(herdr), new WorkspaceControl(herdr), + new ClaudeCodeLauncher(new AgentControl(herdr), new WorkspaceControl(herdr), new SubscriptionGuard(Set.of("gx00.gw")), Map.of("ltms-local", cfg), "ltms-local", _ -> "would-be-secret").spawn(); @@ -164,20 +164,20 @@ class WorkerServiceTest { @Test void isForeignWorkerMatchesOurSchemeWithANonSelfNonce() { - assertTrue(WorkerService.isForeignWorker("claude-ollama-be09c2-2", "aaaaaa"), + assertTrue(ClaudeCodeLauncher.isForeignWorker("claude-ollama-be09c2-2", "aaaaaa"), "a bridge worker name with a different nonce is a prior daemon's orphan"); - assertTrue(WorkerService.isForeignWorker("claude-gx10-4127af-11", "aaaaaa"), + assertTrue(ClaudeCodeLauncher.isForeignWorker("claude-gx10-4127af-11", "aaaaaa"), "profile and multi-digit seq are still parsed; foreign nonce ⇒ reap"); } @Test void isForeignWorkerSparesOurOwnLiveWorkersAndNonWorkers() { - assertFalse(WorkerService.isForeignWorker("claude-ollama-abcdef-3", "abcdef"), + assertFalse(ClaudeCodeLauncher.isForeignWorker("claude-ollama-abcdef-3", "abcdef"), "a worker with THIS process's nonce is ours and live — never reap it"); - assertFalse(WorkerService.isForeignWorker(null, "abcdef"), "an unnamed agent is not a worker"); - assertFalse(WorkerService.isForeignWorker("claude", "abcdef"), "a bare kind name is not a worker"); - assertFalse(WorkerService.isForeignWorker("my-repl", "abcdef"), "a user's own label is not a worker"); - assertFalse(WorkerService.isForeignWorker("claude-ollama-XYZ123-2", "abcdef"), + assertFalse(ClaudeCodeLauncher.isForeignWorker(null, "abcdef"), "an unnamed agent is not a worker"); + assertFalse(ClaudeCodeLauncher.isForeignWorker("claude", "abcdef"), "a bare kind name is not a worker"); + assertFalse(ClaudeCodeLauncher.isForeignWorker("my-repl", "abcdef"), "a user's own label is not a worker"); + assertFalse(ClaudeCodeLauncher.isForeignWorker("claude-ollama-XYZ123-2", "abcdef"), "a non-hex nonce does not match our scheme"); } @@ -193,7 +193,7 @@ class WorkerServiceTest { @Test void reapsAForeignOrphanButSparesOurOwnWorkerAndUserSessions() { FakeHerdr herdr = new FakeHerdr(); - WorkerService svc = multiProfile(herdr); + ClaudeCodeLauncher svc = multiProfile(herdr); herdr.withAgent("claude-ollama-be09c2-2", "term_orphan", "wQ:pF", "wQ:t8") // prior daemon's leak .withAgent("claude-gx10-" + svc.nameNonce() + "-1", "term_mine", "wQ:pMine", "wQ:tMine"); // ours, live // (the fake's default unnamed term_a stands in for a user's own Claude session) @@ -210,7 +210,7 @@ class WorkerServiceTest { @Test void reapCountsAnAlreadyGoneOrphanAsReaped() { FakeHerdr herdr = new FakeHerdr().paneCloseFailsWith("pane_not_found"); - WorkerService svc = multiProfile(herdr); + ClaudeCodeLauncher svc = multiProfile(herdr); herdr.withAgent("claude-ollama-0d856d-3", "term_gone", "wQ:pS", "wQ:tD"); assertEquals(1, svc.reapOrphanWorkers(), @@ -228,7 +228,7 @@ class WorkerServiceTest { @Test void spawnReturnsPeerHandleWithIdEqualToPaneId() { FakeHerdr herdr = new FakeHerdr(); - WorkerService svc = service(herdr, List.of("ccs", "ltms-local"), null); + ClaudeCodeLauncher svc = service(herdr, List.of("ccs", "ltms-local"), null); PeerHandle handle = svc.spawn(new SpawnRequest(null, null, null)); @@ -239,7 +239,7 @@ class WorkerServiceTest { @Test void spawnReturnsPeerHandleWithCorrectTerminalId() { FakeHerdr herdr = new FakeHerdr(); - WorkerService svc = service(herdr, List.of("ccs", "ltms-local"), null); + ClaudeCodeLauncher svc = service(herdr, List.of("ccs", "ltms-local"), null); PeerHandle handle = svc.spawn(new SpawnRequest("ltms-local", null, "/caller")); @@ -249,7 +249,7 @@ class WorkerServiceTest { @Test void capabilitiesIncludeMidTurnAskWorktreeOrphanReap() { FakeHerdr herdr = new FakeHerdr(); - WorkerService svc = service(herdr, List.of("ccs", "ltms-local"), null); + ClaudeCodeLauncher svc = service(herdr, List.of("ccs", "ltms-local"), null); Set caps = svc.capabilities(); @@ -265,7 +265,7 @@ class WorkerServiceTest { "impl", "http://gx00.gw:8000", "coder", null, "BRIDGED_WORKER_TOKEN", List.of("ccs", "impl"), "tab", "bridged-workers", "w #{n}", null, null, null, "GITEA_ACCESS_TOKEN", null); - WorkerService svc = new WorkerService(new AgentControl(herdr), new WorkspaceControl(herdr), + ClaudeCodeLauncher svc = new ClaudeCodeLauncher(new AgentControl(herdr), new WorkspaceControl(herdr), new SubscriptionGuard(Set.of("gx00.gw")), Map.of("impl", cfg), "impl", _ -> "tok"); @@ -276,7 +276,7 @@ class WorkerServiceTest { @Test void capabilitiesExcludeSelfPrWhenNoGitToken() { FakeHerdr herdr = new FakeHerdr(); - WorkerService svc = service(herdr, List.of("ccs", "ltms-local"), null); + ClaudeCodeLauncher svc = service(herdr, List.of("ccs", "ltms-local"), null); assertFalse(svc.capabilities().contains(Capability.SELF_PR), "no git token profile → no SELF_PR capability"); @@ -285,7 +285,7 @@ class WorkerServiceTest { @Test void effectiveCwdViaSpawnRequestMatchesExistingResolution() { FakeHerdr herdr = new FakeHerdr(); - WorkerService svc = service(herdr, List.of("ccs", "ltms-local"), null); + ClaudeCodeLauncher svc = service(herdr, List.of("ccs", "ltms-local"), null); String cwd = svc.effectiveCwd(new SpawnRequest("ltms-local", "/work/proj", "/caller/home")); @@ -295,7 +295,7 @@ class WorkerServiceTest { @Test void profilesViaPeerLauncherMatchesExistingApi() { FakeHerdr herdr = new FakeHerdr(); - WorkerService svc = multiProfile(herdr); + ClaudeCodeLauncher svc = multiProfile(herdr); assertEquals(Set.of("gx10", "ollama"), svc.profiles(), "profiles() via PeerLauncher must match"); } @@ -303,7 +303,7 @@ class WorkerServiceTest { @Test void defaultProfileViaPeerLauncherMatches() { FakeHerdr herdr = new FakeHerdr(); - WorkerService svc = multiProfile(herdr); + ClaudeCodeLauncher svc = multiProfile(herdr); assertEquals("gx10", svc.defaultProfile(), "defaultProfile() via PeerLauncher must match"); } @@ -311,7 +311,7 @@ class WorkerServiceTest { @Test void stopViaPeerLauncherTearsDownByHandleId() { FakeHerdr herdr = new FakeHerdr(); - WorkerService svc = service(herdr, List.of("ccs", "ltms-local"), null); + ClaudeCodeLauncher svc = service(herdr, List.of("ccs", "ltms-local"), null); PeerHandle handle = svc.spawn(new SpawnRequest(null, null, null)); svc.stop(handle.id());