diff --git a/surfaces/gui/e2e/cloud.spec.ts b/surfaces/gui/e2e/cloud.spec.ts index 4ec0bcc1..5d73a61c 100644 --- a/surfaces/gui/e2e/cloud.spec.ts +++ b/surfaces/gui/e2e/cloud.spec.ts @@ -63,23 +63,19 @@ test("signed in: account row shows the name; one-click appears; sign out from th await expect(page.getByTestId("account-row")).toContainText("Not signed in"); }); -test("telemetry toggle: lives in Settings, signed-in only, default on, opt-out round-trips", async ({ +test("telemetry/Privacy card is gone from Settings (owner ask 2026-07-22), signed in or out", async ({ page, }) => { - // Signed out: Settings has no toggle at all — nothing is sent, nothing to configure. await page.goto("/"); await page.getByTestId("account-row").click(); await page.getByTestId("account-menu").getByRole("button", { name: "Settings" }).click(); await expect(page.getByRole("heading", { name: "General" })).toBeVisible(); await expect(page.getByTestId("telemetry-toggle")).toHaveCount(0); + await expect(page.getByText("Privacy", { exact: true })).toHaveCount(0); await signIn(page); await page.getByTestId("account-row").click(); await page.getByTestId("account-menu").getByRole("button", { name: "Settings" }).click(); - const toggle = page.getByTestId("telemetry-toggle"); - await expect(toggle).toBeChecked({ timeout: 10_000 }); // default-on when signed in - await expect(page.getByText("never your prompts, files, or connector")).toBeVisible(); - - await toggle.uncheck(); - await expect(toggle).not.toBeChecked(); // survives the status re-fetch (persisted) + await expect(page.getByTestId("telemetry-toggle")).toHaveCount(0); + await expect(page.getByText("Privacy", { exact: true })).toHaveCount(0); }); diff --git a/surfaces/gui/e2e/composer.spec.ts b/surfaces/gui/e2e/composer.spec.ts index a90f365d..56c9b8d2 100644 --- a/surfaces/gui/e2e/composer.spec.ts +++ b/surfaces/gui/e2e/composer.spec.ts @@ -25,12 +25,13 @@ test("composer: send-gating, + attach menu, Mode menu", async ({ page }) => { await page.locator(".fixed.inset-0.z-30").click(); await expect(page.getByRole("button", { name: "Photo or image" })).toHaveCount(0); - // Mode menu (workspace personas only): the five permission options with the current one - // marked, plus the Unattended/send-to-Inbox toggle at the bottom (§22). + // Mode menu: the three shipped permission options with the current one marked, plus the + // Unattended/send-to-Inbox toggle (§22). Plan + Custom hidden for this release (2026-07-22). await page.getByRole("button", { name: "Mode", exact: true }).click(); const menu = page.getByTestId("mode-menu"); await expect(menu.getByText("Discuss")).toBeVisible(); - await expect(menu.getByText("Explore read-only, propose a plan")).toBeVisible(); + await expect(menu.getByText("Plan", { exact: true })).toHaveCount(0); + await expect(menu.getByText("Custom", { exact: true })).toHaveCount(0); // The current mode is marked with a ✓. await expect(menu.locator("button").filter({ hasText: "Ask for approval" })).toContainText("✓"); await expect(menu.getByRole("switch", { name: "Send approvals to the Inbox" })).toBeVisible(); diff --git a/surfaces/gui/e2e/session-shell.spec.ts b/surfaces/gui/e2e/session-shell.spec.ts index 39389a52..0156c87a 100644 --- a/surfaces/gui/e2e/session-shell.spec.ts +++ b/surfaces/gui/e2e/session-shell.spec.ts @@ -33,7 +33,7 @@ test("top-left cluster renders only while the sidebar is collapsed", async ({ pa await expect(page.getByTestId("topbar-cluster")).toHaveCount(0); }); -test("facts subtitle: absent on a fresh session, persona · model after the first turn; click → persona page", async ({ +test("facts subtitle: absent on a fresh session, model-only after the first turn, inert", async ({ page, }) => { await page.goto("/"); @@ -51,14 +51,13 @@ test("facts subtitle: absent on a fresh session, persona · model after the firs await page.getByRole("button", { name: "Send" }).click(); await expect(page.getByText(/Echo: hello/)).toBeVisible(); + // Model only — no persona name (owner ask 2026-07-22: personas are hidden this release), + // and the subtitle is a plain fact line, not a button to the persona page. const sub = page.getByTestId("session-subtitle"); - await expect(sub).toContainText("Coworker · Claude Opus 4.8"); + await expect(sub).toHaveText("Claude Opus 4.8"); await expect(page.locator(".dd").filter({ hasText: "Claude Opus 4.8" })).toBeVisible(); - - // The subtitle is the session's fixed facts — clicking it opens the coworker (persona) page, - // replacing the old topbar sliders button. await sub.click(); - await expect(page.getByRole("button", { name: "Back", exact: true })).toBeVisible(); + await expect(page.getByRole("button", { name: "Back", exact: true })).toHaveCount(0); }); test("composer is three controls (+ attach · Mode · send); folder and branch chips are gone", async ({ diff --git a/surfaces/gui/src/App.tsx b/surfaces/gui/src/App.tsx index 9ae5e0d2..94195b7b 100644 --- a/surfaces/gui/src/App.tsx +++ b/surfaces/gui/src/App.tsx @@ -28,7 +28,7 @@ import { type SurfaceVisibility, } from "./api"; import type { ApprovalDecision, Attachment, Item, SessionInfo, TodoItem, WsEvent } from "./types"; -import { isProjectScoped, shortPersonaName } from "./personaScope"; +import { isProjectScoped } from "./personaScope"; import { baseName } from "./paths"; import { itemsFromMessages } from "./itemsFromMessages"; import { streamMode } from "./streamGate"; @@ -1076,9 +1076,7 @@ export function App() { const pendingDirReq = [...items].reverse().find((i) => i.kind === "dirreq" && !i.resolved); const pendingPlan = [...items].reverse().find((i) => i.kind === "planreq" && !i.resolved); const pendingQuestion = [...items].reverse().find((i) => i.kind === "question" && !i.resolved); - // Topbar trim: the active persona's short display name (mock's "· SRE persona"). - const personaName = shortPersonaName(personaOf(agent)?.name, agent); - // Facts subtitle (§22): the session's FIXED facts, not controls — persona · model (+ the + // Facts subtitle (§22): the session's FIXED facts, not controls — model (+ the // workspace folder for project-scoped sessions). Renders only once the session has history; // until then the model is still choosable in the composer, so there's no locked fact to state. const hasHistory = items.length > 0; @@ -1087,7 +1085,9 @@ export function App() { const modelDisplay = modelLabels[model]?.split(" · ")[0] || (model.includes(":") ? model.split(":").slice(1).join(":") : model); - const subtitleParts = [personaName, modelDisplay]; + // Persona name dropped for this release (owner ask 2026-07-22): personas are hidden, + // so "Coworker" read as noise. The model (+ project folder) are the real fixed facts. + const subtitleParts = [modelDisplay]; if (isProjectScoped(personaOf(agent)) && workspace) subtitleParts.push(baseName(workspace)); const activeInfo = sessions.find((s) => s.session_id === sessionId); const activeTitle = activeInfo?.title || "New session"; @@ -1343,16 +1343,12 @@ export function App() { > {activeTitle} + {/* Plain facts, no affordance: the persona page it used to open is hidden for + this release (owner ask 2026-07-22). */} {hasHistory && ( - + )} {/* Right: session-settings icon (§23) + panel toggle. Model/mode/persona chrome is diff --git a/surfaces/gui/src/components/Composer.tsx b/surfaces/gui/src/components/Composer.tsx index 894c892e..852e4d6f 100644 --- a/surfaces/gui/src/components/Composer.tsx +++ b/surfaces/gui/src/components/Composer.tsx @@ -15,12 +15,14 @@ import { type DictationStatus, } from "../tauri"; +// Plan + Custom hidden for this release (owner ask 2026-07-22): Plan's approval flow isn't +// polished enough to ship, and Custom (config.toml auto-allow rules) is a power-user mode +// with no in-app explanation. The server still honors both — a session already in one of +// those modes keeps working; the picker just doesn't offer them. const PERMISSION_OPTIONS: Option[] = [ { value: "discuss", label: "Discuss", description: "Chat and explore — no edits or commands" }, - { value: "plan", label: "Plan", description: "Explore read-only, propose a plan for approval, then build" }, { value: "interactive", label: "Ask for approval", description: "Ask before edits and commands" }, { value: "auto", label: "Full access", description: "Run everything without asking" }, - { value: "custom", label: "Custom", description: "Use auto-allow rules from config.toml" }, ]; // No hardcoded model fallback: until the server supplies the list (a few seconds after a diff --git a/surfaces/gui/src/components/SettingsView.tsx b/surfaces/gui/src/components/SettingsView.tsx index ecb686b8..6ee9f54e 100644 --- a/surfaces/gui/src/components/SettingsView.tsx +++ b/surfaces/gui/src/components/SettingsView.tsx @@ -1,14 +1,10 @@ import { useEffect, useState } from "react"; import { - CLOUD_CHANGED, - getCloudStatus, getSettings, - setCloudTelemetry, setOnboarded, setPdfSettings, setScratchBase, setSessionsPeek, - type CloudStatus, type ModelSettings, type PdfSettings, } from "../api"; @@ -428,8 +424,6 @@ function AppearanceSection() { - - {desktop && (
Always-on
@@ -524,46 +518,8 @@ function UpdateInline() { ); } -// -- Telemetry (moved here from the retired Connectors-page cloud strip, §26) ----- -function TelemetryCard() { - const [cloud, setCloud] = useState(null); - const [telemetry, setTelemetry] = useState(null); - - useEffect(() => { - const load = () => getCloudStatus().then(setCloud).catch(() => {}); - load(); - // Sign-in/out lands out-of-band (browser flow, account row) — refetch on the announce. - window.addEventListener(CLOUD_CHANGED, load); - return () => window.removeEventListener(CLOUD_CHANGED, load); - }, []); - - // Signed out there is nothing to toggle — telemetry is a no-op without a cloud session. - if (!cloud?.signed_in) return null; - return ( -
-
Privacy
- -
- ); -} +// Telemetry/Privacy card removed for this release (owner ask 2026-07-22); the +// setCloudTelemetry API stays for a future opt-out surface. // -- Sidebar density ------------------------------------------------------------- // -- Token savings (PDF attachments; owner ask, 2026-07-17) ---------------------