diff --git a/bridged/src/main/java/dev/ltms/bridged/session/SessionManager.java b/bridged/src/main/java/dev/ltms/bridged/session/SessionManager.java index 83da133..1ad2d2c 100644 --- a/bridged/src/main/java/dev/ltms/bridged/session/SessionManager.java +++ b/bridged/src/main/java/dev/ltms/bridged/session/SessionManager.java @@ -147,7 +147,13 @@ public final class SessionManager implements TurnListener { String ownerTerminal, WorktreeRequest wt) { String resolvedProfile = (profile == null || profile.isBlank()) ? launcher.defaultProfile() : profile; - String repoRoot = worktrees.repoRoot(firstNonBlank(requestedCwd, callerCwd)); + // CB-507: resolve through the launcher's CB-112 chain (requested → profile cwd → caller → + // daemon cwd → "."), never the raw args. A plain REST spawn supplies neither a requested + // nor a caller cwd, so taking the first non-blank of those two yielded null and put + // `git -C null` on the command line — an NPE out of ProcessBuilder, surfacing as HTTP 500. + // The non-worktree path always used this chain; only this branch was missed. + String repoRoot = worktrees.repoRoot( + launcher.effectiveCwd(new SpawnRequest(resolvedProfile, requestedCwd, callerCwd))); String branch = "worker/" + slug(wt.ticketSlug()) + "-" + nonce(); String path = null; PeerHandle handle; @@ -192,13 +198,6 @@ public final class SessionManager implements TurnListener { return String.format("%06x", nonceRandom.nextInt(1 << 24)) + "-" + nonceSeq.incrementAndGet(); } - private static String firstNonBlank(String... values) { - for (String v : values) { - if (v != null && !v.isBlank()) return v; - } - return null; - } - /** * Release the old session and acquire a fresh one with the same profile and working directory. * The new session is guaranteed to have a pane id distinct from the old one (no-reuse invariant).