diff --git a/surfaces/gui/e2e/sidebar-sessions.spec.ts b/surfaces/gui/e2e/sidebar-sessions.spec.ts index 5b997399..35534f82 100644 --- a/surfaces/gui/e2e/sidebar-sessions.spec.ts +++ b/surfaces/gui/e2e/sidebar-sessions.spec.ts @@ -7,12 +7,13 @@ import { test, expect } from "./fixtures"; test("session list caps at the peek count with Show more", async ({ page }) => { await page.goto("/"); - // Boot resumes a cowork session, so the Coworker accordion body is expanded. + // Boot resumes a cowork session, so the Coworker accordion body is expanded. The body holds + // 8 sessions (7 weekly plans + the Slack-origin one, §31 rev) against sessions_peek=5. await expect(page.getByTitle("Weekly plan 1")).toBeVisible(); await expect(page.getByTitle("Weekly plan 5")).toBeVisible(); await expect(page.getByTitle("Weekly plan 6")).toHaveCount(0); - await page.getByRole("button", { name: "Show more (2)" }).click(); + await page.getByRole("button", { name: "Show more (3)" }).click(); await expect(page.getByTitle("Weekly plan 6")).toBeVisible(); await expect(page.getByTitle("Weekly plan 7")).toBeVisible(); }); @@ -42,26 +43,23 @@ test("archive via the row menu is reversible via the Archived disclosure", async await expect(page.getByTitle("Weekly plan 2")).toBeVisible(); }); -test("mention-spawned sessions collapse under From Slack with the platform icon (§31)", async ({ +test("mention-spawned sessions list in Recent with the platform icon — no From Slack band (§31 rev)", async ({ page, }) => { + // Flat chronological layout — the launch default (personas off). + await page.route("**/v1/settings", (r) => r.fulfill({ json: { nav_layout: "flat" } })); await page.goto("/"); await expect(page.getByTitle("Weekly plan 1")).toBeVisible(); - // Collapsed by default with a count; the session row hidden until expanded… - const toggle = page.getByTestId("from-slack-toggle"); - await expect(toggle).toContainText("From Slack (1)"); - await expect(page.getByTitle("#general — check the deploy?")).toHaveCount(0); - - await toggle.click(); + // No collapsed band; the session sits directly in Recent, exactly once (its fixture + // timestamp sorts it past the peek cap, so expand first)… + await expect(page.getByTestId("from-slack-toggle")).toHaveCount(0); + await page.getByText(/Show \d+ more/).click(); const row = page.getByTitle("#general — check the deploy?"); await expect(row).toBeVisible(); - // …wearing the Slack logo (hover-hidden cluster, so assert attachment not visibility)… - await expect( - page.getByTestId("from-slack-list").locator('[data-logo="slack"]'), - ).toHaveCount(1); - // …and never duplicated into any other list. await expect(page.getByTitle("#general — check the deploy?")).toHaveCount(1); + // …wearing the Slack logo (hover-hidden cluster, so assert attachment not visibility). + await expect(row.locator('[data-logo="slack"]')).toHaveCount(1); }); test("pin via the row menu moves the session to the Pinned band and back", async ({ page }) => { diff --git a/surfaces/gui/src/components/Sidebar.test.tsx b/surfaces/gui/src/components/Sidebar.test.tsx index b1aea346..81d69e16 100644 --- a/surfaces/gui/src/components/Sidebar.test.tsx +++ b/surfaces/gui/src/components/Sidebar.test.tsx @@ -179,7 +179,7 @@ describe("From Slack group (§31)", () => { origin_label: "#general · T0AB", }; - it("mention-spawned sessions collapse under From Slack with the platform icon, out of Recent", async () => { + it("mention-spawned sessions list chronologically in Recent with the platform icon (no band)", async () => { stubFetch([ { match: "/v1/personas", method: "GET", json: PERSONAS }, { match: "/v1/settings", method: "GET", json: { nav_layout: "flat" } }, @@ -187,20 +187,14 @@ describe("From Slack group (§31)", () => { render(); await screen.findByText("incident watch"); // flat Recent rendered - // Collapsed by default: the header shows a count, the row itself is hidden… - const toggle = screen.getByTestId("from-slack-toggle"); - expect(toggle.textContent).toContain("From Slack (1)"); - expect(screen.queryByText("#general — check the deploy?")).toBeNull(); - - // …and the session does NOT duplicate into the chronological Recent list. - fireEvent.click(toggle); + // No collapsed band — the session sits directly in the Recent list, exactly once… + expect(screen.queryByTestId("from-slack-toggle")).toBeNull(); const row = await screen.findByText("#general — check the deploy?"); expect(screen.getAllByText("#general — check the deploy?")).toHaveLength(1); - // The row wears the Slack logo, right-aligned in the indicator cluster. - const list = screen.getByTestId("from-slack-list"); - expect(list.querySelector('[data-logo="slack"]')).toBeTruthy(); - expect(row).toBeTruthy(); + // …wearing the Slack logo in the row's indicator cluster. + const cluster = row.closest(".group"); + expect(cluster?.querySelector('[data-logo="slack"]')).toBeTruthy(); }); }); diff --git a/surfaces/gui/src/components/Sidebar.tsx b/surfaces/gui/src/components/Sidebar.tsx index 0b532f95..06e718b2 100644 --- a/surfaces/gui/src/components/Sidebar.tsx +++ b/surfaces/gui/src/components/Sidebar.tsx @@ -339,15 +339,9 @@ export function Sidebar(props: Props) { const pinnedSessions = props.sessions.filter( (s) => s.pinned && !s.session_id.startsWith("__") && !s.archived, ); - // §31: mention-spawned sessions gather in one cross-persona "From Slack" group, collapsed by - // default (owner call 2026-07-13; no auto-archive — a future global setting). Pinned wins. - const slackSessions = props.sessions - .filter( - (s) => - s.origin === "slack" && !s.archived && !s.pinned && !s.session_id.startsWith("__"), - ) - .sort((a, b) => (b.updated_at || "").localeCompare(a.updated_at || "")); - const [slackOpen, setSlackOpen] = useState(false); + // §31 (revised 2026-07-21): mention-spawned sessions list chronologically in Recent like any + // other session — the OriginIcon in the row's indicator cluster marks where they came from. + // The separate collapsed "From Slack" band hid fresh mentions below week-old sessions. // A row in the account menu (§26): closes the menu, then runs the destination. const appMenuItem = ( icon: IconName, @@ -411,9 +405,7 @@ export function Sidebar(props: Props) { // EXCLUDED here: they live in the cross-persona Pinned band only, so they don't repeat inside the // persona group / project list (matching the flat layout's Recent, which also drops pinned). const all = props.sessions.filter((s) => s.agent === browseKey && !s.session_id.startsWith("__")); - // Origin (mention-spawned) sessions live in the cross-persona "From Slack" band, not the - // persona lists — pinned still wins (Pinned band), and archived keeps the persona disclosure. - const mine = all.filter((s) => !s.archived && !s.pinned && !s.origin); + const mine = all.filter((s) => !s.archived && !s.pinned); const archived = all.filter((s) => s.archived); // Only PROJECT-SCOPED personas group sessions by project (git-bound Code, project-bound Ops). // Scratch/deliverable conversations are orphan (each has its own per-conversation scratch dir), @@ -429,7 +421,7 @@ export function Sidebar(props: Props) { // Recent = every non-pinned, non-archived, real session across ALL personas, newest first // (by updated_at; missing timestamps keep store order), search-filtered. Drives the flat layout. const recentSessions = [...props.sessions] - .filter((s) => !s.archived && !s.session_id.startsWith("__") && !s.pinned && !s.origin) + .filter((s) => !s.archived && !s.session_id.startsWith("__") && !s.pinned) .filter((s) => personaVisible(s.agent)) .filter(matches) .sort((a, b) => (b.updated_at || "").localeCompare(a.updated_at || "")); @@ -705,27 +697,6 @@ export function Sidebar(props: Props) { ) : null; - // §31: the collapsed cross-persona "From Slack" band — mention-spawned sessions, both layouts. - // Disclosure shape mirrors the persona body's Archived row. - const fromSlackBand = () => - slackSessions.length > 0 ? ( -
- - {slackOpen && ( -
- {slackSessions.map((s) => cardRow(s))} -
- )} -
- ) : null; - // RECENT header with the group/filter control (§20) — the group toggle moved off the brand bar. // "Group by" flips the persona accordion ↔ chronological list; "Filter by coworker" narrows to // the checked personas (none checked = all shown). @@ -1075,7 +1046,6 @@ export function Sidebar(props: Props) {
{pinnedBand()} {scheduledBand()} - {fromSlackBand()}
{recentHeader()} {layout === "grouped" ? (