From 144c8fb3d2083a2cb7b2a0c8ff806300e6f1619a Mon Sep 17 00:00:00 2001
From: Wes <wesbillman@users.noreply.github.com>
Date: Fri, 17 Jul 2026 16:10:19 -0600
Subject: [PATCH 1/3] fix(desktop): honor selected onboarding runtime config

Use the preferred Goose or Buzz Agent runtime for onboarding model discovery and readiness instead of treating any installed Goose runtime as configured.

Co-authored-by: Pinky <44b8e82baa6e0e254e0208d68f335c283c94e7b78dd1fa10d5a49d3f13dd0435@sprout-oss.stage.blox.sqprod.co>
Signed-off-by: Wes <wesbillman@users.noreply.github.com>
---
 .../agents/ui/GlobalAgentConfigEditor.tsx     |  2 +-
 .../agents/ui/GlobalAgentConfigFields.tsx     |  6 +-
 .../onboarding/ui/DefaultConfigStep.tsx       | 11 ++-
 .../onboarding/ui/agentReadiness.test.mjs     | 73 +++++++++----------
 .../features/onboarding/ui/agentReadiness.ts  | 42 ++++++-----
 .../e2e/onboarding-agent-defaults.spec.ts     | 26 +++++++
 6 files changed, 98 insertions(+), 62 deletions(-)

diff --git a/desktop/src/features/agents/ui/GlobalAgentConfigEditor.tsx b/desktop/src/features/agents/ui/GlobalAgentConfigEditor.tsx
index f190ab15f5..ad44dcb14e 100644
--- a/desktop/src/features/agents/ui/GlobalAgentConfigEditor.tsx
+++ b/desktop/src/features/agents/ui/GlobalAgentConfigEditor.tsx
@@ -180,7 +180,7 @@ export function GlobalAgentConfigEditor({
       ) : (
         <GlobalAgentConfigFields
           bakedEnv={bakedEnv}
-          buzzAgentRuntime={buzzAgentRuntime}
+          selectedRuntime={buzzAgentRuntime}
           config={config}
           isCustomModelEditing={isCustomModelEditing}
           isCustomProvider={isCustomProvider}
diff --git a/desktop/src/features/agents/ui/GlobalAgentConfigFields.tsx b/desktop/src/features/agents/ui/GlobalAgentConfigFields.tsx
index 6108ce1670..eb19007ce4 100644
--- a/desktop/src/features/agents/ui/GlobalAgentConfigFields.tsx
+++ b/desktop/src/features/agents/ui/GlobalAgentConfigFields.tsx
@@ -57,7 +57,7 @@ const BAKED_STRUCTURED_KEYS = new Set([
 
 export type GlobalAgentConfigFieldsProps = {
   bakedEnv: BakedEnvEntry[];
-  buzzAgentRuntime: AcpRuntimeCatalogEntry | undefined;
+  selectedRuntime: AcpRuntimeCatalogEntry | undefined;
   config: GlobalAgentConfig;
   isCustomModelEditing: boolean;
   isCustomProvider: boolean;
@@ -69,7 +69,7 @@ export type GlobalAgentConfigFieldsProps = {
 
 export function GlobalAgentConfigFields({
   bakedEnv,
-  buzzAgentRuntime,
+  selectedRuntime,
   config,
   isCustomModelEditing,
   isCustomProvider,
@@ -134,7 +134,7 @@ export function GlobalAgentConfigFields({
     modelFieldVisible: true,
     open: true,
     provider: providerForDiscovery,
-    selectedRuntime: buzzAgentRuntime,
+    selectedRuntime,
   });
 
   const currentEffortForAutoClear =
diff --git a/desktop/src/features/onboarding/ui/DefaultConfigStep.tsx b/desktop/src/features/onboarding/ui/DefaultConfigStep.tsx
index 31a0f357ab..272dbe924b 100644
--- a/desktop/src/features/onboarding/ui/DefaultConfigStep.tsx
+++ b/desktop/src/features/onboarding/ui/DefaultConfigStep.tsx
@@ -80,9 +80,12 @@ function AgentDefaultsSection() {
     };
   }, []);
 
-  const buzzAgentRuntime = React.useMemo(
-    () => (runtimesQuery.data ?? []).find((r) => r.id === "buzz-agent"),
-    [runtimesQuery.data],
+  const selectedRuntime = React.useMemo(
+    () =>
+      (runtimesQuery.data ?? []).find(
+        (runtime) => runtime.id === config.preferred_runtime,
+      ),
+    [config.preferred_runtime, runtimesQuery.data],
   );
 
   const readiness = resolveAgentReadiness(runtimesQuery.data ?? [], config);
@@ -98,7 +101,7 @@ function AgentDefaultsSection() {
         <div className="rounded-2xl bg-white/85 p-2 shadow-[0_0_55px_25px_rgba(255,255,255,0.6)]">
           <GlobalAgentConfigFields
             bakedEnv={bakedEnv}
-            buzzAgentRuntime={buzzAgentRuntime}
+            selectedRuntime={selectedRuntime}
             config={config}
             isCustomModelEditing={isCustomModelEditing}
             isCustomProvider={isCustomProvider}
diff --git a/desktop/src/features/onboarding/ui/agentReadiness.test.mjs b/desktop/src/features/onboarding/ui/agentReadiness.test.mjs
index 66b11b6be8..954e59b0c6 100644
--- a/desktop/src/features/onboarding/ui/agentReadiness.test.mjs
+++ b/desktop/src/features/onboarding/ui/agentReadiness.test.mjs
@@ -30,6 +30,7 @@ function makeConfig(overrides = {}) {
     env_vars: {},
     provider: null,
     model: null,
+    preferred_runtime: "goose",
     ...overrides,
   };
 }
@@ -38,27 +39,26 @@ function makeConfig(overrides = {}) {
 // CLI path
 // ---------------------------------------------------------------------------
 
-test("resolveAgentReadiness_cli_returns_ready_when_cli_runtime_available_and_logged_in", () => {
-  const runtimes = [makeRuntime({ id: "goose", label: "Goose" })];
-  const result = resolveAgentReadiness(runtimes, makeConfig());
+test("resolveAgentReadiness_cli_returns_ready_when_preferred_cli_runtime_is_logged_in", () => {
+  const runtimes = [makeRuntime({ id: "claude", label: "Claude" })];
+  const result = resolveAgentReadiness(
+    runtimes,
+    makeConfig({ preferred_runtime: "claude" }),
+  );
   assert.deepEqual(result, {
     ready: true,
     reason: "cli",
-    runtimeLabel: "Goose",
+    runtimeLabel: "Claude",
   });
 });
 
-test("resolveAgentReadiness_cli_uses_first_matching_runtime", () => {
+test("resolveAgentReadiness_uses_only_the_preferred_runtime", () => {
   const runtimes = [
     makeRuntime({ id: "claude", label: "Claude" }),
     makeRuntime({ id: "goose", label: "Goose" }),
   ];
   const result = resolveAgentReadiness(runtimes, makeConfig());
-  assert.equal(result.ready, true);
-  if (result.ready) {
-    assert.equal(result.reason, "cli");
-    assert.equal(result.runtimeLabel, "Claude");
-  }
+  assert.equal(result.ready, false);
 });
 
 test("resolveAgentReadiness_cli_skips_logged_out_runtimes", () => {
@@ -73,9 +73,7 @@ test("resolveAgentReadiness_cli_skips_logged_out_runtimes", () => {
   assert.equal(result.ready, false);
 });
 
-test("resolveAgentReadiness_cli_ready_for_login_free_harness_with_not_applicable_auth", () => {
-  // Goose uses not_applicable because it has no login concept; it should
-  // still show green if the runtime is available.
+test("resolveAgentReadiness_goose_requires_provider_and_model", () => {
   const runtimes = [
     makeRuntime({
       id: "goose",
@@ -85,11 +83,7 @@ test("resolveAgentReadiness_cli_ready_for_login_free_harness_with_not_applicable
     }),
   ];
   const result = resolveAgentReadiness(runtimes, makeConfig());
-  assert.deepEqual(result, {
-    ready: true,
-    reason: "cli",
-    runtimeLabel: "Goose",
-  });
+  assert.equal(result.ready, false);
 });
 
 test("resolveAgentReadiness_cli_not_ready_for_unknown_auth_status", () => {
@@ -151,12 +145,15 @@ test("resolveAgentReadiness_cli_ignores_buzz_agent_runtime", () => {
 
 test("resolveAgentReadiness_buzz_agent_ready_when_provider_model_and_key_set", () => {
   // anthropic requires ANTHROPIC_API_KEY
-  const config = makeConfig({
-    provider: "anthropic",
-    model: "claude-3-5-sonnet-latest",
-    env_vars: { ANTHROPIC_API_KEY: "sk-ant-test" },
-  });
-  const result = resolveAgentReadiness([], config);
+  const result = resolveAgentReadiness(
+    [makeRuntime({ id: "buzz-agent", label: "Buzz Agent" })],
+    makeConfig({
+      preferred_runtime: "buzz-agent",
+      provider: "anthropic",
+      model: "claude-3-5-sonnet-latest",
+      env_vars: { ANTHROPIC_API_KEY: "sk-ant-test" },
+    }),
+  );
   assert.deepEqual(result, { ready: true, reason: "buzz-agent" });
 });
 
@@ -200,19 +197,21 @@ test("resolveAgentReadiness_neither_returns_not_ready", () => {
 });
 
 // ---------------------------------------------------------------------------
-// CLI path takes priority over buzz-agent path
+// Preferred runtime isolation
 // ---------------------------------------------------------------------------
 
-test("resolveAgentReadiness_cli_wins_over_buzz_agent_when_both_ready", () => {
-  const runtimes = [makeRuntime({ id: "goose", label: "Goose" })];
-  const config = makeConfig({
-    provider: "anthropic",
-    model: "claude-3-5-sonnet-latest",
-    env_vars: { ANTHROPIC_API_KEY: "sk-ant-test" },
-  });
-  const result = resolveAgentReadiness(runtimes, config);
-  assert.equal(result.ready, true);
-  if (result.ready) {
-    assert.equal(result.reason, "cli");
-  }
+test("resolveAgentReadiness_preferred_goose_does_not_borrow_ready_buzz_agent_config", () => {
+  const runtimes = [
+    makeRuntime({ id: "goose", label: "Goose" }),
+    makeRuntime({ id: "buzz-agent", label: "Buzz Agent" }),
+  ];
+  const result = resolveAgentReadiness(
+    runtimes,
+    makeConfig({
+      provider: "anthropic",
+      model: null,
+      env_vars: { ANTHROPIC_API_KEY: "sk-ant-test" },
+    }),
+  );
+  assert.equal(result.ready, false);
 });
diff --git a/desktop/src/features/onboarding/ui/agentReadiness.ts b/desktop/src/features/onboarding/ui/agentReadiness.ts
index ce8a691b85..786a45e81a 100644
--- a/desktop/src/features/onboarding/ui/agentReadiness.ts
+++ b/desktop/src/features/onboarding/ui/agentReadiness.ts
@@ -12,9 +12,9 @@ export type AgentReadinessResult =
 /**
  * Determine whether the user has a working agent path configured.
  *
- * CLI path: at least one non-buzz-agent runtime is available and logged in.
- * buzz-agent path: provider and model are set, and all required credential
- * env vars for that provider are present.
+ * CLI path: the preferred Claude or Codex runtime is available and logged in.
+ * Provider path: the preferred Buzz Agent or Goose runtime has provider and
+ * model set, plus all required credential env vars for that provider.
  *
  * Returns enough info for the UI to say which path matched, or that neither did.
  */
@@ -22,25 +22,33 @@ export function resolveAgentReadiness(
   runtimes: readonly AcpRuntimeCatalogEntry[],
   globalConfig: GlobalAgentConfig,
 ): AgentReadinessResult {
-  // CLI path — any non-buzz-agent runtime that is available and has an
-  // auth status indicating it can run: logged_in for runtimes that require
-  // auth (Claude Code), or not_applicable for login-free harnesses (Goose).
-  for (const runtime of runtimes) {
-    if (runtime.id === "buzz-agent") continue;
-    if (
-      runtime.availability === "available" &&
-      (runtime.authStatus.status === "logged_in" ||
-        runtime.authStatus.status === "not_applicable")
-    ) {
-      return { ready: true, reason: "cli", runtimeLabel: runtime.label };
-    }
+  const preferredRuntime = runtimes.find(
+    (runtime) => runtime.id === globalConfig.preferred_runtime,
+  );
+  if (preferredRuntime?.availability !== "available") {
+    return { ready: false };
+  }
+
+  if (
+    (preferredRuntime.id === "claude" || preferredRuntime.id === "codex") &&
+    (preferredRuntime.authStatus.status === "logged_in" ||
+      preferredRuntime.authStatus.status === "not_applicable")
+  ) {
+    return {
+      ready: true,
+      reason: "cli",
+      runtimeLabel: preferredRuntime.label,
+    };
+  }
+
+  if (preferredRuntime.id !== "buzz-agent" && preferredRuntime.id !== "goose") {
+    return { ready: false };
   }
 
-  // buzz-agent path — provider + model + required credential keys all present.
   const provider = globalConfig.provider?.trim() ?? "";
   const model = globalConfig.model?.trim() ?? "";
   if (provider.length > 0 && model.length > 0) {
-    const required = requiredCredentialEnvKeys("buzz-agent", provider);
+    const required = requiredCredentialEnvKeys(preferredRuntime.id, provider);
     const allKeysPresent = required.every(
       (key) => (globalConfig.env_vars[key] ?? "").trim().length > 0,
     );
diff --git a/desktop/tests/e2e/onboarding-agent-defaults.spec.ts b/desktop/tests/e2e/onboarding-agent-defaults.spec.ts
index e252601be9..fe5606c0c4 100644
--- a/desktop/tests/e2e/onboarding-agent-defaults.spec.ts
+++ b/desktop/tests/e2e/onboarding-agent-defaults.spec.ts
@@ -375,6 +375,32 @@ test("Finish button is always enabled on config page regardless of readiness", a
 // B1 regression: rapid consecutive edits must not lose the later change
 // ---------------------------------------------------------------------------
 
+test("Goose config page discovers models through the selected Goose runtime", async ({
+  page,
+}) => {
+  await installMockBridge(
+    page,
+    {
+      acpRuntimesCatalog: [
+        availableRuntime("goose", { status: "not_applicable" }),
+      ],
+    },
+    { skipCommunitySeed: true, skipOnboardingSeed: true },
+  );
+  await page.goto("/");
+  await navigateToSetupPage(page);
+  await page.getByTestId("onboarding-runtime-goose").click();
+  await page.getByTestId("onboarding-setup-next").click();
+  await expect(page.getByTestId("onboarding-page-config")).toBeVisible();
+
+  await page.locator("#global-agent-provider").selectOption("openai");
+  await expect(
+    page
+      .locator("#global-agent-model")
+      .getByRole("option", { name: "GPT-5.5" }),
+  ).toBeAttached();
+});
+
 test("provider credentials are first-class and drive model discovery", async ({
   page,
 }) => {

From 54685294f2928f03f90eb01ab92c8f41bcdc038d Mon Sep 17 00:00:00 2001
From: Wes <wesbillman@users.noreply.github.com>
Date: Fri, 17 Jul 2026 16:25:35 -0600
Subject: [PATCH 2/3] fix(desktop): preserve welcome agent readiness

Keep broad runtime readiness for the legacy Welcome kickoff while limiting selected-runtime readiness to the onboarding configuration page.

Co-authored-by: Pinky <44b8e82baa6e0e254e0208d68f335c283c94e7b78dd1fa10d5a49d3f13dd0435@sprout-oss.stage.blox.sqprod.co>
Signed-off-by: Wes <wesbillman@users.noreply.github.com>
---
 .../onboarding/ui/DefaultConfigStep.tsx       |  6 ++-
 .../onboarding/ui/agentReadiness.test.mjs     | 51 ++++++++++++++++---
 .../features/onboarding/ui/agentReadiness.ts  | 23 +++++++--
 3 files changed, 69 insertions(+), 11 deletions(-)

diff --git a/desktop/src/features/onboarding/ui/DefaultConfigStep.tsx b/desktop/src/features/onboarding/ui/DefaultConfigStep.tsx
index 272dbe924b..83ca6e8a8c 100644
--- a/desktop/src/features/onboarding/ui/DefaultConfigStep.tsx
+++ b/desktop/src/features/onboarding/ui/DefaultConfigStep.tsx
@@ -88,7 +88,11 @@ function AgentDefaultsSection() {
     [config.preferred_runtime, runtimesQuery.data],
   );
 
-  const readiness = resolveAgentReadiness(runtimesQuery.data ?? [], config);
+  const readiness = resolveAgentReadiness(
+    runtimesQuery.data ?? [],
+    config,
+    "preferred",
+  );
 
   return (
     <section className="w-full space-y-4 text-left">
diff --git a/desktop/src/features/onboarding/ui/agentReadiness.test.mjs b/desktop/src/features/onboarding/ui/agentReadiness.test.mjs
index 954e59b0c6..9d6deafb09 100644
--- a/desktop/src/features/onboarding/ui/agentReadiness.test.mjs
+++ b/desktop/src/features/onboarding/ui/agentReadiness.test.mjs
@@ -57,7 +57,7 @@ test("resolveAgentReadiness_uses_only_the_preferred_runtime", () => {
     makeRuntime({ id: "claude", label: "Claude" }),
     makeRuntime({ id: "goose", label: "Goose" }),
   ];
-  const result = resolveAgentReadiness(runtimes, makeConfig());
+  const result = resolveAgentReadiness(runtimes, makeConfig(), "preferred");
   assert.equal(result.ready, false);
 });
 
@@ -69,7 +69,7 @@ test("resolveAgentReadiness_cli_skips_logged_out_runtimes", () => {
       authStatus: { status: "logged_out" },
     }),
   ];
-  const result = resolveAgentReadiness(runtimes, makeConfig());
+  const result = resolveAgentReadiness(runtimes, makeConfig(), "preferred");
   assert.equal(result.ready, false);
 });
 
@@ -82,7 +82,7 @@ test("resolveAgentReadiness_goose_requires_provider_and_model", () => {
       authStatus: { status: "not_applicable" },
     }),
   ];
-  const result = resolveAgentReadiness(runtimes, makeConfig());
+  const result = resolveAgentReadiness(runtimes, makeConfig(), "preferred");
   assert.equal(result.ready, false);
 });
 
@@ -96,7 +96,7 @@ test("resolveAgentReadiness_cli_not_ready_for_unknown_auth_status", () => {
       authStatus: { status: "unknown" },
     }),
   ];
-  const result = resolveAgentReadiness(runtimes, makeConfig());
+  const result = resolveAgentReadiness(runtimes, makeConfig(), "preferred");
   assert.equal(result.ready, false);
 });
 
@@ -109,7 +109,7 @@ test("resolveAgentReadiness_cli_not_ready_for_config_invalid_auth_status", () =>
       authStatus: { status: "config_invalid" },
     }),
   ];
-  const result = resolveAgentReadiness(runtimes, makeConfig());
+  const result = resolveAgentReadiness(runtimes, makeConfig(), "preferred");
   assert.equal(result.ready, false);
 });
 
@@ -122,7 +122,7 @@ test("resolveAgentReadiness_cli_skips_unavailable_runtimes", () => {
       authStatus: { status: "logged_in" },
     }),
   ];
-  const result = resolveAgentReadiness(runtimes, makeConfig());
+  const result = resolveAgentReadiness(runtimes, makeConfig(), "preferred");
   assert.equal(result.ready, false);
 });
 
@@ -135,7 +135,7 @@ test("resolveAgentReadiness_cli_ignores_buzz_agent_runtime", () => {
       authStatus: { status: "not_applicable" },
     }),
   ];
-  const result = resolveAgentReadiness(runtimes, makeConfig());
+  const result = resolveAgentReadiness(runtimes, makeConfig(), "preferred");
   assert.equal(result.ready, false);
 });
 
@@ -196,6 +196,42 @@ test("resolveAgentReadiness_neither_returns_not_ready", () => {
   assert.deepEqual(result, { ready: false });
 });
 
+test("resolveAgentReadiness_welcome_readiness_uses_ready_cli_without_preference", () => {
+  const runtimes = [makeRuntime({ id: "claude", label: "Claude" })];
+  const result = resolveAgentReadiness(
+    runtimes,
+    makeConfig({ preferred_runtime: null }),
+  );
+  assert.deepEqual(result, {
+    ready: true,
+    reason: "cli",
+    runtimeLabel: "Claude",
+  });
+});
+
+test("resolveAgentReadiness_legacy_config_without_preference_uses_buzz_agent_fields", () => {
+  const runtimes = [makeRuntime({ id: "buzz-agent", label: "Buzz Agent" })];
+  const result = resolveAgentReadiness(
+    runtimes,
+    makeConfig({
+      preferred_runtime: null,
+      provider: "anthropic",
+      model: "claude-3-5-sonnet-latest",
+      env_vars: { ANTHROPIC_API_KEY: "sk-ant-test" },
+    }),
+  );
+  assert.deepEqual(result, { ready: true, reason: "buzz-agent" });
+});
+
+test("resolveAgentReadiness_legacy_config_does_not_treat_goose_binary_as_ready", () => {
+  const result = resolveAgentReadiness(
+    [makeRuntime({ id: "goose", label: "Goose" })],
+    makeConfig({ preferred_runtime: null }),
+    "preferred",
+  );
+  assert.deepEqual(result, { ready: false });
+});
+
 // ---------------------------------------------------------------------------
 // Preferred runtime isolation
 // ---------------------------------------------------------------------------
@@ -212,6 +248,7 @@ test("resolveAgentReadiness_preferred_goose_does_not_borrow_ready_buzz_agent_con
       model: null,
       env_vars: { ANTHROPIC_API_KEY: "sk-ant-test" },
     }),
+    "preferred",
   );
   assert.equal(result.ready, false);
 });
diff --git a/desktop/src/features/onboarding/ui/agentReadiness.ts b/desktop/src/features/onboarding/ui/agentReadiness.ts
index 786a45e81a..a76f13c541 100644
--- a/desktop/src/features/onboarding/ui/agentReadiness.ts
+++ b/desktop/src/features/onboarding/ui/agentReadiness.ts
@@ -21,10 +21,27 @@ export type AgentReadinessResult =
 export function resolveAgentReadiness(
   runtimes: readonly AcpRuntimeCatalogEntry[],
   globalConfig: GlobalAgentConfig,
+  scope: "any" | "preferred" = "any",
 ): AgentReadinessResult {
-  const preferredRuntime = runtimes.find(
-    (runtime) => runtime.id === globalConfig.preferred_runtime,
-  );
+  if (scope === "any") {
+    for (const runtime of runtimes) {
+      if (runtime.id === "buzz-agent") continue;
+      if (
+        runtime.availability === "available" &&
+        (runtime.authStatus.status === "logged_in" ||
+          runtime.authStatus.status === "not_applicable")
+      ) {
+        return { ready: true, reason: "cli", runtimeLabel: runtime.label };
+      }
+    }
+  }
+
+  const preferredRuntime =
+    scope === "preferred"
+      ? runtimes.find(
+          (runtime) => runtime.id === globalConfig.preferred_runtime,
+        )
+      : runtimes.find((runtime) => runtime.id === "buzz-agent");
   if (preferredRuntime?.availability !== "available") {
     return { ready: false };
   }

From 341285828665265bae52994bdba1c0b6e781b959 Mon Sep 17 00:00:00 2001
From: Wes <wesbillman@users.noreply.github.com>
Date: Fri, 17 Jul 2026 16:34:57 -0600
Subject: [PATCH 3/3] fix(desktop): always perform initial welcome presence
 check

Run the first teammate presence read before applying the readiness deadline so a busy event loop cannot skip polling entirely.

Co-authored-by: Pinky <44b8e82baa6e0e254e0208d68f335c283c94e7b78dd1fa10d5a49d3f13dd0435@sprout-oss.stage.blox.sqprod.co>
Signed-off-by: Wes <wesbillman@users.noreply.github.com>
---
 desktop/src/features/onboarding/welcomeKickoff.test.mjs | 2 +-
 desktop/src/features/onboarding/welcomeKickoff.ts       | 3 ++-
 2 files changed, 3 insertions(+), 2 deletions(-)

diff --git a/desktop/src/features/onboarding/welcomeKickoff.test.mjs b/desktop/src/features/onboarding/welcomeKickoff.test.mjs
index 244d6de05e..9b7b57e21b 100644
--- a/desktop/src/features/onboarding/welcomeKickoff.test.mjs
+++ b/desktop/src/features/onboarding/welcomeKickoff.test.mjs
@@ -242,7 +242,7 @@ test("readiness wait returns the subset that became online by the deadline", asy
       [bumble.pubkey]: "offline",
     }),
     pollMs: 0,
-    waitMs: 1,
+    waitMs: 0,
   });
 
   assert.deepEqual(online, [honey]);
diff --git a/desktop/src/features/onboarding/welcomeKickoff.ts b/desktop/src/features/onboarding/welcomeKickoff.ts
index c6e1e49874..c0d981dd5e 100644
--- a/desktop/src/features/onboarding/welcomeKickoff.ts
+++ b/desktop/src/features/onboarding/welcomeKickoff.ts
@@ -186,7 +186,7 @@ export async function waitForWelcomeTeammatesOnline(
   const pubkeys = teammates.map((agent) => agent.pubkey);
   let latestOnline: ManagedAgent[] = [];
 
-  while (!options.isCancelled() && Date.now() < deadline) {
+  while (!options.isCancelled()) {
     try {
       latestOnline = onlineWelcomeTeammates(
         teammates,
@@ -198,6 +198,7 @@ export async function waitForWelcomeTeammatesOnline(
     } catch (error) {
       console.warn("Welcome teammate presence check failed; retrying.", error);
     }
+    if (Date.now() >= deadline) break;
     await new Promise((resolve) => globalThis.setTimeout(resolve, pollMs));
   }
   return options.isCancelled() ? [] : latestOnline;
