diff --git a/docs/release-control/v6/internal/subsystems/agent-lifecycle.md b/docs/release-control/v6/internal/subsystems/agent-lifecycle.md
index d09d0f07f1..d2203f8e39 100644
--- a/docs/release-control/v6/internal/subsystems/agent-lifecycle.md
+++ b/docs/release-control/v6/internal/subsystems/agent-lifecycle.md
@@ -165,7 +165,10 @@ management, and fleet control surfaces.
be saved now should be visible before the operator leaves the screen, and
the completion surface should present one canonical primary next-step path
into Infrastructure Install instead of repeating competing install or
- dashboard CTAs across multiple sections.
+ dashboard CTAs across multiple sections. Once the first monitored host is
+ already connected, that same surface must pivot its primary CTA and headline
+ to `/` so the operator is sent to the dashboard rather than being told to
+ install the first host again.
## Current State
diff --git a/frontend-modern/src/components/SetupWizard/SetupCompletionPanel.tsx b/frontend-modern/src/components/SetupWizard/SetupCompletionPanel.tsx
index 6a88043b8f..6d7f26b33f 100644
--- a/frontend-modern/src/components/SetupWizard/SetupCompletionPanel.tsx
+++ b/frontend-modern/src/components/SetupWizard/SetupCompletionPanel.tsx
@@ -1,4 +1,12 @@
-import { Component, createSignal, createEffect, onCleanup, Show, For } from 'solid-js';
+import {
+ Component,
+ createSignal,
+ createEffect,
+ createMemo,
+ onCleanup,
+ Show,
+ For,
+} from 'solid-js';
import { useNavigate } from '@solidjs/router';
import { unwrap } from 'solid-js/store';
import { copyToClipboard } from '@/utils/clipboard';
@@ -318,6 +326,8 @@ Keep these credentials secure!
navigate(RELAY_SETTINGS_PATH);
};
+ const hasConnectedAgents = createMemo(() => connectedAgents().length > 0);
+
return (
@@ -334,11 +344,12 @@ Keep these credentials secure!
- Install your first monitored host
+ {hasConnectedAgents() ? 'First monitored host connected' : 'Install your first monitored host'}
- Your admin account is ready. Next, open Infrastructure Install and run the generated
- command on the first system you want Pulse to monitor.
+ {hasConnectedAgents()
+ ? 'Your admin account is ready and Pulse is already receiving telemetry. Open the dashboard to verify your first overview, or return to Infrastructure Install when you want to add more systems.'
+ : 'Your admin account is ready. Next, open Infrastructure Install and run the generated command on the first system you want Pulse to monitor.'}
@@ -408,8 +419,8 @@ Keep these credentials secure!
- Save the admin login and API token before leaving this screen, then continue into
- Infrastructure Install for the first host.
+ Save the admin login and API token before leaving this screen, then continue into{' '}
+ {hasConnectedAgents() ? 'the dashboard or Infrastructure Install.' : 'Infrastructure Install for the first host.'}
@@ -629,13 +640,12 @@ Keep these credentials secure!
d="M8 9l3 3-3 3m5 0h3M5 20h14a2 2 0 002-2V6a2 2 0 00-2-2H5a2 2 0 00-2 2v12a2 2 0 002 2z"
/>
- Install your first host
+ {hasConnectedAgents() ? 'Open your first dashboard view' : 'Install your first host'}
- The canonical install flow now lives in Infrastructure Operations. Open that
- workspace to generate an install token, set the agent connection URL, configure TLS
- or custom CA options, and copy the correct command for the first system you want
- Pulse to monitor.
+ {hasConnectedAgents()
+ ? 'Pulse already has a live monitored system. Open the dashboard to confirm the first overview, then return to Infrastructure Install any time you want to add more hosts or regenerate commands.'
+ : 'The canonical install flow now lives in Infrastructure Operations. Open that workspace to generate an install token, set the agent connection URL, configure TLS or custom CA options, and copy the correct command for the first system you want Pulse to monitor.'}
@@ -647,31 +657,37 @@ Keep these credentials secure!
Next step
- Open Infrastructure Install to bring your first monitored system into Pulse.
+ {hasConnectedAgents()
+ ? 'Open the dashboard to review your first connected system.'
+ : 'Open Infrastructure Install to bring your first monitored system into Pulse.'}
- Use that workspace any time you want to add more systems later.
+ {hasConnectedAgents()
+ ? 'Infrastructure Install stays available any time you want to add more systems later.'
+ : 'Use that workspace any time you want to add more systems later.'}
- 0}>
+
- 0}>
+
diff --git a/frontend-modern/src/components/SetupWizard/__tests__/SetupCompletionPanel.guardrails.test.ts b/frontend-modern/src/components/SetupWizard/__tests__/SetupCompletionPanel.guardrails.test.ts
index f065c3ea6c..e2b94fb4eb 100644
--- a/frontend-modern/src/components/SetupWizard/__tests__/SetupCompletionPanel.guardrails.test.ts
+++ b/frontend-modern/src/components/SetupWizard/__tests__/SetupCompletionPanel.guardrails.test.ts
@@ -55,6 +55,13 @@ describe('SetupCompletionPanel guardrails', () => {
expect(setupCompletionPanelSource).toContain('Save the admin login and API token before leaving this screen');
expect(setupCompletionPanelSource).toContain('Recommended next step');
expect(setupCompletionPanelSource).toContain('Go to Dashboard');
+ expect(setupCompletionPanelSource).toContain('First monitored host connected');
+ expect(setupCompletionPanelSource).toContain(
+ 'hasConnectedAgents() ? handleGoToDashboard() : handleOpenInstallWorkspace()',
+ );
+ expect(setupCompletionPanelSource).toContain(
+ "{hasConnectedAgents() ? 'Go to Dashboard' : 'Open Infrastructure Install'}",
+ );
expect(setupCompletionPanelSource).not.toContain(
"connectedAgents().length > 0 ? 'Go to Dashboard' : 'Open Infrastructure Install'",
);
diff --git a/frontend-modern/src/components/SetupWizard/__tests__/SetupCompletionPanel.test.tsx b/frontend-modern/src/components/SetupWizard/__tests__/SetupCompletionPanel.test.tsx
index 10a05e43dd..8f00f20515 100644
--- a/frontend-modern/src/components/SetupWizard/__tests__/SetupCompletionPanel.test.tsx
+++ b/frontend-modern/src/components/SetupWizard/__tests__/SetupCompletionPanel.test.tsx
@@ -1,5 +1,5 @@
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
-import { cleanup, fireEvent, render, screen, waitFor } from '@solidjs/testing-library';
+import { cleanup, fireEvent, render, screen, waitFor, within } from '@solidjs/testing-library';
import { SetupCompletionPanel } from '../SetupCompletionPanel';
import type { WizardState } from '../SetupWizard';
@@ -203,9 +203,25 @@ describe('SetupCompletionPanel', () => {
});
expect(screen.getByText('Tower')).toBeInTheDocument();
+ expect(screen.getByText('First monitored host connected')).toBeInTheDocument();
+ expect(
+ screen.getByText(
+ 'Your admin account is ready and Pulse is already receiving telemetry. Open the dashboard to verify your first overview, or return to Infrastructure Install when you want to add more systems.',
+ ),
+ ).toBeInTheDocument();
+ expect(
+ screen.getByText('Open the dashboard to review your first connected system.'),
+ ).toBeInTheDocument();
expect(screen.getAllByRole('button', { name: 'Go to Dashboard' }).length).toBeGreaterThan(0);
+ expect(
+ screen.getAllByRole('button', { name: 'Open Infrastructure Install' }).length,
+ ).toBeGreaterThan(0);
- fireEvent.click(screen.getAllByRole('button', { name: 'Go to Dashboard' })[0]);
+ const nextStepHeading = screen.getByRole('heading', { name: 'Open your first dashboard view' });
+ const nextStepCard = nextStepHeading.closest('div.bg-surface.rounded-md.border.border-border.p-6.text-left.mb-6');
+ expect(nextStepCard).not.toBeNull();
+
+ fireEvent.click(within(nextStepCard as HTMLElement).getByRole('button', { name: 'Go to Dashboard' }));
expect(onComplete).toHaveBeenCalledWith('/');
});