From ff4c1271a868f75cdcbac50180178bf5d937e427 Mon Sep 17 00:00:00 2001 From: Zero Liu Date: Sun, 5 Jul 2026 13:02:49 +0800 Subject: [PATCH] docs(agent-mode): note the compose-lane model; align picker test with swap path Add a one-line architecture note (AGENTS.md) on the compose lane, and update the cross-backend picker test to assert the new replaceSessionInPlace(inheritLane, seedSelection) path instead of the old createSession + closeSession. Co-Authored-By: Claude Opus 4.8 --- AGENTS.md | 1 + .../ui/agentModelPickerHelpers.test.ts | 18 +++++++++++------- 2 files changed, 12 insertions(+), 7 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index 6948cd39..494819c6 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -48,5 +48,6 @@ Read the matching guide when your task touches that area — they aren't loaded - Local model support via Ollama / LM Studio. - Rate limiting is implemented for all API calls. - Message & chat architecture (Repository → Manager → UIState → UI; single `MessageRepository`; per-project isolation) → [`designdocs/MESSAGE_ARCHITECTURE.md`](./designdocs/MESSAGE_ARCHITECTURE.md). +- A tab's compose lane (`AgentSession.composeLaneId`) is stable across in-place session swaps; the Agent Mode composer keys by lane, while the transcript/token-meter/persistence key by session. - Tech debt and known issues → [`designdocs/todo/TECHDEBT.md`](./designdocs/todo/TECHDEBT.md). Current session plan → [`TODO.md`](./TODO.md). - Available Tailwind tokens/classes → [`tailwind.config.js`](./tailwind.config.js). diff --git a/src/agentMode/ui/agentModelPickerHelpers.test.ts b/src/agentMode/ui/agentModelPickerHelpers.test.ts index c6ee17d3..9da5b58c 100644 --- a/src/agentMode/ui/agentModelPickerHelpers.test.ts +++ b/src/agentMode/ui/agentModelPickerHelpers.test.ts @@ -152,6 +152,7 @@ function makeManager(opts: { persistDefaultSelection?: jest.Mock; createSession?: jest.Mock; closeSession?: jest.Mock; + replaceSessionInPlace?: jest.Mock; }): AgentSessionManager { return { getCachedBackendState: (id: string) => opts.cachedStateById?.[id] ?? null, @@ -162,6 +163,7 @@ function makeManager(opts: { persistDefaultSelection: opts.persistDefaultSelection ?? jest.fn().mockResolvedValue(undefined), createSession: opts.createSession ?? jest.fn().mockResolvedValue(undefined), closeSession: opts.closeSession ?? jest.fn().mockResolvedValue(undefined), + replaceSessionInPlace: opts.replaceSessionInPlace ?? jest.fn().mockResolvedValue(undefined), } as unknown as AgentSessionManager; } @@ -612,14 +614,14 @@ describe("buildModelOnChange", () => { expect(applySelection).not.toHaveBeenCalled(); }); - it("cross-backend pick seeds the new session transiently without persisting the default", async () => { + it("cross-backend pick swaps the tab in place, seeding transiently without persisting the default", async () => { const persistDefaultSelection = jest.fn().mockResolvedValue(undefined); - const createSession = jest.fn().mockResolvedValue(undefined); + const replaceSessionInPlace = jest.fn().mockResolvedValue(undefined); const setDefaultBackend = jest.fn(); const closeSession = jest.fn().mockResolvedValue(undefined); const manager = makeManager({ persistDefaultSelection, - createSession, + replaceSessionInPlace, setDefaultBackend, closeSession, // The legacy single-arg path seeds effort from the target's persisted @@ -632,12 +634,14 @@ describe("buildModelOnChange", () => { // Allow the IIFE to run. await new Promise((r) => window.setTimeout(r, 0)); expect(persistDefaultSelection).not.toHaveBeenCalled(); - expect(createSession).toHaveBeenCalledWith("claude", undefined, { - baseModelId: "opus", - effort: "low", + // With an existing tab the pick swaps in place, inheriting the old session's + // composer lane and seeding the model — it does NOT mint+close a new tab. + expect(replaceSessionInPlace).toHaveBeenCalledWith("tab-1", "claude", { + inheritLane: true, + seedSelection: { baseModelId: "opus", effort: "low" }, }); expect(setDefaultBackend).toHaveBeenCalledWith("claude"); - expect(closeSession).toHaveBeenCalledWith("tab-1"); + expect(closeSession).not.toHaveBeenCalled(); }); it("ignores entries with no _backendId or unresolvable baseModelId", () => {