mirror of
https://github.com/rcourtman/Pulse.git
synced 2026-08-20 14:33:30 +00:00
Keep discovery manual refresh visible
This commit is contained in:
parent
b0c4faa4a0
commit
f81490ca4f
7 changed files with 62 additions and 26 deletions
|
|
@ -170,7 +170,7 @@ runtime cost control, and shared AI transport surfaces.
|
|||
divergence must be sourced from canonical `ReadState.DockerHosts()` views,
|
||||
with model-shaped data limited to the watcher adapter input rather than
|
||||
direct `StateSnapshot.DockerHosts` reads in the Patrol run loop.
|
||||
4. Keep discovery scheduling authoritative through `internal/config/ai.go`: `discovery_enabled` and `discovery_interval_hours` must govern both lightweight infrastructure discovery and deep service-discovery background loops. `internal/api/ai_handlers.go` must preserve an explicitly supplied `discovery_interval_hours: 0` as the manual-only setting and may only apply the 24-hour default when discovery is enabled without an explicit interval payload. Discovery analysis remains a Pulse tool-led model workflow: Pulse supplies agent/API/metrics evidence and cache orchestration, while the selected model provides the intelligence. Background, settings-triggered, or drawer-triggered discovery progress must describe that discovery analysis directly and must not imply a live Pulse Assistant chat transcript unless the run is actually executing inside the chat surface. Assistant and Patrol access to discovery must stay behind the governed `pulse_discovery` tool, including an explicit forced refresh action for known resources, while settings-level manual runs must use the canonical `/api/discovery/run` new/changed/stale sweep rather than a frontend-only shortcut.
|
||||
4. Keep discovery scheduling authoritative through `internal/config/ai.go`: `discovery_enabled` and `discovery_interval_hours` must govern both lightweight infrastructure discovery and deep service-discovery background loops. `internal/api/ai_handlers.go` must preserve an explicitly supplied `discovery_interval_hours: 0` as the manual-only setting and may only apply the 24-hour default when discovery is enabled without an explicit interval payload. Discovery analysis remains a Pulse tool-led model workflow: Pulse supplies agent/API/metrics evidence and cache orchestration, while the selected model provides the intelligence. Background, settings-triggered, or drawer-triggered discovery progress must describe that discovery analysis directly and must not imply a live Pulse Assistant chat transcript unless the run is actually executing inside the chat surface. Settings-triggered manual discovery is an explicit operator refresh and must not open, append to, or masquerade as a Pulse Assistant session. Assistant and Patrol access to discovery must stay behind the governed `pulse_discovery` tool, including an explicit forced refresh action for known resources, while settings-level manual runs must use the canonical `/api/discovery/run` new/changed/stale sweep rather than a frontend-only shortcut.
|
||||
5. Preserve auditability for outbound model-bound context exports and keep the export record aligned with the prompt boundary that actually reaches the provider
|
||||
External provider-bound unified-resource context must enforce the same
|
||||
data-handling policy the export audit records: `local-only` resources are
|
||||
|
|
|
|||
|
|
@ -905,8 +905,9 @@ AI runtime.
|
|||
settings pair so selecting "Every 6 hours" or "Manual only" round-trips
|
||||
through `/api/settings/ai` without depending on stale read-side diffing.
|
||||
The same workload-discovery settings section must expose a manual
|
||||
"Run discovery now" action wired through `/api/discovery/run`, while
|
||||
resource-drawer discovery remains the forced single-resource refresh path.
|
||||
"Run discovery now" action wired through `/api/discovery/run` even when
|
||||
recurring workload discovery is off, while resource-drawer discovery remains
|
||||
the forced single-resource refresh path.
|
||||
Assistant-only controls inside the shared shell, such
|
||||
as execution permissions and session maintenance, must stay explicitly
|
||||
labeled as Pulse Assistant controls, while Patrol schedule and autonomy
|
||||
|
|
|
|||
|
|
@ -122,27 +122,30 @@ export const AIRuntimeControlsSection: Component<AIRuntimeControlsSectionProps>
|
|||
: 'Workload discovery will automatically re-scan resources at this interval'}
|
||||
</p>
|
||||
</div>
|
||||
<div class="flex items-center justify-between gap-3 rounded border border-border bg-surface-alt px-3 py-2">
|
||||
<p class="text-[10px] text-muted">
|
||||
Runs the new, changed, and stale workload sweep used by the schedule.
|
||||
</p>
|
||||
<button
|
||||
type="button"
|
||||
class="inline-flex min-h-10 sm:min-h-9 items-center gap-1.5 rounded-md border border-border px-3 py-1.5 text-xs font-medium text-base-content hover:bg-surface-hover disabled:cursor-not-allowed disabled:opacity-50"
|
||||
onClick={() => void state.handleRunDiscoveryRefresh()}
|
||||
disabled={state.saving() || state.discoveryRunRunning()}
|
||||
>
|
||||
<Show
|
||||
when={state.discoveryRunRunning()}
|
||||
fallback={<RefreshCwIcon class="h-3.5 w-3.5" />}
|
||||
>
|
||||
<RefreshCwIcon class="h-3.5 w-3.5 animate-spin" />
|
||||
</Show>
|
||||
{state.discoveryRunRunning() ? 'Running...' : 'Run discovery now'}
|
||||
</button>
|
||||
</div>
|
||||
</Show>
|
||||
|
||||
<div class="flex flex-col gap-2 rounded border border-border bg-surface-alt px-3 py-2 sm:flex-row sm:items-center sm:justify-between sm:gap-3">
|
||||
<p class="text-[10px] text-muted sm:flex-1">
|
||||
{state.form.discoveryEnabled
|
||||
? 'Runs the new, changed, and stale workload sweep used by the schedule.'
|
||||
: 'Runs a one-time workload discovery refresh without changing the schedule.'}
|
||||
</p>
|
||||
<button
|
||||
type="button"
|
||||
class="inline-flex min-h-10 items-center gap-1.5 self-start rounded-md border border-border px-3 py-1.5 text-xs font-medium text-base-content hover:bg-surface-hover disabled:cursor-not-allowed disabled:opacity-50 sm:min-h-9 sm:self-auto sm:flex-shrink-0"
|
||||
onClick={() => void state.handleRunDiscoveryRefresh()}
|
||||
disabled={state.saving() || state.discoveryRunRunning()}
|
||||
>
|
||||
<Show
|
||||
when={state.discoveryRunRunning()}
|
||||
fallback={<RefreshCwIcon class="h-3.5 w-3.5" />}
|
||||
>
|
||||
<RefreshCwIcon class="h-3.5 w-3.5 animate-spin" />
|
||||
</Show>
|
||||
{state.discoveryRunRunning() ? 'Running...' : 'Run discovery now'}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<p class="text-[10px] text-muted">{getAISettingsWorkloadDiscoverySummary().text}</p>
|
||||
</div>
|
||||
</Show>
|
||||
|
|
|
|||
|
|
@ -506,6 +506,35 @@ describe('AISettings workload discovery persistence', () => {
|
|||
});
|
||||
});
|
||||
|
||||
it('keeps the manual discovery refresh visible when recurring discovery is off', async () => {
|
||||
getSettingsMock.mockResolvedValue({
|
||||
...baseSettings(),
|
||||
discovery_enabled: false,
|
||||
discovery_interval_hours: 0,
|
||||
});
|
||||
|
||||
renderComponent();
|
||||
|
||||
await waitFor(() => {
|
||||
expect(screen.getByRole('button', { name: /Workload Discovery Off/i })).toBeInTheDocument();
|
||||
});
|
||||
fireEvent.click(screen.getByRole('button', { name: /Workload Discovery Off/i }));
|
||||
|
||||
expect(screen.queryByLabelText('Scan Interval')).not.toBeInTheDocument();
|
||||
expect(
|
||||
screen.getByText('Runs a one-time workload discovery refresh without changing the schedule.'),
|
||||
).toBeInTheDocument();
|
||||
|
||||
fireEvent.click(screen.getByRole('button', { name: /Run discovery now/i }));
|
||||
|
||||
await waitFor(() => {
|
||||
expect(runDiscoveryRefreshMock).toHaveBeenCalledTimes(1);
|
||||
expect(notificationSuccessMock).toHaveBeenCalledWith(
|
||||
'Discovery refresh finished: 1 workload refreshed.',
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
it('reports when a manual discovery refresh has no pending work', async () => {
|
||||
getSettingsMock.mockResolvedValue({
|
||||
...baseSettings(),
|
||||
|
|
|
|||
|
|
@ -268,6 +268,9 @@ describe('settings architecture guardrails', () => {
|
|||
expect(aiSettingsStateSource).toContain('discoveryRunRunning');
|
||||
expect(aiRuntimeControlsSectionSource).toContain('state.handleRunDiscoveryRefresh()');
|
||||
expect(aiRuntimeControlsSectionSource).toContain('Run discovery now');
|
||||
expect(aiRuntimeControlsSectionSource).toContain(
|
||||
'Runs a one-time workload discovery refresh without changing the schedule.',
|
||||
);
|
||||
expect(aiRuntimeControlsSectionSource).not.toContain("fetch('/api/discovery/run");
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -38,10 +38,10 @@ describe('aiSettingsPresentation', () => {
|
|||
expect(getAISettingsWorkloadDiscoveryHelpContent()).toEqual({
|
||||
title: 'What is workload discovery?',
|
||||
description:
|
||||
'Workload discovery scans your VMs, containers, and container runtimes to identify running services, versions, and access details. Pulse Assistant uses that context for concrete troubleshooting guidance, and Patrol uses it to verify the right workloads continuously.',
|
||||
'Workload discovery scans your VMs, containers, and container runtimes to identify running services, versions, and access details. Pulse stores that context so Assistant can use it in chat and Patrol can use it during verification.',
|
||||
});
|
||||
expect(getAISettingsWorkloadDiscoverySummary()).toEqual({
|
||||
text: 'Workload discovery gives Pulse Assistant and Patrol concrete service context, so chat responses and verification findings can reference real services and commands instead of generic advice.',
|
||||
text: 'Workload discovery stores concrete service context for Assistant chat and Patrol verification, so responses and findings can reference real services and commands instead of generic advice.',
|
||||
});
|
||||
expect(getAISettingsSetupDialogPresentation()).toEqual({
|
||||
ariaLabel: 'Set up Assistant and Patrol',
|
||||
|
|
|
|||
|
|
@ -44,13 +44,13 @@ export function getAISettingsWorkloadDiscoveryHelpContent() {
|
|||
return {
|
||||
title: 'What is workload discovery?',
|
||||
description:
|
||||
'Workload discovery scans your VMs, containers, and container runtimes to identify running services, versions, and access details. Pulse Assistant uses that context for concrete troubleshooting guidance, and Patrol uses it to verify the right workloads continuously.',
|
||||
'Workload discovery scans your VMs, containers, and container runtimes to identify running services, versions, and access details. Pulse stores that context so Assistant can use it in chat and Patrol can use it during verification.',
|
||||
} as const;
|
||||
}
|
||||
|
||||
export function getAISettingsWorkloadDiscoverySummary() {
|
||||
return {
|
||||
text: 'Workload discovery gives Pulse Assistant and Patrol concrete service context, so chat responses and verification findings can reference real services and commands instead of generic advice.',
|
||||
text: 'Workload discovery stores concrete service context for Assistant chat and Patrol verification, so responses and findings can reference real services and commands instead of generic advice.',
|
||||
} as const;
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue