diff --git a/apps/web/src/command-palette-actions.test.ts b/apps/web/src/command-palette-actions.test.ts index ffe39598e..776019f17 100644 --- a/apps/web/src/command-palette-actions.test.ts +++ b/apps/web/src/command-palette-actions.test.ts @@ -71,15 +71,27 @@ describe("ACTION_COMMANDS", () => { } }); - test("new-workbench and new-agent speak consumer language, not mint", () => { + test("exactly one New workbench create row — no duplicate title+destination (CL-6820)", () => { + const newWorkbenchRows = ACTION_COMMANDS.filter( + (c) => c.title === "New workbench", + ); + expect(newWorkbenchRows).toHaveLength(1); + expect(newWorkbenchRows[0]?.id).toBe("new-workbench"); + + const titleCounts = new Map(); + for (const command of ACTION_COMMANDS) { + titleCounts.set(command.title, (titleCounts.get(command.title) ?? 0) + 1); + } + for (const [title, count] of titleCounts) { + expect(count, `duplicate title: ${title}`).toBe(1); + } + }); + + test("new-workbench speaks consumer language, not mint", () => { const workbench = ACTION_COMMANDS.find((c) => c.id === "new-workbench"); - const agent = ACTION_COMMANDS.find((c) => c.id === "new-agent"); expect(workbench?.title).toBe("New workbench"); - expect(agent?.title).toBe("New workbench"); expect(workbench?.subtitle).toBe("Start a new workbench"); - expect(agent?.subtitle).toBe("Start a new workbench"); expect(workbench?.subtitle.toLowerCase()).not.toContain("mint"); - expect(agent?.subtitle.toLowerCase()).not.toContain("mint"); }); test("labels 'New skill' to match the app's authoring model, not 'Install skill'", () => { @@ -96,13 +108,6 @@ describe("runActionCommand", () => { expect(navigated).toEqual([NEW_WORKBENCH_PATH]); }); - test("new-agent is the same entry point as new-workbench — opens the template picker", async () => { - const { ctx, navigated, dispatched } = context({ path: "/w/abc" }); - await runActionCommand("new-agent", ctx); - expect(dispatched).toEqual([]); - expect(navigated).toEqual([NEW_WORKBENCH_PATH]); - }); - test("new-routine opens the routine panel synchronously, beside whatever page is showing — no navigation, no pending flag", async () => { const { ctx, navigated, openedRoutines } = context({ path: "/library" }); await runActionCommand("new-routine", ctx); diff --git a/apps/web/src/command-palette-actions.ts b/apps/web/src/command-palette-actions.ts index e612a9034..9d8c86bd7 100644 --- a/apps/web/src/command-palette-actions.ts +++ b/apps/web/src/command-palette-actions.ts @@ -12,11 +12,11 @@ // tasks are dispatched by Myra from inside a workbench now. // // Workbench creation is not one of those — there is no dialog to race, no -// page to mount first: "new-workbench" and "new-agent" both navigate -// straight to the template picker (`/new`, CL-6342 — superseding CL-6138's -// direct mint for these two entry points), the same hop the sidebar's own -// "+" control uses. "New thread" is out of scope (killed by owner -// decision). +// page to mount first: "new-workbench" navigates straight to the template +// picker (`/new`, CL-6342 — superseding CL-6138's direct mint), the same +// hop the sidebar's own "+" control uses. A second "new-agent" row that +// said and did the same thing was dropped (CL-6820). "New thread" is out +// of scope (killed by owner decision). // // "New routine" (CL-6125, reworked CL-6139) needs none of that: it opens // the canvas column's routine pane, and canvas state lives in @@ -75,7 +75,6 @@ export function resetPendingDialogRequests(): void { export type ActionCommandId = | "new-workbench" - | "new-agent" | "new-routine" | "new-skill" | "upload-artifact" @@ -101,11 +100,6 @@ export const ACTION_COMMANDS: readonly ActionCommand[] = [ title: CHAT_STRINGS.newWorkbenchAction, subtitle: "Start a new workbench", }, - { - id: "new-agent", - title: CHAT_STRINGS.newWorkbenchAction, - subtitle: "Start a new workbench", - }, { id: "new-routine", title: "New routine", @@ -150,19 +144,18 @@ export type ActionCommandContext = { }; /** - * Runs one action command. "new-workbench" and "new-agent" both open the - * template picker (see the module doc); "new-skill" still goes through a - * pending flag when the palette fires it off-route (see the module doc), - * so the target page's own mount effect opens the dialog instead of a - * dispatch racing against that page's not-yet-mounted listener. + * Runs one action command. "new-workbench" opens the template picker (see + * the module doc); "new-skill" still goes through a pending flag when the + * palette fires it off-route (see the module doc), so the target page's + * own mount effect opens the dialog instead of a dispatch racing against + * that page's not-yet-mounted listener. */ export async function runActionCommand( id: ActionCommandId, ctx: ActionCommandContext, ): Promise { switch (id) { - case "new-workbench": - case "new-agent": { + case "new-workbench": { ctx.navigate(NEW_WORKBENCH_PATH); return; } diff --git a/apps/web/test/create-agent-panel.test.tsx b/apps/web/test/create-agent-panel.test.tsx index a8733fd35..5e37f267b 100644 --- a/apps/web/test/create-agent-panel.test.tsx +++ b/apps/web/test/create-agent-panel.test.tsx @@ -362,7 +362,7 @@ describe("agent creation entry points", () => { expect(chatPageSource).not.toContain("instant-agent-create"); }); - test("the command palette's new-workbench/new-agent open the template picker, not an instant mint (CL-6342)", () => { + test("the command palette's new-workbench opens the template picker, not an instant mint (CL-6342)", () => { const commandPaletteActionsSource = readFileSync( new URL("../src/command-palette-actions.ts", import.meta.url), "utf8",