From 7f6819ee846546f5c0b860ea473c4db3ae591c40 Mon Sep 17 00:00:00 2001 From: kenny lopez Date: Fri, 24 Jul 2026 19:39:27 -0700 Subject: [PATCH] Respect hidden harnesses during onboarding Signed-off-by: kenny lopez --- .../src/features/onboarding/ui/SetupStep.tsx | 11 +++++--- .../ui/onboardingRuntimeSelection.test.mjs | 14 ++++++++++ .../ui/onboardingRuntimeSelection.ts | 3 +- .../e2e/onboarding-agent-defaults.spec.ts | 28 +++++++++++++++++++ 4 files changed, 51 insertions(+), 5 deletions(-) diff --git a/desktop/src/features/onboarding/ui/SetupStep.tsx b/desktop/src/features/onboarding/ui/SetupStep.tsx index b6a6a5dd5..f653bfcb5 100644 --- a/desktop/src/features/onboarding/ui/SetupStep.tsx +++ b/desktop/src/features/onboarding/ui/SetupStep.tsx @@ -8,6 +8,7 @@ import { useConnectAcpRuntimeMutation, useInstallAcpRuntimeMutation, } from "@/features/agents/hooks"; +import { useDisabledAcpRuntimeIds } from "@/features/agents/lib/runtimeVisibilityPreference"; import { describeResolvedCommand } from "@/features/agents/ui/agentUi"; import type { AcpAuthMethod, AcpRuntimeCatalogEntry } from "@/shared/api/types"; import { getInstallErrorMessage } from "@/shared/lib/installError"; @@ -656,12 +657,14 @@ function SetupStepContent({ const { runtimeProviders } = state; const [installResults, setInstallResults] = React.useState({}); + const disabledRuntimeIds = useDisabledAcpRuntimeIds(); const readyRuntimeIds = React.useMemo( () => - getReadyOnboardingRuntimes(runtimeProviders.items).map( - (runtime) => runtime.id, - ), - [runtimeProviders.items], + getReadyOnboardingRuntimes( + runtimeProviders.items, + disabledRuntimeIds, + ).map((runtime) => runtime.id), + [disabledRuntimeIds, runtimeProviders.items], ); const readyRuntimeIdsKey = readyRuntimeIds.join("\0"); // The key prevents catalog object refreshes from creating an effect loop diff --git a/desktop/src/features/onboarding/ui/onboardingRuntimeSelection.test.mjs b/desktop/src/features/onboarding/ui/onboardingRuntimeSelection.test.mjs index c49c10db8..59f45f2a9 100644 --- a/desktop/src/features/onboarding/ui/onboardingRuntimeSelection.test.mjs +++ b/desktop/src/features/onboarding/ui/onboardingRuntimeSelection.test.mjs @@ -81,3 +81,17 @@ test("ready onboarding runtimes exclude unknown and non-ready harnesses", () => ["claude", "goose", "buzz-agent"], ); }); + +test("ready onboarding runtimes exclude device-disabled harnesses", () => { + const runtimes = [ + runtime("codex", "available", "logged_in"), + runtime("claude", "available", "logged_in"), + ]; + + assert.deepEqual( + getReadyOnboardingRuntimes(runtimes, ["claude", "codex"]).map( + ({ id }) => id, + ), + [], + ); +}); diff --git a/desktop/src/features/onboarding/ui/onboardingRuntimeSelection.ts b/desktop/src/features/onboarding/ui/onboardingRuntimeSelection.ts index 591ea25ba..dc4cbb0f2 100644 --- a/desktop/src/features/onboarding/ui/onboardingRuntimeSelection.ts +++ b/desktop/src/features/onboarding/ui/onboardingRuntimeSelection.ts @@ -39,8 +39,9 @@ export function getVisibleOnboardingRuntimes( export function getReadyOnboardingRuntimes( runtimes: readonly AcpRuntimeCatalogEntry[], + disabledRuntimeIds: readonly string[] = [], ) { - return getVisibleOnboardingRuntimes(runtimes).filter( + return getVisibleOnboardingRuntimes(runtimes, disabledRuntimeIds).filter( runtimeIsReadyForOnboarding, ); } diff --git a/desktop/tests/e2e/onboarding-agent-defaults.spec.ts b/desktop/tests/e2e/onboarding-agent-defaults.spec.ts index c7909ef42..ef057ab1e 100644 --- a/desktop/tests/e2e/onboarding-agent-defaults.spec.ts +++ b/desktop/tests/e2e/onboarding-agent-defaults.spec.ts @@ -144,6 +144,34 @@ test("ready state is detected and enables Next without persisting a default", as expect(await readSavedRuntime(page)).toBeNull(); }); +test("setup cannot advance to defaults when every ready harness is device-disabled", async ({ + page, +}) => { + await page.addInitScript((storageKey) => { + window.localStorage.setItem( + storageKey, + JSON.stringify(["claude", "codex"]), + ); + }, ACP_RUNTIME_VISIBILITY_STORAGE_KEY); + await installMockBridge( + page, + { + acpRuntimesCatalog: [ + runtime("claude", "available", { status: "logged_in" }), + runtime("codex", "available", { status: "logged_in" }), + ], + }, + { skipCommunitySeed: true, skipOnboardingSeed: true }, + ); + await page.goto("/"); + await navigateToSetupPage(page); + + await expect(page.getByTestId("onboarding-setup-next")).toBeDisabled(); + await page.getByTestId("onboarding-setup-skip").click(); + await expect(page.getByText("Join or create a community")).toBeVisible(); + await expect(page.getByTestId("onboarding-page-config")).toHaveCount(0); +}); + test("setup shows runtime discovery loading before rendering harnesses", async ({ page, }) => {