Skip to content

feat(web): select diagnostics environment#4486

Open
colonelpanic8 wants to merge 3 commits into
pingdotgg:mainfrom
colonelpanic8:feat/diagnostics-environment-selector
Open

feat(web): select diagnostics environment#4486
colonelpanic8 wants to merge 3 commits into
pingdotgg:mainfrom
colonelpanic8:feat/diagnostics-environment-selector

Conversation

@colonelpanic8

@colonelpanic8 colonelpanic8 commented Jul 24, 2026

Copy link
Copy Markdown

What Changed

Adds an explicit diagnostics-source picker and routes every diagnostics request through the selected backend.

Why

Client-only mode has no primary local environment, so the diagnostics page previously had no backend to query. Diagnostics are backend-specific, not client-specific.

Behavior

  • Defaults to primary, then active, then first available environment.
  • Keeps an explicit machine selection while it remains connected.
  • Gives a clear Connections call to action when no environments are available.
  • Replaces indefinite loading states with an explanatory notice when the selected environment is not connected, and disables refresh / "Open logs folder" while it is unreachable.
  • Keys logs-folder errors and in-flight process signals by environment, so one machine's state never leaks into another's.

UI evidence

All shots come from the same build, light theme, and one 1800px-wide window. Two environments are configured: dev-workstation (this machine, connected) and demo-box (a saved remote backend that was deliberately shut down).

"Before" is acccd0e9f (this branch's pre-review-fix head); "after" is eb272e4f6. The branch has since been rebased onto upstream main as 0db411ea1815; that rebase changed no file content, so these captures still reflect the current head exactly.

1. Selecting an environment that is not connected

Diagnostics RPCs only resolve while the environment's supervisor is connected, so before this fix the page waited forever.

Before — acccd0e9f: every stat reads ..., every table sits on "Loading live processes…" / "Collecting process resource samples…" / "Loading failures…" / "Loading span names…" indefinitely, and all three refresh icons spin forever. "Open logs folder" is still enabled even though it cannot work.

Before: diagnostics for a disconnected environment load forever

After — eb272e4f6 (content-identical to current head 0db411ea1815): every stat reads , and every table explains the actual state ("Reconnecting to demo-box… Reason: …") instead of pretending to load. The refresh icons are static and disabled, and "Open logs folder" is disabled too.

After: diagnostics for a disconnected environment explain the connection state

The notice wording follows the connection phase, so a backend that is offline, available, connecting, or error gets its own message rather than the reconnecting one shown here.

2. The new "Diagnostics source" picker

This is what the PR adds; there is no equivalent on main, where the page always queried the primary backend.

Diagnostics collected from the selected backend, with its connection status and URL under the heading:

After: diagnostics page with the Diagnostics source row and live data

Picker open — machine choice is explicit, and local vs remote backends are distinguished by icon:

After: the environment picker open, listing both machines

Verification

  • vp test run apps/web/src/components/settings/DiagnosticsSettings.logic.test.ts
  • vp check (passes; existing repo warnings only)
  • Web integration: paired two isolated servers as separate environments, opened /settings/diagnostics, confirmed diagnostics are collected from whichever machine is selected, then killed the remote backend and re-checked the page on both the pre-fix and current commits to produce the before/after images above.
  • vp run typecheck is blocked by an incomplete local dependency tree (apps/marketing cannot find astro); focused web typecheck also reports 10 pre-existing errors outside this change.

Note

Low Risk
Settings UI and client-side state only; no auth or server contract changes. Process kill still goes through existing signalProcess RPCs for the selected environment.

Overview
Adds a Diagnostics source picker so process, resource, and trace diagnostics target a chosen backend instead of always using the primary server. Resolution order is explicit selection (while still available), then primary, active, then first listed environment; with no environments, the page links to Manage connections.

When the selected machine is not connected, the page shows phase-specific notices (via diagnosticsConnectionNotice) instead of endless loading spinners, uses for stats, and disables refresh and open-logs actions. Observability and queries are read from the selected environment’s config.

Panel state is scoped per environment: logs-folder open/errors and in-flight process signals use environment+pid keys so switching machines does not leak UI state. Logic lives in DiagnosticsSettings.logic.ts with unit tests.

Reviewed by Cursor Bugbot for commit 0db411e. Bugbot is set up for automated code reviews on this repo. Configure here.

Note

Add environment selection and connection-aware controls to diagnostics settings

  • Adds an environment picker to DiagnosticsSettingsPanel so users can target a specific environment for diagnostics, resolved via resolveDiagnosticsEnvironmentId (selected > primary > active > first available).
  • Introduces connection-aware UI: refresh and log-folder actions are disabled when the environment is not connected, stats show placeholders instead of spinners, and a diagnosticsConnectionNotice message explains non-connected states.
  • Replaces single signalingPid tracking with a per-environment, per-pid PendingProcessSignal array, allowing multiple in-flight process signals to be tracked concurrently.
  • Behavioral Change: ProcessDiagnosticsTable now receives signalingPids: ReadonlySet<number> instead of signalingPid: number | null, which is a breaking prop change for any direct consumers.

Macroscope summarized 0db411e.

@coderabbitai

coderabbitai Bot commented Jul 24, 2026

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: fc90c46a-6cb6-47e4-bcd1-9b8ff1522837

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@github-actions github-actions Bot added vouch:unvouched PR author is not yet trusted in the VOUCHED list. size:L 100-499 changed lines (additions + deletions). labels Jul 24, 2026
Comment thread apps/web/src/components/settings/DiagnosticsSettings.tsx
Comment thread apps/web/src/components/settings/DiagnosticsSettings.tsx
Comment thread apps/web/src/components/settings/DiagnosticsSettings.tsx
Comment thread apps/web/src/components/settings/DiagnosticsSettings.tsx
@macroscopeapp

macroscopeapp Bot commented Jul 24, 2026

Copy link
Copy Markdown
Contributor

Approvability

Verdict: Needs human review

This PR introduces a new feature (environment selection for diagnostics) with significant UI and state management changes. An unresolved medium-severity bug regarding refresh button behavior while disconnected has been identified, warranting human review.

You can customize Macroscope's approvability policy. Learn more.

<DiagnosticsLastChecked checkedAt={processData?.readAt ?? null} />
<DiagnosticsRefreshButton
isPending={isProcessPending}
isPending={isProcessPending && isConnected}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Refresh enabled while disconnected

Medium Severity

Passing isPending && isConnected into DiagnosticsRefreshButton stops the spinner when the backend is down, but also clears disabled because that prop alone gates both states. Disconnected diagnostics queries stay on Effect.never, so refresh stays clickable with no progress. Open logs in the same change correctly uses an explicit !isConnected disable.

Additional Locations (2)
Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 60f3a62. Configure here.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Correct, and fixed — the spinner and the disabled state were the same prop, so suppressing the spinner while disconnected also re-enabled the button.

DiagnosticsRefreshButton now takes a separate isDisabled alongside isPending, and renders disabled={isPending || isDisabled}. All three refresh buttons pass isDisabled={!isConnected}, so while an environment is disconnected they are static and disabled rather than static but clickable. "Open logs folder" is disabled on the same condition, so it can't hang on Effect.never either.

current !== null && current.environmentId === environmentId
? { environmentId, isOpening: false, error }
: current,
);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Logs open completion race

Low Severity

The open-logs completion updater only checks that current.environmentId matches the request’s environment. A single shared logsDirectoryState slot can be overwritten by another environment, so returning to the original environment and starting a new open while the first request is still in flight lets the older completion clear the newer pending isOpening state.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 60f3a62. Configure here.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed, and fixed. Matching the completion by environment id was the wrong key — a second request for the same environment could still clobber an earlier one.

The logs-directory state is now a single LogsDirectoryState { environmentId, isOpening, error } record, and it only renders when state.environmentId === environmentId, so nothing leaks across an environment switch. The async completion matches its own request by object identity rather than by environment id, so neither a later request in another environment nor a later request in the same environment can retire the wrong one.

colonelpanic8 and others added 3 commits July 24, 2026 20:20
The diagnostics environment picker made three pieces of panel-local state
lie about which machine they belong to:

- `openLogsDirectoryError` / `isOpeningLogsDirectory` kept showing an
  environment A failure (or spinner) after switching to environment B.
  They are now stored together with the environment they belong to, so
  they only render for that environment and a late completion can only
  update its own request.
- `signalingPid` was a single shared value, so completing a signal on
  environment A re-enabled the signal controls for an in-flight signal on
  environment B and allowed a duplicate SIGKILL. Pending signals are now
  identified by environment id plus pid.
- Diagnostics queries only run while the selected environment's
  supervisor is connected, so a disconnected machine showed permanent
  "Loading..." placeholders with the refresh buttons disabled. The panel
  now derives a connection notice and renders it instead of the loading
  state.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Follow-up to the review feedback on the previous commit:

- `DiagnosticsRefreshButton` used a single `isPending` prop for both the
  spinner and `disabled`, so suppressing the spinner while the selected
  environment is disconnected also made the button clickable even though
  the query cannot run. It now takes an explicit `isDisabled` prop.
- The open-logs completion matched on environment id alone, so returning
  to an environment and starting a second open while the first was still
  in flight let the older completion clear the newer pending state. The
  completion now matches the request by identity.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@colonelpanic8
colonelpanic8 force-pushed the feat/diagnostics-environment-selector branch from eb272e4 to 0db411e Compare July 25, 2026 03:23

@cursor cursor Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes using high effort and found 2 potential issues.

There are 4 total unresolved issues (including 2 from previous reviews).

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 0db411e. Configure here.

(isProcessInitialLoading
? "Loading live processes..."
: "No live descendant processes found."
: "No live descendant processes found.")

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Stale diagnostics while disconnected

High Severity

The useEnvironmentQuery hook retains cached data after an environment disconnects. This causes the diagnostics UI to display stale metrics and process lists, keeps actions like process signaling enabled, and prevents the intended disconnected state indicators (like or connection notices) from appearing.

Additional Locations (1)
Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 0db411e. Configure here.

}: {
processes: ReadonlyArray<ServerProcessDiagnosticsEntry>;
signalingPid: number | null;
signalingPids: ReadonlySet<number>;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Collapse state leaks across environments

Low Severity

ProcessDiagnosticsTable keeps collapsedPids in component state and is rendered without a key tied to the selected environment. Switching the diagnostics source therefore reuses prior collapse choices, so a PID collapsed on one machine can hide the same PID on another.

Additional Locations (1)
Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 0db411e. Configure here.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:L 100-499 changed lines (additions + deletions). vouch:unvouched PR author is not yet trusted in the VOUCHED list.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant