fix(app): read setup's skills choice as off when no skill is picked

Saving an empty selection in the skills picker left setup's skills switch
on. Continue then asked the host to install nothing, the host reported
skills as not installed, and the step stayed put with no error. Setup now
reads skills as off with nothing picked, as Settings does.
This commit is contained in:
Mohamed Boudra
2026-11-02 18:13:40 +01:00
parent b32da3056d
commit d3317c4cb7
3 changed files with 47 additions and 5 deletions
@@ -23,6 +23,11 @@ import {
skipSetup,
startSetupHost,
} from "../support/helpers/onboarding";
import {
chooseCustomSkills,
openSkillSelection,
saveSkillSelection,
} from "../support/helpers/agent-skills";
import { installDaemonWebSocketGate } from "../support/helpers/daemon-websocket-gate";
import { enableRelayAndExpectOffer } from "../support/helpers/pair-device";
import { seedWorkspace } from "../support/helpers/seed-client";
@@ -236,6 +241,31 @@ test("setup choices carry over to Settings and the phone step pairs over relay",
}
});
test("with no skills picked, skills read as off and Continue moves on", async ({ page }) => {
const host = await startSetupHost("no-skills");
try {
await openAppAsNewUser(page, host);
await expectSetupStep(page, "Welcome to Paseo");
await goToSetupStep(page, "Orchestration");
await expectSetupStep(page, "Let agents use Paseo");
await openSkillSelection(page);
await chooseCustomSkills(page, []);
await saveSkillSelection(page);
const skillsSwitch = setupModal(page).getByRole("switch", {
name: "Orchestration skills",
exact: true,
});
await expect(skillsSwitch).not.toBeChecked();
await continueSetup(page);
await expectSetupStep(page, "Take your agents with you");
} finally {
await host.close();
}
});
test("in the compact layout, Finish on Orchestration applies its switches", async ({ page }) => {
await page.setViewportSize({ width: 390, height: 844 });
const host = await startSetupHost("compact");
+2 -1
View File
@@ -38,7 +38,8 @@ export function areSkillsInstalled(status: AgentSkillsStatus | null): boolean {
return status !== null && status.state !== "not-installed";
}
function hasSelectedSkills(status: AgentSkillsStatus | null): boolean {
/** Whether the saved selection names any skill there is to install. */
export function hasSelectedSkills(status: AgentSkillsStatus | null): boolean {
if (!status) return false;
if (status.selection.mode === "all") return status.available.length > 0;
return status.selection.skills.some((name) => status.available.includes(name));
@@ -1,6 +1,11 @@
import { useCallback, useState } from "react";
import { useCallback, useMemo, useState } from "react";
import { useTranslation } from "react-i18next";
import { areSkillsInstalled, useAgentSkills, type AgentSkillsController } from "@/agent-skills";
import {
areSkillsInstalled,
hasSelectedSkills,
useAgentSkills,
type AgentSkillsController,
} from "@/agent-skills";
import { useToast } from "@/contexts/toast-context";
import { useCliInstall } from "@/desktop/hooks/use-install-status";
import { useDaemonConfig } from "@/hooks/use-daemon-config";
@@ -35,7 +40,13 @@ export function useOrchestrationSetup(serverId: string): OrchestrationSetupContr
const skills = useAgentSkills(serverId);
const cli = useCliInstall();
const { config, patchConfig } = useDaemonConfig(serverId);
const [choices, setChoices] = useState<Choices>(ALL_ON);
const [chosen, setChosen] = useState<Choices>(ALL_ON);
// With nothing picked there is nothing to install, so skills read as off, as they do in Settings.
const skillsSelected = skills.status === null || hasSelectedSkills(skills.status);
const choices = useMemo<Choices>(
() => ({ ...chosen, skills: chosen.skills && skillsSelected }),
[chosen, skillsSelected],
);
const [isApplying, setIsApplying] = useState(false);
const offered: Choices = {
tools: true,
@@ -44,7 +55,7 @@ export function useOrchestrationSetup(serverId: string): OrchestrationSetupContr
};
const setChoice = useCallback((choice: OrchestrationChoice, on: boolean) => {
setChoices((current) => ({ ...current, [choice]: on }));
setChosen((current) => ({ ...current, [choice]: on }));
}, []);
// What the host and this computer have now, or null while it is still being read. A choice is