Respect hidden harnesses during onboarding

Signed-off-by: kenny lopez <klopez4212@gmail.com>
This commit is contained in:
kenny lopez
2026-07-27 13:30:21 +01:00
parent c1c262b6c5
commit 7f6819ee84
4 changed files with 51 additions and 5 deletions
@@ -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<InstallResultsState>({});
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
@@ -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,
),
[],
);
});
@@ -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,
);
}
@@ -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,
}) => {