Skip to content

[codex] Stabilize renderer stress with minimal terminal changes#4570

Open
Kitenite wants to merge 18 commits into
mainfrom
instant-replay-switch
Open

[codex] Stabilize renderer stress with minimal terminal changes#4570
Kitenite wants to merge 18 commits into
mainfrom
instant-replay-switch

Conversation

@Kitenite
Copy link
Copy Markdown
Collaborator

@Kitenite Kitenite commented May 14, 2026

Summary

Narrows the workspace-switching fix back down to renderer paths that reproduced the freeze, while keeping production terminal behavior close to main and preserving browser panes across workspace switches.

Key changes:

  • Coalesce rapid V2 workspace navigations and replace existing V2 route history entries, so a fast sidebar sweep does not queue a long route spiral.
  • Keep browser webviews alive across normal workspace switches, but cap detached browser runtimes with LRU eviction so hidden webviews cannot grow without bound.
  • Destroy browser runtimes when the browser pane itself closes.
  • Share terminal remote-control session polling per workspace with React Query instead of one manual polling interval per button.
  • Keep terminal replay/lifecycle mostly untouched. Terminal replay is not rebuilt on ordinary workspace switches.
  • Centralize terminal WebGL addon setup for both terminal creation paths, preserving the context-loss DOM fallback behavior without duplicating it.
  • Clear the xterm WebGL texture atlas through WebglAddon.clearTextureAtlas() only when xterm reports atlas-model pressure or a large atlas page. This does not touch PTYs, replay, serialized buffers, pane ownership, or parking.
  • Add a deterministic stress detector for xterm WebGL glyph corruption by screenshotting a sentinel frame before/after clearTextureAtlas().

Root Cause

The deeper renderer freeze was not primarily terminal replay. Rapid workspace switching with browser panes left inactive workspace browser webviews alive without any budget. The previous cleanup path only handled pane IDs removed from persisted state, so pane IDs in inactive workspaces still existed and their webviews, documents, frames, and listeners could accumulate across switches.

We do want browser panes to survive ordinary workspace switches, so this PR keeps that behavior and adds a cap to detached browser runtimes instead of destroying every browser on workspace unmount.

The stable terminal glyph corruption is xterm WebGL atlas pressure. Upstream has an open matching issue: xtermjs/xterm.js#5847. The local repro writes the same true-color atlas rewrite pattern, then compares a fixed sentinel frame before and after clearing the atlas. Before the guard, that detector changed 241,147 pixels; after the guard, it changed 0 pixels.

Impact

Switching workspaces should feel immediate, including when background terminals or browser panes exist. Browser panes from recently used workspaces stay live across switches. Older detached browser runtimes can be evicted once the retained detached-browser budget is exceeded, and returning to those panes recreates the webview from persisted pane state.

If a terminal loses its WebGL context, the current terminal repaints through xterm's fallback path and later terminal runtimes avoid WebGL in that renderer process. If xterm reaches the WebGL atlas-pressure path, the atlas is cleared and the visible rows are refreshed without rebuilding terminal sessions or replaying terminal history.

Validation

  • bun run lint:fix
  • bun run lint
  • bun --cwd apps/desktop typecheck
  • git diff --check
  • Real dev app launched with SUPERSET_RENDERER_STRESS_CDP_PORT=9333 bun dev, then stress-tested through the Electron renderer over CDP.
  • Focused rapid switching: workspace-switch, 1000 clicks, interval 0 ms, passed with errorCount: 0, maxHeartbeatDelayMs: 123.2, maxLongTaskDurationMs: 122, documents 4 -> 9, nodes 1641 -> 1624.
  • Targeted long background terminal replay: terminal-heavy, 2 tabs x 1 pane, 80 iterations, 120 lines, 2048-byte payloads, passed with errorCount: 0, maxHeartbeatDelayMs: 63.4, maxLongTaskDurationMs: 0.
  • Deterministic WebGL glyph corruption repro before atlas guard: terminal-heavy, 1500 atlas-rewrite cycles, 1 tab x 1 pane, --terminal-corruption-check, failed as expected with changedPixels: 241147 after clearTextureAtlas().
  • Deterministic WebGL glyph corruption repro after atlas guard: same command passed with WebGL still active, pendingAtlasClear: true, 4096px atlas pages present, and changedPixels: 0 against threshold 3891.
  • Final full renderer stress after the tightened guard: all, 1000 workspace switches, 240 route sweeps, 500 mixed-pane operations, 200 terminal-heavy operations, 32 terminal tabs x 4 panes, passed with errorCount: 0, failures: [], maxHeartbeatDelayMs: 897.4, maxLongTaskDurationMs: 464.
  • Forced visible WebGL context loss over CDP: visible terminal WebGL canvases lost context; fresh terminal runtime in the same renderer process reported renderer: null and canvasCount: 0, confirming DOM fallback for later terminals.

Notes

I checked the current xterm beta line against our installed beta. The upstream issue remains open and the newer beta commits did not show an obvious atlas/glyph-corruption fix, so this PR does not upgrade xterm.

Desktop/dev logs were noisy with Electric shape long polls, host-service tunnel reconnects, expired local Stripe test key errors, and missing local worktree watcher errors. Those were separate from the renderer stress and WebGL atlas behavior tested here.

@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 a development-only renderer stress harness (CDP scripts, fixture generator, in-app bridge), wires stress-debug attributes and navigation hooks into V2 workspace UI, introduces terminal/browser runtime stress utilities and deferred disposal/pruning, and adds a host-service git diff-stat TRPC query with parsing and tests.

Changes

Renderer stress, UI wiring, terminal & browser runtime

Layer / File(s) Summary
Dev tooling: docs & CLI scripts
apps/desktop/docs/RENDERER_STRESS_QA.md, apps/desktop/package.json, apps/desktop/scripts/stress-renderer.ts, apps/desktop/scripts/prepare-renderer-stress-fixtures.ts
Adds QA doc; two Bun scripts (stress:renderer, stress:renderer:fixtures); implements CDP-driven stress harness and fixture-generator with DB seeding and JSON output.
Electron CDP enablement & extension gating
apps/desktop/src/main/index.ts, apps/desktop/src/main/lib/extensions/index.ts, turbo.jsonc
Enables remote-debugging-port via SUPERSET_RENDERER_STRESS_CDP_PORT in dev, skips loading React DevTools when that env var is present, and passthroughs the env var in turbo config.
In-page bridge & renderer wiring
apps/desktop/src/renderer/index.tsx, apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/hooks/useRendererStressWorkspaceBridge/*, apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/page.tsx, apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspaces/.../V2WorkspaceRow.tsx, apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/.../DashboardSidebarWorkspaceItem*.tsx
Adds useRendererStressWorkspaceBridge, registers global bridge, passes worktreePath and deduped renderer-stress filePaths into workspace content, and adds data-renderer-stress-workspace-id attributes to workspace UI elements.
Terminal stress utilities & lifecycle changes
apps/desktop/src/renderer/lib/terminal/terminal-runtime-registry.ts, apps/desktop/src/renderer/lib/terminal/terminal-runtime.ts, apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/hooks/usePaneRegistry/.../TerminalPane/*, apps/desktop/src/renderer/lib/terminal/terminal-image-addon.ts, apps/desktop/src/renderer/lib/terminal/terminal-addons.ts, tests *terminal-runtime-registry.test.ts, terminal-image-addon.test.ts
Adds writeForStress and getStressDebugInfo exports, deferred terminal disposal helper to avoid synchronous teardown, factory for terminal image addon and tests, and stress-oriented runtime behavior/instrumentation and tests.
Pane & navigation improvements
apps/desktop/src/renderer/routes/_authenticated/_dashboard/utils/workspace-navigation.ts, useDashboardSidebarShortcuts, usePaneRegistry wiring, TerminalRemoteControlButton.tsx
Adds queued/coalesced V2 navigation (coalescing when no search params), uses void when calling navigation helper, and refactors remote-control UI to react-query driven sessions.
Telemetry & dev-only helpers
apps/desktop/src/renderer/lib/posthog.ts, apps/desktop/src/renderer/lib/posthog.test.ts
Extracts buildPostHogInitConfig and POSTHOG_SESSION_REPLAY_BLOCK_SELECTOR, centralizing privacy-related PostHog options and adding tests.

Host-service git diff stats

Layer / File(s) Summary
Numstat parsing helpers & types
packages/host-service/src/trpc/router/git/utils/git-helpers.ts, tests git-helpers.test.ts
Introduces NumstatRecord type, implements parseNumstatRecords with safer rename/copy handling, and updates parseNumstat to aggregate records into a path-indexed Map; adds unit tests for parsing behavior.
Diff-stats TRPC query & integration test
packages/host-service/src/trpc/router/git/git.ts, packages/host-service/test/integration/git.integration.test.ts
Adds git.getDiffStats TRPC query that runs base/staged/unstaged numstat diff collection in parallel, aggregates per-path additions/deletions, and returns totals; adds an integration test asserting expected totals.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related issues

Possibly related PRs

"I hopped through hashes, coalesced each call,
I nudged panes to sleep and watched webviews fall,
I queued each route and ignored the stray race,
I stamped last-used marks and pruned with grace,
A rabbit on the branch, keeping runtimes ace 🐇"

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 5.62% 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
Title check ✅ Passed The title clearly and concisely describes the main objective: stabilizing renderer stress while minimizing terminal changes, which aligns with the core changeset.
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.
Description check ✅ Passed PR description is comprehensive with clear sections for Summary, Root Cause, Impact, Validation, and Notes covering all key changes and testing results.

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

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch instant-replay-switch

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 and usage tips.

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 14, 2026

🚀 Preview Deployment

🔗 Preview Links

Service Status Link
Neon Database (Neon) View Branch
Vercel API (Vercel) Open Preview
Vercel Web (Vercel) Open Preview
Vercel Marketing (Vercel) Open Preview
Vercel Admin (Vercel) Open Preview
Vercel Docs (Vercel) Open Preview

Preview updates automatically with new commits

@Kitenite Kitenite changed the title [codex] Reapply renderer stress fixes with instant terminal restore [codex] Reapply renderer stress fixes with minimal terminal changes May 14, 2026
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

🤖 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/utils/workspace-navigation.ts`:
- Around line 113-123: The current code immediately calls
queuedV2WorkspaceNavigation?.resolve(), which settles a superseded
navigateToV2Workspace caller before navigation completes; instead, change the
queuing logic in workspace-navigation so that when a new request supersedes an
existing queuedV2WorkspaceNavigation you call its reject with a clear
SupersededError (or similar) or otherwise defer settling previous promises until
the scheduled drain finishes; specifically update the block that assigns
queuedV2WorkspaceNavigation and calls scheduleV2WorkspaceNavigationDrain() to
reject the prior queuedV2WorkspaceNavigation (using its reject()) or accumulate
callers and only resolve/reject them when the actual navigation drain
(scheduleV2WorkspaceNavigationDrain / the drain handler) completes.
- Around line 166-170: A pending bare-workspace switch enqueued by
enqueueV2WorkspaceNavigation can later fire and overwrite a newer immediate
navigation that includes search params; before calling
performV2WorkspaceNavigation(workspaceId, ...) you must cancel/clear any queued
bare-workspace entry for the same workspaceId. Add a call to the cancellation
helper (e.g. clearEnqueuedV2WorkspaceNavigation(workspaceId) or
cancelQueuedV2WorkspaceNavigation(workspaceId)) right before
performV2WorkspaceNavigation, or implement such a helper to clear the
timer/queue entry created by enqueueV2WorkspaceNavigation so the queued timer
cannot later navigate without terminalId/chatSessionId/openUrl*.
🪄 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: c7ca410a-dca6-4a42-b523-5ff155a57050

📥 Commits

Reviewing files that changed from the base of the PR and between 89ec385 and de350fb.

📒 Files selected for processing (8)
  • apps/desktop/scripts/stress-renderer.ts
  • apps/desktop/src/renderer/lib/terminal/terminal-runtime-registry.test.ts
  • apps/desktop/src/renderer/lib/terminal/terminal-runtime-registry.ts
  • apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/DashboardSidebar/components/DashboardSidebarWorkspaceItem/hooks/useDashboardSidebarWorkspaceItemActions/useDashboardSidebarWorkspaceItemActions.ts
  • apps/desktop/src/renderer/routes/_authenticated/_dashboard/utils/workspace-navigation.ts
  • apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/hooks/usePaneRegistry/components/TerminalPane/components/TerminalRemoteControlButton/TerminalRemoteControlButton.tsx
  • apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/hooks/usePaneRegistry/usePaneRegistry.tsx
  • apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/page.tsx
🚧 Files skipped from review as they are similar to previous changes (3)
  • apps/desktop/src/renderer/lib/terminal/terminal-runtime-registry.test.ts
  • apps/desktop/src/renderer/lib/terminal/terminal-runtime-registry.ts
  • apps/desktop/scripts/stress-renderer.ts

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 (2)
apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/hooks/usePaneRegistry/components/BrowserPane/browserRuntimeRegistry.ts (2)

427-436: 💤 Low value

Prune loop logic looks correct; minor style nit on return.

The detached-only filter plus ascending lastUsedAt sort correctly evicts least-recently-used detached entries, and because detach()/attach() bump lastUsedAt before pruning, the just-detached entry won’t be the eviction target. One small nit: if (!paneId) return; exits the whole method on a falsy paneId — since Map keys here are non-empty strings, this branch is unreachable, but break (or omitting the guard) communicates intent more clearly.

♻️ Optional tweak
 	private pruneDetachedRuntimes(): void {
 		const detachedEntries = Array.from(this.entries.entries())
 			.filter(([, entry]) => !entry.visible)
 			.sort(([, a], [, b]) => a.lastUsedAt - b.lastUsedAt);
 		while (detachedEntries.length > MAX_DETACHED_BROWSER_RUNTIMES) {
 			const [paneId] = detachedEntries.shift() ?? [];
-			if (!paneId) return;
+			if (!paneId) break;
 			this.destroy(paneId);
 		}
 	}
🤖 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/renderer/routes/_authenticated/_dashboard/v2-workspace/`$workspaceId/hooks/usePaneRegistry/components/BrowserPane/browserRuntimeRegistry.ts
around lines 427 - 436, The guard inside pruneDetachedRuntimes currently does
`if (!paneId) return;` which inadvertently suggests an early-return on an
impossible falsy key; change this to `break` (or remove the guard entirely) so
the loop only stops the current prune iteration rather than exiting the whole
method, leaving the rest of the eviction loop to continue; update the
pruneDetachedRuntimes method that iterates over this.entries, uses
detachedEntries and calls this.destroy(paneId) to use `break` (or drop the
check) instead of `return`.

164-179: 💤 Low value

Consider moving lastUsedAt update past the no-op early return.

entry.lastUsedAt = Date.now() runs before the if (!changed) return guard, so every webview event that produces an identical state patch still refreshes the LRU timestamp. For detached entries that keep receiving background events (e.g., late did-stop-loading, repeated favicon updates with the same URL), this can defer eviction beyond what the LRU intends. If you want “any activity counts as used” keep as is; otherwise move the assignment below the change check so only real state mutations bump the timestamp.

♻️ Optional tweak
 	private setState(paneId: string, patch: Partial<BrowserRuntimeState>) {
 		const entry = this.entries.get(paneId);
 		if (!entry) return;
-		entry.lastUsedAt = Date.now();
 		let changed = false;
 		for (const key in patch) {
 			const k = key as keyof BrowserRuntimeState;
 			if (entry.state[k] !== patch[k]) {
 				changed = true;
 				break;
 			}
 		}
 		if (!changed) return;
+		entry.lastUsedAt = Date.now();
 		entry.state = { ...entry.state, ...patch };
 		this.notify(paneId);
 	}
🤖 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/renderer/routes/_authenticated/_dashboard/v2-workspace/`$workspaceId/hooks/usePaneRegistry/components/BrowserPane/browserRuntimeRegistry.ts
around lines 164 - 179, The setState method currently updates entry.lastUsedAt
before checking whether the incoming patch actually changes state; move the
assignment to entry.lastUsedAt = Date.now() so it happens only after you detect
a real change (i.e., after computing `changed` and before assigning the merged
state and calling this.notify), ensuring only real state mutations in setState
(not no-op patches) bump the LRU timestamp; locate this in the setState function
where `entry`, `changed`, `patch`, `entry.state`, and `this.notify(paneId)` are
referenced.
🤖 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/renderer/routes/_authenticated/_dashboard/v2-workspace/`$workspaceId/hooks/usePaneRegistry/components/BrowserPane/browserRuntimeRegistry.ts:
- Around line 427-436: The guard inside pruneDetachedRuntimes currently does `if
(!paneId) return;` which inadvertently suggests an early-return on an impossible
falsy key; change this to `break` (or remove the guard entirely) so the loop
only stops the current prune iteration rather than exiting the whole method,
leaving the rest of the eviction loop to continue; update the
pruneDetachedRuntimes method that iterates over this.entries, uses
detachedEntries and calls this.destroy(paneId) to use `break` (or drop the
check) instead of `return`.
- Around line 164-179: The setState method currently updates entry.lastUsedAt
before checking whether the incoming patch actually changes state; move the
assignment to entry.lastUsedAt = Date.now() so it happens only after you detect
a real change (i.e., after computing `changed` and before assigning the merged
state and calling this.notify), ensuring only real state mutations in setState
(not no-op patches) bump the LRU timestamp; locate this in the setState function
where `entry`, `changed`, `patch`, `entry.state`, and `this.notify(paneId)` are
referenced.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 68cc2c7b-03da-4bd3-92dd-c5ff90f7fc3c

📥 Commits

Reviewing files that changed from the base of the PR and between de350fb and 230c7c0.

📒 Files selected for processing (2)
  • apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/hooks/usePaneRegistry/components/BrowserPane/browserRuntimeRegistry.ts
  • apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/page.tsx

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.

10 issues found across 39 files

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="apps/desktop/src/main/index.ts">

<violation number="1" location="apps/desktop/src/main/index.ts:61">
P2: Validate `SUPERSET_RENDERER_STRESS_CDP_PORT` is within the valid port range before enabling CDP; numeric-only validation accepts invalid ports.</violation>
</file>

<file name="apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/DashboardSidebar/hooks/useDashboardSidebarShortcuts/useDashboardSidebarShortcuts.ts">

<violation number="1" location="apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/DashboardSidebar/hooks/useDashboardSidebarShortcuts/useDashboardSidebarShortcuts.ts:111">
P2: Handle rejected `navigateToV2Workspace` promises in these hotkey paths. Prefixing with `void` only marks the Promise as intentionally ignored; it does not prevent unhandled rejections if navigation fails.

(Based on your team's feedback about handling async rejections explicitly in this file.) [FEEDBACK_USED]</violation>
</file>

<file name="apps/desktop/src/renderer/index.tsx">

<violation number="1" location="apps/desktop/src/renderer/index.tsx:54">
P2: Handle navigation failures inside the dev stress helper to avoid unhandled promise rejections.

(Based on your team's feedback about handling async rejections explicitly.) [FEEDBACK_USED]</violation>
</file>

<file name="apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/DashboardSidebar/components/DashboardSidebarWorkspaceItem/DashboardSidebarWorkspaceItem.tsx">

<violation number="1" location="apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/DashboardSidebar/components/DashboardSidebarWorkspaceItem/DashboardSidebarWorkspaceItem.tsx:220">
P2: Passing `diffStats` as `null` unconditionally removes diff-stat badges for all workspace rows. Preserve the previous pending-only behavior so non-pending workspaces can still show additions/deletions.</violation>
</file>

<file name="apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/hooks/usePaneRegistry/components/BrowserPane/browserRuntimeRegistry.ts">

<violation number="1" location="apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/hooks/usePaneRegistry/components/BrowserPane/browserRuntimeRegistry.ts:434">
P2: Detached-runtime eviction now calls `destroy()`, but unregister errors are silently swallowed. This hides backend sync failures introduced by the new LRU eviction path.

(Based on your team's feedback about surfacing async unregister failures instead of swallowing them.) [FEEDBACK_USED]</violation>
</file>

<file name="apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspaces/components/V2WorkspacesList/components/V2WorkspaceRow/V2WorkspaceRow.tsx">

<violation number="1" location="apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspaces/components/V2WorkspacesList/components/V2WorkspaceRow/V2WorkspaceRow.tsx:61">
P2: Handle the navigation promise rejection instead of discarding it with `void`, otherwise failed workspace transitions can surface as unhandled promise rejections.

(Based on your team's feedback about handling async navigation errors explicitly.) [FEEDBACK_USED]</violation>
</file>

<file name="apps/desktop/src/renderer/routes/_authenticated/_dashboard/utils/workspace-navigation.ts">

<violation number="1" location="apps/desktop/src/renderer/routes/_authenticated/_dashboard/utils/workspace-navigation.ts:170">
P2: Immediate search navigations do not clear pending queued workspace switches, so an older queued navigation can run afterward and override the user’s latest action.</violation>
</file>

<file name="apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/DashboardSidebar/components/DashboardSidebarHoverCardOverlay/DashboardSidebarHoverCardOverlay.tsx">

<violation number="1" location="apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/DashboardSidebar/components/DashboardSidebarHoverCardOverlay/DashboardSidebarHoverCardOverlay.tsx:30">
P2: Gating `useDiffStats` by `open` can leave hover-card diff stats stale after reopening, because updates are event-driven and the query cache is never considered stale.</violation>
</file>

<file name="apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/hooks/usePaneRegistry/components/TerminalPane/components/TerminalRemoteControlButton/TerminalRemoteControlButton.tsx">

<violation number="1" location="apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/hooks/usePaneRegistry/components/TerminalPane/components/TerminalRemoteControlButton/TerminalRemoteControlButton.tsx:79">
P2: `localActive` fallback is applied even when fresh query data says there is no live session, so the button can incorrectly stay “live” with a stale link after server-side revoke.</violation>
</file>

<file name="apps/desktop/scripts/prepare-renderer-stress-fixtures.ts">

<violation number="1" location="apps/desktop/scripts/prepare-renderer-stress-fixtures.ts:565">
P2: Avoid swallowing `git add` failures; emit a contextual warning so incomplete fixture setup is detectable.

(Based on your team's feedback about avoiding empty catch blocks that hide async failures.) [FEEDBACK_USED]</violation>
</file>

Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.
Re-trigger cubic

Comment thread apps/desktop/src/main/index.ts Outdated
if (workspace) {
revealWorkspace(workspace.id);
navigateToV2Workspace(workspace.id, navigate);
void navigateToV2Workspace(workspace.id, navigate);
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.

P2: Handle rejected navigateToV2Workspace promises in these hotkey paths. Prefixing with void only marks the Promise as intentionally ignored; it does not prevent unhandled rejections if navigation fails.

(Based on your team's feedback about handling async rejections explicitly in this file.)

View Feedback

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/DashboardSidebar/hooks/useDashboardSidebarShortcuts/useDashboardSidebarShortcuts.ts, line 111:

<comment>Handle rejected `navigateToV2Workspace` promises in these hotkey paths. Prefixing with `void` only marks the Promise as intentionally ignored; it does not prevent unhandled rejections if navigation fails.

(Based on your team's feedback about handling async rejections explicitly in this file.) </comment>

<file context>
@@ -108,7 +108,7 @@ export function useDashboardSidebarShortcuts(
 			if (workspace) {
 				revealWorkspace(workspace.id);
-				navigateToV2Workspace(workspace.id, navigate);
+				void navigateToV2Workspace(workspace.id, navigate);
 			}
 		},
</file context>
Suggested change
void navigateToV2Workspace(workspace.id, navigate);
void navigateToV2Workspace(workspace.id, navigate).catch((error) => {
console.warn("Failed to navigate to workspace via shortcut", error);
});

Comment thread apps/desktop/src/renderer/index.tsx Outdated
renameValue={renameValue}
shortcutLabel={shortcutLabel}
diffStats={isPending ? null : diffStats}
diffStats={null}
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.

P2: Passing diffStats as null unconditionally removes diff-stat badges for all workspace rows. Preserve the previous pending-only behavior so non-pending workspaces can still show additions/deletions.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/DashboardSidebar/components/DashboardSidebarWorkspaceItem/DashboardSidebarWorkspaceItem.tsx, line 220:

<comment>Passing `diffStats` as `null` unconditionally removes diff-stat badges for all workspace rows. Preserve the previous pending-only behavior so non-pending workspaces can still show additions/deletions.</comment>

<file context>
@@ -219,11 +217,12 @@ export function DashboardSidebarWorkspaceItem({
 				renameValue={renameValue}
 				shortcutLabel={shortcutLabel}
-				diffStats={isPending ? null : diffStats}
+				diffStats={null}
 				workspaceStatus={workspaceStatus}
 				isInSection={isInSection}
</file context>

const handleOpen = useCallback(() => {
const open = () => navigateToV2Workspace(workspace.id, navigate);
const open = () => {
void navigateToV2Workspace(workspace.id, navigate);
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.

P2: Handle the navigation promise rejection instead of discarding it with void, otherwise failed workspace transitions can surface as unhandled promise rejections.

(Based on your team's feedback about handling async navigation errors explicitly.)

View Feedback

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspaces/components/V2WorkspacesList/components/V2WorkspaceRow/V2WorkspaceRow.tsx, line 61:

<comment>Handle the navigation promise rejection instead of discarding it with `void`, otherwise failed workspace transitions can surface as unhandled promise rejections.

(Based on your team's feedback about handling async navigation errors explicitly.) </comment>

<file context>
@@ -57,7 +57,9 @@ export function V2WorkspaceRow({
 	const handleOpen = useCallback(() => {
-		const open = () => navigateToV2Workspace(workspace.id, navigate);
+		const open = () => {
+			void navigateToV2Workspace(workspace.id, navigate);
+		};
 		if (workspace.hostType === "local-device") {
</file context>
Suggested change
void navigateToV2Workspace(workspace.id, navigate);
void navigateToV2Workspace(workspace.id, navigate).catch((error) => {
console.error("Failed to navigate to workspace", {
workspaceId: workspace.id,
error,
});
});


const open = hoveredId !== null && payload !== null && !contextMenuOpen;
const diffStats = useDiffStats(hoveredId ?? "");
const diffStats = useDiffStats(hoveredId ?? "", { enabled: open });
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.

P2: Gating useDiffStats by open can leave hover-card diff stats stale after reopening, because updates are event-driven and the query cache is never considered stale.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/DashboardSidebar/components/DashboardSidebarHoverCardOverlay/DashboardSidebarHoverCardOverlay.tsx, line 30:

<comment>Gating `useDiffStats` by `open` can leave hover-card diff stats stale after reopening, because updates are event-driven and the query cache is never considered stale.</comment>

<file context>
@@ -27,7 +27,7 @@ export function DashboardSidebarHoverCardOverlay({
 
 	const open = hoveredId !== null && payload !== null && !contextMenuOpen;
-	const diffStats = useDiffStats(hoveredId ?? "");
+	const diffStats = useDiffStats(hoveredId ?? "", { enabled: open });
 
 	// Suppress the transform transition until Radix has placed the popover at
</file context>

Comment thread apps/desktop/scripts/prepare-renderer-stress-fixtures.ts Outdated
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.

1 issue found across 7 files (changes from recent commits).

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="apps/desktop/src/renderer/lib/terminal/terminal-runtime-registry.ts">

<violation number="1" location="apps/desktop/src/renderer/lib/terminal/terminal-runtime-registry.ts:121">
P2: Avoid swallowing errors in this catch block. Log the failure (or rethrow) so atlas-clear failures are observable during stress diagnostics.

(Based on your team's feedback about handling errors explicitly and avoiding empty catch blocks.) [FEEDBACK_USED]</violation>
</file>

Tip: Review your code locally with the cubic CLI to iterate faster.
Re-trigger cubic

Comment thread apps/desktop/src/renderer/lib/terminal/terminal-runtime-registry.ts Outdated
@stage-review
Copy link
Copy Markdown

stage-review Bot commented May 15, 2026

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

Title
1 Introduce structured git diff parsing
2 Implement aggregated diff stats endpoint
3 Optimize sidebar diff stats fetching
4 Centralize terminal WebGL lifecycle
5 Standardize addons across terminal paths
6 Manage browser runtimes and terminal errors
Open in Stage

Chapters generated by Stage for commit c1313b3 on May 16, 2026 1:22am UTC.

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.

6 issues found across 28 files (changes from recent commits).

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="apps/desktop/src/renderer/lib/posthog.ts">

<violation number="1">
P1: This init call removed explicit PostHog capture opt-outs, which reverts to default autocapture/session-recording behavior. Re-add the explicit disable flags to avoid unintended client-side data capture and event volume.</violation>
</file>

<file name="apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/layout.tsx">

<violation number="1">
P2: This guard now blocks all rendering when `isReady` is false, which defeats the in-flight `cloudRow` fallback and can hide workspace creation/error states behind a blank loading view.

(Based on your team's feedback about verifying the removed `!workspace` loading guard behavior.) [FEEDBACK_USED]</violation>
</file>

<file name="apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/hooks/usePaneRegistry/components/TerminalPane/components/TerminalRemoteControlButton/TerminalRemoteControlButton.tsx">

<violation number="1">
P2: `hydrate` swallows polling errors without surfacing them, which hides recurring backend/network failures and makes remote-control state issues hard to diagnose.

(Based on your team's feedback about surfacing async polling failures instead of silent catches.) [FEEDBACK_USED]</violation>

<violation number="2">
P2: Polling is now per-button interval polling, which can multiply identical `listForWorkspace` requests by the number of terminal panes in a workspace. This creates avoidable renderer/network load under multi-terminal usage.</violation>
</file>

<file name="apps/desktop/src/renderer/routes/_authenticated/_dashboard/utils/workspace-navigation.ts">

<violation number="1">
P1: This now defaults to push navigation for V2 workspace switches, which reintroduces route-history growth during rapid switching. Preserve the previous `replace` default when already inside a V2 workspace route.</violation>
</file>

<file name="apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/DashboardSidebar/components/DashboardSidebarWorkspaceItem/hooks/useDashboardSidebarWorkspaceItemActions/useDashboardSidebarWorkspaceItemActions.ts">

<violation number="1">
P2: Direct navigation omits `search`, which preserves current query params and can carry workspace-specific route state into the next workspace. Explicitly clear search params here to keep sidebar workspace switching deterministic.</violation>
</file>

Tip: Review your code locally with the cubic CLI to iterate faster.
Re-trigger cubic

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