From 7dd424e583760bd693251a2997477a026ff8ffe8 Mon Sep 17 00:00:00 2001 From: Dotta Date: Thu, 15 Oct 2026 07:11:59 -0500 Subject: [PATCH] fix: unlock Claude setup when automatic runner discovery changes Co-Authored-By: Paperclip --- ui/src/components/OnboardingWizard.test.tsx | 30 +++++++++++++++++++++ ui/src/components/OnboardingWizard.tsx | 2 +- 2 files changed, 31 insertions(+), 1 deletion(-) diff --git a/ui/src/components/OnboardingWizard.test.tsx b/ui/src/components/OnboardingWizard.test.tsx index e7f2d74d928..cc5ecbc1637 100644 --- a/ui/src/components/OnboardingWizard.test.tsx +++ b/ui/src/components/OnboardingWizard.test.tsx @@ -3485,6 +3485,36 @@ describe("OnboardingWizard restore-gate (stale localStorage across accounts)", ( } finally { await act(async () => root.unmount()); } }); + it("unlocks Claude setup when automatic runner availability changes during verification", async () => { + mockAdapterBuild.buildAdapterConfig.mockImplementation(values => buildClaudeLocalConfig(values!)); + mockAdaptersApi.list.mockResolvedValue([setupAdapterInfo({ type: "claude_local", defaultRunner: "paperclip" })]); + const finishes: Array<(value: typeof passed) => void> = []; + mockAgentsApi.testEnvironment.mockImplementation(() => new Promise(resolve => finishes.push(resolve))); + const { root, queryClient } = await openStep4({ adapterType: "claude_local" }); + try { + await pickSource(/Claude/); + await detected(); + expect(mockAgentsApi.testEnvironment).toHaveBeenCalledTimes(1); + expectTesting(); + mockAdaptersApi.list.mockResolvedValue([setupAdapterInfo({ type: "claude_local", defaultRunner: "legacy" })]); + await act(async () => queryClient.setQueryData(queryKeys.adapters.availability("company-new", LOCAL_ENVIRONMENT.id), [setupAdapterInfo({ type: "claude_local", defaultRunner: "legacy" })])); + await settle(); + expect(document.body.textContent).toContain("Configuration changed during setup"); + expect(button("Connect").disabled).toBe(false); + expect(mockAgentsApi.testEnvironment).toHaveBeenCalledTimes(1); + await act(async () => finishes[0](passed)); + await settle(); + expect(mockAgentsApi.hire).not.toHaveBeenCalled(); + await click("Connect"); + expect(mockAgentsApi.testEnvironment).toHaveBeenCalledTimes(2); + await act(async () => finishes[1](passed)); + await settle(); + expect(mockAgentsApi.hire).toHaveBeenCalledTimes(1); + const hired = mockAgentsApi.hire.mock.calls[0] as unknown as [string, { adapterConfig: Record }]; + expect(hired[1].adapterConfig.maxTurnsPerRun).toBe(1000); + } finally { await act(async () => root.unmount()); } + }); + it("fails closed on Claude discovery rejection and retries availability before verification", async () => { mockAdaptersApi.list.mockRejectedValue(new Error("Discovery unavailable")); const { root, queryClient } = await openStep4({ adapterType: "claude_local" }); diff --git a/ui/src/components/OnboardingWizard.tsx b/ui/src/components/OnboardingWizard.tsx index 522c9ad2ec6..4abfb9ad743 100644 --- a/ui/src/components/OnboardingWizard.tsx +++ b/ui/src/components/OnboardingWizard.tsx @@ -1032,7 +1032,7 @@ function OnboardingWizardInner({ null, [loginEnvironmentList, resolvedLoginEnvironmentId], ); - const claudeSetupSelectionKey = JSON.stringify([createdCompanyId, adapterType, runner, model, command, + const claudeSetupSelectionKey = JSON.stringify([createdCompanyId, adapterType, runner, resolvedRunner, model, command, args, url, credentialMode, apiKey, selectedSavedKey, selectedApiKey?.binding, savedSubscription?.binding, resolvedLoginEnvironmentId, resolvedLoginEnvironment?.config]); const claudeSetupSelectionRef = useRef(claudeSetupSelectionKey);