Commit Graph

140 Commits

Author SHA1 Message Date
rcourtman 39f988b220 Remove AI Discovery
AI Discovery ran a separate model-backed service scanner beside Patrol
and the Assistant: its own store, scheduler, model route, API, tool and
drawer UI. No telemetry counter ever measured its use (the telemetry
discovery_enabled flag OR'd it with network subnet discovery), the
maintainer judged it a flop, and the context it produced is already
available to the models through pulse_query, pulse_read and the
unified-resource context sections.

This deletes internal/servicediscovery, internal/ai/infradiscovery, the
pulse_discovery tool, the /api/discovery routes, the mock Discovery
fixtures, the Discovery tabs in the resource, guest, node and Docker host
drawers, the identified-service cards, readiness badges, provenance
markers, suggested web-interface URLs, the Workloads AI Context column,
the chat "Discovery is off" hint and the AI Service Context settings page.
The old settings route redirects to Assistant settings. The model-bound
sanitizer allow-list existed only to pass Discovery context through, so it
goes too, along with the CopyableCodeRow primitive that only the
Discovery tab used.

Network subnet discovery (/api/discover, the Infrastructure discovery
settings) and the discoveryTarget agent coordinates are separate features
and stay. Stored discovery_enabled, discovery_interval_hours and
discovery_model AI settings are ignored on load; the settings API decoder
is lenient, so older frontends saving those fields still succeed.
2026-10-11 22:20:25 +01:00
pulse-triage[bot] f0e4ba7722 Check surviving settings links without requiring removed controls
Reconcile PR2751's removed Terms consumer with current main. Inspect actual surviving JSX for the shared primitive, shipped Terms route and opener safety, with whole-removal and unsafe-link controls. Keep all surviving fixed consumers and runtime source unchanged.

Contract-Neutral: Test and consumer-registry reconciliation only; no runtime, route, documentation destination or shared external-link safety contract changes.
Change-source: pulse-maintainer
2026-10-09 09:10:26 +01:00
rcourtman 217ebc3db5 Remove the unreachable Workloads chart visibility toggle (#2734)
The Workloads View disclosure rendered `ChartVisibilityToggleButton`
only inside `<Show when={props.onChartsToggle}>`, and no surface passes
`onChartsToggle` or `chartsCollapsed` since the workload summary chart
section was deleted with the summary components. The button had no other
production consumer.

This removes:
- the `Summary` block and import in `WorkloadsFilter.tsx`, and the
`chartsCollapsed` / `onChartsToggle` props in `workloadsFilterModel.ts`
- `ChartVisibilityToggleButton` and its icon import in
`shared/FilterToolbar.tsx`
- the `chart-visibility-toggle-button` rule and
`chart-visibility-local-toggle-labels` pattern guard in
`scripts/shared-template-registry.json`
- the unit tests for the button and the WorkloadsFilter charts describe

The guardrail case that required the primitive now keeps the exact
retired registry ids, export and props out. It deliberately does not
block unrelated future chart controls: a returning summary section is a
product decision that should register its own primitive.

Contract prose: `frontend-primitives.md` (View-disclosure list, the
FilterToolbar rule, the registry paragraph, the FilterBar view-options
list), `performance-and-scalability.md` (the workload chart-visibility
sentence) and `unified-resources.md` rule 12 now say no chart show/hide
control exists instead of describing the deleted one.

Verification:
- frontend `src/features`, `src/components/shared`,
`src/components/Workloads`, `src/utils` suites on pulse-dev: 783 files
passed, one load-induced 5s timeout in `AgentsMachinesTable.test.tsx`
that passes alone (32/32) and does not reference these symbols; tsc,
prettier and the shared-template audit pass
- browser: production builds of this change and of main's three runtime
files, served against the 50-node mock rig, Proxmox and vSphere
overviews at 1440x900 and 390x844 (phone via Filters > View). The View
preferences disclosure shows identical headings, controls and pressed
states before and after, with no Summary heading, no Charts button, no
horizontal overflow and no console errors. The after bundle no longer
carries the `Show charts` copy.
2026-10-08 03:11:57 +01:00
rcourtman 04f61d431f Remove the unreachable Workloads chart visibility toggle
WorkloadsFilter rendered ChartVisibilityToggleButton only when a surface
passed onChartsToggle, and none has since the workload summary chart
section was deleted with the summary components. The button had no other
production consumer, so the shared primitive, its two shared-template
registry entries, the filter-model props and the tests that pinned them
described a Show/Hide charts control that nobody could reach.

Delete them. The guardrail that required the primitive now keeps the
exact retired registry ids, export and props out: a returning summary
section is a product decision that should register its own control.
Contract prose in frontend-primitives, performance-and-scalability and
unified-resources now says no chart show/hide control exists instead of
describing the deleted one.

No visible change: the Workloads View disclosure on the Proxmox and
vSphere overviews shows the same sections and controls before and after,
at desktop and phone widths.
2026-10-08 00:24:23 +01:00
rcourtman 59cc3ce10b Wrap long identity chips inside phone drawers
Below the lg breakpoint, the inline drawer's Identity section rendered
alias and IP chips as hand-rolled nowrap spans inside a fixed-layout
table cell. A generated alias such as
proxmox-lxc-docker:Production West:pve1:113 or
k8s-production-1:deployment:redis-ffcd-b17c78f012 is wider than that
value cell on a phone, so the cell's hidden overflow and the drawer's
clipping content shell cut it off with no ellipsis. On a 390px mock
estate 53 chips across Docker and Kubernetes drawers were cut. The part
that tells two aliases apart sits at the end, and a phone has no hover
to show the title.

MetadataBadge owns read-only chip whitespace handling, so it gains an
opt-in wrap mode that caps the chip at its container and breaks inside
a token only where no space or hyphen fits. The drawer's IP, alias and
alias-count chips now compose MetadataBadge, and the identity values
wrap in full. The default stays nowrap, so existing badge consumers are
unchanged, and desktop drawers still show each alias on one line.

The shared template registry now requires ResourceDetailSummary to
compose MetadataBadge and forbids the old hand-rolled chip classes
there. A mobile integration check opens a Docker container drawer and
fails if any Identity-section element or text crosses the detail cell,
table wrapper or drawer shell; it fails on the old code and passes with
this change.
2026-10-08 00:17:01 +01:00
rcourtman 2265d28242 Remove the never-mounted unified resource table
PlatformResourceTable was the only renderer of UnifiedResourceTable, and
nothing imports it. Every platform page (Proxmox, Docker, Kubernetes,
TrueNAS, vSphere, Machines) renders its own native tables, and
/infrastructure only shows the opening-workspace placeholder. A
production build of main already tree-shook the whole cluster (none of
its strings ship), yet it still carried 11 source files, 7 test files,
registry ownership in two subsystems and contract rules for a table
nobody sees.

Removed: PlatformResourceTable and its PLATFORM_STATUS_FILTER_OPTIONS;
UnifiedResourceTable with its host, service, PBS and PMG cards, source
badge cell, state hook, viewport sync and models; and
ClusterDeployBanner, which only the host card rendered (the live cluster
deploy flow is the Settings ClusterAgentDeployDialog). Their tests go
with them.

Coverage for live code those tests also exercised is kept: the detail
mapper no-fetch boundary and the online boolean sensor case move to
resourceDetailMappers.test.ts, the ResourceFacetSummary render cases
move to their own test file, and both files become registered proofs for
the policies that govern their subjects. Guardrail tests, the shared
template registry, registry.json, status.json evidence and the
performance, unified-resources, frontend-primitives and cloud-paid
contracts drop the deleted paths and the rules that only governed them.

Rollup used to name the large shared chunk after sharedPlatformPage; it
now names it MetricBar, so the bundle baseline entry is re-keyed at its
measured size.
2026-10-06 20:34:35 +01:00
rcourtman b5444cf8bf Read vSphere activity as vCenter's own sentences
The vSphere Activity tab titled every vCenter event with its class name
("HostConnectedEvent", "HostProfileAppliedEvent"), and its When column
clipped the time on every row at most widths.

Pulse never asked vCenter for the readable message: QueryEvents leaves
out fullFormattedMessage unless the filter sets disableFullMessage, so
on a real vCenter the drawer's Message row was always empty too. The
client now sends disableFullMessage: false, and the provider titles an
event change with that message, falling back to the class. That also
gives Patrol's change context a readable reason. The page model prefers
the message for rows stored before this change, and keeps the class as
an Event type row in the drawer and in search.

When now reads as an age ("1d ago"), as Kubernetes events do, with the
exact time on hover and in the drawer. A vCenter time before 2000 (its
unset marker) still shows as "-". The Activity table moves from the
date-time registry rule to the relative-time rule.
2026-10-06 11:34:56 +01:00
rcourtman 8b4df6e1fa Use the shared link and button primitives in two remaining spots (#2549)
This clears the last shared-template audit findings. Combined with
#2546, the audit then passes.

- **Backup export and import warnings:** the "migration guide" links
were raw `target="_blank"` anchors. They now use `ExternalTextLink`. Its
new `inlineInherit` variant keeps the warning's own amber or yellow
colour and underline, so a link inside a tinted callout sentence doesn't
switch to link blue.
- **External watchdog:** the Refresh and Show/Hide buttons now use the
shared outline `Button` instead of a copied border shell. On desktop
they look and measure the same as on main (26px and 38px). On phones
they become 44px touch targets.
- `BackupTransferDialogs.tsx` is now a registered required consumer of
`ExternalTextLink`, and the guardrail pins the new variant.

Verification:
- Checked live on a mock stack against main, in light and dark mode at
1440px and at 390px.
- The links keep their callout colour, stay inline and open in a new tab
with noopener.
- The receipt is included.
2026-10-06 00:53:39 +01:00
rcourtman eaaea2214e Use the shared link and button primitives in two remaining spots
The shared template audit still flagged hand-built controls in two
places: the backup export and import warnings opened the migration
guide through raw new-tab anchors, and the external watchdog section
copied the outline button shell for Refresh and Show/Hide.

The guide links now compose ExternalTextLink. Its new inlineInherit
variant keeps the warning's own colour and underline, so a link inside
a tinted callout sentence does not switch to link blue. The watchdog
buttons compose Button (outline, xs and settingsAction), which keeps
their desktop look and gives them a 44px touch target on phones.
2026-10-06 00:08:07 +01:00
rcourtman f7e61446bd Move three forms onto shared controls and draw the select arrow
The shared template audit listed Settings, Alerts and Discovery forms
still building their own controls. The Thresholds CPU averaging selects
and the Discovery availability proposal now compose FormSelect (the
proposal's name field takes the shared field classes beside them), and
an availability check's request body composes FormTextarea, which links
its help text and, with sm:min-h-24, finally gets the height its class
asked for on desktop.

FormSelect's default chrome set appearance-none and drew no arrow, so
every default select in Settings read as a text field. A
form-select-arrow utility draws the caret in the padding the class
already reserved, and hands the native arrow back in forced-colors mode.

Two registry entries were wrong: AlertEscalationSection has used
checkboxes instead of a select since its destinations became
per-escalation, and the inline detail row rule matched a test file;
test files are now excluded from it.
2026-10-05 23:01:46 +01:00
rcourtman 09f07d8514 Drop the storage table from the title-case helper consumers
The shared-template rule required every TrueNAS table to use the shared
title-case helper. The storage table used it only for the risk-level
pill, which the Health reason replaced, so it no longer formats any raw
provider value and the required-consumer check failed. The scope-wide
guard against a local titleCase helper still covers the file.
2026-10-05 07:37:46 +01:00
rcourtman 1a313366ab Render light-mode banner and callout text at full strength
Light-mode text on tinted callouts, banners and selection cards drew at
25% alpha: CalloutCard titles and bodies in every tone (the Security
overview's "Recommended hardening steps"), the Alerts thresholds help,
the update-channel card titles, Patrol and Action banners. The Tailwind
v3 config redefined each colour's 900 and 950 shades as 25% alpha so
dark-mode backgrounds could tint, which also faded every light-mode
text-*-900. The Tailwind 4 migration kept that parity by rewriting the
usages to -900/25.

Every light-mode text-*-900/25 and text-*-950/25, hover included, now
uses the solid shade. Dark variants, backgrounds, borders and rings keep
their tint, so dark mode is unchanged.

Contract-Neutral: restores light-mode text colour tokens only; no behaviour, data, route or contract change
2026-10-05 03:04:46 +01:00
rcourtman 9e7a5ffdeb Move frontend to Tailwind CSS 4 and jscpd 5 to drop braces
GHSA-vfj7-8cjw-p6xm affects every braces release and has no fix, so the
required frontend audit failed with seven high findings on any pull
request that changes the dependency graph. Tailwind CSS 3 (chokidar,
fast-glob, micromatch) and jscpd 4 (@jscpd/finder, fast-glob) were the
only paths in, both dev-only, and no override can remove braces from
fast-glob or micromatch. Tailwind CSS 4.3.3 through @tailwindcss/vite and
jscpd 5.4.0 leave none; npm audit over the full graph reports zero.

The stylesheet keeps the v3 rendering. It pins the v3 palette (inlined,
so opacity modifiers compile to static colors), font stacks and line
heights, keeps v3's cascade order by placing former component and
unlayered rules around the generated utilities, and restores v3 hover on
touch devices, gray-200 default borders, the 3px blue ring, placeholder
color, button cursor, table-cell padding and the space-* and divide-*
child selectors. The 900/950 shades Pulse overrode with 25% alpha are
opaque now and their callers say /25, because v4 multiplies an opacity
modifier into a translucent color instead of replacing it. Opacity
modifiers on var() colors that v3 never generated are removed.

Utility order changed from lexical to numeric, so a few caller overrides
that silently lost in v3 would start to win. Touch targets keep their
authored 44px phone size (the Machines row stays 32px); cosmetic
overrides v3 never applied are removed, and overrides it did apply use
the important suffix.

Contract-Neutral: Tailwind CSS 4 migration for GHSA-vfj7-8cjw-p6xm: utility class renames and stylesheet compatibility, no subsystem contract change
2026-10-03 15:19:35 +01:00
rcourtman 13ceeffe7d Converge remaining drawer detail rows 2026-08-27 20:41:43 +01:00
rcourtman 6341080cea fix(frontend): calm update and star prompts 2026-08-25 21:26:36 +01:00
rcourtman 0a58e77bc6 Eliminate remaining frontend template drift 2026-08-24 21:52:47 +01:00
rcourtman a3d7a90b1b Canonicalize platform frontend contracts 2026-08-24 18:56:56 +01:00
rcourtman 714b26b69f Canonicalize platform table row density 2026-08-24 16:19:21 +01:00
rcourtman 7e363e40e7 Make drawer headers full-width collapse targets 2026-08-24 15:40:29 +01:00
rcourtman 08e501bbc8 Unify drawer detail density 2026-08-24 14:59:02 +01:00
rcourtman 66fd37ee6f Simplify object drawer headers 2026-08-24 12:48:31 +01:00
rcourtman 9e622c291d Improve object drawer information hierarchy 2026-08-24 12:37:39 +01:00
rcourtman 66c87fb6e8 fix(frontend): place estate totals beside labels 2026-08-19 19:42:06 +01:00
rcourtman 4de1ede603 fix(frontend): make estate overview glanceable 2026-08-19 18:21:10 +01:00
rcourtman ba4a6c46d9 fix(frontend): fold estate totals into platform controls 2026-08-19 18:21:10 +01:00
rcourtman 85ddd9f7f9 feat: add canonical platform estate overviews 2026-08-19 17:25:39 +01:00
rcourtman 737bde7362 fix(ui): refine responsive platform tables
Contract-Neutral: responsive presentation and touch-target adjustments only; no API or payload changes
2026-08-06 15:55:11 +01:00
courtmanr@gmail.com a38d21bb86 feat(alerts): add initial delivery routing 2026-07-30 15:37:53 +01:00
rcourtman 478a9e933d Complete canonical web interface links
Preserve tenant-scoped metadata through partial URL updates and project stable URLs across runtime identities. Use a safe adjacent launch control across overview tables with desktop and mobile regression coverage.
2026-07-24 12:42:16 +01:00
rcourtman c8b1e9d07b Re-home Agent Doctor from a settings modal to a routed page
Agent Doctor now lives at /settings/infrastructure/agent-doctor as a
full page in the settings shell instead of a max-w-lg dialog stacked
over the Infrastructure workspace, which was cramped on phones. The
per-agent card pile becomes one fleet table (agent, system, status,
reported and target versions, last seen) with per-row expansions
holding the diagnosis reasons, identity evidence, repair actions, and
the host-local update command. A lone scoped target auto-expands so
platform-page deep links land straight on the diagnosis.

Legacy ?agentDoctor=1 / ?agentUpdates=1 workspace links redirect onto
the route with their agents scope preserved, and closing the page is
now ordinary back navigation. The dialog component is renamed to
InfrastructureAgentDoctorPage; shared-template registry, the
agent-lifecycle subsystem inventory, and the frontend-primitives
settings deep-link clause follow the change.
2026-07-22 10:26:06 +01:00
rcourtman 69533c7afb Build canonical availability resource facets 2026-07-19 15:11:01 +01:00
rcourtman 47d6ad94db test: fix 4 pre-existing frontend test failures blocking clean suite
recoveryDatePresentation: locale-independent date assertion (en-GB
systems produce '9 Mar 2026' not 'Mar 9, 2026')
quickstartCopyContract: update 'tools' to 'capabilities' matching
docs/AI.md copy change
AIIntelligence: update stale empty-state text to full current copy
SharedPrimitives.guardrails: align guardrail + registry with
d79640c41 migration of ProxmoxBackupsTable from FilterButtonGroup to
FilterSegmentedControl

Full suite: 659 files, 6719 tests, 0 failures.
2026-06-26 11:26:16 +01:00
rcourtman 27ba85ddc6 frontend: Patrol intelligence surface, shared primitives, and platform parity
Patrol operator UI (header, workspace, banners, findings, autonomy controls); convergence of shared primitives (buttons, spinners, notices, badges); settings information architecture (Pulse Intelligence, plans and billing, agent integrations); platform parity restoration across Proxmox, Kubernetes, Docker, storage, memory, and alerts; i18n foundation.
2026-06-23 17:15:58 +01:00
rcourtman 6559c37626 Converge AI Chat message actions on shared Button primitives 2026-06-13 23:48:55 +01:00
rcourtman dbb936e623 Converge standalone machine actions on ActionIconButton 2026-06-13 23:35:23 +01:00
rcourtman 52f094f11f Converge AI Chat icon actions on ActionIconButton 2026-06-13 23:27:33 +01:00
rcourtman 05f1a3298e Converge Patrol approval actions on shared Button primitives 2026-06-13 22:59:58 +01:00
rcourtman 85ae9ad540 Converge organization settings actions on shared Button primitives 2026-06-13 22:48:05 +01:00
rcourtman 418cf1648d Converge RBAC settings actions on shared Button primitives 2026-06-13 22:31:23 +01:00
rcourtman a418b8da27 Converge security auth actions on shared Button primitive 2026-06-13 22:20:03 +01:00
rcourtman e73420d4d5 Converge Proxmox backup view selector on shared primitive 2026-06-13 22:08:09 +01:00
rcourtman 55e058d1e6 Converge API token scope pills on shared selectable primitive 2026-06-13 21:52:18 +01:00
rcourtman 9578cd7368 Converge billing admin row actions on shared Button primitives 2026-06-13 21:37:30 +01:00
rcourtman 1af7d47792 Converge SSO actions on shared Button primitives 2026-06-13 21:23:21 +01:00
rcourtman 03b593461f Converge panel empty states on shared primitive 2026-06-13 21:11:57 +01:00
rcourtman c48dfa8261 Converge standalone metrics on platform table helper 2026-06-13 20:50:06 +01:00
rcourtman 66002e8ba7 Converge Pro license actions on shared Button primitives 2026-06-13 20:37:12 +01:00
rcourtman 51c47a2279 Converge GitHub star banner on shared action primitives 2026-06-13 20:24:17 +01:00
rcourtman 5330ce40c7 Converge commercial migration banner on shared notice primitive 2026-06-13 20:13:17 +01:00
rcourtman 0f58d9d9d4 Converge demo banner notice on InlineNotice primitive 2026-06-13 20:00:56 +01:00