Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 17 additions & 12 deletions apps/web/src/command-palette-actions.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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<string, number>();
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'", () => {
Expand All @@ -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);
Expand Down
29 changes: 11 additions & 18 deletions apps/web/src/command-palette-actions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -75,7 +75,6 @@ export function resetPendingDialogRequests(): void {

export type ActionCommandId =
| "new-workbench"
| "new-agent"
| "new-routine"
| "new-skill"
| "upload-artifact"
Expand All @@ -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",
Expand Down Expand Up @@ -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<void> {
switch (id) {
case "new-workbench":
case "new-agent": {
case "new-workbench": {
ctx.navigate(NEW_WORKBENCH_PATH);
return;
}
Expand Down
2 changes: 1 addition & 1 deletion apps/web/test/create-agent-panel.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
Loading