Skip to content

ADE-96: Auto-create lane loading and error states break in new chat pane#513

Merged
arul28 merged 8 commits into
mainfrom
ade-96-auto-create-lane-loading-and-error-states-break-in-new-chat-pane
Jun 2, 2026
Merged

ADE-96: Auto-create lane loading and error states break in new chat pane#513
arul28 merged 8 commits into
mainfrom
ade-96-auto-create-lane-loading-and-error-states-break-in-new-chat-pane

Conversation

@arul28
Copy link
Copy Markdown
Owner

@arul28 arul28 commented Jun 2, 2026

Fixes ADE-96

Summary

Describe the change.

What Changed

Key files and behaviors.

Validation

How you tested.

Risks

Anything to watch.

Linked Linear issues

ADE   Open in ADE  ·  ade-96-auto-create-lane-loading-and-error-states-break-in-new-chat-pane branch  ·  PR #513

Summary by CodeRabbit

  • New Features

    • Added a toggle in settings to show/hide clipboard reminder notices when copying prompts to clipboard.
    • Enhanced draft launch job persistence to maintain launch status across page reloads.
  • Bug Fixes

    • Fixed draft launches being incorrectly shared across multiple lane panes; now properly scoped to individual panes.
    • Improved handling of stale pending launches with better detection and visibility controls.
  • Tests

    • Added comprehensive test coverage for draft launch job lifecycle and scoping behavior.

Greptile Summary

This PR fixes scoping bugs where draft launch job state (loading/error strips in the chat pane) was incorrectly shared across lane panes or lost on remount. It moves draftLaunchJobs from component-local useState into the global appStore keyed by projectRoot + laneId + surfaceProfile + draftKind, adds a "naming-lane" status step, a 2-minute stale threshold with a dismiss button, and a paneMountedRef guard to prevent stale async continuations from affecting unmounted panes.

  • Lane-scoped job store: draftLaunchJobsByScope in appStore replaces per-pane useState, letting jobs survive remounts and preventing cross-pane bleed.
  • Stale-job UX: Active jobs older than 2 minutes show an expanded message and a "Hide stale launch status" button; the paneMountedRef guard prevents background async from resetting selectedSessionId after the job is dismissed.
  • Clipboard notice toggle: A new launchPromptClipboardNoticeEnabled preference lets users hide the "prompt will be copied" footer notice without disabling the copy behavior itself.

Confidence Score: 4/5

Safe to merge with one fix: the job-still-visible guard reads from the wrong store instance in production, silently preventing foreground session auto-open and error display.

The draftLaunchJobExists callback uses useAppStore.getState(), which is statically bound to rootAppStore.getState regardless of React context. In production every AgentChatPane runs inside an AppStoreProvider backed by a per-project store, so draftLaunchJobsByScope is always empty when read this way. This makes jobStillVisible permanently false, so the guard exits every successful launch before calling openLaunchedDraftSession. The new tests all run against rootAppStore directly and do not catch this divergence.

apps/desktop/src/renderer/components/chat/AgentChatPane.tsx — the draftLaunchJobExists callback around the patchDraftLaunchJob success/failure guards

Important Files Changed

Filename Overview
apps/desktop/src/renderer/components/chat/AgentChatPane.tsx Moves draft launch job state from local useState to the global appStore (scoped by lane + project). Adds stale-job detection, a dismiss button for stale active rows, and a paneMountedRef guard. draftLaunchJobExists uses useAppStore.getState() which is always the root store, not the project store, so the job-still-visible guard always fires false in production.
apps/desktop/src/renderer/lib/draftLaunchJobs.ts New module extracting DraftLaunchJob types and helper functions from AgentChatPane. Adds a new "naming-lane" status and a 2-minute stale threshold. Clean extraction with no issues.
apps/desktop/src/renderer/state/appStore.ts Adds draftLaunchJobsByScope (in-memory, not persisted) and setDraftLaunchJobs to the store; adds launchPromptClipboardNoticeEnabled as a persisted user preference. Clean implementation.
apps/desktop/src/renderer/components/chat/AgentChatPane.test.tsx Adds comprehensive tests for lane-scoped draft launch jobs, stale-row dismissal, remount persistence, and cross-lane isolation. Tests run against rootAppStore and do not exercise the project-store code path used in production.
apps/desktop/src/renderer/components/settings/AppearanceSection.tsx Adds a "Show clipboard reminder" sub-toggle nested under the existing clipboard-copy toggle. Straightforward UI addition.
apps/desktop/src/renderer/lib/launchedLanesHighlight.ts Fixes lane-only creates incorrectly triggering the Lanes-tab loading overlay: laneIds are now only published when there are associated sessionIds to wait for.
apps/desktop/src/renderer/components/chat/AgentChatComposer.tsx Splits the clipboard helper into a footer notice and a focus-gated tooltip; adds launchPromptClipboardNoticeEnabled prop. Clean change.

Comments Outside Diff (1)

  1. apps/desktop/src/renderer/components/chat/AgentChatPane.tsx, line 6109-6120 (link)

    P2 Active jobs are un-dismissible if their async never settles

    The dismiss button is intentionally hidden for in-flight jobs (!isActiveJob). The current catch/finally block in startDraftLaunch is thorough, so a stuck active job requires a hanging IPC call (e.g., suggestLaneName never resolving). If that ever happens, the job will spin forever in the store with no user escape hatch — draftLaunchJobsByScope is in-memory only, so a full app restart is the only remedy.

    Consider adding a timeout or a global "clear all stale active jobs" path (e.g., triggered on project open) as a safety valve for cases where the backing async is silently abandoned.

    Prompt To Fix With AI
    This is a comment left during a code review.
    Path: apps/desktop/src/renderer/components/chat/AgentChatPane.tsx
    Line: 6109-6120
    
    Comment:
    **Active jobs are un-dismissible if their async never settles**
    
    The dismiss button is intentionally hidden for in-flight jobs (`!isActiveJob`). The current catch/finally block in `startDraftLaunch` is thorough, so a stuck active job requires a hanging IPC call (e.g., `suggestLaneName` never resolving). If that ever happens, the job will spin forever in the store with no user escape hatch — `draftLaunchJobsByScope` is in-memory only, so a full app restart is the only remedy.
    
    Consider adding a timeout or a global "clear all stale active jobs" path (e.g., triggered on project open) as a safety valve for cases where the backing async is silently abandoned.
    
    How can I resolve this? If you propose a fix, please make it concise.

    Fix in Claude Code

Fix All in Claude Code

Prompt To Fix All With AI
Fix the following 1 code review issue. Work through them one at a time, proposing concise fixes.

---

### Issue 1 of 1
apps/desktop/src/renderer/components/chat/AgentChatPane.tsx:5800-5806
**`draftLaunchJobExists` reads from the root store, not the active project store**

`useAppStore.getState` is hardwired to `rootAppStore.getState` via the `Object.assign` in `appStore.ts`. In production every `AgentChatPane` is mounted inside an `AppStoreProvider` that provides a *per-project* store. `setDraftLaunchJobs` correctly writes to that project store (the `useAppStore` hook respects context), but `draftLaunchJobExists` reads `draftLaunchJobsByScope` from the root store, which is always `{}`.

The practical effect: `jobStillVisible` is always `false` after a successful foreground launch, so `if (!jobStillVisible) { return; }` fires unconditionally, preventing `openLaunchedDraftSession` from being called — the newly-created session is never auto-opened. The same guard also suppresses `setError` on every failure.

The fix is to use `useAppStoreApi().getState()` (the exported `useAppStoreApi` hook returns the context-resolved store) instead of the static `useAppStore.getState()`.

Reviews (5): Last reviewed commit: "Guard hidden draft launch completions" | Re-trigger Greptile

@linear-code
Copy link
Copy Markdown

linear-code Bot commented Jun 2, 2026

ADE-96

@vercel
Copy link
Copy Markdown

vercel Bot commented Jun 2, 2026

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

1 Skipped Deployment
Project Deployment Actions Updated (UTC)
ade Ignored Ignored Preview Jun 2, 2026 10:29am

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Jun 2, 2026

Review Change Stack

📝 Walkthrough

Walkthrough

This PR introduces a user-facing clipboard reminder toggle preference, adds infrastructure for persisting draft launch job state across component remounts, and refactors AgentChatPane to use scoped storage with stale detection and lifecycle guards to prevent post-unmount state mutations.

Changes

Clipboard reminder and draft job persistence

Layer / File(s) Summary
Draft launch job library and types
apps/desktop/src/renderer/lib/draftLaunchJobs.ts
New module exports constants (MAX_DRAFT_LAUNCH_JOBS, DRAFT_LAUNCH_JOB_STALE_AFTER_MS), data-shape types for native controls, snapshots, and job records, and helpers to determine terminal/stale status and prune job lists.
App store state and persistence
apps/desktop/src/renderer/state/appStore.ts, apps/desktop/src/renderer/state/appStore.test.ts
AppState extends with launchPromptClipboardNoticeEnabled (persisted user preference) and draftLaunchJobsByScope (per-scope draft job storage). Store actions setLaunchPromptClipboardNoticeEnabled and setDraftLaunchJobs are added with full persistence and updater-function support. Tests verify preference persistence and state initialization.
Clipboard notice toggle and composer
apps/desktop/src/renderer/components/chat/AgentChatComposer.tsx, apps/desktop/src/renderer/components/app/App.tsx, apps/desktop/src/renderer/components/chat/AgentChatComposer.test.tsx
AgentChatComposer adds optional launchPromptClipboardNoticeEnabled prop (default true) that gates the clipboard-copy reminder notice. showLaunchClipboardNotice requires both clipboard and notice flags. App.tsx includes the new preference in rootPrefs hydration. Test verifies the notice is suppressed when the flag is disabled.
Settings UI
apps/desktop/src/renderer/components/settings/AppearanceSection.tsx
Adds conditional "Show clipboard reminder" checkbox that displays only when clipboard copying is enabled; wires to launchPromptClipboardNoticeEnabled in the app store.
App hydration tests
apps/desktop/src/renderer/components/app/App.workKeepAlive.test.tsx
Mocks and initializes the new preference in test store; adds test case verifying hydration updates when the notice flag changes.
AgentChatPane draft job refactoring
apps/desktop/src/renderer/components/chat/AgentChatPane.tsx
Replaces local draft-job state with scoped storage via draftLaunchJobsByScope. Imports draft-launch types and helpers from the new library. Adds draftLaunchJobNowMs clock for stale-status rendering (active only when jobs exist). Refactors UI label/message generation via imported helpers. Adds paneMountedRef to prevent state updates after unmount. Auto-create lanes start in naming-lane, resolveDraftLaunchLane accepts onAutoCreateNameResolved callback, and dismiss button visibility is gated to non-active or stale-active jobs. Draft-launch cards gain stale detection and aria-live accessibility.
AgentChatPane tests
apps/desktop/src/renderer/components/chat/AgentChatPane.test.tsx
Adds helper draftLaunchJobsScopeKeyForTest for building scope keys. Tests cover clipboard notice suppression, draft-launch persistence across remount (pending status, stale-job hiding, failure recovery), and scope isolation (jobs scoped to the initiating pane).
Launched lanes highlight fix
apps/desktop/src/renderer/lib/launchedLanesHighlight.ts, apps/desktop/src/renderer/lib/launchedLanesHighlight.test.ts
rememberLaunchedLanes now publishes cross-page highlight only when sessionIds are non-empty; lane-only launches return early without highlight. Test suite verifies non-publication for lane-only creates and correct preservation of lane/session IDs for session-based launches.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related issues

  • arul28/ADE#509: The main changes directly address this issue by moving AgentChatPane's auto-create lane state from ephemeral local state into a shared draftLaunchJobs store (appStore.draftLaunchJobsByScope) with paneMountedRef/staleness/persistence logic so loading and error states survive unmount/remount without being lost.

Possibly related PRs

  • arul28/ADE#506: Both PRs modify clipboard-related components and app-store wiring (AgentChatComposer, AgentChatPane, AppearanceSection, appStore.ts) to extend the existing clipboard/prompt-to-clipboard feature with notice control.
  • arul28/ADE#328: Both PRs modify AgentChatPane.tsx and its submit test to guard against unwanted late state overwrite via mount-tracking refs similar to draftLaunchConfigTouchedKeyRef.

Suggested labels

desktop

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 8.33% 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 PR title clearly identifies the issue being fixed (ADE-96: auto-create lane loading and error states) and the context where it occurs (new chat pane). It accurately reflects the main changes in the 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 Check skipped - CodeRabbit’s high-level summary is enabled.

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

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch ade-96-auto-create-lane-loading-and-error-states-break-in-new-chat-pane

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.

@arul28
Copy link
Copy Markdown
Owner Author

arul28 commented Jun 2, 2026

@copilot review but do not make fixes

Comment thread apps/desktop/src/renderer/components/chat/AgentChatPane.tsx
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: 2

Caution

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

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

2373-2395: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Use the raw draft kind in the job scope key.

draftLaunchJobsScopeKey currently reuses workDraftStorageKind, which intentionally collapses both "chat" and "cli" into "work-start". That makes chat and CLI draft jobs share the same store bucket again, so a launch started from one surface can appear or be dismissed from the other. This PR’s isolation fix needs the actual workDraftKind here, not the normalized storage key.

Suggested fix
   const draftLaunchJobsScopeKey = useMemo(
     () => [
       "draft-launch-jobs",
       projectRoot?.trim() || "project",
       laneId ?? "no-lane",
       surfaceProfile,
-      workDraftStorageKind,
+      workDraftKind,
     ].map(encodeURIComponent).join(":"),
-    [laneId, projectRoot, surfaceProfile, workDraftStorageKind],
+    [laneId, projectRoot, surfaceProfile, workDraftKind],
   );
🤖 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/components/chat/AgentChatPane.tsx` around lines
2373 - 2395, draftLaunchJobsScopeKey is using the normalized
workDraftStorageKind which collapses "chat"/"cli" into "work-start" and causes
shared buckets; change the array building draftLaunchJobsScopeKey to use the raw
workDraftKind (the incoming prop/variable) instead of workDraftStorageKind, and
update the useMemo dependency array to include workDraftKind (remove or keep
workDraftStorageKind only if still referenced elsewhere) so chat and CLI draft
jobs are isolated; target the draftLaunchJobsScopeKey definition and its
dependencies in AgentChatPane.tsx.
🤖 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/components/chat/AgentChatPane.test.tsx`:
- Around line 3774-3784: The test is asserting the "creating" copy while
suggestLaneName is still unresolved; instead, assert the naming-phase UI first
(i.e., check the UI state for the new "naming-lane" phase while suggestLaneName
is pending) using the same render/unmount flow (renderAutoCreateDraftPane /
rendered.unmount), then resolve or wait for createLane to start and only after
that assert the "creating-lane" copy; target the mocked suggestLaneName and
createLane flows so you explicitly check the naming-phase text before resolving
suggestLaneName and the creating-phase text after createLane begins.

In `@apps/desktop/src/renderer/lib/draftLaunchJobs.ts`:
- Around line 96-106: pruneDraftLaunchJobs currently forces at least one
terminal slot when any active job exists, which can let the returned array
exceed MAX_DRAFT_LAUNCH_JOBS; to enforce a hard cap, change the
remainingTerminalSlots calculation in pruneDraftLaunchJobs to use
Math.max(MAX_DRAFT_LAUNCH_JOBS - active.length, 0) (so when active.length === 0
you get the full MAX, otherwise you reserve zero or more terminal slots up to
the remaining capacity), remove the forced minimum of 1, and add a brief comment
next to MAX_DRAFT_LAUNCH_JOBS or the pruneDraftLaunchJobs function documenting
the intended total-cap semantics so callers like AgentChatPane aren’t surprised.

---

Outside diff comments:
In `@apps/desktop/src/renderer/components/chat/AgentChatPane.tsx`:
- Around line 2373-2395: draftLaunchJobsScopeKey is using the normalized
workDraftStorageKind which collapses "chat"/"cli" into "work-start" and causes
shared buckets; change the array building draftLaunchJobsScopeKey to use the raw
workDraftKind (the incoming prop/variable) instead of workDraftStorageKind, and
update the useMemo dependency array to include workDraftKind (remove or keep
workDraftStorageKind only if still referenced elsewhere) so chat and CLI draft
jobs are isolated; target the draftLaunchJobsScopeKey definition and its
dependencies in AgentChatPane.tsx.
🪄 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: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 4dda871f-4c3b-4319-9794-50915972704e

📥 Commits

Reviewing files that changed from the base of the PR and between 950b536 and 62789c9.

⛔ Files ignored due to path filters (5)
  • docs/features/chat/README.md is excluded by !docs/**
  • docs/features/chat/composer-and-ui.md is excluded by !docs/**
  • docs/features/lanes/README.md is excluded by !docs/**
  • docs/features/linear-integration/README.md is excluded by !docs/**
  • docs/features/onboarding-and-settings/README.md is excluded by !docs/**
📒 Files selected for processing (12)
  • apps/desktop/src/renderer/components/app/App.tsx
  • apps/desktop/src/renderer/components/app/App.workKeepAlive.test.tsx
  • apps/desktop/src/renderer/components/chat/AgentChatComposer.test.tsx
  • apps/desktop/src/renderer/components/chat/AgentChatComposer.tsx
  • apps/desktop/src/renderer/components/chat/AgentChatPane.test.tsx
  • apps/desktop/src/renderer/components/chat/AgentChatPane.tsx
  • apps/desktop/src/renderer/components/settings/AppearanceSection.tsx
  • apps/desktop/src/renderer/lib/draftLaunchJobs.ts
  • apps/desktop/src/renderer/lib/launchedLanesHighlight.test.ts
  • apps/desktop/src/renderer/lib/launchedLanesHighlight.ts
  • apps/desktop/src/renderer/state/appStore.test.ts
  • apps/desktop/src/renderer/state/appStore.ts

Comment thread apps/desktop/src/renderer/components/chat/AgentChatPane.test.tsx
Comment thread apps/desktop/src/renderer/lib/draftLaunchJobs.ts
@arul28 arul28 force-pushed the ade-96-auto-create-lane-loading-and-error-states-break-in-new-chat-pane branch from 62789c9 to 5a27222 Compare June 2, 2026 09:59
Comment thread apps/desktop/src/renderer/components/chat/AgentChatPane.tsx
@arul28 arul28 merged commit bc9091d into main Jun 2, 2026
28 checks passed
@arul28 arul28 deleted the ade-96-auto-create-lane-loading-and-error-states-break-in-new-chat-pane branch June 2, 2026 15:16
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