Skip to content

Quick fixes: chat UX, feedback, diagnostics, opencode, MCP#144

Merged
arul28 merged 2 commits intomainfrom
quick-fixes
Apr 9, 2026
Merged

Quick fixes: chat UX, feedback, diagnostics, opencode, MCP#144
arul28 merged 2 commits intomainfrom
quick-fixes

Conversation

@arul28
Copy link
Copy Markdown
Owner

@arul28 arul28 commented Apr 9, 2026

Summary

  • Chat surface: subagent strip, task panels, work log, terminal drawer, git toolbar, turn divider, command menu polish
  • Feedback reporter: modal overhaul with improved UX
  • Diagnostics dashboard: enhanced section with test coverage
  • OpenCode: inventory and runtime hardening, new server manager
  • MCP server: implementation and test improvements
  • Shared: model catalog refinements, provider selector updates, Claude cache TTL badge/utilities
  • Orchestrator: coordinator agent and AI orchestrator service updates

Test plan

  • Verify chat surface components render correctly
  • Test feedback reporter modal flow
  • Confirm diagnostics dashboard loads without errors
  • Validate OpenCode runtime and inventory behavior
  • Run MCP server tests
  • Check model catalog and provider selector functionality

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features

    • Claude plan mode for structured reasoning workflows
    • OpenCode runtime diagnostics surfaced in the diagnostics dashboard
    • Claude prompt cache TTL indicator for idle sessions
    • Subagent panel with timeline/detail view
    • In-chat file diff viewer (Tasks panel)
    • Animated “thinking” brain Lottie for reasoning states
  • Improvements

    • Refined chat UI (composer glow, updated send/stop controls, visual polish)
    • Git workflow: “Stage & Commit” with implicit AI commit generation
    • Resizable terminal drawer and improved diagnostics polling

… runtime, MCP server improvements

Broad set of incremental improvements across the desktop app:
- Chat surface: subagent strip, task panels, work log, terminal drawer, git toolbar polish
- Feedback reporter modal overhaul
- Diagnostics dashboard enhancements
- OpenCode inventory and runtime hardening with server manager
- MCP server test and implementation updates
- Model catalog and provider selector refinements
- Claude cache TTL badge and utilities
- Coordinator agent and orchestrator service updates

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@vercel
Copy link
Copy Markdown

vercel bot commented Apr 9, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
ade Ready Ready Preview, Comment Apr 9, 2026 5:28am

@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Apr 9, 2026

Caution

Review failed

Pull request was closed or merged during review

📝 Walkthrough

Walkthrough

Introduces a lease-based OpenCode server manager and runtime integration (shared/dedicated leases, dynamic MCP attach/fallback), Claude plan-mode and idle-tracking in chat service, structured feedback generation, new OpenCode diagnostics IPC + preload API, many renderer UI updates (components, styling, Lottie asset), and multiple tests/type additions.

Changes

Cohort / File(s) Summary
OpenCode server manager & runtime
apps/desktop/src/main/services/opencode/openCodeServerManager.ts, apps/desktop/src/main/services/opencode/openCodeRuntime.ts, apps/desktop/src/main/services/opencode/openCodeServerManager.test.ts, apps/desktop/src/main/services/opencode/openCodeRuntime.test.ts
New centralized lease manager (shared/dedicated), refactored runtime to acquire/release leases, dynamic MCP registration with retry/fallback, added runtime diagnostics snapshot APIs and test coverage.
OpenCode inventory & integration
apps/desktop/src/main/services/opencode/openCodeInventory.ts, .../openCodeInventory.test.ts
Migrated inventory to use shared-server leasing, stable fingerprinting, discovery handling tweaks, and updated shutdown to use shared-server manager.
Main process / IPC / preload
apps/desktop/src/main/main.ts, apps/desktop/src/main/services/ipc/registerIpc.ts, apps/desktop/src/preload/preload.ts, apps/desktop/src/preload/global.d.ts, apps/desktop/src/shared/ipc.ts
Reordered startup flow for user-selected projects, swapped shutdown call to server manager, added aiGetOpenCodeRuntimeDiagnostics IPC and preload exposure.
Chat service & orchestrator
apps/desktop/src/main/services/chat/agentChatService.ts, ...agentChatService.test.ts, apps/desktop/src/main/services/orchestrator/aiOrchestratorService.ts, .../coordinatorAgent.ts, .../providerOrchestratorAdapter.ts
Reduced session inactivity TTL, added idleSinceAt persistence, Claude plan-mode transitions/guards, richer OpenCode teardown reasons/busy/eviction handling, coordinator resume/end changes, and workspace-binding propagation for MCP launches.
Feedback reporter
apps/desktop/src/main/services/feedback/feedbackReporterService.ts, .../feedbackReporterService.test.ts
Switched to structured-output-driven AI parsing with schema, normalization/whitelist/fallback drafting, improved error classification and GitHub posting error handling; new tests.
MCP launch / runtime helpers
apps/desktop/src/main/services/runtime/adeMcpLaunch.ts, .../adeMcpLaunch.test.ts, apps/mcp-server/src/mcpServer.ts, .../mcpServer.test.ts
Added workspaceBinding option (explicit vs project_root), adjusted resolved workspaceRoot derivation, lane/worktree resolution and caller-context enrichment for MCP operations and worker launches.
Types / IPC surface
apps/desktop/src/shared/types/*, apps/desktop/src/types/opencode-ai-sdk.d.ts
Added OpenCode runtime snapshot/diagnostic types, extended chat/session types with idleSinceAt/chatIdleSinceAt, and ambient types for @opencode-ai/sdk; new IPC key constant.
Renderer: new components & UI changes
apps/desktop/src/renderer/components/chat/ChatSubagentsPanel.tsx, ChatTasksPanel.tsx, BottomDrawerSection.tsx, ClaudeCacheTtlBadge.tsx, FeedbackReporterModal.tsx, DiagnosticsDashboardSection.tsx, ModelCatalogPanel.tsx, AgentChat*, ChatComposerShell.tsx, ChatTerminalDrawer.tsx, ...
Added bottom-drawer panels (subagents/tasks), Claude cache TTL badge, many component refactors and style/theme changes, Lottie thinking animation integration, expanded tests for UI behaviors. (See many renderer files grouped by feature.)
Renderer utilities & assets
apps/desktop/src/renderer/lib/claudeCacheTtl.ts, .../claudeCacheTtl.test.ts, apps/desktop/src/renderer/assets/brain-thinking.json, apps/desktop/package.json, apps/desktop/src/test/__mocks__/lottie-react.js, apps/desktop/vitest.workspace.ts, apps/desktop/src/renderer/index.css
Added Claude TTL utilities/tests, Lottie animation asset, added lottie-react dependency and test mock, vitest alias, and expanded CSS theme/animation variables.
Tests: additions & removals
apps/desktop/src/main/.../*.test.ts, apps/desktop/src/renderer/.../*.test.ts, apps/mcp-server/src/*.test.ts
Many new tests for OpenCode manager/runtime, orchestrator/coordinator, feedback, diagnostics, renderer components; some older renderer tests removed or trimmed.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

Suggested labels

desktop, mcp

✨ 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 quick-fixes

Copy link
Copy Markdown

@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: 11

Note

Due to the large number of review comments, Critical, Major severity comments were prioritized as inline comments.

Caution

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

⚠️ Outside diff range comments (11)
apps/desktop/src/renderer/components/chat/ChatCommandMenu.tsx (1)

314-342: ⚠️ Potential issue | 🟡 Minor

Escape still won't close the menu when the ref isn't ready.

onClose is now wired in, but if (!handle) return false; runs before the Escape case, so the new close path is bypassed whenever menuRef.current is null. That leaves a small but real dismissal gap during mount/unmount timing.

🐛 Suggested fix
 export function handleCommandMenuKeyDown(
   e: React.KeyboardEvent,
   menuRef: React.RefObject<ChatCommandMenuHandle | null>,
   onClose?: () => void,
 ): boolean {
-  const handle = menuRef.current;
-  if (!handle) return false;
-
   switch (e.key) {
     case "ArrowUp": {
+      const handle = menuRef.current;
+      if (!handle) return false;
       e.preventDefault();
       handle.moveUp();
       return true;
     }
     case "ArrowDown": {
+      const handle = menuRef.current;
+      if (!handle) return false;
       e.preventDefault();
       handle.moveDown();
       return true;
     }
     case "Enter":
     case "Tab": {
+      const handle = menuRef.current;
+      if (!handle) return false;
       e.preventDefault();
       handle.selectCurrent();
       return true;
     }
     case "Escape": {
       e.preventDefault();
       onClose?.();
       return true;
     }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/desktop/src/renderer/components/chat/ChatCommandMenu.tsx` around lines
314 - 342, The handler guard `if (!handle) return false;` prevents Escape from
closing the menu when `menuRef.current` is null; update handleCommandMenuKeyDown
to handle the "Escape" key before bailing out (or move the null-check after the
Escape branch) so that onClose is invoked even if `menuRef.current` is not
ready; reference the function handleCommandMenuKeyDown, the parameters menuRef
and onClose, and ensure Escape still calls e.preventDefault() and onClose?.()
and returns true.
apps/desktop/src/renderer/components/chat/AgentChatMessageList.tsx (2)

788-799: ⚠️ Potential issue | 🟡 Minor

Respect animate={false} for historical activity rows.

Callers already pass a non-live state, but this component always renders the ping/dot animation. Older activity entries will still look active after the turn is over.

Suggested fix
-function ActivityIndicator({ activity, detail }: { activity: string; detail?: string; animate?: boolean }) {
+function ActivityIndicator({ activity, detail, animate = true }: { activity: string; detail?: string; animate?: boolean }) {
   const label = ACTIVITY_LABELS[activity] ?? activity;
-  const displayText = detail ? `${label}: ${replaceInternalToolNames(detail)}` : `${label}...`;
+  const displayText = detail
+    ? `${label}: ${replaceInternalToolNames(detail)}`
+    : animate
+      ? `${label}...`
+      : label;
 
   return (
     <div className="flex items-center gap-2.5 py-1.5 font-sans text-[12px] text-emerald-200/80">
       <span className="relative flex items-center justify-center">
-        <span className="absolute h-5 w-5 animate-ping rounded-full bg-emerald-400/10" style={{ animationDuration: '2s' }} />
-        <ThinkingDots toneClass="bg-emerald-400/80" />
+        {animate ? (
+          <>
+            <span className="absolute h-5 w-5 animate-ping rounded-full bg-emerald-400/10" style={{ animationDuration: "2s" }} />
+            <ThinkingDots toneClass="bg-emerald-400/80" />
+          </>
+        ) : (
+          <Circle size={8} weight="fill" className="text-emerald-400/70" />
+        )}
       </span>
       <span className="truncate font-medium">{displayText}</span>
     </div>
   );
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/desktop/src/renderer/components/chat/AgentChatMessageList.tsx` around
lines 788 - 799, ActivityIndicator declares an animate prop but ignores it, so
historical (non-live) rows still show the ping/dot animation; update
ActivityIndicator to respect animate by defaulting it to true and conditionally
rendering the ping span only when animate is true, and pass the animate value
into the ThinkingDots call (or adjust its props) so ThinkingDots can disable its
internal animation when animate is false; modify the display logic in the
ActivityIndicator function and the JSX around the absolute ping span and
ThinkingDots usage to be conditional on the animate prop.

1963-2019: ⚠️ Potential issue | 🟠 Major

Keep the approval/request summary in the transcript.

bodyText is still derived here, but the rendered card only shows generic labels like “Waiting for input” or “Approval required”. Once the composer banner is gone, the transcript no longer tells the user what was blocked or what response was needed.

Suggested fix
     return (
       <div className={cn(GLASS_CARD_CLASS, "px-4 py-2.5")} style={SURFACE_INLINE_CARD_STYLE}>
         <div className="flex items-center gap-2">
           {isResolved ? (
             <span
@@
             </>
           )}
         </div>
+        {bodyText ? (
+          <div className="mt-1.5 text-[12px] leading-relaxed text-fg/58">
+            {bodyText}
+          </div>
+        ) : null}
       </div>
     );

Based on learnings, "Keep user-facing copy concrete and stateful: say what changed, what is blocked, and what the next action is".

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/desktop/src/renderer/components/chat/AgentChatMessageList.tsx` around
lines 1963 - 2019, The rendered card currently shows only generic labels but
already computes bodyText (from requestDescription, primaryQuestionText,
question, event.description); update the JSX in AgentChatMessageList.tsx (inside
the return block that renders resolvedLabel/isResolved and the status text) to
include bodyText in the transcript UI — e.g., render bodyText as a short,
stateful summary line or paragraph under the status span (for both resolved and
unresolved branches), preserving existing styling logic (isPlanApproval,
isAskUser, isResolved, resolvedLabel) and optionally truncate/ellipsis long
bodyText for layout.
apps/desktop/src/main/services/orchestrator/coordinatorAgent.test.ts (1)

5-9: ⚠️ Potential issue | 🟠 Major

Make the mocked OpenCode handle lifecycle instance-scoped.

Every mocked session handle shares mockState.close, so the new eviction-handler test cannot actually prove that only the stale handle was closed. close needs to be per-handle, just like touch, setBusy, and setEvictionHandler.

Suggested fix
 const mockState = vi.hoisted(() => ({
   eventBatches: [] as Array<any[]>,
   promptAsync: vi.fn(async () => undefined),
   close: vi.fn(),
+  handles: [] as Array<{
+    close: ReturnType<typeof vi.fn>;
+    touch: ReturnType<typeof vi.fn>;
+    setBusy: ReturnType<typeof vi.fn>;
+    setEvictionHandler: ReturnType<typeof vi.fn>;
+  }>,
 }));
@@
-  startOpenCodeSession: vi.fn(async (args: { directory: string }) => ({
-    client: {
-      session: {
-        promptAsync: mockState.promptAsync,
-      },
-    },
-    server: { url: "http://127.0.0.1:4096", close: mockState.close },
-    sessionId: "session-1",
-    directory: args.directory,
-    close: mockState.close,
-    touch: vi.fn(),
-    setBusy: vi.fn(),
-    setEvictionHandler: vi.fn(),
-  })),
+  startOpenCodeSession: vi.fn(async (args: { directory: string }) => {
+    const handleSpies = {
+      close: vi.fn(),
+      touch: vi.fn(),
+      setBusy: vi.fn(),
+      setEvictionHandler: vi.fn(),
+    };
+    mockState.handles.push(handleSpies);
+    return {
+      client: {
+        session: {
+          promptAsync: mockState.promptAsync,
+        },
+      },
+      server: { url: "http://127.0.0.1:4096", close: mockState.close },
+      sessionId: "session-1",
+      directory: args.directory,
+      ...handleSpies,
+    };
+  }),
@@
 beforeEach(() => {
   mockState.eventBatches.length = 0;
+  mockState.handles.length = 0;
   mockState.promptAsync.mockReset();
   mockState.close.mockReset();
 });

Also applies to: 18-31, 175-179

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/desktop/src/main/services/orchestrator/coordinatorAgent.test.ts` around
lines 5 - 9, The mock OpenCode handle shares a single mockState.close which
makes lifecycle tests invalid; update the hoisted mock to create per-handle
closures so each handle gets its own close mock (just like touch, setBusy,
setEvictionHandler and promptAsync), e.g. change mockState to produce a factory
that returns a new object for each handle with its own close() mock and ensure
tests that construct multiple handles call that factory so the eviction-handler
test can assert only the stale handle's close was called; update all usages
(lines referenced around mockState and the other handle property mocks) to use
the per-handle instance returned by the factory.
apps/desktop/src/main/services/orchestrator/aiOrchestratorService.ts (1)

6247-6265: ⚠️ Potential issue | 🟠 Major

Ensure coordinator shutdown runs even when pauseRun fails.

Right now, if orchestratorService.pauseRun(...) throws, Line 6264 is skipped and the coordinator can remain alive while the intervention path still proceeds.

💡 Suggested fix
-    try {
+    try {
       orchestratorService.pauseRun({
         runId: args.runId,
         reason: `${args.title}: ${args.body}`.slice(0, 400),
         metadata: {
           aiDecisions: {
             ...existingAiDecisionMeta,
             lastFailureAt: nowIso(),
             lastFailureSource: args.source,
             lastFailureReason: args.reasonCode
           }
         }
       });
       const evalTimer = pendingCoordinatorEvals.get(args.runId);
       if (evalTimer) {
         clearTimeout(evalTimer);
         pendingCoordinatorEvals.delete(args.runId);
       }
-      endCoordinatorAgentV2(args.runId);
     } catch (error) {
       logger.debug("ai_orchestrator.pause_run_failed", {
         runId: args.runId,
         source: args.source,
         reasonCode: args.reasonCode,
         error: error instanceof Error ? error.message : String(error)
       });
+    } finally {
+      endCoordinatorAgentV2(args.runId);
     }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/desktop/src/main/services/orchestrator/aiOrchestratorService.ts` around
lines 6247 - 6265, The call to orchestratorService.pauseRun can throw and
currently prevents cleanup (clearing pendingCoordinatorEvals and calling
endCoordinatorAgentV2); wrap the pauseRun call so its errors are caught and do
not short-circuit shutdown: move the pendingCoordinatorEvals lookup/clear and
endCoordinatorAgentV2(args.runId) into a finally block (or after a catch) so
they always execute, and when catching pauseRun errors log them (preserve
existing metadata handling) but continue to clear the eval timer from
pendingCoordinatorEvals and call endCoordinatorAgentV2(args.runId).
apps/desktop/src/main/services/opencode/openCodeInventory.ts (1)

53-62: ⚠️ Potential issue | 🟡 Minor

Keep peekOpenCodeInventoryCache() aligned with the new fingerprint inputs.

fingerprintOpenCodeConfig() now folds discoveredLocalModels into the cache key, but peekOpenCodeInventoryCache() still recomputes the fingerprint without them. Any probe that ran with discovered local models will therefore miss this cache path even when the project/config is otherwise unchanged.

Suggested fix
 export function peekOpenCodeInventoryCache(args: {
   projectRoot: string;
   projectConfig: ProjectConfigFile | EffectiveProjectConfig;
+  discoveredLocalModels?: DiscoveredLocalModelEntry[];
 }): { modelIds: string[]; providers: OpenCodeProviderInfo[]; error: string | null } | null {
-  const fp = fingerprintOpenCodeConfig(args.projectConfig);
+  const fp = fingerprintOpenCodeConfig(args.projectConfig, args.discoveredLocalModels);
   if (!inventoryCache) return null;
   if (inventoryCache.projectRoot !== args.projectRoot || inventoryCache.configFingerprint !== fp) return null;
   return { modelIds: inventoryCache.modelIds, providers: inventoryCache.providers, error: inventoryCache.error };
 }

Also applies to: 257-265

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/desktop/src/main/services/opencode/openCodeInventory.ts` around lines 53
- 62, peekOpenCodeInventoryCache() currently recomputes the cache fingerprint
without including discoveredLocalModels while fingerprintOpenCodeConfig() now
includes them, causing cache misses; update peekOpenCodeInventoryCache to accept
an optional discoveredLocalModels parameter and pass it into
fingerprintOpenCodeConfig(projectConfig, discoveredLocalModels) so the same key
is produced, and update any other call sites that compute the fingerprint
(including the other occurrence around the probe/cache lookup) to likewise
forward discoveredLocalModels to fingerprintOpenCodeConfig to keep keys
consistent.
apps/desktop/src/renderer/components/settings/DiagnosticsDashboardSection.tsx (2)

272-279: ⚠️ Potential issue | 🟡 Minor

Reuse the sequenced OpenCode refresher in the full-check path.

This branch bypasses refreshOpenCodeSnapshot(), so it never advances openCodeRequestSeqRef. A slower poll that started earlier can still resolve afterward and overwrite the newer full-check snapshot with stale data.

Suggested fix
-      const [refreshed, refreshedOpenCode] = await Promise.all([
-        window.ade.lanes.diagnosticsGetStatus(),
-        window.ade.ai.getOpenCodeRuntimeDiagnostics().catch(() => openCode),
-      ]);
+      const [refreshed, refreshedOpenCode] = await Promise.all([
+        window.ade.lanes.diagnosticsGetStatus(),
+        refreshOpenCodeSnapshot().then((snapshot) => snapshot ?? openCode),
+      ]);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@apps/desktop/src/renderer/components/settings/DiagnosticsDashboardSection.tsx`
around lines 272 - 279, In handleRunFullCheck, don’t call
window.ade.ai.getOpenCodeRuntimeDiagnostics() directly (which bypasses
refreshOpenCodeSnapshot and thus never advances openCodeRequestSeqRef); instead
await and use refreshOpenCodeSnapshot() to obtain the open-code snapshot so the
openCodeRequestSeqRef is incremented and prevents stale poll overwrites—replace
the second Promise in the Promise.all with a call to refreshOpenCodeSnapshot()
(still catching errors as needed) and use its result as refreshedOpenCode while
keeping the diagnosticsRunFullCheck()/diagnosticsGetStatus() flow intact.

173-218: ⚠️ Potential issue | 🟡 Minor

Honor openCodeMode="hidden" before fetching or rendering OpenCode state.

The component still does the initial OpenCode fetch and starts the 5s poll when the mode is "hidden", and openCodeUnavailable can still force the unavailable card to render. That makes the hidden mode both noisy and semantically incorrect for callers that want this block fully suppressed.

Suggested fix
-        await refreshOpenCodeSnapshot(true);
+        if (openCodeMode !== "hidden") {
+          await refreshOpenCodeSnapshot(true);
+        }
 ...
-          await refreshOpenCodeSnapshot(true);
+          if (openCodeMode !== "hidden") {
+            await refreshOpenCodeSnapshot(true);
+          }
 ...
-    const openCodePoll = window.setInterval(() => {
-      void refreshOpenCodeSnapshot();
-    }, 5_000);
+    const openCodePoll = openCodeMode === "hidden"
+      ? null
+      : window.setInterval(() => {
+          void refreshOpenCodeSnapshot();
+        }, 5_000);
 ...
-      window.clearInterval(openCodePoll);
+      if (openCodePoll != null) {
+        window.clearInterval(openCodePoll);
+      }
 ...
-  }, [syncLastChecked, refreshOpenCodeSnapshot]);
+  }, [openCodeMode, syncLastChecked, refreshOpenCodeSnapshot]);

-  const showOpenCodeSection = openCodeMode === "full"
+  const showOpenCodeSection = openCodeMode !== "hidden" && (
+    openCodeMode === "full"
     || openCodeUnavailable
     || (openCodeMode === "issues-only"
-      && ((dedicatedOpenCodeCount ?? 0) > 0 || (openCodeFallbackCount ?? 0) > 0 || (openCodeRetryCount ?? 0) > 0));
+      && ((dedicatedOpenCodeCount ?? 0) > 0 || (openCodeFallbackCount ?? 0) > 0 || (openCodeRetryCount ?? 0) > 0))
+  );

Also applies to: 327-336

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@apps/desktop/src/renderer/components/settings/DiagnosticsDashboardSection.tsx`
around lines 173 - 218, The component fetches and polls OpenCode even when
openCodeMode === "hidden"; update the useEffect that calls diagnosticsGetStatus,
refreshOpenCodeSnapshot, sets openCode state (setOpenCode) and starts the 5s
openCodePoll so that any OpenCode-specific work is skipped when openCodeMode ===
"hidden". Concretely: in the useEffect containing loadStatus, check if
openCodeMode === "hidden" and if so avoid calling refreshOpenCodeSnapshot(),
avoid setting openCode/unavailable state
(setOpenCode/setLastChecked/openCodeUnavailable), and do not start the
window.setInterval openCodePoll; keep diagnosticsGetStatus and lane checks but
only run OpenCode fetch/poll when openCodeMode !== "hidden", and ensure the
interval is not created in hidden mode and is still cleared on cleanup.
apps/desktop/src/renderer/index.css (1)

2028-2038: ⚠️ Potential issue | 🟡 Minor

Disable ade-thinking-pulse under reduced motion too.

The reduced-motion block was updated for several new effects, but .ade-thinking-pulse is still left running. Since the updated keyframe now scales as well as fades, users with prefers-reduced-motion: reduce will still get the pulsing indicator.

Suggested fix
   .ade-streaming-shimmer,
   .ade-streaming-shimmer::after,
   .ade-tool-bounce,
   .ade-fade-in,
+  .ade-thinking-pulse,
   .ade-glow-pulse,
   .ade-glow-pulse-blue,
   .ade-glow-pulse-red,
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/desktop/src/renderer/index.css` around lines 2028 - 2038, The
reduced-motion CSS block omitted the .ade-thinking-pulse class, so users with
prefers-reduced-motion still see its scale/fade animation; add
.ade-thinking-pulse to the selector list alongside .ade-streaming-shimmer,
.ade-tool-bounce, .ade-fade-in, .ade-glow-pulse, .ade-glow-pulse-blue,
.ade-glow-pulse-red, .ade-glow-pulse-amber, .ade-conflict-breathe, .ade-float,
and .pull-btn-flash so that .ade-thinking-pulse is included in the rule that
disables animations under `@media` (prefers-reduced-motion: reduce).
apps/desktop/src/main/services/chat/agentChatService.ts (1)

13059-13068: ⚠️ Potential issue | 🔴 Critical

Only evict runtimes after they are actually idle.

This now tears down any live runtime whose lastActivityTimestamp is older than 5 minutes, including active turns. A long tool call with no streamed events will get killed mid-turn, which is the same false-positive class the comment above says was removed. Gate this on managed.session.status === "idle" and age from idleSinceAt instead.

Suggested fix
   const sessionCleanupTimer = setInterval(() => {
     const now = Date.now();
     for (const [, managed] of managedSessions) {
-      if (
-        managed.runtime
-        && !managed.closed
-        && now - managed.lastActivityTimestamp > SESSION_INACTIVITY_TIMEOUT_MS
-      ) {
+      if (!managed.runtime || managed.closed) continue;
+      if (managed.session.status !== "idle") continue;
+
+      const idleSinceMs = managed.session.idleSinceAt
+        ? Date.parse(managed.session.idleSinceAt)
+        : NaN;
+      const idleForMs = Number.isFinite(idleSinceMs)
+        ? now - idleSinceMs
+        : now - managed.lastActivityTimestamp;
+
+      if (idleForMs > SESSION_INACTIVITY_TIMEOUT_MS) {
         teardownRuntime(managed, "idle_ttl");
       }
     }
   }, SESSION_CLEANUP_INTERVAL_MS);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/desktop/src/main/services/chat/agentChatService.ts` around lines 13059 -
13068, The current interval tears down live runtimes based on
managed.lastActivityTimestamp and can kill a runtime mid-turn; change the check
to only evict when the session is actually idle by verifying
managed.session.status === "idle" and computing age from
managed.session.idleSinceAt (i.e., if managed.runtime && !managed.closed &&
managed.session?.status === "idle" && now - managed.session.idleSinceAt >
SESSION_INACTIVITY_TIMEOUT_MS) then call teardownRuntime(managed, "idle_ttl");
ensure you reference managedSessions, teardownRuntime,
SESSION_INACTIVITY_TIMEOUT_MS, managed.session.status, and idleSinceAt when
making the change.
apps/desktop/src/main/services/opencode/openCodeRuntime.ts (1)

661-678: ⚠️ Potential issue | 🟠 Major

Release the lease when session.create() throws.

If await client.session.create(...) rejects, this function exits before the !created.data cleanup runs. That leaves the runtime leased (refCount never drops) and can also leak an attached dynamic MCP registration.

🧹 Proposed fix
-  const created = await client.session.create({
-    query: { directory: args.directory },
-    body: { title: args.title },
-  });
+  let created;
+  try {
+    created = await client.session.create({
+      query: { directory: args.directory },
+      body: { title: args.title },
+    });
+  } catch (error) {
+    await dynamicMcp?.disconnect().catch(() => {});
+    lease.close("error");
+    throw error;
+  }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/desktop/src/main/services/opencode/openCodeRuntime.ts` around lines 661
- 678, Wrap the client.session.create call in a try/catch so that if
client.session.create(...) throws you perform the same cleanup as the existing
!created.data branch: call dynamicMcp?.disconnect().catch(() => {}) and
lease.close("error") before rethrowing the error; keep the happy-path return
using createOpenCodeSessionHandle unchanged and only move the existing
null-payload cleanup into the try and the thrown-error cleanup into the catch so
the lease and dynamic MCP are always released.
🟡 Minor comments (11)
apps/mcp-server/src/mcpServer.test.ts-134-137 (1)

134-137: ⚠️ Potential issue | 🟡 Minor

Make the lane worktree mock fail closed.

Falling back to laneRows[0] means any bug that drops or mangles laneId still resolves to a valid worktree, so the new spawn/lane-path tests can pass for the wrong reason.

Suggested fix
       getLaneWorktreePath: vi.fn((laneId: string) => {
-        const lane = laneRows.find((row) => row.id === laneId) ?? laneRows[0]!;
-        return lane.worktreePath;
+        const lane = laneRows.find((row) => row.id === laneId);
+        return lane?.worktreePath ?? null;
       }),

As per coding guidelines, apps/mcp-server/**: MCP server — check for proper protocol compliance, error handling, and API security.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/mcp-server/src/mcpServer.test.ts` around lines 134 - 137, The mock for
getLaneWorktreePath currently falls back to laneRows[0], hiding missing or
incorrect laneId; change getLaneWorktreePath (the vi.fn mock) to fail closed by
throwing an error or returning undefined when no matching lane is found (i.e.,
remove the "?? laneRows[0]!" fallback), so tests will surface invalid/mangled
laneId inputs instead of silently using the first lane; update any tests that
expect an error accordingly.
apps/mcp-server/src/mcpServer.test.ts-2628-2663 (1)

2628-2663: ⚠️ Potential issue | 🟡 Minor

Assert that ADE_CHAT_SESSION_ID is the source of truth here.

This test sets ADE_CHAT_SESSION_ID="chat-1" and initialize(... chatSessionId: "chat-from-identity"), but the mock returns the same payload for any input and never verifies which session ID was used. A regression back to trusting the initialize payload would still pass.

Suggested fix
       const response = await callTool(handler, "memory_update_core", {
         projectSummary: "Worker-specific checkout strategy",
         activeFocus: ["checkout reliability"],
       });
 
       expect(response?.isError).toBeUndefined();
+      expect(fixture.runtime.agentChatService.getSessionSummary).toHaveBeenCalledWith("chat-1");
       expect(fixture.runtime.workerAgentService.updateCoreMemory).toHaveBeenCalledWith(
         "worker-agent-1",
         expect.objectContaining({
           projectSummary: "Worker-specific checkout strategy",

As per coding guidelines, apps/mcp-server/**: MCP server — check for proper protocol compliance, error handling, and API security.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/mcp-server/src/mcpServer.test.ts` around lines 2628 - 2663, The test
currently lets getSessionSummary accept any sessionId so it doesn't verify
ADE_CHAT_SESSION_ID is used; update the test to assert the handler called
agentChatService.getSessionSummary with the ADE_CHAT_SESSION_ID value (or set
process.env.ADE_CHAT_SESSION_ID to the intended id and then expect the mock to
be invoked with that exact string). Concretely, adjust the getSessionSummary
mock or add an explicit
expect(fixture.runtime.agentChatService.getSessionSummary).toHaveBeenCalledWith(process.env.ADE_CHAT_SESSION_ID)
after initialize/callTool, and keep the rest of the assertions on
workerAgentService.updateCoreMemory and ctoStateService.updateCoreMemory
unchanged.
apps/desktop/src/renderer/components/chat/ChatTasksPanel.tsx-80-108 (1)

80-108: ⚠️ Potential issue | 🟡 Minor

Dead code: The i < existing.turnIndex branch is unreachable.

Since the loop iterates forward (i starts at 0 and increases), and turnIndex is set to i when a file is first seen, subsequent iterations will always have i >= existing.turnIndex. The branch at lines 87-89 can never execute.

If the intent is to track the earliest beforeSha for a file, the logic should be inverted or simplified since the first occurrence already captures the earliest beforeSha.

♻️ Suggested simplification
 function aggregateFiles(summaries: TurnDiffSummary[]): AggregatedFile[] {
   const byPath = new Map<string, AggregatedFile>();
   for (let i = 0; i < summaries.length; i++) {
     const turn = summaries[i];
     for (const file of summaries[i].files) {
       const existing = byPath.get(file.path);
       if (existing) {
-        if (i < existing.turnIndex) {
-          existing.beforeSha = turn.beforeSha;
-        }
-        if (i >= existing.turnIndex) {
-          existing.afterSha = turn.afterSha;
-          existing.turnIndex = i; // use latest turn
-          existing.status = file.status;
-          existing.additions = file.additions;
-          existing.deletions = file.deletions;
-        }
+        // Update to latest turn's state (keep original beforeSha from first occurrence)
+        existing.afterSha = turn.afterSha;
+        existing.turnIndex = i;
+        existing.status = file.status;
+        existing.additions = file.additions;
+        existing.deletions = file.deletions;
       } else {
         byPath.set(file.path, {
           ...file,
           beforeSha: turn.beforeSha,
           afterSha: turn.afterSha,
           turnIndex: i,
         });
       }
     }
   }
   return [...byPath.values()];
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/desktop/src/renderer/components/chat/ChatTasksPanel.tsx` around lines 80
- 108, In aggregateFiles, the conditional branch that checks `if (i <
existing.turnIndex)` inside the loop is unreachable because `turnIndex` is set
to `i` when a file is first added and the loop advances forward; remove that
branch (the lines that set `existing.beforeSha` in that conditional) and
simplify the merge logic to only update `afterSha`, `turnIndex`, `status`,
`additions`, and `deletions` when encountering a later occurrence; keep the
initial creation branch (where `beforeSha` and `turnIndex` are set) as-is.
apps/desktop/src/main/main.ts-3211-3216 (1)

3211-3216: ⚠️ Potential issue | 🟡 Minor

Log the startup project-switch failure instead of silently falling back.

If ADE_PROJECT_ROOT points at a bad path or a non-repo, this catch now drops back to the dormant state with no trace. That makes explicit startup launches look like ADE ignored the requested project.

🛠️ Suggested fix
-    } catch {
+    } catch (error) {
+      getActiveContext().logger.warn("project.startup_selection_failed", {
+        initialCandidate,
+        error: error instanceof Error ? error.message : String(error),
+      });
       setActiveProject(null);
-      dormantContext = createDormantProjectContext();
+      dormantContext = createDormantProjectContext(initialCandidate);
     }

Based on learnings ADE_PROJECT_ROOT=/workspace env var can auto-open a project at startup, but this path is sensitive to startup timing/race behavior.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/desktop/src/main/main.ts` around lines 3211 - 3216, The catch around
switchProjectFromDialog(initialCandidate) is swallowing errors and silently
falling back to setActiveProject(null)/createDormantProjectContext(); update the
catch to accept the error (e.g., catch (err)) and log a clear, contextual error
message including initialCandidate and the error details before performing the
existing fallback; use the application's logger (or console.error if no logger
symbol exists) so startup failures to open ADE_PROJECT_ROOT are recorded for
debugging while preserving the current fallback behavior.
apps/desktop/src/renderer/components/chat/AgentChatComposer.tsx-857-865 (1)

857-865: ⚠️ Potential issue | 🟡 Minor

Normalize provider aliases before deriving the glow color.

sessionProvider in this component uses runtime values like "claude" and "codex", but this memo only handles "anthropic" and "openai". That drops the glow for active Claude/Codex chats, and the modelId ? "anthropic" fallback paints every model orange when sessionProvider is unset.

♻️ Suggested fix
-  const composerGlowColor = useMemo(() => {
-    const provider = sessionProvider ?? (modelId ? "anthropic" : null);
+  const composerGlowColor = useMemo(() => {
+    const provider =
+      sessionProvider === "claude"
+        ? "anthropic"
+        : sessionProvider === "codex"
+          ? "openai"
+          : sessionProvider ?? selectedModel?.family ?? null;
     if (!provider) return null;
     if (provider === "anthropic") return "rgba(249, 115, 22, 0.25)";
     if (provider === "openai") return "rgba(255, 255, 255, 0.15)";
     if (provider === "cursor") return "rgba(59, 130, 246, 0.25)";
     if (provider === "opencode") return "rgba(255, 255, 255, 0.12)";
     return null;
-  }, [sessionProvider, modelId]);
+  }, [sessionProvider, selectedModel?.family]);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/desktop/src/renderer/components/chat/AgentChatComposer.tsx` around lines
857 - 865, The composerGlowColor memo currently treats sessionProvider and
modelId crudely, causing Claude/Codex to be unmapped and the modelId fallback to
default to "anthropic"; update the logic in the composerGlowColor computation to
first normalize sessionProvider aliases (e.g., map "claude" -> "anthropic",
"codex" -> "openai", and any other runtime aliases) into canonical provider
names, then derive the provider from modelId only when you can reliably map a
modelId to a provider (otherwise use null) so you don't always default to
"anthropic"; adjust the conditional checks that emit the rgba strings to use the
normalized provider value (references: composerGlowColor, sessionProvider,
modelId).
apps/desktop/src/renderer/components/shared/ModelCatalogPanel.tsx-710-713 (1)

710-713: ⚠️ Potential issue | 🟡 Minor

Use sentence case for the panel title.

Select Model should follow the desktop UI copy rule here.

Suggested fix
-            <span className="font-semibold text-[15px] text-fg/[0.94] font-sans">Select Model</span>
+            <span className="font-semibold text-[15px] text-fg/[0.94] font-sans">Select model</span>

As per coding guidelines, "Use sentence case for headings and labels unless the existing UI pattern is intentionally uppercase".

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/desktop/src/renderer/components/shared/ModelCatalogPanel.tsx` around
lines 710 - 713, Update the panel title text in the ModelCatalogPanel component
from "Select Model" to sentence case ("Select model") so it conforms to the
desktop UI copy rule; locate the JSX span inside ModelCatalogPanel (the span
with className "font-semibold text-[15px] text-fg/[0.94] font-sans") and replace
its inner text accordingly.
apps/desktop/src/renderer/components/chat/AgentChatPane.tsx-2078-2084 (1)

2078-2084: ⚠️ Potential issue | 🟡 Minor

Revert the optimistic session-state patch when submit fails.

patchSessionSummary() flips the row to active before send()/steer() succeeds, but the catch path only restores the draft and attachments. A transport/runtime failure leaves status, awaitingInput, and idleSinceAt stuck in the optimistic state until some later refresh happens.

Suggested fix
     } catch (submitError) {
       const message = submitError instanceof Error ? submitError.message : String(submitError);
       setDraft((current) => (current.trim().length ? current : draftSnapshot));
       setAttachments((current) => (current.length ? current : attachmentsSnapshot));
       setOptimisticOutgoingMessage(null);
       setError(message);
+      await refreshSessions().catch(() => {});
       if (
         /ade chat could not authenticate/i.test(message)
         || /not authenticated/i.test(message)
         || /login required/i.test(message)
       ) {

Also applies to: 2123-2135

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/desktop/src/renderer/components/chat/AgentChatPane.tsx` around lines
2078 - 2084, The code optimistically updates session state via
touchSession(sessionId) and patchSessionSummary(sessionId, {...status: "active",
awaitingInput: false, idleSinceAt: null, lastActivityAt: ...}) before calling
send()/steer(), but the catch path only restores draft/attachments; update the
catch handlers in the submit flow that call send() and steer() to also revert
the optimistic session patch by calling patchSessionSummary(sessionId, { status:
previousStatus, awaitingInput: previousAwaitingInput, idleSinceAt:
previousIdleSinceAt, lastActivityAt: previousLastActivityAt }) (capture previous
values before the optimistic update or read them from state) so that failures
restore the prior session fields; apply the same change for the other occurrence
around lines 2123-2135 where the same optimistic update is made.
apps/desktop/src/renderer/index.css-2754-2760 (1)

2754-2760: ⚠️ Potential issue | 🟡 Minor

Use the existing theme shimmer token here.

--chat-streaming-shimmer is already defined for both themes, but this pseudo-element bypasses it and hard-codes a purple sweep. That makes the streaming effect inconsistent in light mode.

Suggested fix
 .ade-streaming-shimmer::after {
   content: '';
   position: absolute;
   inset: 0;
-  background: linear-gradient(90deg, transparent, rgba(167, 139, 250, 0.06), transparent);
+  background: var(--chat-streaming-shimmer);
   animation: ade-streaming-shimmer 2s ease-in-out infinite;
   pointer-events: none;
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/desktop/src/renderer/index.css` around lines 2754 - 2760, The
pseudo-element .ade-streaming-shimmer::after hard-codes a purple linear-gradient
which bypasses the theme token; change the background to use the theme variable
--chat-streaming-shimmer (e.g. background: var(--chat-streaming-shimmer))
instead of the rgba gradient so the shimmer follows both light and dark themes,
keeping the rest of the rules (position, inset, animation, pointer-events)
intact.
apps/desktop/src/renderer/index.css-60-62 (1)

60-62: ⚠️ Potential issue | 🟡 Minor

Sync --color-card-rgb with the new card color.

--color-card changed to #1A1830, but --color-card-rgb still points at the old value. Any translucent surfaces that use rgba(var(--color-card-rgb), …) will now drift from the updated card tone.

Suggested fix
   --color-card: `#1A1830`;
   --color-card-fg: `#F0F0F2`;
-  --color-card-rgb: 28, 25, 38;
+  --color-card-rgb: 26, 24, 48;
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/desktop/src/renderer/index.css` around lines 60 - 62, The CSS variables
are out of sync: update --color-card-rgb to match the new --color-card (`#1A1830`)
by replacing its value with the RGB triple for that hex (26, 24, 48) and verify
any translucent surfaces using rgba(var(--color-card-rgb), …) still produce the
intended tone; adjust if you intended a perceptually different translucency.
apps/desktop/src/renderer/index.css-1953-1960 (1)

1953-1960: ⚠️ Potential issue | 🟡 Minor

Make ade-glow-pulse use theme tokens.

This keyframe is now hard-coded purple, so light theme UI will still pulse purple even though the accent color changes there. Please derive the glow from a theme token instead.

Suggested fix
 `@keyframes` ade-glow-pulse {
   0%,
   100% {
-    box-shadow: 0 0 0 0 rgba(167, 139, 250, 0);
+    box-shadow: 0 0 0 0 transparent;
   }
   50% {
-    box-shadow: 0 0 12px 2px rgba(167, 139, 250, 0.15);
+    box-shadow: 0 0 12px 2px var(--color-glow);
   }
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/desktop/src/renderer/index.css` around lines 1953 - 1960, The
ade-glow-pulse keyframes hard-code a purple RGBA value; change it to derive the
glow from a theme CSS custom property so the pulse follows the current accent
color. Replace rgba(167, 139, 250, ...) in the `@keyframes` ade-glow-pulse with
something like rgba(var(--accent-rgb, 167 139 250), 0) and
rgba(var(--accent-rgb, 167 139 250), 0.15) (or use your project's existing token
name such as --colors-accent-rgb or --accent-rgb) so the 0%/100% and 50% states
use the theme token; ensure the corresponding --*-rgb token is defined or
provide a sensible fallback.
apps/desktop/src/main/services/chat/agentChatService.ts-5080-5088 (1)

5080-5088: ⚠️ Potential issue | 🟡 Minor

Stamp idleSinceAt on idle transitions by default.

setSessionIdle() leaves idleSinceAt untouched unless every caller passes it explicitly. Several new call sites use the bare helper, so sessions can become idle with idleSinceAt = null, which makes the new idle-duration state inconsistent after reopen/failure flows.

Suggested fix
   const setSessionIdle = (
     managed: ManagedChatSession,
     options?: { idleSinceAt?: string | null },
   ): void => {
+    const wasIdle = managed.session.status === "idle";
     managed.session.status = "idle";
     if (options && "idleSinceAt" in options) {
       managed.session.idleSinceAt = options.idleSinceAt ?? null;
+    } else if (!wasIdle) {
+      managed.session.idleSinceAt = nowIso();
     }
   };
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/desktop/src/main/services/chat/agentChatService.ts` around lines 5080 -
5088, setSessionIdle currently leaves managed.session.idleSinceAt unchanged
unless callers pass idleSinceAt; change it to stamp idleSinceAt to the current
time by default when setting status to "idle" while still honoring an explicit
options.idleSinceAt (including null to clear). Update the setSessionIdle
function so after setting managed.session.status = "idle" it assigns
managed.session.idleSinceAt = options && "idleSinceAt" in options ?
options.idleSinceAt ?? null : new Date().toISOString(); (referencing
setSessionIdle and ManagedChatSession.session.idleSinceAt).

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 19b2ec2e-e491-42b2-8032-e859fd901270

📥 Commits

Reviewing files that changed from the base of the PR and between 2295971 and da3e09d.

⛔ Files ignored due to path filters (3)
  • .ade/ade.yaml is excluded by !.ade/**
  • .ade/cto/identity.yaml is excluded by !.ade/**
  • apps/desktop/package-lock.json is excluded by !**/package-lock.json, !**/package-lock.json
📒 Files selected for processing (68)
  • apps/desktop/package.json
  • apps/desktop/src/main/main.ts
  • apps/desktop/src/main/services/chat/agentChatService.test.ts
  • apps/desktop/src/main/services/chat/agentChatService.ts
  • apps/desktop/src/main/services/feedback/feedbackReporterService.test.ts
  • apps/desktop/src/main/services/feedback/feedbackReporterService.ts
  • apps/desktop/src/main/services/ipc/registerIpc.ts
  • apps/desktop/src/main/services/opencode/openCodeInventory.test.ts
  • apps/desktop/src/main/services/opencode/openCodeInventory.ts
  • apps/desktop/src/main/services/opencode/openCodeRuntime.test.ts
  • apps/desktop/src/main/services/opencode/openCodeRuntime.ts
  • apps/desktop/src/main/services/opencode/openCodeServerManager.test.ts
  • apps/desktop/src/main/services/opencode/openCodeServerManager.ts
  • apps/desktop/src/main/services/orchestrator/aiOrchestratorService.test.ts
  • apps/desktop/src/main/services/orchestrator/aiOrchestratorService.ts
  • apps/desktop/src/main/services/orchestrator/coordinatorAgent.test.ts
  • apps/desktop/src/main/services/orchestrator/coordinatorAgent.ts
  • apps/desktop/src/main/services/orchestrator/providerOrchestratorAdapter.ts
  • apps/desktop/src/main/services/runtime/adeMcpLaunch.test.ts
  • apps/desktop/src/main/services/runtime/adeMcpLaunch.ts
  • apps/desktop/src/preload/global.d.ts
  • apps/desktop/src/preload/preload.ts
  • apps/desktop/src/renderer/assets/AI Brain.lottie
  • apps/desktop/src/renderer/assets/brain-thinking.json
  • apps/desktop/src/renderer/components/app/FeedbackReporterModal.test.tsx
  • apps/desktop/src/renderer/components/app/FeedbackReporterModal.tsx
  • apps/desktop/src/renderer/components/chat/AgentChatComposer.tsx
  • apps/desktop/src/renderer/components/chat/AgentChatMessageList.tsx
  • apps/desktop/src/renderer/components/chat/AgentChatPane.submit.test.tsx
  • apps/desktop/src/renderer/components/chat/AgentChatPane.tsx
  • apps/desktop/src/renderer/components/chat/BottomDrawerSection.tsx
  • apps/desktop/src/renderer/components/chat/ChatCommandMenu.tsx
  • apps/desktop/src/renderer/components/chat/ChatComposerShell.tsx
  • apps/desktop/src/renderer/components/chat/ChatGitToolbar.tsx
  • apps/desktop/src/renderer/components/chat/ChatProposedPlanCard.tsx
  • apps/desktop/src/renderer/components/chat/ChatSubagentStrip.tsx
  • apps/desktop/src/renderer/components/chat/ChatSubagentsPanel.test.tsx
  • apps/desktop/src/renderer/components/chat/ChatSubagentsPanel.tsx
  • apps/desktop/src/renderer/components/chat/ChatSurfaceShell.tsx
  • apps/desktop/src/renderer/components/chat/ChatTasksPanel.tsx
  • apps/desktop/src/renderer/components/chat/ChatTerminalDrawer.tsx
  • apps/desktop/src/renderer/components/chat/ChatTurnDivider.tsx
  • apps/desktop/src/renderer/components/chat/ChatWorkLogBlock.tsx
  • apps/desktop/src/renderer/components/chat/chatExecutionSummary.ts
  • apps/desktop/src/renderer/components/chat/chatStatusVisuals.tsx
  • apps/desktop/src/renderer/components/chat/chatSurfaceTheme.ts
  • apps/desktop/src/renderer/components/chat/chatTranscriptRows.ts
  • apps/desktop/src/renderer/components/missions/MissionThreadMessageList.tsx
  • apps/desktop/src/renderer/components/run/RunNetworkPanel.tsx
  • apps/desktop/src/renderer/components/settings/DiagnosticsDashboardSection.test.tsx
  • apps/desktop/src/renderer/components/settings/DiagnosticsDashboardSection.tsx
  • apps/desktop/src/renderer/components/shared/ClaudeCacheTtlBadge.tsx
  • apps/desktop/src/renderer/components/shared/ModelCatalogPanel.tsx
  • apps/desktop/src/renderer/components/shared/ProviderModelSelector.tsx
  • apps/desktop/src/renderer/components/terminals/SessionCard.tsx
  • apps/desktop/src/renderer/components/terminals/WorkStartSurface.tsx
  • apps/desktop/src/renderer/components/terminals/WorkViewArea.tsx
  • apps/desktop/src/renderer/index.css
  • apps/desktop/src/renderer/lib/claudeCacheTtl.test.ts
  • apps/desktop/src/renderer/lib/claudeCacheTtl.ts
  • apps/desktop/src/renderer/lib/sessions.ts
  • apps/desktop/src/shared/ipc.ts
  • apps/desktop/src/shared/types/chat.ts
  • apps/desktop/src/shared/types/config.ts
  • apps/desktop/src/shared/types/sessions.ts
  • apps/desktop/src/types/opencode-ai-sdk.d.ts
  • apps/mcp-server/src/mcpServer.test.ts
  • apps/mcp-server/src/mcpServer.ts

…move brittle UI render tests

- Add global lottie-react stub (vitest.workspace.ts) to prevent jsdom canvas crashes
- Fix MCP server test: align identity.missionId with env ADE_MISSION_ID
- Fix AgentChatPane test: add missing git/lanes/prs mocks, update button text
- Remove 10 brittle AgentChatMessageList render tests broken by ChatWorkLogBlock redesign
- Remove stale CtoPage, CommitTimeline, missionHelpers, PrAiResolverPanel tests

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@arul28 arul28 merged commit ef670af into main Apr 9, 2026
21 of 22 checks passed
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