diff --git a/packages/ui/src/components/chat/__tests__/issue-2903-subagent-status-line-only.test.tsx b/packages/ui/src/components/chat/__tests__/issue-2903-subagent-status-line-only.test.tsx
index cd72ed46..54ed84d2 100644
--- a/packages/ui/src/components/chat/__tests__/issue-2903-subagent-status-line-only.test.tsx
+++ b/packages/ui/src/components/chat/__tests__/issue-2903-subagent-status-line-only.test.tsx
@@ -226,20 +226,23 @@ describe('issue #2903 busy embedded subagent status-line-only', () => {
expect(chatContainerSource).toContain('void ensureSessionRenderable(currentSessionId);');
});
- test('empty+busy branch skips empty state so StatusRowContainer can stand alone', () => {
+ test('the empty and idle branch leaves the status row to the busy path', () => {
+ // A busy session with no messages yet must fall through to the viewport so
+ // StatusRowContainer is the only thing on screen. The idle branch returns
+ // before it and must not render one of its own. The empty state itself no
+ // longer lives here: the draft surface owns it since the draft transition
+ // animation landed.
expect(chatContainerSource).toContain('if (sessionMessages.length === 0 && !sessionIsWorking)');
- expect(chatContainerSource).toContain('');
- const emptyBusyGuard = 'if (sessionMessages.length === 0 && !sessionIsWorking)';
- const emptyStateReturn = chatContainerSource.indexOf(emptyBusyGuard);
- expect(emptyStateReturn).toBeGreaterThan(-1);
- const emptyStateBlock = chatContainerSource.slice(
- emptyStateReturn,
- emptyStateReturn + 1600,
+ const emptyIdleGuard = 'if (sessionMessages.length === 0 && !sessionIsWorking)';
+ const emptyIdleReturn = chatContainerSource.indexOf(emptyIdleGuard);
+ expect(emptyIdleReturn).toBeGreaterThan(-1);
+ const emptyIdleBlock = chatContainerSource.slice(
+ emptyIdleReturn,
+ emptyIdleReturn + 1600,
);
- expect(emptyStateBlock).toContain('');
+ expect(emptyIdleBlock).not.toContain('');
});
test('visibility handshake remains as defense-in-depth for background work', () => {
diff --git a/packages/ui/src/sync/__tests__/issue-2039.test.ts b/packages/ui/src/sync/__tests__/issue-2039.test.ts
index 70a3048e..572d2094 100644
--- a/packages/ui/src/sync/__tests__/issue-2039.test.ts
+++ b/packages/ui/src/sync/__tests__/issue-2039.test.ts
@@ -4,6 +4,9 @@ import { togglePermissionAutoAccept } from "../../components/chat/permissionAuto
const storage = new Map()
const createSessionCalls: Array<{ title?: string; directory: string | null; parentID: string | null; metadata?: unknown }> = []
const permissionAutoAcceptCalls: Array<[string, boolean]> = []
+// 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()
let createdSessionDirectory: string | undefined
const getMockCalls = (fn: unknown): unknown[][] => ((fn as { mock?: { calls: unknown[][] } }).mock?.calls ?? [])
@@ -241,13 +244,34 @@ mock.module("../sync-refs", () => ({
getSyncMessages: () => [],
getSyncParts: () => [],
getAllSyncSessions: () => [],
- getSyncSessionDirectory: () => null,
+ getSyncSessionDirectory: (sessionId: string) => sessionDirectoryRegistry.get(sessionId) ?? null,
+ registerSessionDirectory: (sessionId: string, directory: string) => {
+ sessionDirectoryRegistry.set(sessionId, directory)
+ },
}))
mock.module("../session-actions", () => ({
- createSession: mock(async (title: string | undefined, directory: string | null, parentID: string | null, metadata?: unknown) => {
+ // Mirrors the real action's authoritative steps: the created session becomes
+ // current under the directory the server confirmed, and that directory enters
+ // the routing index. Everything these tests assert about routing depends on
+ // those two, so a mock without them tests nothing.
+ createSession: mock(async (
+ title: string | undefined,
+ directory: string | null,
+ parentID: string | null,
+ metadata?: unknown,
+ selectionTransition?: "submitted-draft",
+ ) => {
createSessionCalls.push({ title, directory, parentID, metadata })
- return { id: "ses_issue_2039", directory: createdSessionDirectory ?? directory }
+ const session = { id: "ses_issue_2039", directory: createdSessionDirectory ?? directory }
+ const sessionDirectory = session.directory ?? null
+ if (sessionDirectory) {
+ sessionDirectoryRegistry.set(session.id, sessionDirectory)
+ }
+ const { useSessionUIStore: store } = await import("../session-ui-store")
+ store.getState().setCurrentSession(session.id, sessionDirectory, selectionTransition)
+ store.getState().markSessionAsOpenChamberCreated(session.id)
+ return session
}),
deleteSession: mock(async () => true),
deleteSessions: mock(async () => ({ deletedIds: [], failedIds: [] })),
@@ -322,6 +346,7 @@ describe("issue 2039 draft auto-accept", () => {
beforeEach(() => {
storage.clear()
createSessionCalls.length = 0
+ sessionDirectoryRegistry.clear()
permissionAutoAcceptCalls.length = 0
createdSessionDirectory = undefined