fix(onboarding): hide the probe's diagnostics while the hire is in flight (#12902)

After a successful sign-in, a block of amber diagnostics flashed up and vanished as the step advanced. They are the identity and target INFO checks every environment test reports: they make the result a warn without blocking anything, and the step rendered any non-pass result, so they owned the screen for the window between the probe returning and setStep(5).

Gated on loading rather than the connect phase. A blocking result stops the hire and handleGiveHeartbeat clears loading in the finally after its early return, so a genuine block still shows its checks while a hire in flight shows none.

The button needed no change: step 4 forces the footer's loading to false and takes its label from connectCta, which reads Connecting for that whole window.
This commit is contained in:
Tonio 2026-09-05 10:08:28 -07:00 committed by GitHub
parent 60469a08e0
commit be407f3456
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 74 additions and 1 deletions

View File

@ -2337,6 +2337,66 @@ describe("OnboardingWizard restore-gate (stale localStorage across accounts)", (
await act(async () => root.unmount());
});
it("shows no probe diagnostics between the sign-in succeeding and the step advancing", async () => {
// Reported from staging: a block of amber diagnostics flashed up right
// after a successful sign-in. They are the identity and target INFO
// checks every run reports, which make the result a `warn` without
// blocking anything — so they rendered for the window between the probe
// returning and the step advancing, reading as an error thrown by the
// sign-in that had just succeeded.
mockAgentsApi.getAdapterAuthSignal.mockResolvedValue({ status: "absent" });
mockAgentsApi.getClaudeSetupTokenLoginStatus.mockResolvedValue({
sessionId: "claude-session-1",
status: "authenticated",
expiresAt: new Date(Date.now() + 600_000).toISOString(),
});
mockAgentsApi.testEnvironment.mockResolvedValue({
adapterType: "claude_local",
status: "warn",
checks: [
{
code: "environment_identity",
level: "info",
message: 'Environment test identity for "Paperclip Computer".',
detail: "paperclipLeaseId=ff9e58e9; provider=daytona",
},
],
testedAt: new Date().toISOString(),
});
// Hold the hire open, which is the window the diagnostics appeared in:
// the probe has returned but `loading` is not cleared until the `finally`
// that runs after the step advances.
let finishHire: (v: { agent: { id: string }; approval: null }) => void = () => {};
mockAgentsApi.hire.mockReturnValue(
new Promise((resolve) => {
finishHire = resolve;
}),
);
const { root } = await openStep4({ adapterType: "claude_local" });
await pickSource(/Claude/);
for (let i = 0; i < 8; i++) await flushReact();
// Past the deliberate hold, so the hire is running and its probe is done.
await act(async () => {
await new Promise((resolve) => window.setTimeout(resolve, CONNECTED_HOLD_MS + 400));
});
for (let i = 0; i < 8; i++) await flushReact();
expect(mockAgentsApi.hire).toHaveBeenCalled();
expect(document.body.textContent).not.toContain("Environment test identity");
expect(document.body.textContent).not.toContain("Warnings");
// And the button goes on saying what is happening rather than going quiet.
expect(
[...document.body.querySelectorAll("button")].pop()?.textContent?.trim(),
).toBe("Connecting");
await act(async () => finishHire({ agent: { id: "agent-1" }, approval: null }));
for (let i = 0; i < 6; i++) await flushReact();
await act(async () => root.unmount());
});
it("starts no login when Back interrupts the collapse", async () => {
// Backing out before the card has opened has nothing to close. Unwinding
// through the card beat regardless mounted the panel — which starts a

View File

@ -3232,7 +3232,20 @@ function OnboardingWizardInner({
step, so this block renders only when a probe has actually
found something: the checks the blocking error tells the
customer to fix have to be visible somewhere. */}
{isLocalAdapter && (adapterEnvError || (adapterEnvResult && adapterEnvResult.status !== "pass")) && (
{/* Not while the hire is in flight. The probe's result lands
before the hire it gates has finished, so a warn that does
not block the identity and target INFO checks, which
every run reports rendered a block of diagnostics for the
moment between the probe returning and the step advancing.
It read as an error thrown up by a sign-in that had just
succeeded.
`loading` is the right gate rather than the connect phase:
it is false again by the time a blocking result has stopped
the hire, because `handleGiveHeartbeat` clears it in its
`finally` after the early return so a genuine block still
shows its checks, which is the whole reason this is here. */}
{isLocalAdapter && !loading && (adapterEnvError || (adapterEnvResult && adapterEnvResult.status !== "pass")) && (
<div className="space-y-2 rounded-md border border-border p-3">
{adapterEnvError && (
<div className="rounded-md border border-destructive/30 bg-destructive/10 px-2.5 py-2 text-(length:--text-micro) text-destructive">