fix(ui): route answer worktrees from source session

This commit is contained in:
Bohdan Triapitsyn
2026-09-04 19:46:21 +03:00
parent f160f3aac4
commit b4a38061bc
7 changed files with 234 additions and 61 deletions
@@ -6,6 +6,8 @@ const createSessionCalls: Array<{ title?: string; directory: string | null; pare
const permissionAutoAcceptCalls: Array<[string, boolean]> = []
const savedVariantCalls: Array<string | undefined> = []
let configVariantOverride: string | null | undefined
let projects: Array<{ id: string; path: string; label: string }> = []
const createdWorktreeProjects: Array<{ id: string; path: string }> = []
// Sync's session→directory index. `createSession` writes it, and directory
// resolution reads it as the authoritative source, so the mock has to keep one.
const sessionDirectoryRegistry = new Map<string, string>()
@@ -111,7 +113,7 @@ mock.module("@/stores/useConfigStore", () => ({
mock.module("@/stores/useProjectsStore", () => ({
useProjectsStore: {
getState: () => ({
projects: [],
projects,
activeProjectId: null,
getActiveProject: () => null,
}),
@@ -127,6 +129,12 @@ mock.module("@/stores/useDirectoryStore", () => ({
},
}))
mock.module("@/stores/useSessionGoalArmStore", () => ({
useSessionGoalArmStore: {
getState: () => ({ setArmed: () => undefined }),
},
}))
mock.module("@/stores/useGlobalSessionsStore", () => ({
useGlobalSessionsStore: {
getState: () => ({
@@ -304,6 +312,33 @@ mock.module("../session-actions", () => ({
abortCurrentOperation: mock(async () => undefined),
}))
mock.module("@/lib/git/branchNameGenerator", () => ({
generateBranchName: () => "generated-branch",
}))
mock.module("@/lib/openchamberConfig", () => ({
getWorktreeSetupCommands: async () => [],
getWorktreeSetupWaitEnabled: async () => false,
}))
mock.module("@/lib/worktrees/worktreeBootstrap", () => ({
waitForWorktreeBootstrap: async () => undefined,
}))
mock.module("@/lib/worktrees/worktreeCreate", () => ({
createWorktreeWithDefaults: async (project: { id: string; path: string }) => {
createdWorktreeProjects.push(project)
return {
source: "sdk",
name: "generated-branch",
path: "/worktrees/generated-branch",
projectDirectory: project.path,
branch: "generated-branch",
label: "generated-branch",
}
},
}))
const { materializeOpenDraftSession, useSessionUIStore } = await import("../session-ui-store")
describe("issue 2039 draft auto-accept", () => {
@@ -500,3 +535,94 @@ describe("issue 2039 draft auto-accept", () => {
expect(useSessionUIStore.getState().getDirectoryForSession(sessionId)).toBe("/canonical/worktree")
})
})
describe("assistant answer worktree routing", () => {
test("reports session creation failure instead of completing silently", async () => {
const state = useSessionUIStore.getState()
const createFromAssistantMessage = state.createSessionFromAssistantMessage
const originalCreateSession = state.createSession
useSessionUIStore.setState({
createSession: async () => null,
})
try {
await expect(createFromAssistantMessage({
sessionId: "source-session",
directory: "/repo",
text: "Implement the plan",
}, {
providerID: "provider",
modelID: "model",
variant: "",
agent: "build",
instructions: "Follow the answer",
})).rejects.toThrow("Failed to create session")
} finally {
useSessionUIStore.setState({ createSession: originalCreateSession })
}
})
test("creates a sibling worktree from the captured source worktree directory", async () => {
projects = [
{ id: "project", path: "/repo", label: "Repo" },
{ id: "source-worktree", path: "/worktrees/source", label: "Source worktree" },
]
createdWorktreeProjects.length = 0
const sourceWorktree = {
path: "/worktrees/source",
projectDirectory: "/repo",
branch: "source",
label: "source",
}
const state = useSessionUIStore.getState()
const createFromAssistantMessage = state.createSessionFromAssistantMessage
const originalCreateSession = state.createSession
const originalSendMessage = state.sendMessage
const originalWorktreeMetadata = state.worktreeMetadata
let createdDirectory: string | null | undefined
useSessionUIStore.setState({
availableWorktreesByProject: new Map([["/repo", [sourceWorktree]]]),
worktreeMetadata: new Map([["source-session", sourceWorktree]]),
createSession: async (_title, directory) => {
createdDirectory = directory
return {
id: "created-session",
slug: "created-session",
projectID: "project",
directory: directory ?? "",
title: "Created session",
version: "1",
time: { created: 1, updated: 1 },
}
},
sendMessage: async () => undefined,
})
try {
await createFromAssistantMessage({
sessionId: "source-session",
directory: "/worktrees/source",
text: "Implement the plan",
}, {
providerID: "provider",
modelID: "model",
variant: "",
agent: "build",
instructions: "Follow the answer",
createWorktree: true,
})
} finally {
useSessionUIStore.setState({
createSession: originalCreateSession,
sendMessage: originalSendMessage,
worktreeMetadata: originalWorktreeMetadata,
})
projects = []
}
expect(createdWorktreeProjects).toEqual([{ id: "project", path: "/repo" }])
expect(createdDirectory).toBe("/worktrees/generated-branch")
})
})