ade-perf-lanes
SkillDev toolsPerformance practices for ADE's Lanes tab. Read before editing
Available today. Use it from your connected AI after setup.
No other account needed.
Connect ahel once, and every AI you use reads what you have installed.
Then ask your AI: use the ade-perf-lanes skill
What this skill tells your AI
The instructions your AI receives, as published by arul28/ade in .agents/skills/ade-perf-lanes/SKILL.md and read by ahel’s review.
Use this as engineering guidance for keeping the Lanes tab fast while adding features. The Lanes tab is a dense workspace: lane list, branch selector, stack graph, Work pane, Git Actions, dialogs, history, diff viewer, and runtime/session state all coexist. Small refresh choices can easily multiply into visible UI noise.
Testing posture
- Test the actual
/lanesroute in the Electron dev app. Do not treat the Work tab with a lane selector as Lanes parity. - Drive visible UI actions and mark each segment with
window.ade.perf.recordEvent({ kind: "manualStep", ... }). Deterministic scenarios are regression guards, not a substitute for clicking through the tab. - Keep a private
perf-passGitHub repo available for real fetch/push/pull behavior. It is safe to create throwaway lanes, commits, stashes, and branches there. - When an action is destructive or externally visible, exercise the prompt/preflight by default. Execute the final action only when the user has allowed it or the target is clearly disposable.
Refresh rules
- Use full decorated snapshots only when runtime decorations, conflict status, rebase suggestions, or auto-rebase state are truly needed.
- For Git Actions local operations such as stage, commit, fetch, push, pull, and history refresh, prefer
refreshLanes({ includeStatus: true, includeSnapshots: false }). This updates lane Git status without rebuilding runtime/rebase/conflict snapshot decorations. - For runtime-only updates from Work pane sessions, use
refreshLanes({ includeStatus: false, includeSnapshots: true, includeConflictStatus: false, includeRebaseSuggestions: false, includeAutoRebaseStatus: false }). Preserve prior lane Git status while refreshing runtime buckets. - For metadata-only updates such as lane color/appearance, use
refreshLanes({ includeStatus: false })and preserve priorstatus/parentStatusin the store. A color change must not recompute Git status. - Avoid calling bare
refreshLanes()from new Lanes UI handlers. Treat it as the expensive path and document why a full refresh is required.
Pane and poller rules
- Expanded/fullscreen panes must unmount the corresponding inline pane body when the duplicate would keep effects alive. CSS hiding is not enough.
- Git Actions should have at most one active polling/effect owner per visible lane. Timers must clean up on lane switch, pane minimize, and fullscreen transitions.
- Poll only visible or active surfaces. Hidden lanes, hidden panes, minimized panes, and closed dialogs should not keep expensive Git, PR, Linear, AI, or runtime status requests alive.
- Background sync/local-runtime failures should be fast-pathed in disabled perf/dev modes at the IPC boundary. Do not make every renderer caller catch slow "service unavailable" failures.
- Presence updates should be idempotent and de-duped by lane/signature so filter changes, layout switches, and tab clicks do not spam sync IPC.
Dialog and menu rules
- Fetch heavy dialog data on open, not on page load. Branch lists, Linear issues, unregistered worktrees, delete-risk preflights, and PR metadata should be lazy and cancelable.
- Do not precompute delete, rebase, merge, force-push, or cherry-pick risk for every lane. Compute it only when the user opens that flow.
- Color/appearance changes should update cheaply. They are not a reason to rebuild snapshots or rerun Git status.
- Create-lane flows may be expensive because they create worktrees and initialize environment state. Keep that cost isolated to submit; opening and editing the dialog should stay light.
Git Actions rules
- Keep local change operations scoped. Stage, unstage, commit, stash, and discard should refresh the active lane's change model and lane Git status, not all snapshot decorations.
- History and diff controls should fetch only the selected commit/file data. Split/unified, wrap, line-number, and copy-path controls should be renderer-local after the file/patch is loaded.
- Network actions are allowed to cost real time.
fetch,push, and child-lane creation can dominate a trace; do not optimize them by hiding progress or skipping correctness checks. - Save Changes currently stashes tracked changes and can leave untracked files visible. If changing that behavior, treat it as functionality work and add tests before using it as a perf cleanup.
Proven patterns
Skip disabled local runtime bridge calls
- Why it helped: When
ADE_DISABLE_LOCAL_RUNTIME_DAEMON=1, preload still attempted local-runtime action/sync/event IPC before falling back to desktop IPC. Real/lanesruns showed slowade.localRuntime.*spans andlanes.idle-at-resthit V8 OOM before summary. - Apply when: Perf/dev launches disable the daemon but traces show slow
ade.localRuntime.callAction,ade.localRuntime.callSync, orade.localRuntime.streamEvents. - Avoid: Renderer-only caches that hide the symptom while the unavailable transport still burns time.
- Verification: Baseline
lanes-20260511-1721-real-baseline-*had local-runtime slow channels and idle OOM. Post-changelanes-20260511-1725-real-optimized-{cold,switch,idle,scroll,stress}passed with total fitness7028.82and noade.localRuntime.*channels.
Suppress hidden duplicate fullscreen pane bodies
- Why it helped: Expanding Git Actions mounted the fullscreen pane while leaving the inline
LaneGitActionsPanebody alive, producing duplicate toolbars and duplicated effects. - Apply when: A Lanes expanded/fullscreen overlay reuses pane configs and a DOM snapshot shows duplicate pane bodies or repeated test regions while only one is visible.
- Avoid: CSS-only hiding for duplicate pane bodies.
- Verification: Git Actions expand went from 2 toolbars to 1. IPC dropped from 30 calls / 223 ms in
lanes-expand-prefix-20260511to 29 calls / 192 ms inlanes-expand-postfix-20260511.
Fast-path disabled sync status and presence
- Why it helped: Perf-mode Lanes traces hit
ade.sync.getStatusandade.sync.setActiveLanePresenceeven though the local runtime daemon and in-process sync service were unavailable. Failed calls cost about 250-370 ms each. - Apply when:
ADE_DISABLE_LOCAL_RUNTIME_DAEMON=1and traces show failedade.sync.*calls with "Sync service is not available." - Avoid: Removing Lanes presence calls globally or catching every failure in the renderer.
- Verification:
lanes-expand-postfix-20260511had 4 failed sync IPC calls totaling 1145 ms.lanes-sync-postfix-20260511had 3 successful sync IPC calls totaling 2 ms.
Scope Git Actions refreshes to lane status
- Why it helped: Stage/commit/fetch used to call full
listSnapshots, rebuilding runtime and decoration state for local Git actions. The scoped path keeps status fresh and skips snapshot decorations. - Apply when: A Git Actions handler finishes local Git work and calls bare
refreshLanes(). - Avoid: Recomputing runtime/rebase/conflict decorations after every stage, commit, stash, fetch, or history refresh.
- Verification: Before the change,
lanes-full-ui-audit-20260511-01stage cycle spent 551 ms inade.lanes.listSnapshots; commit spent 278 ms inlistSnapshots. After the change,lanes-refresh-light-20260511stage usedade.lanes.listat 40 ms with nolistSnapshots, and typed commit usedade.lanes.listat 213 ms with nolistSnapshots.
Split runtime refresh from Git status refresh
- Why it helped: Work pane pty/chat updates need runtime buckets, not fresh Git status for every lane. Runtime-only snapshot refresh avoids Git-status recompute while preserving prior status in store.
- Apply when: A session/runtime event updates running/awaiting/ended counts.
- Avoid: Calling full snapshots with
includeStatus:truefor runtime-only changes. - Verification:
lanes-refresh-light-20260511runtime snapshot refresh usedade.lanes.listSnapshotswithincludeStatus:false; the only runtime snapshot call was 76 ms while prior Git status stayed intact.
Keep appearance refresh metadata-only
- Why it helped: Lane color changes in manage/context flows previously refreshed full decorated snapshots. Appearance is metadata and should use the statusless list path while preserving previous Git status in the store.
- Apply when: New lane metadata or appearance handlers update color/name/description without changing branch state.
- Avoid: Bare
refreshLanes()after appearance-only updates. - Verification: Real UI manage-dialog trace
lanes-refresh-light-20260511showedade.lanes.updateAppearanceat 1 ms followed by an unnecessaryade.lanes.listSnapshotsat 308 ms. The lightweight path usesrefreshLanes({ includeStatus: false })instead.
Gate Stack Graph agent rosters behind visibility
- Why it helped: Lanes loaded per-lane agent rosters even while Stack Graph was closed. With 30 lanes, initial
/lanesload fanned outagentChat.list({ laneId })andsessions.list({ laneId })for every lane before the user opened the graph. - Apply when: A closed Lanes surface computes per-lane chat/session/agent data that is only rendered inside Stack Graph.
- Avoid: Fetching every lane's agent roster on page load to keep a closed dropdown warm.
- Verification:
lanes-20260531-1421-baselineLanes nav spent 87ade.localRuntime.callActioncalls / 25.3 s total IPC time. After gating rosters until Stack Graph opens,lanes-20260531-1421-after1dropped Lanes nav to 28callActioncalls / 13.3 s. Stack Graph then paid the roster cost on demand: 63 calls / 450 ms in the open segment.
Filter chat session lists before applying caps
- Why it helped:
agentChat.listSessions()loaded the newest 500 terminal sessions and then filtered to chat rows. Many newer CLI or shell sessions could push older chats out of the capped result, making chat panes look empty or stale even though persisted chats existed. - Apply when: A session list is intended to show a specific tool family, provider, or surface and the underlying table also stores high-volume shell/run-owned sessions.
- Avoid: Applying a global
limitbefore the meaningful filter, or widening limits as a substitute for the right query. - Verification:
fix(chat): filter chat session lists before capsaddstoolTypestosessionService.list, uses it fromagentChatService.listSessions, preserves legacy chat rows inferred from resume commands, and covers a regression with 505 newer shell sessions hiding an older chat.
Refresh visible linked PRs opportunistically
- Why it helped: Lanes PR badges and attached-PR status depended on the broad PR poller, so mergeability/check/review state could sit stale for about a minute after opening or switching lanes.
- Apply when: A Lanes surface needs attached PR status for lanes already visible in the grid. Refresh only linked, stale PR ids for visible lanes, dedupe them, cap the batch, and track recent request timestamps so scrolling or layout churn cannot stampede GitHub.
- Avoid: Kicking the global PR poller, refreshing GitHub-only PR rows without lane links, or widening the full PR refresh cadence to make one visible grid feel fresher.
- Verification:
perf(lanes): refresh visible PR status opportunisticallyrefreshes at most 4 stale visible linked PRs after a 260 ms debounce with a 15 s stale/request gate, merges returned summaries into Lanes tags, and covers selection/dedupe/cap behavior inLanesPage.test.ts.
Pause minimized and delayed Git Actions effects
- Why it helped: Minimized pane bodies were CSS-hidden but still mounted, so Git Actions kept diff/stash/sync/conflict loads, auto-rebase status, sync-status polling, event subscriptions, and commit-history requests alive in the background. Multiple visible lanes could also mount Git Actions panes at once.
- Apply when: A pane body owns timers, subscriptions, local Git reads, PR/AI/runtime status, or history loads. Pass pane minimized state into the render path, make child effects explicitly inactive while minimized, and stagger non-primary visible pane mounts so only the immediate lane warms eagerly.
- Avoid: Treating visual collapse as inactive, or adding a single page-level throttle while hidden pane components keep their own timers running.
- Verification:
perf(lanes): pause minimized git actions panesaddsPaneConfig.renderChildren, passesactive={!minimized}toLaneGitActionsPaneandCommitTimeline, staggers inline Git Actions bodies by visible-lane order, and covers inactive/active transitions in component tests. A real Electron/lanessegment,lanes-minimized-git-idleinlanes-20260531-1421-throttles-after3, kept the Git Actions pane minimized for 33.4 s with no slow Git Actions status/history IPC; main/browser p95 CPU was 0.15% and renderer tab p95 was 0.05%.
Signals
- GitHub stars
- 104
- Forks
- 12
- Last commit
- Sep 2026
Advanced
- Catalog kind
- skill
- Gateway key
ade-perf-lanes- Source
- github.com/arul28/ade