Skip to content

Add Lane Modal Button Toast#689

Merged
arul28 merged 4 commits into
mainfrom
ade/start-aimage-add-lane-option-91256e11
Jul 2, 2026
Merged

Add Lane Modal Button Toast#689
arul28 merged 4 commits into
mainfrom
ade/start-aimage-add-lane-option-91256e11

Conversation

@arul28

@arul28 arul28 commented Jul 2, 2026

Copy link
Copy Markdown
Owner

Summary

Describe the change.

What Changed

Key files and behaviors.

Validation

How you tested.

Risks

Anything to watch.

ADE   Open in ADE  ·  ade/start-aimage-add-lane-option-91256e11 branch  ·  PR #689

Summary by CodeRabbit

  • New Features

    • Added lane lifecycle notifications across desktop and CLI, including updates for lane creation, archiving, and deletion.
    • Introduced toast-based feedback for lane actions and background lane setup, with quick access actions where helpful.
    • Enabled creating new lanes from the Work sidebar and on iOS via a dedicated add-lane flow.
  • Bug Fixes

    • Improved lane setup handling when a preferred template is unavailable, falling back to a standard setup path.
    • Added clearer error messaging when lane setup or lane actions fail.

Greptile Summary

This PR wires up a shared toast store across the desktop app, CLI, and iOS, surfacing lane lifecycle notifications (created, archived, deleted), background env-setup failures with retryable sticky toasts, and success toasts for push/sync/rebase actions in LaneGitActionsPane and historyLaneActions. The create-lane dialog logic is extracted from LanesPage into a standalone CreateLaneDialogHost with two post-create behaviours (close-on-create for the Work sidebar, stay-open-setup for the Lanes tab), and iOS gets a new Add Lane button in the Work sidebar.

  • New toastStore + useLaneEventToasts + ToastStack provide a centralized, timer-managed toast system with pause-on-hover, capped at 4 concurrent toasts, backed by full test coverage.
  • CreateLaneDialogHost owns all create-lane form state, orchestrates close-on-create (env-setup runs detached) and stay-open-setup (dialog stays open, streams progress), surfacing retryable failure toasts for the former.
  • CLI receives runDefaultLaneSetup which re-fetches the template list on each call, providing a fresh-validation fallback if the default template was deleted.

Confidence Score: 5/5

Safe to merge; the change is well-structured with full test coverage for the new toast store and only one edge-case quality gap in the detached setup retry path.

The new toast system, lifecycle event plumbing, and CreateLaneDialogHost refactor are all correct and well-tested. The only noteworthy gap is that the detached background setup retry in close-on-create mode passes the same potentially-stale templateId on every retry without re-validating, which can leave the user stuck in a retry loop if the template is deleted after lane creation — but this requires an unlikely race and does not affect data integrity.

apps/desktop/src/renderer/components/lanes/CreateLaneDialogHost.tsx — the applyLaneEnvSetup helper in the detached setup path.

Important Files Changed

Filename Overview
apps/desktop/src/renderer/components/app/toast/toastStore.ts New module-level singleton toast store using useSyncExternalStore; timer, pause/resume, and cap logic are correct and fully tested.
apps/desktop/src/renderer/components/app/toast/toastStore.test.ts Comprehensive test coverage for timer, pause/resume, stack cap, merge-patch, and lane lifecycle toast integration via jsdom harness.
apps/desktop/src/renderer/components/app/toast/useLaneEventToasts.ts Subscribes globally to lane lifecycle and rebase events; fires toasts for all create/archive/delete and non-user terminal rebase states. "Lane created" toast fires in stay-open-setup mode (noted in previous review thread).
apps/desktop/src/renderer/components/lanes/CreateLaneDialogHost.tsx New host component extracting create-lane state from LanesPage; close-on-create detached setup captures templateId at submit time with no fallback if template is later deleted.
apps/desktop/src/main/services/lanes/laneService.ts Adds broadcastLifecycleEvent on create/archive/delete; each code path fires exactly once; broadcastLifecycleEvent is defined after first use-site but both are closures within the factory so no TDZ issue.
apps/desktop/src/preload/preload.ts onLifecycleEvent exposes dual IPC+remote subscription, consistent with existing delete/rebase event patterns; remote path correctly calls clearGitReadCaches.
apps/desktop/src/renderer/components/history/historyLaneActions.ts Adds success toasts for push/force-push/merge/rebase and error toasts for the same set of failures; onError still fires alongside toast for inline error surface.
apps/desktop/src/renderer/components/lanes/LaneGitActionsPane.tsx Adds post-action success toasts including a conflict-state check for the pull action before claiming success; correct async sequencing.
apps/ade-cli/src/tuiClient/adeApi.ts runDefaultLaneSetup re-fetches template list on each invocation and validates template existence, falling back to initEnv — more robust than the desktop detached path.
apps/ios/ADE/Views/Work/WorkRootComponents.swift Splits action area into two buttons (Start new chat + Add lane); both disabled when not live, correct accessibility labels.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A[User clicks Add Lane] --> B{Which surface?}
    B -->|Work sidebar / CLI| C["close-on-create mode"]
    B -->|Lanes tab| D["stay-open-setup mode"]

    C --> E[CreateLaneDialogHost opens]
    D --> F[CreateLaneDialogHost opens]

    E --> G[handleCreateSubmit]
    F --> G

    G --> H[create / createChild / importBranch]
    H --> I["laneService.broadcastLifecycleEvent(lane-created)"]
    I --> J["IPC → renderer onLifecycleEvent"]
    J --> K["useLaneEventToasts → showToast (Lane created)"]

    H --> L{behavior?}
    L -->|close-on-create| M[resetState + onOpenChange false]
    M --> N["runDetachedLaneSetup (background)"]
    N --> O{setup result}
    O -->|failed| P["showSetupFailureToast (sticky, Retry)"]
    O -->|success| Q["dismissToast (failure toast)"]

    L -->|stay-open-setup| R["runSetupForCreatedLane (in-dialog streaming)"]
    R --> S{setup result}
    S -->|failed| T[in-dialog error + Retry button]
    S -->|success| U[resetState + close dialog]

    K -.->|fires for both modes| V["⚠ toast + open dialog visible simultaneously in stay-open mode (known)"]
Loading
%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%%
flowchart TD
    A[User clicks Add Lane] --> B{Which surface?}
    B -->|Work sidebar / CLI| C["close-on-create mode"]
    B -->|Lanes tab| D["stay-open-setup mode"]

    C --> E[CreateLaneDialogHost opens]
    D --> F[CreateLaneDialogHost opens]

    E --> G[handleCreateSubmit]
    F --> G

    G --> H[create / createChild / importBranch]
    H --> I["laneService.broadcastLifecycleEvent(lane-created)"]
    I --> J["IPC → renderer onLifecycleEvent"]
    J --> K["useLaneEventToasts → showToast (Lane created)"]

    H --> L{behavior?}
    L -->|close-on-create| M[resetState + onOpenChange false]
    M --> N["runDetachedLaneSetup (background)"]
    N --> O{setup result}
    O -->|failed| P["showSetupFailureToast (sticky, Retry)"]
    O -->|success| Q["dismissToast (failure toast)"]

    L -->|stay-open-setup| R["runSetupForCreatedLane (in-dialog streaming)"]
    R --> S{setup result}
    S -->|failed| T[in-dialog error + Retry button]
    S -->|success| U[resetState + close dialog]

    K -.->|fires for both modes| V["⚠ toast + open dialog visible simultaneously in stay-open mode (known)"]
Loading

Comments Outside Diff (1)

  1. apps/desktop/src/renderer/components/app/toast/useLaneEventToasts.ts, line 183-197 (link)

    P2 "Lane created" toast fires in stay-open-setup mode

    useLaneEventToasts subscribes globally, so it fires a "Lane created" success toast for every lane-created lifecycle event — including those triggered from the Lanes tab where CreateLaneDialogHost is in stay-open-setup mode and the dialog remains open streaming env-setup progress. The user then sees both the open dialog (still showing setup status) and a corner toast with a "View" action pointing at the same lane. Clicking "View" navigates away while the setup dialog is still mounted. This is harmless but visually redundant in the Lanes tab flow. Consider suppressing the toast when the create dialog is currently open, or scoping it to the close-on-create path only.

    Prompt To Fix With AI
    This is a comment left during a code review.
    Path: apps/desktop/src/renderer/components/app/toast/useLaneEventToasts.ts
    Line: 183-197
    
    Comment:
    **"Lane created" toast fires in stay-open-setup mode**
    
    `useLaneEventToasts` subscribes globally, so it fires a "Lane created" success toast for every `lane-created` lifecycle event — including those triggered from the Lanes tab where `CreateLaneDialogHost` is in `stay-open-setup` mode and the dialog remains open streaming env-setup progress. The user then sees both the open dialog (still showing setup status) and a corner toast with a "View" action pointing at the same lane. Clicking "View" navigates away while the setup dialog is still mounted. This is harmless but visually redundant in the Lanes tab flow. Consider suppressing the toast when the create dialog is currently open, or scoping it to the `close-on-create` path only.
    
    How can I resolve this? If you propose a fix, please make it concise.

    Fix in Claude Code

Reviews (4): Last reviewed commit: "ship: suppress pull success toast when c..." | Re-trigger Greptile

@vercel

vercel Bot commented Jul 2, 2026

Copy link
Copy Markdown

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

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

@coderabbitai

coderabbitai Bot commented Jul 2, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

This PR adds lane lifecycle event emission (create/archive/delete) propagated from laneService through desktop main/preload/IPC and buffered in ade-cli; adds ade-cli default lane setup (auto template/env init); introduces a renderer toast store and UI wired to lane/rebase events and scattered action toasts; refactors lane creation into a new CreateLaneDialogHost used by LanesPage and SessionListPane; and adds an iOS "Add Lane" sheet flow.

Changes

Lane lifecycle events

Layer / File(s) Summary
LaneLifecycleEvent type and emission
apps/desktop/src/shared/types/lanes.ts, apps/desktop/src/main/services/lanes/laneService.ts
Adds LaneLifecycleEvent type and broadcastLifecycleEvent calls on lane create/archive/delete.
IPC/preload/main wiring
apps/desktop/src/shared/ipc.ts, apps/desktop/src/main/main.ts, apps/desktop/src/preload/*, apps/desktop/src/renderer/browserMock.ts
New IPC channel forwards lifecycle events to renderer; preload exposes ade.lanes.onLifecycleEvent with local and remote subscription support.
ade-cli event buffering
apps/ade-cli/src/bootstrap.ts
Lifecycle events are pushed into the ade-cli runtime event buffer.

ade-cli default lane setup

Layer / File(s) Summary
runDefaultLaneSetup core
apps/ade-cli/src/tuiClient/adeApi.ts, apps/ade-cli/src/tuiClient/__tests__/adeApi.test.ts
New function resolves templates/default template and applies template or inits env; tests cover both paths.
TUI wiring
apps/ade-cli/src/tuiClient/app.tsx
runLaneSetupAfterCreate runs after lane creation in /new lane and form-submit flows, surfacing errors as notices.

Renderer toast system

Layer / File(s) Summary
Toast store
apps/desktop/src/renderer/components/app/toast/toastStore.ts, toastStore.test.ts
New external store with show/update/dismiss/pause/resume APIs and tests.
ToastStack UI & CSS
apps/desktop/src/renderer/components/app/toast/ToastStack.tsx, apps/desktop/src/renderer/index.css
Renders toast cards with tone styling, dismiss/action buttons, hover pause/resume, and entrance animation.
Lane event toasts & AppShell wiring
useLaneEventToasts.ts, AppShell.tsx, AppShell.aiStatus.test.tsx
Hook emits toasts for lifecycle/rebase events; AppShell now renders ToastStack fed by useToasts/useLaneEventToasts.
Action toast call sites
WorkspaceGraphPage.tsx, historyLaneActions.ts, LaneGitActionsPane.tsx
Success/error toasts added for sync/push/rebase/merge and integration actions.

Create-lane dialog refactor

Layer / File(s) Summary
Dialog shell footer
LaneDialogShell.tsx, CreateLaneDialog.tsx
Adds optional footer prop, moving Cancel/submit buttons out of a removed sticky action bar.
CreateLaneDialogHost
CreateLaneDialogHost.tsx
New host component owns create/setup state, base-branch persistence, detached background env setup with retryable toasts, and full submit orchestration.
LanesPage/SessionListPane wiring
LanesPage.tsx, SessionListPane.tsx
Both components delegate lane creation to CreateLaneDialogHost, removing prior in-page state and logic.

iOS Add Lane

Layer / File(s) Summary
Add Lane sheet flow
WorkRootComponents.swift, WorkRootScreen.swift, WorkPreviews.swift
Adds an Add Lane toolbar button and onAddLane callback that presents AddLaneSheet, updating selected lane and reloading on creation.

Estimated code review effort: 4 (Complex) | ~75 minutes

Suggested labels: desktop, ios

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
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.
Title check ✅ Passed The title is related to the PR’s main theme of lane creation and toast notifications, though it is a bit terse and not fully descriptive.
✨ 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/start-aimage-add-lane-option-91256e11

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.

@arul28

arul28 commented Jul 2, 2026

Copy link
Copy Markdown
Owner Author

@copilot review but do not make fixes

@mintlify

mintlify Bot commented Jul 2, 2026

Copy link
Copy Markdown

Preview deployment for your docs. Learn more about Mintlify Previews.

Project Status Preview Updated (UTC)
ade-ac1c6011 🟢 Ready View Preview Jul 2, 2026, 9:15 PM

💡 Tip: Enable Workflows to automatically generate PRs for you.

Comment thread apps/desktop/src/renderer/components/app/toast/ToastStack.tsx Outdated
@arul28

arul28 commented Jul 2, 2026

Copy link
Copy Markdown
Owner Author

@codex review

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 0e91364d1e

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread apps/desktop/src/renderer/components/lanes/CreateLaneDialogHost.tsx
@arul28

arul28 commented Jul 2, 2026

Copy link
Copy Markdown
Owner Author

@codex review

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 3edb6d662a

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

} else if (actionName === "force push") {
showToast({ title: `Force-pushed ${laneLabel}`, tone: "success" });
} else if (actionName === "pull") {
showToast({ title: `Pulled ${laneLabel}`, tone: "success" });

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Suppress pull success toasts when conflicts remain

When the Pull action runs window.ade.git.sync, a merge/rebase that stops with unmerged files resolves instead of throwing (gitOperationsService treats detected conflicts as a successful return), so this new global toast can say the lane was pulled even though the user still has to resolve conflicts. In that conflict scenario, check getConflictState after the sync or suppress the success toast before reporting completion.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

Addressed in 4c62a96 — after a pull, the pane now checks getConflictState and shows a conflicts-remaining error toast instead of the success toast when the sync stopped on merge/rebase conflicts.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Caution

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

⚠️ Outside diff range comments (2)
apps/ade-cli/src/tuiClient/app.tsx (1)

9223-9254: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

new-lane-from-unstaged skips default lane setup.

Every other lane-creation path in this file (/new lane, and create/createChild/importBranch under submitRightForm) now calls runLaneSetupAfterCreate after creation, but createFromUnstaged does not. Since this also produces a brand-new worktree, it likely needs the same template/env initialization; otherwise lanes rescued from unstaged work will silently lack the default environment setup that other new lanes get.

🔧 Suggested fix
         addNotice(`Moved unstaged work to ${created.name}.`, "success");
+        runLaneSetupAfterCreate(conn, created);
         await refreshState();
🤖 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/ade-cli/src/tuiClient/app.tsx` around lines 9223 - 9254, The
new-lane-from-unstaged flow in app.tsx creates a brand-new lane but never runs
the same post-create initialization used elsewhere. After the conn.action call
in the new-lane-from-unstaged branch, invoke runLaneSetupAfterCreate for the
created lane, matching the behavior already used by /new lane and the
submitRightForm create/createChild/importBranch paths. Keep the existing
selection, drawer, and notice updates, but ensure the setup runs before
refreshing state so rescued lanes get the default template/env setup.
apps/desktop/src/main/services/lanes/laneService.ts (1)

3824-3857: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Missing lifecycle event on importBranch's post-commit recovery path.

When laneInserted is true and a later step throws (e.g. computeLaneStatus), this catch block recovers and returns a valid LaneSummary — i.e. the lane was actually created successfully — but broadcastLifecycleEvent({ type: "lane-created", ... }) is never called here, unlike the straight-through success path just above (lines 3816-3823). Users hitting this recovery branch will get a created lane with no "Lane created" toast.

🐛 Proposed fix
           return toLaneSummary({
             row: persistedRow,
             status: status ?? { dirty: false, ahead: 0, behind: 0, remoteBehind: -1, rebaseInProgress: false },
             parentStatus,
             childCount: 0,
             stackDepth,
           });
+          // (emit broadcastLifecycleEvent with the summary above before returning)
🤖 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/main/services/lanes/laneService.ts` around lines 3824 -
3857, The post-commit recovery path in laneService’s importBranch flow returns a
valid LaneSummary when laneInserted is true, but it skips the lane-created
lifecycle notification. Mirror the successful path by calling
broadcastLifecycleEvent with type "lane-created" before returning from this
catch branch, using the same lane data/message payload as the normal success
case. Keep the recovery behavior intact so the summary is still returned after
the event is emitted.
🧹 Nitpick comments (5)
apps/ios/ADE/Views/Work/WorkRootScreen.swift (1)

96-96: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

SwiftLint flags addLaneSheetPresented as non-private.

Matches the existing convention in this struct where other @State properties (e.g. sessions, filterPanelOpen) are also non-private, so this isn't a new regression, but consider tightening visibility here and across the struct if addressing lint debt.

🤖 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/ios/ADE/Views/Work/WorkRootScreen.swift` at line 96, SwiftLint is
flagging the `WorkRootScreen` state property `addLaneSheetPresented` as
non-private; update the `WorkRootScreen` struct to tighten visibility by making
this `@State` property private, and if you’re addressing the lint debt
consistently, review the other `@State` members in the same struct (such as
`sessions` and `filterPanelOpen`) and apply the same visibility convention where
appropriate.

Source: Linters/SAST tools

apps/ade-cli/src/tuiClient/__tests__/adeApi.test.ts (1)

59-109: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Add test coverage for the "failed" setup outcome.

Both tests only cover the "completed" overallStatus. runLaneSetupAfterCreate (app.tsx) specifically branches on overallStatus === "failed" to extract the failed step and surface a notice — that path is untested here, so a regression in step/status shape wouldn't be caught.

✅ Suggested additional test
it("surfaces a failed overallStatus without throwing", async () => {
  const connection = {
    action: async (domain: string, action: string) => {
      if (action === "listTemplates") return [];
      if (action === "getDefaultTemplate") return null;
      if (action === "initEnv") {
        return {
          laneId: "lane-1",
          steps: [{ kind: "env-files", label: "Env files", status: "failed", error: "boom" }],
          startedAt: "2026-01-01T00:00:00.000Z",
          overallStatus: "failed",
        };
      }
      throw new Error(`unexpected action ${action}`);
    },
  } as unknown as AdeCodeConnection;

  const result = await runDefaultLaneSetup(connection, "lane-1");
  expect(result.progress.overallStatus).toBe("failed");
});
🤖 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/ade-cli/src/tuiClient/__tests__/adeApi.test.ts` around lines 59 - 109,
Add a test in runDefaultLaneSetup coverage for the failed setup path so the
overallStatus "failed" shape is validated, not just "completed". Update the
existing describe("runDefaultLaneSetup") block in adeApi.test.ts to include a
case where the connection.action mock returns a failed initEnv payload with
steps, then assert runDefaultLaneSetup returns progress.overallStatus as
"failed" without throwing. Use the existing runDefaultLaneSetup and
AdeCodeConnection symbols to keep the test aligned with the branch that
runLaneSetupAfterCreate depends on.
apps/desktop/src/main/services/lanes/laneService.ts (1)

5492-5569: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

attach() doesn't emit a lane-created lifecycle event.

attach() inserts a new lane row and returns a LaneSummary, functionally the same "a new lane now exists" outcome as createWorktreeLane/importBranch, both of which call broadcastLifecycleEvent({ type: "lane-created", ... }). This path is missing that call, so attaching an existing worktree as a lane won't surface a "Lane created" toast, unlike other creation flows.

Consider adding a matching broadcastLifecycleEvent call before the return toLaneSummary(...) at the end of attach(), for consistency with the other creation paths.

🤖 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/main/services/lanes/laneService.ts` around lines 5492 -
5569, The attach() flow creates and persists a new lane but never emits the same
lane-created lifecycle event used by createWorktreeLane and importBranch. Add a
broadcastLifecycleEvent call in attach() after the lane row is inserted and
before returning toLaneSummary, using the new lane’s row data so attached lanes
trigger the same creation toast and lifecycle behavior as the other creation
paths.
apps/desktop/src/renderer/components/app/toast/ToastStack.tsx (2)

82-86: 🩺 Stability & Availability | 🔵 Trivial | ⚡ Quick win

Guard the action onClick so dismissal always happens.

If toast.action.onClick() throws, dismissToast(toast.id) is skipped and the toast stays stuck.

🛡️ Proposed fix
                       onClick={() => {
-                        toast.action?.onClick();
-                        dismissToast(toast.id);
+                        try {
+                          toast.action?.onClick();
+                        } finally {
+                          dismissToast(toast.id);
+                        }
                       }}
🤖 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/app/toast/ToastStack.tsx` around lines
82 - 86, The toast action handler in ToastStack should ensure
dismissToast(toast.id) runs even if toast.action?.onClick() throws. Update the
onClick callback for the toast action so the action invocation is wrapped in
error-safe flow, and make dismissal happen in a finally-like path so the toast
is always removed. Use the ToastStack component and its toast.action/onClick and
dismissToast symbols to locate the handler.

46-55: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

Add a live region so toasts are announced to screen readers.

Toast cards render without role/aria-live, so screen-reader users won't be notified when a new toast appears (e.g. push/rebase success or failure).

♿ Proposed fix
           <div
             key={toast.id}
+            role="status"
+            aria-live="polite"
             className={cn(
               "ade-toast-enter pointer-events-auto overflow-hidden rounded-xl border px-3 py-3 shadow-float backdrop-blur",
               tone.panel,
             )}
🤖 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/app/toast/ToastStack.tsx` around lines
46 - 55, The toast cards rendered by ToastStack are missing an accessibility
live region, so screen readers won’t announce new notifications. Update the
toast container rendered in ToastStack (where each toast <div> is created) to
expose appropriate live-region semantics by adding the right role and aria-live
attributes, and ensure the toast content is announced when new items appear
without changing the existing pause/resume behavior.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In `@apps/ade-cli/src/tuiClient/app.tsx`:
- Around line 9223-9254: The new-lane-from-unstaged flow in app.tsx creates a
brand-new lane but never runs the same post-create initialization used
elsewhere. After the conn.action call in the new-lane-from-unstaged branch,
invoke runLaneSetupAfterCreate for the created lane, matching the behavior
already used by /new lane and the submitRightForm
create/createChild/importBranch paths. Keep the existing selection, drawer, and
notice updates, but ensure the setup runs before refreshing state so rescued
lanes get the default template/env setup.

In `@apps/desktop/src/main/services/lanes/laneService.ts`:
- Around line 3824-3857: The post-commit recovery path in laneService’s
importBranch flow returns a valid LaneSummary when laneInserted is true, but it
skips the lane-created lifecycle notification. Mirror the successful path by
calling broadcastLifecycleEvent with type "lane-created" before returning from
this catch branch, using the same lane data/message payload as the normal
success case. Keep the recovery behavior intact so the summary is still returned
after the event is emitted.

---

Nitpick comments:
In `@apps/ade-cli/src/tuiClient/__tests__/adeApi.test.ts`:
- Around line 59-109: Add a test in runDefaultLaneSetup coverage for the failed
setup path so the overallStatus "failed" shape is validated, not just
"completed". Update the existing describe("runDefaultLaneSetup") block in
adeApi.test.ts to include a case where the connection.action mock returns a
failed initEnv payload with steps, then assert runDefaultLaneSetup returns
progress.overallStatus as "failed" without throwing. Use the existing
runDefaultLaneSetup and AdeCodeConnection symbols to keep the test aligned with
the branch that runLaneSetupAfterCreate depends on.

In `@apps/desktop/src/main/services/lanes/laneService.ts`:
- Around line 5492-5569: The attach() flow creates and persists a new lane but
never emits the same lane-created lifecycle event used by createWorktreeLane and
importBranch. Add a broadcastLifecycleEvent call in attach() after the lane row
is inserted and before returning toLaneSummary, using the new lane’s row data so
attached lanes trigger the same creation toast and lifecycle behavior as the
other creation paths.

In `@apps/desktop/src/renderer/components/app/toast/ToastStack.tsx`:
- Around line 82-86: The toast action handler in ToastStack should ensure
dismissToast(toast.id) runs even if toast.action?.onClick() throws. Update the
onClick callback for the toast action so the action invocation is wrapped in
error-safe flow, and make dismissal happen in a finally-like path so the toast
is always removed. Use the ToastStack component and its toast.action/onClick and
dismissToast symbols to locate the handler.
- Around line 46-55: The toast cards rendered by ToastStack are missing an
accessibility live region, so screen readers won’t announce new notifications.
Update the toast container rendered in ToastStack (where each toast <div> is
created) to expose appropriate live-region semantics by adding the right role
and aria-live attributes, and ensure the toast content is announced when new
items appear without changing the existing pause/resume behavior.

In `@apps/ios/ADE/Views/Work/WorkRootScreen.swift`:
- Line 96: SwiftLint is flagging the `WorkRootScreen` state property
`addLaneSheetPresented` as non-private; update the `WorkRootScreen` struct to
tighten visibility by making this `@State` property private, and if you’re
addressing the lint debt consistently, review the other `@State` members in the
same struct (such as `sessions` and `filterPanelOpen`) and apply the same
visibility convention where appropriate.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 850275af-7798-4e07-b6e2-70e89395d87f

📥 Commits

Reviewing files that changed from the base of the PR and between 8b753e2 and 3edb6d6.

⛔ Files ignored due to path filters (6)
  • docs/ARCHITECTURE.md is excluded by !docs/**
  • docs/features/lanes/README.md is excluded by !docs/**
  • docs/features/lanes/runtime.md is excluded by !docs/**
  • docs/features/terminals-and-sessions/README.md is excluded by !docs/**
  • docs/features/terminals-and-sessions/ui-surfaces.md is excluded by !docs/**
  • docs/perf/work-tab-action-inventory.md is excluded by !docs/**
📒 Files selected for processing (29)
  • apps/ade-cli/src/bootstrap.ts
  • apps/ade-cli/src/tuiClient/__tests__/adeApi.test.ts
  • apps/ade-cli/src/tuiClient/adeApi.ts
  • apps/ade-cli/src/tuiClient/app.tsx
  • apps/desktop/src/main/main.ts
  • apps/desktop/src/main/services/lanes/laneService.ts
  • apps/desktop/src/preload/global.d.ts
  • apps/desktop/src/preload/preload.ts
  • apps/desktop/src/renderer/browserMock.ts
  • apps/desktop/src/renderer/components/app/AppShell.aiStatus.test.tsx
  • apps/desktop/src/renderer/components/app/AppShell.tsx
  • apps/desktop/src/renderer/components/app/toast/ToastStack.tsx
  • apps/desktop/src/renderer/components/app/toast/toastStore.test.ts
  • apps/desktop/src/renderer/components/app/toast/toastStore.ts
  • apps/desktop/src/renderer/components/app/toast/useLaneEventToasts.ts
  • apps/desktop/src/renderer/components/graph/WorkspaceGraphPage.tsx
  • apps/desktop/src/renderer/components/history/historyLaneActions.ts
  • apps/desktop/src/renderer/components/lanes/CreateLaneDialog.tsx
  • apps/desktop/src/renderer/components/lanes/CreateLaneDialogHost.tsx
  • apps/desktop/src/renderer/components/lanes/LaneDialogShell.tsx
  • apps/desktop/src/renderer/components/lanes/LaneGitActionsPane.tsx
  • apps/desktop/src/renderer/components/lanes/LanesPage.tsx
  • apps/desktop/src/renderer/components/terminals/SessionListPane.tsx
  • apps/desktop/src/renderer/index.css
  • apps/desktop/src/shared/ipc.ts
  • apps/desktop/src/shared/types/lanes.ts
  • apps/ios/ADE/Views/Work/WorkPreviews.swift
  • apps/ios/ADE/Views/Work/WorkRootComponents.swift
  • apps/ios/ADE/Views/Work/WorkRootScreen.swift

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@arul28 arul28 merged commit e15e974 into main Jul 2, 2026
29 checks passed
@arul28 arul28 deleted the ade/start-aimage-add-lane-option-91256e11 branch July 3, 2026 03:15
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