Skip to content

Reduce renderer polling for resource and terminal surfaces#4581

Merged
Kitenite merged 7 commits into
mainfrom
debug-renderer-crash-regr
May 16, 2026
Merged

Reduce renderer polling for resource and terminal surfaces#4581
Kitenite merged 7 commits into
mainfrom
debug-renderer-crash-regr

Conversation

@Kitenite
Copy link
Copy Markdown
Collaborator

@Kitenite Kitenite commented May 14, 2026

Summary

Reduces renderer work from polling-heavy surfaces without changing workspace switching behavior. The PR keeps expensive data fetching cold until the relevant UI is opened, adds a lightweight background terminal count path for the closed tab-bar button, and preserves fast explicit background-terminal UX with a local optimistic marker.

What changed

  • Resource monitor no longer mounts or polls metrics while closed; it fetches every 2s only while the popover is open.
  • Background terminals no longer run the full session list while closed. The closed button uses a shallow countBackgroundSessions query every 10s, and the full list runs every 2s only while the dropdown is open.
  • Explicitly backgrounded terminals show immediately via a workspace-scoped optimistic marker, then reconcile against the count/list results.
  • Terminal session dropdown no longer polls session lists while closed.
  • Sidebar live queries avoid expensive host joins and add targeted local collection indexes for the dashboard/sidebar data paths.
  • Local setup prepares Electric env before writing .env, detects occupied Electric ports, and stops inactive Electric containers on startup failure.
  • Dev-only stress instrumentation logs high renderer commit rates and forwards useful renderer/main-process diagnostics while developing.

Notes

Passive background terminal creation is detected by the shallow closed-state poll, so it can take up to 10s to appear. Explicit “move to background” actions still appear immediately.

Validation

  • bun test 'apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/components/BackgroundTerminalsButton/BackgroundTerminalsButton.utils.test.ts'
  • bun run lint
  • bun run typecheck
  • Host-service terminal e2e previously passed on this branch: bun run test:e2e in packages/host-service

@capy-ai
Copy link
Copy Markdown

capy-ai Bot commented May 14, 2026

Capy auto-review is paused for this organization because the monthly auto-review limit has been reached. Increase the limit or turn it off in billing settings to resume automatic reviews.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 14, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Adds developer-facing render/stress instrumentation and structured logging, refactors background-terminal polling and counting (client + host-service), introduces remote-host status policy and unavailable UI, gates resource-monitor querying with a policy, adds workspace-client idle eviction and ref counting, updates collection indices, and miscellaneous test/shell/script/compatibility fixes.

Changes

Renderer dev instrumentation & main-process logging

Layer / File(s) Summary
Electron CLI switch (dev-only)
apps/desktop/src/lib/electron-app/factories/app/setup.ts
Adds conditional --remote-debugging-port switch when env.NODE_ENV === "development" and process.env.RENDERER_REMOTE_DEBUG_PORT is set.
Main window structured logging
apps/desktop/src/main/windows/main.ts
Imports electron-log/main, forwards renderer console-message and responsiveness events to electron-log, and switches render-process-gone reporting to log.error.
Render stress helpers
apps/desktop/src/renderer/lib/performance/stress-instrumentation.ts
Adds useRenderStressInstrumentation(name, options) hook and logStressEvent helper; both are gated to development builds.

Background terminals: query, UI, and host-service count

Layer / File(s) Summary
Background terminal utilities
.../BackgroundTerminalsButton/BackgroundTerminalsButton.utils.ts, .../BackgroundTerminalsButton/BackgroundTerminalsButton.utils.test.ts
Adds debounce/refetch constants, stable attached-terminal-IDs key generation/parsing, filtering/sorting of background sessions, and tests for these utilities and intervals.
BackgroundTerminalsButton component
.../BackgroundTerminalsButton/BackgroundTerminalsButton.tsx
Refactors into a memo-wrapped component; derives attached IDs via debounce; uses separate TRPC queries for closed-state count and open-state list; adoption/kill handlers invalidate both queries; adds loading/empty states and render-stress instrumentation.
Terminal session dropdown polling policy
.../TerminalSessionDropdown.utils.ts, .../TerminalSessionDropdown.utils.test.ts, .../TerminalSessionDropdown.tsx
Adds constants and helpers to enable polling only when dropdown is open; wires query enabled/refetchInterval/staleTime via helpers and adds render-stress instrumentation; tests added.
Host-service session counting and TRPC
packages/host-service/src/terminal/terminal.ts, packages/host-service/src/trpc/router/terminal/terminal.ts
Adds countTerminalSessions helper and exposes protected TRPC terminal.countBackgroundSessions(workspaceId, attachedTerminalIds) that returns non-exited background session counts excluding attached IDs.
Integration test updates
packages/host-service/test/integration/terminal.integration.test.ts
Integration test now asserts countBackgroundSessions behavior for newly created sessions (detached vs attached).

Remote host status policy and host-unavailable UI

Layer / File(s) Summary
Policy: remote host status
.../remoteHostStatusPolicy.ts, .../remoteHostStatusPolicy.test.ts
Adds RemoteHostStatus types and getRemoteHostStatus(input) with ordered short-circuit logic (loading, skip, unavailable, incompatible, ready) and tests covering readiness/compatibility scenarios.
Hook delegates to policy
.../useRemoteHostStatus.ts, .../index.ts
Refactors useRemoteHostStatus to delegate status derivation to getRemoteHostStatus, adjusts query enablement and imports/exports.
Unavailable UI
.../WorkspaceHostUnavailableState/WorkspaceHostUnavailableState.tsx, .../index.ts
Adds WorkspaceHostUnavailableState component and re-exports it from the index.
Layout wiring
apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/layout.tsx
Registers render-stress instrumentation for workspace host status, logs host-status changes, renders WorkspaceHostUnavailableState for status === "unavailable", and sets WorkspaceProvider key to workspace.id.

Resource monitor policy and UI refactor

Layer / File(s) Summary
Polling policy
.../resourceConsumptionPolicy.ts, .../resourceConsumptionPolicy.test.ts
Adds RESOURCE_MONITOR_REFETCH_INTERVAL_MS, shouldQueryResourceMonitor({enabled, open, metadataReady}), and getResourceMonitorRefetchInterval(open) plus tests validating open/closed behavior.
Component refactor & gating
.../ResourceConsumption/ResourceConsumption.tsx
Parent component now queries getShowResourceMonitor and returns null if disabled; refactors content to use policy helpers for query enablement/refetch interval; adds render-stress instrumentation; reorganizes Popover trigger/content and updates navigation handlers to call onClose().

Dashboard sidebar data and hotkey behaviors

Layer / File(s) Summary
Host lookup memoization
.../useDashboardSidebarData.ts
Derives hostsByMachineId from live hosts query and enriches sidebar workspace rows with hostIsOnline via that map; updates memo dependencies accordingly.
Relative workspace hotkeys
.../useDashboardSidebarShortcuts.utils.ts, .../useDashboardSidebarShortcuts.utils.test.ts, .../useDashboardSidebarShortcuts.ts
Adds WORKSPACE_SWITCH_* timing constants, shouldRunWorkspaceSwitchHotkey, getRelativeWorkspaceTarget, tests for these utils, and refactors hotkey handlers to throttle, coalesce rapid repeats, queue deferred switches, and log stress events for navigations and coalesced drops.

Workspace client cache & eviction policy

Layer / File(s) Summary
Cache policy primitives
packages/workspace-client/src/providers/WorkspaceClientProvider/workspaceClientCachePolicy.ts, .../workspaceClientCachePolicy.test.ts
Adds MAX_WORKSPACE_CLIENT_CACHE_ENTRIES, WORKSPACE_CLIENT_IDLE_DISPOSE_MS, WorkspaceClientCachePolicyEntry and getIdleWorkspaceClientEvictionKeys(...) with tests covering eviction ordering and protected-key exclusion.
Provider: ref counting and idle disposal
packages/workspace-client/src/providers/WorkspaceClientProvider/WorkspaceClientProvider.tsx
Extends cached client entries with activeRefs, lastAccessedAt, disposeTimer; updates reuse/creation logic to manage last-access and schedule disposal; uses retainWorkspaceClients in an effect; reduces React Query gcTime from 30 to 5 minutes.

Collection indices and small infra updates

Layer / File(s) Summary
Collections indices
apps/desktop/src/renderer/routes/_authenticated/providers/CollectionsProvider/collections.ts
Imports BasicIndex from @tanstack/db, introduces basicIndexConfig, and creates explicit indices on multiple v2 organization and local-storage collections (projects, hosts, usersHosts, workspaces, sidebar projects/sections, workspace local state).
turbo env passthrough
turbo.jsonc
Adds RENDERER_REMOTE_DEBUG_PORT to globalPassThroughEnv.
Superset Electric scripts
.superset/lib/setup/steps.sh, .superset/lib/setup/main.sh
Adds step_prepare_electric(), reorders main setup steps, adds port-in-use guard, and ensures Electric container is stopped on readiness failure.

Host-service compatibility & tests

Layer / File(s) Summary
Headless terminal ESM compatibility
packages/host-service/src/terminal/terminal-mode-tracker.ts
Replaces static @xterm/headless import with createRequire(import.meta.url) require and re-exports Terminal as HeadlessTerminal.
Adoption tests: disposal & assertions
packages/host-service/src/terminal/terminal.adoption.node-test.ts
Switches test cleanup to disposeSessionAndWait, sets test shell to /bin/sh during setup, uses assert.fail(...) for returned errors, broadens orphan-workspace error regex, and relaxes replay verification to absence of prior sentinel in buffer.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Poem

🐰 I wired logs and stress to watch the panes,
Debounced the hops and counted stray trains.
Hosts now tell when they’re gone or unfit,
Idle clients sleep — indices to commit.
A tidy hop — diagnostics hum along again.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 2.44% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Title check ✅ Passed The PR title accurately captures the main optimization: reducing renderer polling for resource and terminal surfaces, which aligns with the primary changeset objectives.
Description check ✅ Passed The PR provides a comprehensive description covering summary, changes, notes, and validation details. However, it does not follow the provided template structure with sections like 'Related Issues', 'Type of Change', 'Testing', and 'Screenshots'.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch debug-renderer-crash-regr

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🧹 Nitpick comments (2)
packages/workspace-client/src/providers/WorkspaceClientProvider/workspaceClientCachePolicy.ts (1)

10-22: 💤 Low value

Consider documenting the best-effort eviction behavior.

The eviction logic correctly prioritizes idle entries and protects active clients, but when entries.length > maxEntries and insufficient idle entries exist (e.g., most entries have activeRefs > 0), the cache will temporarily exceed maxEntries. This is the correct behavior—you cannot evict clients in use—but a brief comment would clarify the intent for future maintainers.

📝 Optional clarifying comment
 export function getIdleWorkspaceClientEvictionKeys(
 	entries: readonly WorkspaceClientCachePolicyEntry[],
 	maxEntries = MAX_WORKSPACE_CLIENT_CACHE_ENTRIES,
 	protectedKey?: string,
 ): string[] {
 	if (entries.length <= maxEntries) return [];
 
+	// Best-effort eviction: only idle entries (activeRefs === 0) are eligible.
+	// Cache may temporarily exceed maxEntries if most entries are active.
 	const idleEntries = entries
 		.filter((entry) => entry.activeRefs === 0 && entry.key !== protectedKey)
 		.sort((left, right) => left.lastAccessedAt - right.lastAccessedAt);
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@packages/workspace-client/src/providers/WorkspaceClientProvider/workspaceClientCachePolicy.ts`
around lines 10 - 22, Add a brief comment to getIdleWorkspaceClientEvictionKeys
explaining that eviction is best-effort: the function only evicts idle entries
(entries.filtered by activeRefs === 0 and not matching protectedKey) and
therefore if there are fewer idle entries than evictionCount the cache may
temporarily exceed maxEntries until active clients become idle; mention the
protectedKey behavior and that this is intentional to avoid evicting in-use
clients.
packages/workspace-client/test/workspaceClientCachePolicy.test.ts (1)

14-27: ⚡ Quick win

Expand test coverage for edge cases.

The current tests validate the happy paths, but several edge cases remain untested:

  • All entries active: What happens when entries.length > maxEntries but every entry has activeRefs > 0? (Expected: returns [], cache exceeds limit temporarily)
  • Protected key is the only idle entry: Verify that eviction returns [] when the sole idle entry is protected.
  • Empty entries array: Boundary check for entries.length === 0.
  • Tie-breaking: Multiple entries with identical lastAccessedAt values.

These cases would clarify the best-effort eviction contract and prevent future regressions.

🧪 Proposed additional test cases
+	test("does not evict when all entries are active", () => {
+		expect(
+			getIdleWorkspaceClientEvictionKeys(
+				[
+					{ key: "active-1", activeRefs: 1, lastAccessedAt: 1 },
+					{ key: "active-2", activeRefs: 2, lastAccessedAt: 2 },
+					{ key: "active-3", activeRefs: 1, lastAccessedAt: 3 },
+				],
+				2,
+			),
+		).toEqual([]);
+	});
+
+	test("does not evict when only idle entry is protected", () => {
+		expect(
+			getIdleWorkspaceClientEvictionKeys(
+				[
+					{ key: "active", activeRefs: 1, lastAccessedAt: 1 },
+					{ key: "protected-idle", activeRefs: 0, lastAccessedAt: 2 },
+				],
+				1,
+				"protected-idle",
+			),
+		).toEqual([]);
+	});
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/workspace-client/test/workspaceClientCachePolicy.test.ts` around
lines 14 - 27, Add tests for edge cases to workspaceClientCachePolicy.test.ts by
extending the existing suite that calls getIdleWorkspaceClientEvictionKeys: (1)
assert when entries.length > maxEntries but every entry has activeRefs > 0 the
function returns [] (cache can temporarily exceed limit), (2) assert when the
only idle entry matches the protected key the function returns [], (3) assert an
empty entries array returns [], and (4) assert deterministic tie-breaking when
multiple entries share identical lastAccessedAt (e.g., ensure stable ordering or
expected keys are evicted). Use the same test structure as the "evicts least
recently used idle clients first" test and reference
getIdleWorkspaceClientEvictionKeys for each case.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In
`@apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/components/WorkspaceHostUnavailableState/WorkspaceHostUnavailableState.tsx`:
- Around line 41-43: The heading in WorkspaceHostUnavailableState (component
WorkspaceHostUnavailableState and the JSX h1 that renders {title}) is not
selectable; update the h1 element's className to include the error-text policy
classes "select-text cursor-text" (e.g., append " select-text cursor-text" to
the existing className string) so users can copy the error heading even when
body selection is disabled.

In `@packages/workspace-client/test/workspaceClientCachePolicy.test.ts`:
- Around line 1-28: Move the test so it lives next to the implementation of
getIdleWorkspaceClientEvictionKeys (co-locate the .test.ts with the source
file), then update the import in the moved test to import
getIdleWorkspaceClientEvictionKeys from the module file using a relative path
from the new test location, and delete the original test in the test/ directory;
ensure the test filename retains the .test.ts suffix and the test continues to
reference the getIdleWorkspaceClientEvictionKeys symbol unchanged.

---

Nitpick comments:
In
`@packages/workspace-client/src/providers/WorkspaceClientProvider/workspaceClientCachePolicy.ts`:
- Around line 10-22: Add a brief comment to getIdleWorkspaceClientEvictionKeys
explaining that eviction is best-effort: the function only evicts idle entries
(entries.filtered by activeRefs === 0 and not matching protectedKey) and
therefore if there are fewer idle entries than evictionCount the cache may
temporarily exceed maxEntries until active clients become idle; mention the
protectedKey behavior and that this is intentional to avoid evicting in-use
clients.

In `@packages/workspace-client/test/workspaceClientCachePolicy.test.ts`:
- Around line 14-27: Add tests for edge cases to
workspaceClientCachePolicy.test.ts by extending the existing suite that calls
getIdleWorkspaceClientEvictionKeys: (1) assert when entries.length > maxEntries
but every entry has activeRefs > 0 the function returns [] (cache can
temporarily exceed limit), (2) assert when the only idle entry matches the
protected key the function returns [], (3) assert an empty entries array returns
[], and (4) assert deterministic tie-breaking when multiple entries share
identical lastAccessedAt (e.g., ensure stable ordering or expected keys are
evicted). Use the same test structure as the "evicts least recently used idle
clients first" test and reference getIdleWorkspaceClientEvictionKeys for each
case.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 55f99952-79a6-407c-a3de-a9ab74498137

📥 Commits

Reviewing files that changed from the base of the PR and between f95b8f4 and add8428.

📒 Files selected for processing (31)
  • apps/desktop/src/lib/electron-app/factories/app/setup.ts
  • apps/desktop/src/main/windows/main.ts
  • apps/desktop/src/renderer/lib/performance/stress-instrumentation.ts
  • apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/DashboardSidebar/hooks/useDashboardSidebarData/useDashboardSidebarData.ts
  • apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/DashboardSidebar/hooks/useDashboardSidebarShortcuts/useDashboardSidebarShortcuts.ts
  • apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/DashboardSidebar/hooks/useDashboardSidebarShortcuts/useDashboardSidebarShortcuts.utils.test.ts
  • apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/DashboardSidebar/hooks/useDashboardSidebarShortcuts/useDashboardSidebarShortcuts.utils.ts
  • apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/TopBar/components/ResourceConsumption/ResourceConsumption.tsx
  • apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/TopBar/components/ResourceConsumption/resourceConsumptionPolicy.test.ts
  • apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/TopBar/components/ResourceConsumption/resourceConsumptionPolicy.ts
  • apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/components/BackgroundTerminalsButton/BackgroundTerminalsButton.tsx
  • apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/components/BackgroundTerminalsButton/BackgroundTerminalsButton.utils.test.ts
  • apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/components/BackgroundTerminalsButton/BackgroundTerminalsButton.utils.ts
  • apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/hooks/usePaneRegistry/components/TerminalPane/components/TerminalSessionDropdown/TerminalSessionDropdown.tsx
  • apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/hooks/usePaneRegistry/components/TerminalPane/components/TerminalSessionDropdown/TerminalSessionDropdown.utils.test.ts
  • apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/hooks/usePaneRegistry/components/TerminalPane/components/TerminalSessionDropdown/TerminalSessionDropdown.utils.ts
  • apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/components/WorkspaceHostUnavailableState/WorkspaceHostUnavailableState.tsx
  • apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/components/WorkspaceHostUnavailableState/index.ts
  • apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/hooks/useRemoteHostStatus/index.ts
  • apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/hooks/useRemoteHostStatus/remoteHostStatusPolicy.test.ts
  • apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/hooks/useRemoteHostStatus/remoteHostStatusPolicy.ts
  • apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/hooks/useRemoteHostStatus/useRemoteHostStatus.ts
  • apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/layout.tsx
  • apps/desktop/src/renderer/routes/_authenticated/providers/CollectionsProvider/collections.ts
  • packages/host-service/src/terminal/terminal.ts
  • packages/host-service/src/trpc/router/terminal/terminal.ts
  • packages/host-service/test/integration/terminal.integration.test.ts
  • packages/workspace-client/src/providers/WorkspaceClientProvider/WorkspaceClientProvider.tsx
  • packages/workspace-client/src/providers/WorkspaceClientProvider/workspaceClientCachePolicy.ts
  • packages/workspace-client/test/workspaceClientCachePolicy.test.ts
  • turbo.jsonc

Comment on lines +1 to +28
import { describe, expect, test } from "bun:test";
import { getIdleWorkspaceClientEvictionKeys } from "../src/providers/WorkspaceClientProvider/workspaceClientCachePolicy";

describe("workspace client cache policy", () => {
test("does not evict when the cache is under the limit", () => {
expect(
getIdleWorkspaceClientEvictionKeys([
{ key: "a", activeRefs: 0, lastAccessedAt: 1 },
{ key: "b", activeRefs: 0, lastAccessedAt: 2 },
]),
).toEqual([]);
});

test("evicts least recently used idle clients first", () => {
expect(
getIdleWorkspaceClientEvictionKeys(
[
{ key: "active-old", activeRefs: 1, lastAccessedAt: 1 },
{ key: "idle-old", activeRefs: 0, lastAccessedAt: 2 },
{ key: "idle-new", activeRefs: 0, lastAccessedAt: 3 },
{ key: "protected", activeRefs: 0, lastAccessedAt: 4 },
],
2,
"protected",
),
).toEqual(["idle-old", "idle-new"]);
});
});
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.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Relocate test file to follow co-location guideline.

The coding guidelines require test files to be co-located with their source files using a .test.ts suffix. This test file should be moved from:

packages/workspace-client/test/workspaceClientCachePolicy.test.ts

to:

packages/workspace-client/src/providers/WorkspaceClientProvider/workspaceClientCachePolicy.test.ts

Other test files in the codebase follow this pattern (e.g., resourceConsumptionPolicy.test.ts, remoteHostStatusPolicy.test.ts). As per coding guidelines, co-locate test files with their source files using .test.ts or .test.tsx suffix.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/workspace-client/test/workspaceClientCachePolicy.test.ts` around
lines 1 - 28, Move the test so it lives next to the implementation of
getIdleWorkspaceClientEvictionKeys (co-locate the .test.ts with the source
file), then update the import in the moved test to import
getIdleWorkspaceClientEvictionKeys from the module file using a relative path
from the new test location, and delete the original test in the test/ directory;
ensure the test filename retains the .test.ts suffix and the test continues to
reference the getIdleWorkspaceClientEvictionKeys symbol unchanged.

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 14, 2026

🧹 Preview Cleanup Complete

The following preview resources have been cleaned up:

  • ⚠️ Neon database branch

Thank you for your contribution! 🎉

@greptile-apps
Copy link
Copy Markdown

greptile-apps Bot commented May 14, 2026

Greptile Summary

This PR addresses a renderer crash triggered by rapid workspace switching by combining source-level backpressure, bounded client lifecycles, lazy polling, and clean subtree teardown.

  • Hotkey throttle: Repeated prev/next workspace events are coalesced behind a 160 ms dual-gate (in-flight flag + elapsed check) and navigated with replace to avoid bloating the route history stack.
  • Bounded workspace client lifecycle: Each per-workspace tRPC and query client is now ref-counted; idle clients schedule a 60-second disposal and an 8-entry LRU eviction prevents unbounded accumulation across many workspace visits.
  • Cold polling: ResourceConsumption, BackgroundTerminalsButton, and TerminalSessionDropdown each split into a lightweight count query (while closed) and a full list query (only while open), eliminating background polling when the UI is hidden.
  • Clean subtree remount: The workspace provider subtree now carries a stable identity derived from the workspace identifier, guaranteeing that all subscriptions and effects tear down completely between workspace switches; a new unavailable-host state surfaces the offline and unreachable cases.

Confidence Score: 4/5

Safe to merge; the crash path is well-targeted and the changes are tested. One architectural concern in the client cache is worth addressing before the pattern spreads.

The hotkey throttle, forced subtree remount, cold-polling splits, and LRU eviction all address the described crash path correctly and are backed by focused unit tests. The one concern is that getWorkspaceClients cancels dispose timers and mutates the LRU cache during the React render phase — if a render is interrupted or replayed, the timer is gone but retainWorkspaceClients has not yet run, leaving a workspace client with zero active refs and no scheduled cleanup. Impact is bounded by the 8-entry LRU limit, so this is not a correctness regression, but it is a lifecycle invariant worth tightening.

packages/workspace-client/src/providers/WorkspaceClientProvider/WorkspaceClientProvider.tsx — the getWorkspaceClients render-time side effects (timer cancellation, eviction) are the main area to revisit.

Important Files Changed

Filename Overview
packages/workspace-client/src/providers/WorkspaceClientProvider/WorkspaceClientProvider.tsx Adds ref-counted LRU cache with idle dispose timers for per-workspace clients; getWorkspaceClients mutates cache and cancels timers during the render phase, which is unsafe under Strict Mode and concurrent rendering.
packages/workspace-client/src/providers/WorkspaceClientProvider/workspaceClientCachePolicy.ts New LRU eviction policy for workspace client cache; logic is correct, evicts oldest idle entries first when over the 8-entry cap, protects the current client from self-eviction.
apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/DashboardSidebar/hooks/useDashboardSidebarShortcuts/useDashboardSidebarShortcuts.ts Adds dual-gate backpressure (in-flight flag + 160 ms throttle) to prev/next workspace hotkeys; timer is properly cleaned up on unmount; uses replace to avoid bloating browser history.
apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/DashboardSidebar/hooks/useDashboardSidebarShortcuts/useDashboardSidebarShortcuts.utils.ts New pure utilities for hotkey throttle and workspace target resolution; well-tested and straightforward.
apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/hooks/useRemoteHostStatus/remoteHostStatusPolicy.ts New policy function extracts host-status derivation logic; adds offline/unreachable distinction and uses strict equality check so null/unknown online status correctly falls through to the connectivity-probe path.
apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/TopBar/components/ResourceConsumption/ResourceConsumption.tsx Splits into a cold trigger and a lazy-mounted content component so polling only runs while open; hardcoded enabled and open params in shouldQueryResourceMonitor are harmless but misleading.
apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/components/BackgroundTerminalsButton/BackgroundTerminalsButton.tsx Splits into separate count query (closed) and list query (open) for cold-start; debounces attached-terminal key to avoid spurious refetches; memo comparison is correct.
apps/desktop/src/renderer/routes/_authenticated/providers/CollectionsProvider/collections.ts Adds BasicIndex secondary indexes on frequently-joined columns (hostId, projectId, machineId, tabOrder) and moves BasicIndex import to core @tanstack/db package.
packages/host-service/src/terminal/terminal.ts New countTerminalSessions function mirrors listTerminalSessions logic but returns a count; correctly filters by listed, workspaceId, exited, and excluded IDs.
apps/desktop/src/renderer/lib/performance/stress-instrumentation.ts New dev-only render-rate counter and stress event logger; gated on NODE_ENV check so zero production overhead.

Comments Outside Diff (1)

  1. packages/workspace-client/src/providers/WorkspaceClientProvider/WorkspaceClientProvider.tsx, line 111-122 (link)

    P2 Dispose-timer mutation during render phase

    getWorkspaceClients calls clearTimeout and nulls disposeTimer synchronously during the render phase. In React 18 Strict Mode, renders are invoked, cleaned up, then invoked again — so the dispose timer is cancelled during the first (discarded) render, but the corresponding retainWorkspaceClients effect only runs after commit of the second render. The gap means the client can sit with activeRefs = 0 and no scheduled cleanup timer until the effect fires. In concurrent mode an interrupted render widens this window further.

    Moving the clearTimeout / timer-cancel logic inside retainWorkspaceClients (so it runs only in the committed effect, not during render) would make the lifetime contract safe regardless of how many times React calls the render function.

    Prompt To Fix With AI
    This is a comment left during a code review.
    Path: packages/workspace-client/src/providers/WorkspaceClientProvider/WorkspaceClientProvider.tsx
    Line: 111-122
    
    Comment:
    **Dispose-timer mutation during render phase**
    
    `getWorkspaceClients` calls `clearTimeout` and nulls `disposeTimer` synchronously during the render phase. In React 18 Strict Mode, renders are invoked, cleaned up, then invoked again — so the dispose timer is cancelled during the first (discarded) render, but the corresponding `retainWorkspaceClients` effect only runs after commit of the second render. The gap means the client can sit with `activeRefs = 0` and no scheduled cleanup timer until the effect fires. In concurrent mode an interrupted render widens this window further.
    
    Moving the `clearTimeout` / timer-cancel logic inside `retainWorkspaceClients` (so it runs only in the committed effect, not during render) would make the lifetime contract safe regardless of how many times React calls the render function.
    
    How can I resolve this? If you propose a fix, please make it concise.
Prompt To Fix All With AI
Fix the following 2 code review issues. Work through them one at a time, proposing concise fixes.

---

### Issue 1 of 2
packages/workspace-client/src/providers/WorkspaceClientProvider/WorkspaceClientProvider.tsx:111-122
**Dispose-timer mutation during render phase**

`getWorkspaceClients` calls `clearTimeout` and nulls `disposeTimer` synchronously during the render phase. In React 18 Strict Mode, renders are invoked, cleaned up, then invoked again — so the dispose timer is cancelled during the first (discarded) render, but the corresponding `retainWorkspaceClients` effect only runs after commit of the second render. The gap means the client can sit with `activeRefs = 0` and no scheduled cleanup timer until the effect fires. In concurrent mode an interrupted render widens this window further.

Moving the `clearTimeout` / timer-cancel logic inside `retainWorkspaceClients` (so it runs only in the committed effect, not during render) would make the lifetime contract safe regardless of how many times React calls the render function.

### Issue 2 of 2
apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/TopBar/components/ResourceConsumption/ResourceConsumption.tsx:255-259
**`shouldQueryResourceMonitor` params are hardcoded — function is a no-op wrapper here**

Both `enabled` and `open` are hardcoded to `true`, so `shouldQueryResourceMonitor` is equivalent to `metadataReady` at this call site. The outer `ResourceConsumption` component already guards against `enabled=false` and `open=false`, so the behaviour is correct, but the policy function's parameters become dead code here and could mislead future readers into thinking the inner component re-validates those conditions independently.

```suggestion
	// ResourceConsumptionContent is only mounted when enabled=true and open=true
	// (enforced by the parent ResourceConsumption component), so we only need to
	// gate on metadata readiness here.
	const shouldQueryMetrics = v2MetadataReady;
```

Reviews (1): Last reviewed commit: "fix(desktop): stabilize rapid workspace ..." | Re-trigger Greptile

Comment on lines +255 to +259
const shouldQueryMetrics = shouldQueryResourceMonitor({
enabled: true,
open: true,
metadataReady: v2MetadataReady,
});
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 shouldQueryResourceMonitor params are hardcoded — function is a no-op wrapper here

Both enabled and open are hardcoded to true, so shouldQueryResourceMonitor is equivalent to metadataReady at this call site. The outer ResourceConsumption component already guards against enabled=false and open=false, so the behaviour is correct, but the policy function's parameters become dead code here and could mislead future readers into thinking the inner component re-validates those conditions independently.

Suggested change
const shouldQueryMetrics = shouldQueryResourceMonitor({
enabled: true,
open: true,
metadataReady: v2MetadataReady,
});
// ResourceConsumptionContent is only mounted when enabled=true and open=true
// (enforced by the parent ResourceConsumption component), so we only need to
// gate on metadata readiness here.
const shouldQueryMetrics = v2MetadataReady;
Prompt To Fix With AI
This is a comment left during a code review.
Path: apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/TopBar/components/ResourceConsumption/ResourceConsumption.tsx
Line: 255-259

Comment:
**`shouldQueryResourceMonitor` params are hardcoded — function is a no-op wrapper here**

Both `enabled` and `open` are hardcoded to `true`, so `shouldQueryResourceMonitor` is equivalent to `metadataReady` at this call site. The outer `ResourceConsumption` component already guards against `enabled=false` and `open=false`, so the behaviour is correct, but the policy function's parameters become dead code here and could mislead future readers into thinking the inner component re-validates those conditions independently.

```suggestion
	// ResourceConsumptionContent is only mounted when enabled=true and open=true
	// (enforced by the parent ResourceConsumption component), so we only need to
	// gate on metadata readiness here.
	const shouldQueryMetrics = v2MetadataReady;
```

How can I resolve this? If you propose a fix, please make it concise.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

🧹 Nitpick comments (1)
apps/desktop/src/lib/electron-app/factories/app/setup.ts (1)

77-82: ⚡ Quick win

Consider validating the port number value.

The code reads RENDERER_REMOTE_DEBUG_PORT directly from the environment without validating that it's a numeric port in the valid range (1-65535). Invalid values could cause Chromium to silently ignore the switch or produce confusing behavior.

🛡️ Suggested validation
 if (env.NODE_ENV === "development" && process.env.RENDERER_REMOTE_DEBUG_PORT) {
+  const port = Number.parseInt(process.env.RENDERER_REMOTE_DEBUG_PORT, 10);
+  if (Number.isNaN(port) || port < 1 || port > 65535) {
+    console.warn(
+      `Invalid RENDERER_REMOTE_DEBUG_PORT: ${process.env.RENDERER_REMOTE_DEBUG_PORT}. Must be 1-65535.`,
+    );
+  } else {
 	app.commandLine.appendSwitch(
 		"remote-debugging-port",
 		process.env.RENDERER_REMOTE_DEBUG_PORT,
 	);
+  }
 }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/desktop/src/lib/electron-app/factories/app/setup.ts` around lines 77 -
82, Validate RENDERER_REMOTE_DEBUG_PORT before passing it to
app.commandLine.appendSwitch: parse the env value
(process.env.RENDERER_REMOTE_DEBUG_PORT) as an integer, ensure it's a finite
integer between 1 and 65535, and only call
app.commandLine.appendSwitch("remote-debugging-port", ...) when the check
passes; otherwise log a warning or ignore the setting. Update the conditional
around env.NODE_ENV === "development" to include this validation and reference
process.env.RENDERER_REMOTE_DEBUG_PORT and app.commandLine.appendSwitch in your
fix.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@apps/desktop/src/lib/electron-app/factories/app/setup.ts`:
- Around line 77-82: Validate RENDERER_REMOTE_DEBUG_PORT before passing it to
app.commandLine.appendSwitch: parse the env value
(process.env.RENDERER_REMOTE_DEBUG_PORT) as an integer, ensure it's a finite
integer between 1 and 65535, and only call
app.commandLine.appendSwitch("remote-debugging-port", ...) when the check
passes; otherwise log a warning or ignore the setting. Update the conditional
around env.NODE_ENV === "development" to include this validation and reference
process.env.RENDERER_REMOTE_DEBUG_PORT and app.commandLine.appendSwitch in your
fix.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 747e48d0-8a23-4e20-98c8-93714d247039

📥 Commits

Reviewing files that changed from the base of the PR and between add8428 and be6f5a1.

📒 Files selected for processing (31)
  • apps/desktop/src/lib/electron-app/factories/app/setup.ts
  • apps/desktop/src/main/windows/main.ts
  • apps/desktop/src/renderer/lib/performance/stress-instrumentation.ts
  • apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/DashboardSidebar/hooks/useDashboardSidebarData/useDashboardSidebarData.ts
  • apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/DashboardSidebar/hooks/useDashboardSidebarShortcuts/useDashboardSidebarShortcuts.ts
  • apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/DashboardSidebar/hooks/useDashboardSidebarShortcuts/useDashboardSidebarShortcuts.utils.test.ts
  • apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/DashboardSidebar/hooks/useDashboardSidebarShortcuts/useDashboardSidebarShortcuts.utils.ts
  • apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/TopBar/components/ResourceConsumption/ResourceConsumption.tsx
  • apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/TopBar/components/ResourceConsumption/resourceConsumptionPolicy.test.ts
  • apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/TopBar/components/ResourceConsumption/resourceConsumptionPolicy.ts
  • apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/components/BackgroundTerminalsButton/BackgroundTerminalsButton.tsx
  • apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/components/BackgroundTerminalsButton/BackgroundTerminalsButton.utils.test.ts
  • apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/components/BackgroundTerminalsButton/BackgroundTerminalsButton.utils.ts
  • apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/hooks/usePaneRegistry/components/TerminalPane/components/TerminalSessionDropdown/TerminalSessionDropdown.tsx
  • apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/hooks/usePaneRegistry/components/TerminalPane/components/TerminalSessionDropdown/TerminalSessionDropdown.utils.test.ts
  • apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/hooks/usePaneRegistry/components/TerminalPane/components/TerminalSessionDropdown/TerminalSessionDropdown.utils.ts
  • apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/components/WorkspaceHostUnavailableState/WorkspaceHostUnavailableState.tsx
  • apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/components/WorkspaceHostUnavailableState/index.ts
  • apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/hooks/useRemoteHostStatus/index.ts
  • apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/hooks/useRemoteHostStatus/remoteHostStatusPolicy.test.ts
  • apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/hooks/useRemoteHostStatus/remoteHostStatusPolicy.ts
  • apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/hooks/useRemoteHostStatus/useRemoteHostStatus.ts
  • apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/layout.tsx
  • apps/desktop/src/renderer/routes/_authenticated/providers/CollectionsProvider/collections.ts
  • packages/host-service/src/terminal/terminal.ts
  • packages/host-service/src/trpc/router/terminal/terminal.ts
  • packages/host-service/test/integration/terminal.integration.test.ts
  • packages/workspace-client/src/providers/WorkspaceClientProvider/WorkspaceClientProvider.tsx
  • packages/workspace-client/src/providers/WorkspaceClientProvider/workspaceClientCachePolicy.ts
  • packages/workspace-client/test/workspaceClientCachePolicy.test.ts
  • turbo.jsonc
✅ Files skipped from review due to trivial changes (1)
  • apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/components/WorkspaceHostUnavailableState/index.ts
🚧 Files skipped from review as they are similar to previous changes (24)
  • apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/TopBar/components/ResourceConsumption/resourceConsumptionPolicy.ts
  • packages/host-service/test/integration/terminal.integration.test.ts
  • apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/hooks/usePaneRegistry/components/TerminalPane/components/TerminalSessionDropdown/TerminalSessionDropdown.utils.ts
  • apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/TopBar/components/ResourceConsumption/resourceConsumptionPolicy.test.ts
  • packages/workspace-client/src/providers/WorkspaceClientProvider/workspaceClientCachePolicy.ts
  • packages/host-service/src/terminal/terminal.ts
  • packages/workspace-client/src/providers/WorkspaceClientProvider/WorkspaceClientProvider.tsx
  • packages/host-service/src/trpc/router/terminal/terminal.ts
  • apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/hooks/usePaneRegistry/components/TerminalPane/components/TerminalSessionDropdown/TerminalSessionDropdown.utils.test.ts
  • apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/layout.tsx
  • apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/hooks/usePaneRegistry/components/TerminalPane/components/TerminalSessionDropdown/TerminalSessionDropdown.tsx
  • apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/components/BackgroundTerminalsButton/BackgroundTerminalsButton.utils.test.ts
  • apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/hooks/useRemoteHostStatus/index.ts
  • apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/hooks/useRemoteHostStatus/useRemoteHostStatus.ts
  • apps/desktop/src/renderer/routes/_authenticated/providers/CollectionsProvider/collections.ts
  • apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/components/WorkspaceHostUnavailableState/WorkspaceHostUnavailableState.tsx
  • apps/desktop/src/main/windows/main.ts
  • apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/DashboardSidebar/hooks/useDashboardSidebarData/useDashboardSidebarData.ts
  • apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/hooks/useRemoteHostStatus/remoteHostStatusPolicy.ts
  • apps/desktop/src/renderer/lib/performance/stress-instrumentation.ts
  • apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/DashboardSidebar/hooks/useDashboardSidebarShortcuts/useDashboardSidebarShortcuts.ts
  • apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/hooks/useRemoteHostStatus/remoteHostStatusPolicy.test.ts
  • apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/components/BackgroundTerminalsButton/BackgroundTerminalsButton.tsx
  • apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/TopBar/components/ResourceConsumption/ResourceConsumption.tsx

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
.superset/lib/setup/main.sh (1)

44-62: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Fix duplicate step numbering.

Lines 44 and 49 both use "Step 6" in their comments. This is a pre-existing numbering inconsistency. The correct sequence should be:

  • Line 44: Step 6 (Seed auth token)
  • Line 49: Step 7 (Setup Neon branch) ← currently labeled "Step 6"
  • Line 54: Step 8 (Allocate port base) ← currently labeled "Step 7"
  • And so on...
📝 Proposed fix
   # Step 6: Seed auth token into superset-dev-data/
   if ! step_seed_auth_token; then
     step_failed "Seed auth token"
   fi
 
-  # Step 6: Setup Neon branch
+  # Step 7: Setup Neon branch
   if ! step_setup_neon_branch; then
     step_failed "Setup Neon branch"
   fi
 
-  # Step 7: Allocate port base (file-backed)
+  # Step 8: Allocate port base (file-backed)
   if ! allocate_port_base; then
     step_failed "Allocate port base"
   fi
 
-  # Step 8: Prepare Electric SQL env
+  # Step 9: Prepare Electric SQL env
   if ! step_prepare_electric; then
     step_failed "Prepare Electric SQL"
   fi

And continue renumbering steps 9→10, 10→11, 11→12.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.superset/lib/setup/main.sh around lines 44 - 62, Update the inline step
comments to correct the sequence: change the comment before step_seed_auth_token
to "Step 6: Seed auth token", change the comment before step_setup_neon_branch
to "Step 7: Setup Neon branch", change the comment before allocate_port_base to
"Step 8: Allocate port base (file-backed)", and change the comment before
step_prepare_electric to "Step 9: Prepare Electric SQL"; locate these comments
near the calls to step_seed_auth_token, step_setup_neon_branch,
allocate_port_base, and step_prepare_electric and renumber any subsequent step
comments accordingly.
.superset/lib/setup/steps.sh (1)

472-483: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Duplicate ELECTRIC_PORT entries written to .env.

With the new step ordering, step_prepare_electric (step 8) exports ELECTRIC_PORT before step_write_env (step 9) runs. This causes ELECTRIC_PORT to be written twice:

  1. Line 476 conditionally writes the exported value
  2. Line 521 writes the local variable (defined at line 503)

While both values are identical and most .env parsers will handle this, duplicate entries are unnecessary and could be confusing.

🔧 Suggested fix

Remove the conditional Electric variable writes (lines 472-483) since step_prepare_electric is now always called before step_write_env:

     write_env_var "DATABASE_URL_UNPOOLED" "$DIRECT_URL"
   fi
 
-  echo ""
-  echo "# Workspace Electric SQL (Docker)"
-  if [ -n "${ELECTRIC_CONTAINER:-}" ]; then
-    write_env_var "ELECTRIC_CONTAINER" "$ELECTRIC_CONTAINER"
-  fi
-  if [ -n "${ELECTRIC_PORT:-}" ]; then
-    write_env_var "ELECTRIC_PORT" "$ELECTRIC_PORT"
-  fi
-  if [ -n "${ELECTRIC_URL:-}" ]; then
-    write_env_var "ELECTRIC_URL" "$ELECTRIC_URL"
-  fi
-  if [ -n "${ELECTRIC_SECRET:-}" ]; then
-    write_env_var "ELECTRIC_SECRET" "$ELECTRIC_SECRET"
-  fi
-
   # Port allocation for multi-worktree dev instances

The port allocation section (starting at line 509) will write all Electric variables including these, using the exported values from step_prepare_electric.

Also applies to: 503-503, 521-521

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.superset/lib/setup/steps.sh around lines 472 - 483, Remove the redundant
conditional writes that call write_env_var for ELECTRIC_CONTAINER,
ELECTRIC_PORT, ELECTRIC_URL, and ELECTRIC_SECRET in the step_write_env area
because step_prepare_electric now runs before step_write_env and will already
export these values; delete that whole if-block that checks -n "${ELECTRIC_*:-}"
and writes them, relying instead on the port allocation/variable-writing later
(the code that writes Electric vars after step_prepare_electric) to produce a
single .env entry.
🧹 Nitpick comments (1)
.superset/lib/setup/steps.sh (1)

267-270: ⚡ Quick win

Consider adding lsof to the dependency check.

The port conflict check using lsof is a useful addition, but lsof is not listed in step_check_dependencies. If lsof is unavailable, the check silently fails (non-zero exit) and is skipped, deferring the port conflict error to Docker startup with a less clear message.

✨ Suggested addition to step_check_dependencies

Add the following check after the existing dependency checks (around line 47):

   if ! command -v caddy &> /dev/null; then
     warn "caddy not found — HTTP/2 proxy for Electric won't work (Run: brew install caddy && caddy trust)"
   fi
+
+  if ! command -v lsof &> /dev/null; then
+    warn "lsof not found — port conflict detection will be skipped (usually available by default on macOS/Linux)"
+  fi
 
   if [ ${`#missing`[@]} -gt 0 ]; then
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.superset/lib/setup/steps.sh around lines 267 - 270, Add lsof to the
dependency verification performed by step_check_dependencies so the port-check
that uses lsof (the block checking lsof -nP -iTCP:"$ELECTRIC_PORT" -sTCP:LISTEN)
will fail fast with a clear error if lsof is missing; update
step_check_dependencies to test for the lsof binary (similar to existing checks
for docker/other tools), and emit a clear error message and non-zero return when
lsof is not found so the port conflict detection using ELECTRIC_PORT behaves
reliably.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In @.superset/lib/setup/main.sh:
- Around line 44-62: Update the inline step comments to correct the sequence:
change the comment before step_seed_auth_token to "Step 6: Seed auth token",
change the comment before step_setup_neon_branch to "Step 7: Setup Neon branch",
change the comment before allocate_port_base to "Step 8: Allocate port base
(file-backed)", and change the comment before step_prepare_electric to "Step 9:
Prepare Electric SQL"; locate these comments near the calls to
step_seed_auth_token, step_setup_neon_branch, allocate_port_base, and
step_prepare_electric and renumber any subsequent step comments accordingly.

In @.superset/lib/setup/steps.sh:
- Around line 472-483: Remove the redundant conditional writes that call
write_env_var for ELECTRIC_CONTAINER, ELECTRIC_PORT, ELECTRIC_URL, and
ELECTRIC_SECRET in the step_write_env area because step_prepare_electric now
runs before step_write_env and will already export these values; delete that
whole if-block that checks -n "${ELECTRIC_*:-}" and writes them, relying instead
on the port allocation/variable-writing later (the code that writes Electric
vars after step_prepare_electric) to produce a single .env entry.

---

Nitpick comments:
In @.superset/lib/setup/steps.sh:
- Around line 267-270: Add lsof to the dependency verification performed by
step_check_dependencies so the port-check that uses lsof (the block checking
lsof -nP -iTCP:"$ELECTRIC_PORT" -sTCP:LISTEN) will fail fast with a clear error
if lsof is missing; update step_check_dependencies to test for the lsof binary
(similar to existing checks for docker/other tools), and emit a clear error
message and non-zero return when lsof is not found so the port conflict
detection using ELECTRIC_PORT behaves reliably.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 5b0af856-9139-4c32-9e9a-03d6fb8b214d

📥 Commits

Reviewing files that changed from the base of the PR and between be6f5a1 and 5340b49.

📒 Files selected for processing (33)
  • .superset/lib/setup/main.sh
  • .superset/lib/setup/steps.sh
  • apps/desktop/src/lib/electron-app/factories/app/setup.ts
  • apps/desktop/src/main/windows/main.ts
  • apps/desktop/src/renderer/lib/performance/stress-instrumentation.ts
  • apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/DashboardSidebar/hooks/useDashboardSidebarData/useDashboardSidebarData.ts
  • apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/DashboardSidebar/hooks/useDashboardSidebarShortcuts/useDashboardSidebarShortcuts.ts
  • apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/DashboardSidebar/hooks/useDashboardSidebarShortcuts/useDashboardSidebarShortcuts.utils.test.ts
  • apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/DashboardSidebar/hooks/useDashboardSidebarShortcuts/useDashboardSidebarShortcuts.utils.ts
  • apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/TopBar/components/ResourceConsumption/ResourceConsumption.tsx
  • apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/TopBar/components/ResourceConsumption/resourceConsumptionPolicy.test.ts
  • apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/TopBar/components/ResourceConsumption/resourceConsumptionPolicy.ts
  • apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/components/BackgroundTerminalsButton/BackgroundTerminalsButton.tsx
  • apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/components/BackgroundTerminalsButton/BackgroundTerminalsButton.utils.test.ts
  • apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/components/BackgroundTerminalsButton/BackgroundTerminalsButton.utils.ts
  • apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/hooks/usePaneRegistry/components/TerminalPane/components/TerminalSessionDropdown/TerminalSessionDropdown.tsx
  • apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/hooks/usePaneRegistry/components/TerminalPane/components/TerminalSessionDropdown/TerminalSessionDropdown.utils.test.ts
  • apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/hooks/usePaneRegistry/components/TerminalPane/components/TerminalSessionDropdown/TerminalSessionDropdown.utils.ts
  • apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/components/WorkspaceHostUnavailableState/WorkspaceHostUnavailableState.tsx
  • apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/components/WorkspaceHostUnavailableState/index.ts
  • apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/hooks/useRemoteHostStatus/index.ts
  • apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/hooks/useRemoteHostStatus/remoteHostStatusPolicy.test.ts
  • apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/hooks/useRemoteHostStatus/remoteHostStatusPolicy.ts
  • apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/hooks/useRemoteHostStatus/useRemoteHostStatus.ts
  • apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/layout.tsx
  • apps/desktop/src/renderer/routes/_authenticated/providers/CollectionsProvider/collections.ts
  • packages/host-service/src/terminal/terminal.ts
  • packages/host-service/src/trpc/router/terminal/terminal.ts
  • packages/host-service/test/integration/terminal.integration.test.ts
  • packages/workspace-client/src/providers/WorkspaceClientProvider/WorkspaceClientProvider.tsx
  • packages/workspace-client/src/providers/WorkspaceClientProvider/workspaceClientCachePolicy.ts
  • packages/workspace-client/test/workspaceClientCachePolicy.test.ts
  • turbo.jsonc
✅ Files skipped from review due to trivial changes (2)
  • turbo.jsonc
  • apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/components/WorkspaceHostUnavailableState/index.ts
🚧 Files skipped from review as they are similar to previous changes (28)
  • apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/TopBar/components/ResourceConsumption/resourceConsumptionPolicy.ts
  • apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/hooks/useRemoteHostStatus/index.ts
  • apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/components/WorkspaceHostUnavailableState/WorkspaceHostUnavailableState.tsx
  • apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/layout.tsx
  • apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/TopBar/components/ResourceConsumption/resourceConsumptionPolicy.test.ts
  • apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/DashboardSidebar/hooks/useDashboardSidebarShortcuts/useDashboardSidebarShortcuts.utils.test.ts
  • packages/workspace-client/test/workspaceClientCachePolicy.test.ts
  • apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/hooks/usePaneRegistry/components/TerminalPane/components/TerminalSessionDropdown/TerminalSessionDropdown.tsx
  • apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/DashboardSidebar/hooks/useDashboardSidebarShortcuts/useDashboardSidebarShortcuts.utils.ts
  • packages/workspace-client/src/providers/WorkspaceClientProvider/workspaceClientCachePolicy.ts
  • packages/host-service/src/terminal/terminal.ts
  • apps/desktop/src/renderer/lib/performance/stress-instrumentation.ts
  • apps/desktop/src/lib/electron-app/factories/app/setup.ts
  • apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/hooks/usePaneRegistry/components/TerminalPane/components/TerminalSessionDropdown/TerminalSessionDropdown.utils.ts
  • apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/hooks/useRemoteHostStatus/useRemoteHostStatus.ts
  • packages/host-service/test/integration/terminal.integration.test.ts
  • apps/desktop/src/main/windows/main.ts
  • apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/hooks/useRemoteHostStatus/remoteHostStatusPolicy.ts
  • apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/DashboardSidebar/hooks/useDashboardSidebarData/useDashboardSidebarData.ts
  • apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/hooks/useRemoteHostStatus/remoteHostStatusPolicy.test.ts
  • apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/components/BackgroundTerminalsButton/BackgroundTerminalsButton.utils.test.ts
  • packages/host-service/src/trpc/router/terminal/terminal.ts
  • apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/components/BackgroundTerminalsButton/BackgroundTerminalsButton.tsx
  • apps/desktop/src/renderer/routes/_authenticated/providers/CollectionsProvider/collections.ts
  • apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/DashboardSidebar/hooks/useDashboardSidebarShortcuts/useDashboardSidebarShortcuts.ts
  • packages/workspace-client/src/providers/WorkspaceClientProvider/WorkspaceClientProvider.tsx
  • apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/TopBar/components/ResourceConsumption/ResourceConsumption.tsx
  • apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/components/BackgroundTerminalsButton/BackgroundTerminalsButton.utils.ts

Copy link
Copy Markdown
Contributor

@cubic-dev-ai cubic-dev-ai Bot left a comment

Choose a reason for hiding this comment

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

No issues found across 22 files

Re-trigger cubic

@stage-review
Copy link
Copy Markdown

stage-review Bot commented May 15, 2026

Ready to review this PR? Stage has broken it down into 5 individual chapters for you:

Title
1 Add renderer stress instrumentation and logging
2 Optimize local database indexes and queries
3 Throttle resource monitor polling
4 Optimize terminal session polling and background tracking
5 Fix terminal adoption and setup script issues
Open in Stage

Chapters generated by Stage for commit ba8e064 on May 16, 2026 12:58am UTC.

@Kitenite Kitenite changed the title [codex] Fix renderer crash during rapid workspace switching Reduce renderer polling for resource and terminal surfaces May 16, 2026
@Kitenite Kitenite merged commit 2b86c05 into main May 16, 2026
17 checks passed
@Kitenite Kitenite deleted the debug-renderer-crash-regr branch May 16, 2026 01:19
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant