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 774dc6d..8f8f581 100644 --- a/bridged/src/test/java/dev/ltms/bridged/session/WorktreeSessionManagerTest.java +++ b/bridged/src/test/java/dev/ltms/bridged/session/WorktreeSessionManagerTest.java @@ -166,4 +166,58 @@ class WorktreeSessionManagerTest { assertEquals(2, sessions.roster().size()); } + /** + * CB-507 regression. A plain REST spawn supplies neither a requested nor a caller cwd + * ({@code BridgedApp} hardcodes {@code callerCwd = null}), and the worktree branch used to + * resolve the repo root from just those two — yielding {@code null}, which the real + * {@code GitWorktrees} turns into {@code git -C null} and an NPE out of {@code ProcessBuilder} + * (HTTP 500). + * + *

Note this asserts on the recorded cwd rather than expecting a throw: + * {@link FakeWorktrees#repoRoot} only records its argument and returns a canned root, so a + * null flows through the fake harmlessly. That permissiveness is precisely why the whole + * suite stayed green while the feature was broken in production — so the assertion has to be + * "a usable cwd was passed down", not "an exception was raised". + */ + @Test + void worktreeAcquireWithNoRequestedOrCallerCwdStillResolvesANonNullRepoRoot() { + FakeHerdr herdr = new FakeHerdr(); + FakeWorktrees worktrees = new FakeWorktrees(); + SessionManager sessions = new SessionManager(workerService(herdr), worktrees); + + sessions.acquire("ltms-local", null, null, null, new WorktreeRequest("cb-507", null)); + + assertFalse(worktrees.repoRootCalls().isEmpty(), + "repoRoot should have been called to resolve the repo root"); + String cwd = worktrees.repoRootCalls().getFirst().cwd(); + assertNotNull(cwd, "a null cwd here becomes `git -C null` and NPEs in the real GitWorktrees"); + assertFalse(cwd.isBlank(), "a blank cwd is as unusable as a null one"); + } + + /** + * The same line carried a second, quieter bug: it never consulted the profile's configured + * {@code cwd:}, so a worktree spawn silently ignored a pinned per-profile working directory. + * Routing through {@code effectiveCwd} honours it. + */ + @Test + void worktreeAcquireHonoursTheProfileConfiguredCwd() { + FakeHerdr herdr = new FakeHerdr(); + FakeWorktrees worktrees = new FakeWorktrees().withRepoRoot("/repo").withPrefix("/wt"); + // Argument order matters: configDir is the 4th parameter, cwd the 11th (after mcpUrl). + 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, "/pinned/dir", null); + ClaudeCodeLauncher launcher = new ClaudeCodeLauncher( + new AgentControl(herdr), new WorkspaceControl(herdr), + new SubscriptionGuard(Set.of("gx00.gw")), Map.of(cfg.profile(), cfg), cfg.profile(), _ -> null); + SessionManager sessions = new SessionManager(launcher, worktrees); + + sessions.acquire("ltms-local", null, null, null, new WorktreeRequest("cb-507b", null)); + + assertEquals(1, worktrees.repoRootCalls().size()); + assertEquals("/pinned/dir", worktrees.repoRootCalls().getFirst().cwd(), + "the profile's configured cwd must reach repoRoot, not be ignored"); + } + }