Skip to content

Autoresearch#286

Merged
arul28 merged 10 commits into
mainfrom
ade/autoresearch-d23e8745
May 12, 2026
Merged

Autoresearch#286
arul28 merged 10 commits into
mainfrom
ade/autoresearch-d23e8745

Conversation

@arul28
Copy link
Copy Markdown
Owner

@arul28 arul28 commented May 12, 2026

Summary

Describe the change.

What Changed

Key files and behaviors.

Validation

How you tested.

Risks

Anything to watch.


Note

Medium Risk
Medium risk because it changes main-process IPC behavior (sync/github/project open), adds new perf logging/sampling on the main thread when enabled, and adjusts git/github detection and git stash/history handling that affect core workflows.

Overview
Adds an opt-in perf-run telemetry pipeline gated by env (ADE_PERF_RUN_ID): main-process event logging (perfLog), IPC hooks to record manualStep/scenario events and finalize into summary.json (perfIpc, aggregator), plus a 1s app.getAppMetrics() sampler and automatic IPC invoke tracing during perf runs.

Hardens local-runtime-disabled mode (ADE_DISABLE_LOCAL_RUNTIME_DAEMON=1) by short-circuiting preload local-runtime calls/event polling and returning deterministic “sync unavailable” snapshots across sync IPC handlers instead of slow failing probes.

Improves GitHub + git operations: adds lightweight github.getRemoteStatus (remote/origin detection via direct git config parsing) to avoid full auth checks for UI elements, skips repo probes for classic PATs in getStatus, updates stash listing to ISO timestamps and adds user-friendly “missing lane worktree” errors for commit history.

Includes assorted UI/test updates supporting these flows (router hash-to-path bridge for BrowserRouter, delayed GitHub status fetch in AppShell, bounded/streaming local usage JSONL scans, and expanded unit tests for chat transcript actions and IPC/perf helpers).

Reviewed by Cursor Bugbot for commit d1e0e01. Configure here.

Summary by CodeRabbit

  • New Features

    • Added performance monitoring and metrics collection for boot, lanes, and work surfaces
    • Added direct GitHub origin detection from Git configuration
    • Added performance scenario framework for automated performance validation
    • Added local runtime daemon disable support for environments without local runtime
  • Bug Fixes

    • Improved error messaging for missing lane worktrees
    • Fixed git stash operations to apply then drop instead of using pop
    • Fixed context menu and file explorer menu positioning to stay within viewport bounds
    • Optimized local usage scanning with streaming and file size limits
  • Documentation

    • Added comprehensive performance optimization guides for boot, lanes, and work surfaces
    • Added autoresearch performance testing methodology documentation
  • Tests

    • Expanded test coverage for chat, lanes, terminals, and file components
    • Added new test suites for performance scenarios and IPC behavior

Greptile Summary

This PR introduces an env-gated performance measurement framework (ADE_PERF_RUN_ID) spanning main and renderer processes, hardens ADE_DISABLE_LOCAL_RUNTIME_DAEMON launches with stable "unavailable" sync snapshots, refines GitHub origin detection by reading git config directly (including worktree support), fixes the local usage-log scan to stream files with size/count limits, and adds a broad suite of component tests.

  • Perf framework: New perfLog, aggregator, metricsSampler, and perfIpc services on the main side collect JSONL events and produce a summary.json with fitness/component metrics; the renderer side adds markers, webVitals, scenarios, and a harness that drives automated scenario runs.
  • Sync service hardening: All sync-related IPC handlers now return a pre-built "unavailable" snapshot instead of throwing when ADE_DISABLE_LOCAL_RUNTIME_DAEMON=1, and the local runtime event pump is skipped entirely.
  • IPC + GitHub improvements: IPC.projectOpenRepo gains an optional rootPath arg for programmatic project switching, a new IPC.githubGetRemoteStatus endpoint exposes lightweight origin detection, and the createShortIpcCache race-condition (stale update from an older in-flight request) is fixed.

Confidence Score: 5/5

Safe to merge — all new perf infrastructure is fully env-gated behind ADE_PERF_RUN_ID and the production code paths look correct.

The entire perf framework only activates when ADE_PERF_RUN_ID is set, so normal app launches are unaffected. The sync-service unavailable-snapshot changes are straightforward early returns. The createShortIpcCache race-condition fix and streaming readline improvements are clear wins. The synchronous I/O concerns are real but intentional trade-offs within the controlled perf-testing context and do not affect production behavior.

apps/desktop/src/main/services/perf/perfIpc.ts and apps/desktop/src/main/services/ipc/registerIpc.ts warrant a second look for the synchronous I/O in the finalize handler and the once-captured traceIpcInvokes flag.

Important Files Changed

Filename Overview
apps/desktop/src/main/services/perf/perfLog.ts New perf-event log; uses appendFileSync on every event write including per-IPC-invocation tracing, which is synchronous I/O on the main process event loop.
apps/desktop/src/main/services/perf/aggregator.ts New aggregator called synchronously from the perfFinalize IPC handler; uses openSync/readSync/writeFileSync to process the entire events file on the main process event loop.
apps/desktop/src/main/services/perf/perfIpc.ts New IPC handlers for perf.getConfig, recordEvent, scenarioComplete, finalize; validation and error handling look correct.
apps/desktop/src/main/services/ipc/registerIpc.ts Adds per-IPC-invocation perf logging (appendFileSync in hot path during runs), unavailable-sync-snapshot helpers, projectOpenRepo rootPath arg, and resolveOptionalSyncService refactor.
apps/desktop/src/main/services/github/githubService.ts New readOriginUrlFromGitConfig/resolveGitDir helpers use synchronous FS calls; adds getRemoteStatus and fine-grained PAT probe distinction.
apps/desktop/src/preload/preload.ts Adds perf IPC bridge, getRemoteStatus, localRuntimeDaemonDisabled guard for event pump, rootPath arg to openRepo, and fixes the createShortIpcCache stale-update race condition.
apps/desktop/src/main/services/usage/usageTrackingService.ts Replaces whole-file reads with streaming readline, adds file-size and entry-count limits, and sorts files by mtime.
apps/desktop/src/renderer/components/app/App.tsx Adds BrowserHashRouteBridge for hash-to-path bridging when using BrowserRouter; path traversal filtering looks correct.
apps/desktop/src/renderer/perf/scenarios/index.ts New scenario registry and runner; error handling, smoke failure collection, and auto-finalize path all look correct.

Sequence Diagram

sequenceDiagram
    participant Main as Electron Main
    participant IPC as IPC Layer
    participant Renderer as Renderer
    participant Harness as Perf Harness
    participant Disk as ~/.ade/perf-runs/id/

    Main->>Main: initPerfRunFromEnv()
    Main->>Main: startMetricsSampler() 1Hz
    Main->>IPC: registerIpc() traceIpcInvokes captured once
    Main->>IPC: registerPerfIpcHandlers()
    Renderer->>IPC: perf.getConfig()
    IPC-->>Renderer: active runId scenario
    Renderer->>Harness: initPerfRuntime()
    Harness->>Harness: setPerfActive installWebVitalsObservers startRendererMemorySampler
    Harness->>IPC: perf.recordEvent rendererPerfReady
    Harness->>Harness: setTimeout 1500ms
    Harness->>IPC: perf.recordEvent scenarioStart
    Harness->>Renderer: scenario.run ctx
    loop Every IPC call during run
        IPC->>Disk: appendFileSync ipcInvoke event
    end
    Harness->>IPC: perf.scenarioComplete
    IPC->>Disk: appendFileSync scenarioEnd
    Harness->>IPC: perf.finalize
    IPC->>Main: stopMetricsSampler
    IPC->>Disk: aggregate readSync events writeFileSync summary
    IPC-->>Renderer: ok summary
Loading

Comments Outside Diff (1)

  1. apps/desktop/src/main/services/github/githubService.ts, line 68-130 (link)

    P2 Synchronous blocking I/O on the main process event loop

    readOriginUrlFromGitConfig uses fs.existsSync, fs.readFileSync, and fs.statSync — all synchronous. It is called from detectRepo and detectOrigin, which are the implementations of IPC handlers invoked for every project status poll. Synchronous file reads block Electron's main process event loop for every call. For the typical small git config this is invisible, but under load (many rapid status calls) or on a network-mounted filesystem it will stall all IPC processing. Consider using fs.promises equivalents instead.

    Prompt To Fix With AI
    This is a comment left during a code review.
    Path: apps/desktop/src/main/services/github/githubService.ts
    Line: 68-130
    
    Comment:
    **Synchronous blocking I/O on the main process event loop**
    
    `readOriginUrlFromGitConfig` uses `fs.existsSync`, `fs.readFileSync`, and `fs.statSync` — all synchronous. It is called from `detectRepo` and `detectOrigin`, which are the implementations of IPC handlers invoked for every project status poll. Synchronous file reads block Electron's main process event loop for every call. For the typical small git config this is invisible, but under load (many rapid status calls) or on a network-mounted filesystem it will stall all IPC processing. Consider using `fs.promises` equivalents instead.
    
    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 3 code review issues. Work through them one at a time, proposing concise fixes.

---

### Issue 1 of 3
apps/desktop/src/main/services/ipc/registerIpc.ts:1993
`traceIpcInvokes` is a `const` that captures `isPerfRunActive()` exactly once, at the moment `registerIpc()` is called. The comment in `main.ts` says `initPerfRunFromEnv()` "must come first", so the current call order keeps this correct. However the dependency is invisible — if `registerIpc` is ever called before `initPerfRunFromEnv` (e.g., during a test or a future refactor), IPC events will be silently dropped for the entire run. Calling `isPerfRunActive()` lazily inside the handler would make the behavior robust to call-order changes.

```suggestion
  const traceIpcInvokes = !app.isPackaged || process.env.ADE_TRACE_IPC === "1" || process.env.ADE_TRACE_IPC === "verbose";
```

### Issue 2 of 3
apps/desktop/src/main/services/ipc/registerIpc.ts:2174-2184
**`appendFileSync` per-IPC-invocation creates a measurement paradox**

During an active perf run every single IPC completion calls `perfAppend`, which calls `appendFileSync` — a synchronous disk write on the main process event loop. The IPC `durationMs` is captured before this write (so it is not included in the recorded value), but the write itself adds real latency that delays the next IPC handler from starting, inflating perceived responsiveness and degrading the very metrics the run is meant to measure. Batching events into an in-memory queue and flushing asynchronously would preserve data fidelity while eliminating this overhead.

### Issue 3 of 3
apps/desktop/src/main/services/perf/perfIpc.ts:90-106
The `aggregate()` call runs entirely on the main process event loop using synchronous file I/O (`openSync` / `readSync` / `writeFileSync`). A long perf run (10 min, 1 Hz metrics, many IPC events) can produce an events file of several hundred KB. Processing it synchronously stalls every other pending IPC handler until the full read-parse-write sequence finishes. A single `await new Promise(resolve => setImmediate(resolve))` before calling `aggregate()` yields to the event loop and prevents this stall.

```suggestion
  ipcMain.handle(IPC.perfFinalize, async () => {
    const run = getActiveRun();
    if (!run) return { ok: false, reason: "no-active-run" };
    stopMetricsSampler();
    await new Promise<void>((resolve) => setImmediate(resolve));
    try {
      const summary = aggregate(run.runId);
      return { ok: true, summary };
    } catch (error) {
      return {
        ok: false,
        reason: "aggregate-failed",
        error: error instanceof Error ? error.message : String(error),
      };
    } finally {
      finishPerfRun(run.runId);
    }
  });
```

Reviews (7): Last reviewed commit: "Align perf run id sanitization" | Re-trigger Greptile

@vercel
Copy link
Copy Markdown

vercel Bot commented May 12, 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 May 12, 2026 0:49am

@arul28
Copy link
Copy Markdown
Owner Author

arul28 commented May 12, 2026

@cursor review

@arul28
Copy link
Copy Markdown
Owner Author

arul28 commented May 12, 2026

@copilot review but do not make fixes

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 12, 2026

📝 Walkthrough

Walkthrough

Implements perf-run system (main/IPC/preload/renderer, scenarios, aggregator, scripts), adds GitHub remote detection with IPC/preload APIs, refines lanes refresh/state and Git UX, introduces local-runtime disabled fast-paths, updates routing/UI clamps, and expands tests and SKILL docs.

Changes

Desktop performance run framework

Layer / File(s) Summary
Main perf init, logging, IPC, and invoke telemetry
apps/desktop/src/main/main.ts, apps/desktop/src/main/services/perf/*, apps/desktop/src/main/services/ipc/registerIpc.ts, apps/desktop/src/shared/ipc.ts
Initialize runs from env, append JSONL events, expose IPC/config/record/finalize, sample metrics, register perf channels, and emit IPC invoke telemetry.
Renderer harness, markers, and web vitals
apps/desktop/src/renderer/perf/*, apps/desktop/src/renderer/main.tsx
Start renderer runtime, install Web Vitals, memory sampler, and mark/measure helpers.
Scenario registry and boot/lanes scenarios
apps/desktop/src/renderer/perf/scenarios/*
Register framework and boot/lanes scenarios with assertions and timing.
Aggregator and summary output
apps/desktop/src/main/services/perf/aggregator.ts
Compute aggregates and fitness, write summary.json.
Preload perf API exposure
apps/desktop/src/preload/*
Expose window.ade.perf methods and types.
CLI tools
scripts/*
Add perf-launch, reset-perf-pass, and run-perf-scenario CLIs.

GitHub remote detection and IPC/preload surface

Layer / File(s) Summary
Service origin detection and status
apps/desktop/src/main/services/github/githubService.ts
Prefer origin from Git config; adjust token probing; add getRemoteStatus.
IPC channel and preload API/cache
apps/desktop/src/main/services/ipc/registerIpc.ts, apps/desktop/src/shared/ipc.ts, apps/desktop/src/preload/*
Add githubGetRemoteStatus IPC and preload cache; support project.openRepo({ rootPath }).
Renderer consumers and tests
apps/desktop/src/renderer/lib/useGithubProjectRemote.ts, apps/desktop/src/renderer/components/app/*
Hook uses remote status; TopBar triggers it; AppShell polling/timer refactor.
Service tests updated
apps/desktop/src/main/services/github/githubService.test.ts
Reset mocks; update classic-token expectations.

Local-runtime disabled fast-paths and sync IPC behavior

Layer / File(s) Summary
Sync resolver and disabled-daemon behavior
apps/desktop/src/main/services/ipc/registerIpc.ts
Add unavailable snapshot; adjust sync flows; projectOpenRepo arg support.
Preload eligibility and fallbacks
apps/desktop/src/preload/preload.ts
Short-circuit local actions/sync; restrict polling; fix cache race.
Preload/IPC sync tests
apps/desktop/src/preload/preload.test.ts, apps/desktop/src/main/services/ipc/runtimeBridge.test.ts
Verify daemon-disabled behavior and resolver logic.

Lanes refresh model and Git history UX/error mapping

Layer / File(s) Summary
Git service worktree mapping and stashes
apps/desktop/src/main/services/git/*
Map missing worktree errors; change stashPop; ISO stash timestamps; tests.
CommitTimeline friendly errors/tests
apps/desktop/src/renderer/components/lanes/*
Normalize errors and show remediation; add tests.
LaneDiff pane tests
apps/desktop/src/renderer/components/lanes/LaneDiffPane.test.tsx
Bound initial list and retry diff loading.
Store refreshLanes and tests
apps/desktop/src/renderer/state/*
Add includeSnapshots; preserve status; reconcile snapshots; tests.
LanesPage mount rules
apps/desktop/src/renderer/components/lanes/LanesPage.tsx, tests
Conditional Git Actions mount; lighter appearance refresh.
Git Actions pane improvements
apps/desktop/src/renderer/components/lanes/LaneGitActionsPane*
Include untracked in stashes; bundle refresh; accessible labels; large-list Show all.

App routing bridge and drop handling

Layer / File(s) Summary
BrowserHashRouteBridge and tests
apps/desktop/src/renderer/components/app/App.tsx, .../App.workKeepAlive.test.tsx
Translate legacy hash routes to path.
Drop handling precision
apps/desktop/src/renderer/components/app/CommandPalette.test.tsx
Assert latest drop wins and correct browse args.

Renderer UI clamps, layout, and controls

Layer / File(s) Summary
FilesPage context menu clamp/embedded layout
apps/desktop/src/renderer/components/files/*
Clamp overlay; adjust split; copy path and read-only tests.
Terminals session menu clamp
apps/desktop/src/renderer/components/terminals/SessionContextMenu.tsx
Clamp to window bounds.
WorkSidebar and FloatingPane state
apps/desktop/src/renderer/components/terminals/*, .../ui/FloatingPane.tsx
Responsive tabs; data-minimized; close-tab data attribute; tests.
Session filter panel and LaneCombobox
apps/desktop/src/renderer/components/terminals/*
Full-width combobox; filter UI classes; collapse/expand tests.

Chat UI behavior, rendering, panels, and tests

Layer / File(s) Summary
Rendering and work log truncation
apps/desktop/src/renderer/components/chat/*
Highlight code blocks; truncate long details.
Composer and message list interactions
apps/desktop/src/renderer/components/chat/*
Edit/remove steer, suggestions, slash commands, copy text/code, expand results, transcript nav.
Drawers/panels interactions
apps/desktop/src/renderer/components/chat/*
Agent drawers, App Control, browser, toolbar, iOS sim, terminal drawer.
iOS panel stale error clearing
apps/desktop/src/renderer/components/chat/ChatIosSimulatorPanel.tsx
Clear stale launch-target errors.
iOS simulator expanded flows
apps/desktop/src/renderer/components/chat/ChatIosSimulatorPanel.test.tsx
Preview, inspector, clipboard, and lab flows.
Subagents tests
apps/desktop/src/renderer/components/chat/ChatSubagentsPanel.test.tsx
Drawer/timeline interactions and copy id.
Cursor Cloud inline launch test
apps/desktop/src/renderer/components/chat/CursorCloudInlineLaunch.test.tsx
Cancel flow validation.
macOS VM panel test
apps/desktop/src/renderer/components/terminals/MacosVmPanel.test.tsx
SelectPoint and context add.
ChatGitToolbar tests
apps/desktop/src/renderer/components/chat/ChatGitToolbar.test.tsx
Lane navigation and run menu behavior.

Usage tracking JSONL streaming and limits

Layer / File(s) Summary
Service streaming and discovery
apps/desktop/src/main/services/usage/usageTrackingService.ts
Stream lines; enforce caps; newest-first selection.
Tests
apps/desktop/src/main/services/usage/usageTrackingService.test.ts
Exclude oversized JSONL file.

CLI server tests (ade-cli)

Layer / File(s) Summary
ade-cli multiProject/stdio tests
apps/ade-cli/src/*.test.ts
Use expectedProjectRoot for projects.add assertions.

Skills docs

Layer / File(s) Summary
ade-autoresearch, ade-perf-boot, ade-perf-lanes, ade-perf-work
.agents/skills/*
Add autoresearch workflow and perf patterns guidance for boot/lanes/work.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~75 minutes

Possibly related PRs

  • arul28/ADE#221 — Overlaps IPC registry and registerIpc wiring with new GitHub/perf channels here.
  • arul28/ADE#195 — Adjusts sync IPC handling related to the disabled-daemon resolver logic added here.
  • arul28/ADE#284 — Modifies lanes behavior/tests adjacent to the lanes refresh/mount rules in this PR.

Suggested labels

desktop

✨ 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/autoresearch-d23e8745

@capy-ai
Copy link
Copy Markdown

capy-ai Bot commented May 12, 2026

Capy auto-review is paused for this organization because the monthly auto-review limit has been reached. Increase the limit or turn it off in billing settings to resume automatic reviews.

Comment thread apps/desktop/src/preload/preload.ts
Comment thread apps/desktop/src/renderer/perf/scenarios/boot.ts Outdated
Comment thread apps/desktop/src/main/services/git/gitOperationsService.ts Outdated
Comment thread apps/desktop/src/renderer/perf/webVitals.ts
Comment thread apps/desktop/src/main/services/github/githubService.ts
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: 20

Caution

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

⚠️ Outside diff range comments (2)
apps/desktop/src/renderer/components/app/AppShell.tsx (1)

793-813: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Reset githubStatus on project-root change to prevent stale banner state.

Switching from project A to B keeps A's githubStatus until the delayed fetch runs (12–30s), so the banner displays incorrect status for B during that window.

Suggested fix
   useEffect(() => {
     let cancelled = false;
     if (!currentProjectRoot) {
       setGithubStatus(null);
       return;
     }
+    // Avoid cross-project banner bleed while waiting for delayed refresh.
+    setGithubStatus(null);
     const githubTimer = window.setTimeout(() => {
       void window.ade.github.getStatus().then((status) => {
         if (cancelled) return;
         setGithubStatus(status);
       }).catch(() => {
         if (cancelled) return;
         setGithubStatus(null);
       });
     }, githubBannerDismissed ? GITHUB_STATUS_DISMISSED_BANNER_DELAY_MS : GITHUB_STATUS_STARTUP_DELAY_MS);
🤖 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/AppShell.tsx` around lines 793 -
813, When the project root changes we need to clear any existing GitHub banner
state immediately to avoid showing stale status; in the useEffect that depends
on currentProjectRoot and githubBannerDismissed (the effect that declares
cancelled, githubTimer, and calls window.ade.github.getStatus()), call
setGithubStatus(null) right at the start of the effect whenever
currentProjectRoot is truthy (i.e., not only when it's falsy) before scheduling
the githubTimer. This ensures AppShell's githubStatus state is reset on project
switches (referencing useEffect, currentProjectRoot, setGithubStatus,
githubTimer, githubBannerDismissed) while preserving the existing delayed fetch
and cleanup logic.
apps/desktop/src/main/services/ipc/registerIpc.ts (1)

1940-1941: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Perf IPC events are still gated behind trace mode.

recordIpcInvokeAggregate() only runs inside the wrapper registered under traceIpcInvokes. In packaged perf runs without ADE_TRACE_IPC, that wrapper never installs, so the new ipcInvoke perf events are never emitted. If perf logging is meant to work outside dev, the wrapper install condition needs to include perf mode too.

Also applies to: 2154-2218

🤖 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/ipc/registerIpc.ts` around lines 1940 - 1941,
The trace wrapper that installs recordIpcInvokeAggregate is only enabled when
traceIpcInvokes is true, so packaged perf runs without ADE_TRACE_IPC never
register the ipcInvoke perf events; change the traceIpcInvokes condition to also
include your perf-mode flag (e.g. ADE_PERF or whatever runtime
isPerfMode()/enablePerfLogging() you use) so the wrapper installs in perf runs
too, and apply the same change to the other wrapper block that registers ipc
invoke handlers (the later block that currently wraps
recordIpcInvokeAggregate/traceEveryIpcInvoke logic) so both locations will emit
ipcInvoke perf events outside dev when perf mode is enabled.
🧹 Nitpick comments (10)
apps/desktop/src/renderer/components/chat/ChatBuiltInBrowserPanel.test.tsx (1)

188-208: ⚡ Quick win

Verify underlying behavior, not just UI text.

The test validates that the expected UI messages appear but doesn't verify that the screenshot capture flow was actually triggered or that the cancellation prevents context from being added.

Consider adding assertions to verify:

  1. api.captureScreenshot was called after clicking "Screenshot"
  2. onAddContext was NOT called after cancelling
✅ Suggested assertions to add
 it("starts and cancels screenshot crop mode when chat context is available", async () => {
   const { api } = installBrowserApi();
   api.captureScreenshot.mockResolvedValue({
     dataUrl: "data:image/png;base64,iVBORw0KGgo=",
     width: 640,
     height: 360,
     capturedAt: "2026-05-12T00:00:00.000Z",
   });

-  render(<ChatBuiltInBrowserPanel sessionId="chat-1" onAddContext={vi.fn()} />);
+  const onAddContext = vi.fn();
+  render(<ChatBuiltInBrowserPanel sessionId="chat-1" onAddContext={onAddContext} />);

   fireEvent.click(await screen.findByText("Screenshot"));

+  await waitFor(() => expect(api.captureScreenshot).toHaveBeenCalled());
   expect(await screen.findByText("Drag a browser region to attach the screenshot crop and nearby page context.")).toBeTruthy();
   expect(screen.getByText("Cancel screenshot")).toBeTruthy();

   fireEvent.click(screen.getByText("Cancel screenshot"));

   expect(await screen.findByText("Browser screenshot capture cancelled.")).toBeTruthy();
   expect(screen.getByText("Screenshot")).toBeTruthy();
+  expect(onAddContext).not.toHaveBeenCalled();
 });
🤖 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/ChatBuiltInBrowserPanel.test.tsx`
around lines 188 - 208, The test currently only verifies UI text; update it to
also assert the underlying behavior by (1) passing a mock for the onAddContext
prop (e.g., a vi.fn()) to the rendered ChatBuiltInBrowserPanel and (2) after
firing the "Screenshot" click assert that the installed browser API's
captureScreenshot mock (api.captureScreenshot) was called, and then after
clicking "Cancel screenshot" assert that the onAddContext mock was NOT called;
locate these changes around the existing render(...) and fireEvent calls in the
"starts and cancels screenshot crop mode when chat context is available" test so
you verify api.captureScreenshot and onAddContext interactions for
ChatBuiltInBrowserPanel using the installBrowserApi helper.
apps/desktop/src/renderer/components/terminals/WorkViewArea.test.tsx (2)

212-278: 💤 Low value

Consider using data attributes instead of className assertions.

The test checks pane().className for the presence of "minimized" (lines 272, 275). While this works, it couples the test to implementation details. Consider having FloatingPane expose a data-minimized attribute for more robust testing.

♻️ Suggested improvement

In FloatingPane component, add:

<div data-pane-id="..." data-minimized={minimized ? "true" : "false"}>

Then in the test:

-expect(pane().className).toContain("minimized");
+expect(pane().getAttribute("data-minimized")).toBe("true");
🤖 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/terminals/WorkViewArea.test.tsx` around
lines 212 - 278, The test currently asserts on pane().className contains
"minimized" which couples it to styling; update the FloatingPane component to
render a stable attribute (e.g., data-minimized with value "true"/"false")
driven by its minimized prop, and then change the test in WorkViewArea.test.tsx
to assert on that attribute via pane().getAttribute('data-minimized') (or use a
queryBy selector for [data-minimized="true"]) instead of inspecting className;
reference FloatingPane and the pane() helper in the test when making these
edits.

323-325: 💤 Low value

Clarify why all "Close tab" buttons are clicked.

The test clicks every element matching the title "Close tab" (line 323-324), but only asserts that onCloseItem was called with the specific session ID. If the intent is to test closing a specific tab, consider querying more precisely (e.g., by session ID or using getByTitle instead of getAllByTitle).

🤖 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/terminals/WorkViewArea.test.tsx` around
lines 323 - 325, The test currently clicks every element returned by
screen.getAllByTitle("Close tab") but only asserts on onCloseItem for a single
session id; narrow the click target to the specific tab you want to test (e.g.,
query the close button for the session id or use getByTitle with a unique title
per tab) or alternatively assert that onCloseItem was called for every session
id clicked. Update the test around the click loop to locate the close button for
the target session (reference onCloseItem and the test that renders the
tab/session id) and perform a single click, then assert onCloseItem was called
with that session id, or if keeping the loop verify the call count and arguments
for each expected id.
apps/desktop/src/renderer/components/terminals/WorkStartSurface.test.tsx (1)

9-12: 💤 Low value

Consider importing the actual store type for stronger type safety.

The inline type annotation for the selector parameter could drift from the actual appStore shape. Consider importing the store's state type to ensure the mock stays in sync with the real implementation.

♻️ Suggested improvement
+import type { AppState } from "../../state/appStore";
+
 vi.mock("../../state/appStore", () => ({
-  useAppStore: (selector: (state: { selectedLaneId: string | null; selectLane: typeof selectLane }) => unknown) =>
+  useAppStore: <T>(selector: (state: AppState) => T): T =>
     selector({ selectedLaneId: null, selectLane }),
 }));
🤖 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/terminals/WorkStartSurface.test.tsx`
around lines 9 - 12, Replace the ad-hoc inline selector type with the real app
store state type by importing the exported state/type from the module that
defines the store (e.g., import { AppStoreState } from "../../state/appStore")
and use that type for the selector parameter in the vi.mock for useAppStore;
update references to selectLane/useAppStore to match the real signatures so the
mock's shape stays in sync with the actual app store implementation.
apps/desktop/src/renderer/components/terminals/SessionContextMenu.tsx (1)

68-89: 💤 Low value

Verify if renaming dependency is necessary.

The useLayoutEffect includes renaming in its dependency array (line 89), which means the position will be recalculated when the user enters/exits rename mode. If the menu dimensions don't change when switching to rename mode, this dependency could be removed to avoid unnecessary layout recalculations.

🤖 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/terminals/SessionContextMenu.tsx` around
lines 68 - 89, The useLayoutEffect that computes and sets clampedPosition
currently lists renaming in its dependency array causing unnecessary reflows;
inspect whether the menu's DOM size or the values used (menu,
menuRef.current.getBoundingClientRect()) actually change when renaming toggles
and if not, remove renaming from the dependency array so the effect depends only
on menu (and any other real sources like menuRef) — update the dependency list
of the useLayoutEffect containing menu, menuRef, and setClampedPosition (the
effect that reads menu, menuRef.current.getBoundingClientRect(), and calls
setClampedPosition) to exclude renaming unless you confirm rename mode changes
layout.
apps/desktop/src/renderer/components/app/App.tsx (1)

331-346: 💤 Low value

Consider validating hash format before navigation.

The bridge navigates to hash.slice(1) after checking hash.startsWith("#/"), which handles the common case. However, edge cases like #//, #/../../, or hashes with query params might behave unexpectedly. Consider adding validation or normalization before calling navigate.

♻️ Suggested validation
 const syncHashRoute = () => {
   const hash = window.location.hash;
   if (!hash.startsWith("#/")) return;
-  navigate(hash.slice(1), { replace: true });
+  const path = hash.slice(1);
+  if (path && path !== "//" && !path.includes("..")) {
+    navigate(path, { replace: true });
+  }
 };
🤖 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/App.tsx` around lines 331 - 346, In
BrowserHashRouteBridge (syncHashRoute), validate and normalize the extracted
route before calling navigate: after confirming hash.startsWith("#/"), compute
raw = hash.slice(1), split off any query/hash suffix (preserve suffix
separately), collapse duplicate slashes, reject or reject-normalize any ".."
path segments (e.g., remove or return root for paths containing traversal),
ensure the path begins with a single "/", then reattach preserved query/hash if
desired; wrap this in a try/catch and only call navigate(normalizedPath, {
replace: true }) when normalizedPath passes these checks to avoid navigating to
unsafe or malformed routes.
scripts/reset-perf-pass.sh (1)

24-25: 💤 Low value

Consider adding a warning for destructive operation.

The git clean -fdx command (line 25) will permanently delete all untracked and ignored files. While appropriate for a reset script, consider adding a warning message or confirmation prompt to prevent accidental data loss, especially if users might have uncommitted work in the perf-pass directory.

⚠️ Suggested warning
+echo "[perf-pass] WARNING: this will delete all untracked files"
 echo "[perf-pass] clearing untracked"
 git clean -fdx
🤖 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 `@scripts/reset-perf-pass.sh` around lines 24 - 25, Add a clear, explicit
warning and an optional confirmation before running the destructive command `git
clean -fdx` in the reset script: update the block around the existing `echo
"[perf-pass] clearing untracked"` / `git clean -fdx` to print a multi-line
warning, prompt the user to confirm (Y/N), and only run `git clean -fdx` when
the user confirms; also provide a non-interactive bypass (e.g., respect an env
var like PERF_PASS_FORCE or CI) so automated runs are unaffected.
apps/desktop/src/main/services/perf/perfIpc.ts (1)

21-64: ⚡ Quick win

Consider adding ipcMain.removeHandler() before handler registration for defensive idempotency.

While registerPerfIpcHandlers() is currently called once during initialization, adding ipcMain.removeHandler() for each channel before registration (e.g., IPC.perfGetConfig, IPC.perfRecordEvent, IPC.perfScenarioComplete, IPC.perfFinalize) would guard against potential re-registration errors if the function is invoked multiple times in future code 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/perf/perfIpc.ts` around lines 21 - 64, Add
defensive idempotency by removing existing IPC handlers before registering new
ones: in registerPerfIpcHandlers() call ipcMain.removeHandler(...) for each
channel (IPC.perfGetConfig, IPC.perfRecordEvent, IPC.perfScenarioComplete,
IPC.perfFinalize) immediately before the corresponding ipcMain.handle(...) so
re-calling registerPerfIpcHandlers() won't throw or double-register handlers;
keep the existing handler implementations (the anonymous callbacks that call
getActiveRun, isRunActive, appendEvent, aggregate, etc.) unchanged.
apps/desktop/src/renderer/perf/markers.ts (1)

47-56: 💤 Low value

Chrome-specific performance.memory API may not be available in all contexts.

The performance.memory property is a non-standard Chrome extension and may be unavailable depending on Electron configuration or future browser changes. The code handles this gracefully by returning null, but the lack of availability logging could make debugging harder.

Consider logging once when memory sampling is unavailable:

 const memoryReader = () => {
   const perf = performance as Performance & {
     memory?: { usedJSHeapSize?: number; totalJSHeapSize?: number };
   };
-  if (!perf.memory) return null;
+  if (!perf.memory) {
+    if (perfActive) console.debug('[perf] performance.memory unavailable');
+    return null;
+  }
   return {
     usedMB: Math.round((perf.memory.usedJSHeapSize ?? 0) / 1024 / 1024),
     totalMB: Math.round((perf.memory.totalJSHeapSize ?? 0) / 1024 / 1024),
   };
 };
🤖 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/perf/markers.ts` around lines 47 - 56, memoryReader
currently returns null when performance.memory is absent but never logs this,
making it hard to debug; update the memoryReader function to emit a single
one-time warning when performance.memory is unavailable by adding a module-level
flag (e.g., memorySamplingUnavailableLogged) and, inside memoryReader, if
!perf.memory and the flag is false, call the chosen logger (console.warn or the
app's logger) with a clear message and set the flag to true so subsequent calls
don't spam logs; reference the memoryReader function and the new
memorySamplingUnavailableLogged flag when implementing.
scripts/run-perf-scenario.mjs (1)

14-17: ⚡ Quick win

Migrate from deprecated rmSync recursive option.

rmSync with { recursive: true } is available in Node.js 22.x but is deprecated. Use fs.promises.rm() with { recursive: true, force: true } instead.

🤖 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 `@scripts/run-perf-scenario.mjs` around lines 14 - 17, The script currently
uses the deprecated synchronous rmSync import; replace uses of rmSync with the
promise API (node:fs.promises.rm or import { rm } from "node:fs/promises") and
call it with options { recursive: true, force: true } (e.g., await
rm(targetPath, { recursive: true, force: true })). Update the top imports to
remove rmSync and add the promise rm (or import { promises as fsPromises } and
use fsPromises.rm), and ensure the surrounding scope is async or use top-level
await so the removal is awaited; keep all other logic unchanged and use the same
target variable names where rmSync was previously invoked.
🤖 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/main/services/github/githubService.ts`:
- Around line 256-257: The current code short-circuits and returns repo: null if
parseGitHubRepoFromRemoteUrl(configOriginUrl) returns null; change the logic in
the block that calls readOriginUrlFromGitConfig(projectRoot) so it only returns
early when parseGitHubRepoFromRemoteUrl(...) yields a non-null repo object,
otherwise continue to the runGit fallback (do not exit with null). Apply the
same change to the similar block around the other origin parsing (lines 268-271)
so both uses of parseGitHubRepoFromRemoteUrl permit falling through to runGit
when the parser fails.

In `@apps/desktop/src/main/services/ipc/registerIpc.ts`:
- Around line 1779-1847: The current module creates a single frozen
unavailableSyncSnapshot and returns it from buildUnavailableSyncSnapshot, which
incorrectly reports transferReadiness.ready = true and uses static timestamps;
change buildUnavailableSyncSnapshot to construct and return a new
SyncRoleSnapshot each call (create a fresh SyncDeviceRecord and SyncRoleSnapshot
inside buildUnavailableSyncSnapshot) and set transferReadiness.ready = false
with an appropriate blocker (e.g., ["local_runtime_daemon_disabled"]) and update
survivableState/survivableStateText and timestamps
(createdAt/updatedAt/lastSeenAt and snapshot updatedAt) so every request gets a
fresh, correctly blocked/unavailable snapshot; update references to
unavailableSyncSnapshot to only hold templates/constants if needed but ensure
buildUnavailableSyncSnapshot performs the full construction.
- Around line 2121-2129: The perfAppend call inside the isPerfRunActive() block
can throw and bubble up into the IPC handler; wrap the perfAppend(...)
invocation in a try/catch so telemetry writes are best-effort and cannot fail
the IPC call. Specifically, in registerIpc.ts around the code that builds the
ipcInvoke perf event, catch any error from perfAppend (while keeping the
existing isPerfRunActive check and event shape with
channel/winId/durationMs/failed) and swallow it or log it to a non-failing
logger so the IPC response is unaffected.

In `@apps/desktop/src/main/services/perf/aggregator.ts`:
- Around line 107-120: The readEvents helper currently loads the entire file
into memory with readFileSync(...).split("\n"), which can OOM for large JSONL
logs; replace it with a streaming line-by-line reader (use fs.createReadStream +
readline.createInterface) inside readEvents to parse each line and push parsed
Event objects as they are read, skipping malformed lines, and return the
accumulated array only after the stream closes; apply the same streaming fix to
the other JSONL-reading code block referenced (the similar pattern used in
aggregate()/finalize code) so no code path materializes the whole file in
memory.
- Around line 131-133: The current computation sets startedAt = events[0]!.ts
and endedAt = events[events.length - 1]!.ts which assumes events are strictly
ordered; instead scan the events array to compute the minimum timestamp for
startedAt and the maximum timestamp for endedAt (and handle the empty-events
case), then derive durationMs from those min/max values so out-of-order appends
won't skew metrics (update references to events, startedAt, endedAt, and
durationMs accordingly).

In `@apps/desktop/src/main/services/perf/perfIpc.ts`:
- Around line 58-63: The handler registered with ipcMain.handle for
IPC.perfFinalize currently calls aggregate(run.runId) without guarding against
exceptions; wrap the aggregate call in a try/catch inside the ipcMain.handle
callback (keeping the existing getActiveRun() early-return) and on error return
a deterministic shaped response like { ok: false, reason: "aggregate-error",
error: String(err) } so the IPC consumer always receives a typed failure
payload; reference the ipcMain.handle(IPC.perfFinalize, ...), getActiveRun(),
and aggregate(run.runId) symbols when making the change.
- Around line 34-39: Validate the incoming kind in the IPC.perfRecordEvent
handler instead of casting to never: check args.kind (from PerfRecordEventArgs)
against the allowed PerfEventKind values
("scenarioStart","scenarioEnd","mark","measure","webVital","longTask","ipcInvoke","processMetrics","rendererMemory","note")
and if it isn’t one of them return { ok: false, reason: "invalid-kind" }; only
then call appendEvent({ ts, kind, ...rest }). Also update the perfFinalize
handler to wrap the call to aggregate() in a try-catch and return a structured
failure (e.g. { ok: false, reason: "aggregate-failed" }) when aggregate() throws
so the IPC call does not reject unhandled.

In `@apps/desktop/src/main/services/perf/perfLog.ts`:
- Around line 36-55: The ADE_PERF_RUN_ID value is used directly in
initPerfRunFromEnv to build a filesystem path, allowing path traversal via
slashes or ..; fix by sanitizing/validating the runId returned by readEnvRunId
before using it: in readEnvRunId or immediately in initPerfRunFromEnv, reject or
normalize values that contain path separators or parent-dir segments and only
allow a safe whitelist (e.g., alphanumeric, hyphen, underscore) or fallback to
path.basename(runId) if appropriate; update ensureDir behavior to operate only
on the validated dir string (references: readEnvRunId, initPerfRunFromEnv,
ensureDir).

In `@apps/desktop/src/renderer/components/chat/CursorCloudInlineLaunch.test.tsx`:
- Around line 62-64: The test uses waitFor to assert a negative case which
causes unnecessary timeout; remove the waitFor wrapper and replace it with a
direct synchronous assertion —
expect(window.ade.ai.cursorCloudCreateRun).not.toHaveBeenCalled(); — immediately
after the synchronous cancel click/onClose action so the test does not wait;
update the assertion that currently references waitFor(...) to a plain
expect(...) and keep references to window.ade.ai.cursorCloudCreateRun and the
onClose/cancel interaction.

In `@apps/desktop/src/renderer/perf/harness/index.ts`:
- Around line 35-38: The setTimeout callback in start() calls
runScenario(cfg.scenario) but ignores the returned Promise, causing unhandled
rejections; update the setTimeout callback to handle the Promise returned by
runScenario (e.g., call runScenario(cfg.scenario as string).catch(err => { /*
log the error */ }) or wrap in an async IIFE and attach .catch) so any rejection
is logged/handled instead of failing silently; refer to the start function and
runScenario(cfg.scenario) call when making the change.

In `@apps/desktop/src/renderer/perf/scenarios/boot.ts`:
- Line 39: Replace the hard-coded macOS path assigned to the perfPass constant
with a platform-agnostic resolution: read process.env.ADE_PERF_PASS_DIR first
(use that if set), otherwise compute a default using the current user home
directory (os.homedir()) and path.join to append the relative "Projects/perf
pass" segment; update the perfPass constant to use this value and ensure the
module imports/uses Node's os and path utilities where perfPass is defined.
- Around line 45-47: The renderer is passing { rootPath: perfPass } to
window.ade?.project.openRepo despite the preload/IPC openRepo signature taking
no parameters; remove the incorrect argument and the fake type assertion. Update
the call at the site using window.ade?.project.openRepo (the const result =
await ... line) to invoke openRepo() with no args and drop the (args?: {
rootPath?: string }) cast, or alternatively if you prefer accepting a rootPath
change the preload declaration (openRepo: async (rootPath?: string) =>
Promise<ProjectInfo | null>) and the IPC handler to accept an optional rootPath
parameter so the renderer, preload, and IPC signatures match.
- Around line 66-68: The code asserts listTargets returns Promise<unknown[]>;
instead import and use the shared RemoteRuntimeTarget type and replace the
unknown[] assertion so targets is typed as Promise<RemoteRuntimeTarget[]> (or
const targets: RemoteRuntimeTarget[] after awaiting). Update the type import
(RemoteRuntimeTarget) and change the assertion on (window.ade as unknown as {
remoteRuntime?: { listTargets?: () => Promise<RemoteRuntimeTarget[]> } }) so
remoteRuntime?.listTargets?.() and the resulting targets variable use
RemoteRuntimeTarget to match the preload IPC contract.

In `@apps/desktop/src/renderer/perf/scenarios/index.ts`:
- Around line 114-132: The early-return branches in runScenario (the "scenario
not found" and the requiresClaude check) skip calling finalize(), leaving runs
without terminal artifacts; wrap the main body of runScenario in a try/finally
or ensure finalize() is invoked before returning in those branches so
finalization always runs. Specifically, when handling the missing scenario
(where you call window.ade?.perf?.recordEvent with note "scenarioNotFound") and
the requiresClaude block (where you call window.ade?.perf?.scenarioComplete),
make sure to call the same finalize() path (or call
window.ade?.perf?.scenarioComplete then finalize()) before returning so
runScenario always finishes with finalize() regardless of early exits.

In `@apps/desktop/src/renderer/perf/scenarios/lanes.ts`:
- Around line 6-9: The readiness check in the ctx.waitFor call uses a permissive
selector ("[data-route='lanes'], main") which can be true before the /lanes
content is rendered; change the condition to wait for a lanes-specific DOM
signal (e.g., only "[data-route='lanes']" or an inner element unique to the
lanes view like ".lanes-container" or ".lanes-list") so timing measures reflect
actual lanes readiness, and make the same change for the second ctx.waitFor
usage at the other occurrence.
- Around line 79-83: The selected element stored in scrollable may not actually
be scrollable; modify the selection/validation so after obtaining the node
(variable scrollable) you verify it's scrollable by checking computed style
overflow (overflow/overflowY contains "auto" or "scroll") and that scrollHeight
> clientHeight (or otherwise use document.scrollingElement as a fallback). If
the checks fail, try to locate a truly scrollable ancestor or fall back and keep
the ctx.assert(false, "no scrollable container found in lanes") behavior when no
scrollable element can be found. Ensure this validation occurs wherever
scrollable is used so the scroll loop records real scrolling.

In `@apps/desktop/src/renderer/perf/webVitals.ts`:
- Around line 59-68: The CLS accumulator is reset per PerformanceObserver
callback because `total` is local inside the `observe("layout-shift", ...)`
callback; change it to a persistent accumulator (e.g., module-level or
closure-scoped variable like `let cumulativeCLS = 0`) and in the
`observe("layout-shift", (entries) => { ... })` handler add each eligible
`entry.value` to `cumulativeCLS` (skip `entry.hadRecentInput`) and then call
`send("webVital", { metric: "CLS", value: cumulativeCLS })` when `cumulativeCLS
> 0` so CLS is cumulative across the page lifecycle rather than per callback
batch.

In `@scripts/perf-launch.mjs`:
- Around line 80-92: The stop function currently sends SIGTERM and schedules a
SIGKILL but immediately calls process.exit(code), preventing the parent from
waiting for the child; update stop to send SIGTERM to child (as it does), then
wait for the child's termination (listen for the child's 'exit' or 'close' event
or await a Promise that resolves on that event) with a timeout of ~5s, and only
if the timeout elapses send SIGKILL and wait again briefly before finally
calling process.exit(code); reference the stop function and the child variable
(and use SIGTERM / SIGKILL) so the signal send and the event-listening Promise
replace the immediate process.exit call.
- Around line 49-53: The code hardcodes a machine-specific defaultPerfPass
("/Users/admin/Projects/perf pass"); remove that constant and stop falling back
to it. Update the projectRoot assignment so it uses args.noProject to create a
temp dir via mkdtempSync(join(tmpdir(), "ade-perf-no-project-")), otherwise
prefer args.projectRoot or process.env.ADE_PERF_PASS_DIR and finally fall back
to a neutral value (e.g., process.cwd() or throw an informative error) instead
of the hardcoded path; change references to defaultPerfPass accordingly so no
machine-specific path remains.

In `@scripts/run-perf-scenario.mjs`:
- Around line 44-47: Replace the macOS-hardcoded defaultPerfPass and add cleanup
for temp dirs: make the default perf pass path platform-agnostic (derive from
os.homedir() and path.join or respect ADE_PERF_PASS_DIR) instead of
"/Users/admin/Projects/perf pass", and when noProject is true use
mkdtempSync(tmpdir(), ...) to create perfPassDir but also record that path and
ensure it is removed after use (or on process termination) by registering a
cleanup handler (process.on('exit') / 'SIGINT' / 'uncaughtException') that
removes the temp directory (fs.rmSync or fs.rmdirSync with recursive option).
Update references to defaultPerfPass, perfPassDir, noProject, mkdtempSync,
tmpdir and ADE_PERF_PASS_DIR accordingly.

---

Outside diff comments:
In `@apps/desktop/src/main/services/ipc/registerIpc.ts`:
- Around line 1940-1941: The trace wrapper that installs
recordIpcInvokeAggregate is only enabled when traceIpcInvokes is true, so
packaged perf runs without ADE_TRACE_IPC never register the ipcInvoke perf
events; change the traceIpcInvokes condition to also include your perf-mode flag
(e.g. ADE_PERF or whatever runtime isPerfMode()/enablePerfLogging() you use) so
the wrapper installs in perf runs too, and apply the same change to the other
wrapper block that registers ipc invoke handlers (the later block that currently
wraps recordIpcInvokeAggregate/traceEveryIpcInvoke logic) so both locations will
emit ipcInvoke perf events outside dev when perf mode is enabled.

In `@apps/desktop/src/renderer/components/app/AppShell.tsx`:
- Around line 793-813: When the project root changes we need to clear any
existing GitHub banner state immediately to avoid showing stale status; in the
useEffect that depends on currentProjectRoot and githubBannerDismissed (the
effect that declares cancelled, githubTimer, and calls
window.ade.github.getStatus()), call setGithubStatus(null) right at the start of
the effect whenever currentProjectRoot is truthy (i.e., not only when it's
falsy) before scheduling the githubTimer. This ensures AppShell's githubStatus
state is reset on project switches (referencing useEffect, currentProjectRoot,
setGithubStatus, githubTimer, githubBannerDismissed) while preserving the
existing delayed fetch and cleanup logic.

---

Nitpick comments:
In `@apps/desktop/src/main/services/perf/perfIpc.ts`:
- Around line 21-64: Add defensive idempotency by removing existing IPC handlers
before registering new ones: in registerPerfIpcHandlers() call
ipcMain.removeHandler(...) for each channel (IPC.perfGetConfig,
IPC.perfRecordEvent, IPC.perfScenarioComplete, IPC.perfFinalize) immediately
before the corresponding ipcMain.handle(...) so re-calling
registerPerfIpcHandlers() won't throw or double-register handlers; keep the
existing handler implementations (the anonymous callbacks that call
getActiveRun, isRunActive, appendEvent, aggregate, etc.) unchanged.

In `@apps/desktop/src/renderer/components/app/App.tsx`:
- Around line 331-346: In BrowserHashRouteBridge (syncHashRoute), validate and
normalize the extracted route before calling navigate: after confirming
hash.startsWith("#/"), compute raw = hash.slice(1), split off any query/hash
suffix (preserve suffix separately), collapse duplicate slashes, reject or
reject-normalize any ".." path segments (e.g., remove or return root for paths
containing traversal), ensure the path begins with a single "/", then reattach
preserved query/hash if desired; wrap this in a try/catch and only call
navigate(normalizedPath, { replace: true }) when normalizedPath passes these
checks to avoid navigating to unsafe or malformed routes.

In `@apps/desktop/src/renderer/components/chat/ChatBuiltInBrowserPanel.test.tsx`:
- Around line 188-208: The test currently only verifies UI text; update it to
also assert the underlying behavior by (1) passing a mock for the onAddContext
prop (e.g., a vi.fn()) to the rendered ChatBuiltInBrowserPanel and (2) after
firing the "Screenshot" click assert that the installed browser API's
captureScreenshot mock (api.captureScreenshot) was called, and then after
clicking "Cancel screenshot" assert that the onAddContext mock was NOT called;
locate these changes around the existing render(...) and fireEvent calls in the
"starts and cancels screenshot crop mode when chat context is available" test so
you verify api.captureScreenshot and onAddContext interactions for
ChatBuiltInBrowserPanel using the installBrowserApi helper.

In `@apps/desktop/src/renderer/components/terminals/SessionContextMenu.tsx`:
- Around line 68-89: The useLayoutEffect that computes and sets clampedPosition
currently lists renaming in its dependency array causing unnecessary reflows;
inspect whether the menu's DOM size or the values used (menu,
menuRef.current.getBoundingClientRect()) actually change when renaming toggles
and if not, remove renaming from the dependency array so the effect depends only
on menu (and any other real sources like menuRef) — update the dependency list
of the useLayoutEffect containing menu, menuRef, and setClampedPosition (the
effect that reads menu, menuRef.current.getBoundingClientRect(), and calls
setClampedPosition) to exclude renaming unless you confirm rename mode changes
layout.

In `@apps/desktop/src/renderer/components/terminals/WorkStartSurface.test.tsx`:
- Around line 9-12: Replace the ad-hoc inline selector type with the real app
store state type by importing the exported state/type from the module that
defines the store (e.g., import { AppStoreState } from "../../state/appStore")
and use that type for the selector parameter in the vi.mock for useAppStore;
update references to selectLane/useAppStore to match the real signatures so the
mock's shape stays in sync with the actual app store implementation.

In `@apps/desktop/src/renderer/components/terminals/WorkViewArea.test.tsx`:
- Around line 212-278: The test currently asserts on pane().className contains
"minimized" which couples it to styling; update the FloatingPane component to
render a stable attribute (e.g., data-minimized with value "true"/"false")
driven by its minimized prop, and then change the test in WorkViewArea.test.tsx
to assert on that attribute via pane().getAttribute('data-minimized') (or use a
queryBy selector for [data-minimized="true"]) instead of inspecting className;
reference FloatingPane and the pane() helper in the test when making these
edits.
- Around line 323-325: The test currently clicks every element returned by
screen.getAllByTitle("Close tab") but only asserts on onCloseItem for a single
session id; narrow the click target to the specific tab you want to test (e.g.,
query the close button for the session id or use getByTitle with a unique title
per tab) or alternatively assert that onCloseItem was called for every session
id clicked. Update the test around the click loop to locate the close button for
the target session (reference onCloseItem and the test that renders the
tab/session id) and perform a single click, then assert onCloseItem was called
with that session id, or if keeping the loop verify the call count and arguments
for each expected id.

In `@apps/desktop/src/renderer/perf/markers.ts`:
- Around line 47-56: memoryReader currently returns null when performance.memory
is absent but never logs this, making it hard to debug; update the memoryReader
function to emit a single one-time warning when performance.memory is
unavailable by adding a module-level flag (e.g.,
memorySamplingUnavailableLogged) and, inside memoryReader, if !perf.memory and
the flag is false, call the chosen logger (console.warn or the app's logger)
with a clear message and set the flag to true so subsequent calls don't spam
logs; reference the memoryReader function and the new
memorySamplingUnavailableLogged flag when implementing.

In `@scripts/reset-perf-pass.sh`:
- Around line 24-25: Add a clear, explicit warning and an optional confirmation
before running the destructive command `git clean -fdx` in the reset script:
update the block around the existing `echo "[perf-pass] clearing untracked"` /
`git clean -fdx` to print a multi-line warning, prompt the user to confirm
(Y/N), and only run `git clean -fdx` when the user confirms; also provide a
non-interactive bypass (e.g., respect an env var like PERF_PASS_FORCE or CI) so
automated runs are unaffected.

In `@scripts/run-perf-scenario.mjs`:
- Around line 14-17: The script currently uses the deprecated synchronous rmSync
import; replace uses of rmSync with the promise API (node:fs.promises.rm or
import { rm } from "node:fs/promises") and call it with options { recursive:
true, force: true } (e.g., await rm(targetPath, { recursive: true, force: true
})). Update the top imports to remove rmSync and add the promise rm (or import {
promises as fsPromises } and use fsPromises.rm), and ensure the surrounding
scope is async or use top-level await so the removal is awaited; keep all other
logic unchanged and use the same target variable names where rmSync was
previously invoked.
🪄 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: 7ab976e0-08f4-43d1-a58e-9a60856a1bfe

📥 Commits

Reviewing files that changed from the base of the PR and between 3af8ad4 and f3cd4af.

⛔ Files ignored due to path filters (7)
  • docs/ARCHITECTURE.md is excluded by !docs/**
  • docs/features/files-and-editor/editor-surfaces.md is excluded by !docs/**
  • docs/features/lanes/README.md is excluded by !docs/**
  • docs/features/remote-runtime/README.md is excluded by !docs/**
  • docs/features/remote-runtime/internal-architecture.md is excluded by !docs/**
  • docs/features/terminals-and-sessions/README.md is excluded by !docs/**
  • docs/perf/work-tab-action-inventory.md is excluded by !docs/**
📒 Files selected for processing (72)
  • .agents/skills/ade-autoresearch/SKILL.md
  • .agents/skills/ade-perf-boot/SKILL.md
  • .agents/skills/ade-perf-lanes/SKILL.md
  • .agents/skills/ade-perf-work/SKILL.md
  • apps/ade-cli/src/multiProjectRpcServer.test.ts
  • apps/ade-cli/src/stdioRpcDaemon.test.ts
  • apps/desktop/src/main/main.ts
  • apps/desktop/src/main/services/git/gitOperationsService.test.ts
  • apps/desktop/src/main/services/git/gitOperationsService.ts
  • apps/desktop/src/main/services/github/githubService.test.ts
  • apps/desktop/src/main/services/github/githubService.ts
  • apps/desktop/src/main/services/ipc/registerIpc.ts
  • apps/desktop/src/main/services/ipc/runtimeBridge.test.ts
  • apps/desktop/src/main/services/localRuntime/localRuntimeConnectionPool.test.ts
  • apps/desktop/src/main/services/perf/aggregator.ts
  • apps/desktop/src/main/services/perf/metricsSampler.ts
  • apps/desktop/src/main/services/perf/perfIpc.ts
  • apps/desktop/src/main/services/perf/perfLog.ts
  • apps/desktop/src/main/services/usage/usageTrackingService.test.ts
  • apps/desktop/src/main/services/usage/usageTrackingService.ts
  • apps/desktop/src/preload/global.d.ts
  • apps/desktop/src/preload/preload.test.ts
  • apps/desktop/src/preload/preload.ts
  • apps/desktop/src/renderer/components/app/App.tsx
  • apps/desktop/src/renderer/components/app/App.workKeepAlive.test.tsx
  • apps/desktop/src/renderer/components/app/AppShell.tsx
  • apps/desktop/src/renderer/components/app/CommandPalette.test.tsx
  • apps/desktop/src/renderer/components/app/TopBar.test.tsx
  • apps/desktop/src/renderer/components/chat/AgentChatComposer.test.tsx
  • apps/desktop/src/renderer/components/chat/AgentChatMessageList.test.tsx
  • apps/desktop/src/renderer/components/chat/AgentChatMessageList.tsx
  • apps/desktop/src/renderer/components/chat/AgentChatPane.companionDrawers.test.tsx
  • apps/desktop/src/renderer/components/chat/ChatAppControlPanel.test.tsx
  • apps/desktop/src/renderer/components/chat/ChatBuiltInBrowserPanel.test.tsx
  • apps/desktop/src/renderer/components/chat/ChatGitToolbar.test.tsx
  • apps/desktop/src/renderer/components/chat/ChatIosSimulatorPanel.test.tsx
  • apps/desktop/src/renderer/components/chat/ChatIosSimulatorPanel.tsx
  • apps/desktop/src/renderer/components/chat/ChatSubagentsPanel.test.tsx
  • apps/desktop/src/renderer/components/chat/ChatTerminalDrawer.test.tsx
  • apps/desktop/src/renderer/components/chat/ChatWorkLogBlock.tsx
  • apps/desktop/src/renderer/components/chat/CursorCloudInlineLaunch.test.tsx
  • apps/desktop/src/renderer/components/files/FilesPage.test.tsx
  • apps/desktop/src/renderer/components/files/FilesPage.tsx
  • apps/desktop/src/renderer/components/lanes/CommitTimeline.test.tsx
  • apps/desktop/src/renderer/components/lanes/CommitTimeline.tsx
  • apps/desktop/src/renderer/components/lanes/LaneDiffPane.test.tsx
  • apps/desktop/src/renderer/components/lanes/LaneGitActionsPane.test.tsx
  • apps/desktop/src/renderer/components/lanes/LaneGitActionsPane.tsx
  • apps/desktop/src/renderer/components/lanes/LanesPage.test.ts
  • apps/desktop/src/renderer/components/lanes/LanesPage.tsx
  • apps/desktop/src/renderer/components/terminals/LaneCombobox.tsx
  • apps/desktop/src/renderer/components/terminals/MacosVmPanel.test.tsx
  • apps/desktop/src/renderer/components/terminals/SessionContextMenu.tsx
  • apps/desktop/src/renderer/components/terminals/SessionListPane.test.tsx
  • apps/desktop/src/renderer/components/terminals/SessionListPane.tsx
  • apps/desktop/src/renderer/components/terminals/WorkSidebar.tsx
  • apps/desktop/src/renderer/components/terminals/WorkStartSurface.test.tsx
  • apps/desktop/src/renderer/components/terminals/WorkViewArea.test.tsx
  • apps/desktop/src/renderer/lib/useGithubProjectRemote.ts
  • apps/desktop/src/renderer/main.tsx
  • apps/desktop/src/renderer/perf/harness/index.ts
  • apps/desktop/src/renderer/perf/markers.ts
  • apps/desktop/src/renderer/perf/scenarios/boot.ts
  • apps/desktop/src/renderer/perf/scenarios/index.ts
  • apps/desktop/src/renderer/perf/scenarios/lanes.ts
  • apps/desktop/src/renderer/perf/webVitals.ts
  • apps/desktop/src/renderer/state/appStore.test.ts
  • apps/desktop/src/renderer/state/appStore.ts
  • apps/desktop/src/shared/ipc.ts
  • scripts/perf-launch.mjs
  • scripts/reset-perf-pass.sh
  • scripts/run-perf-scenario.mjs

Comment thread apps/desktop/src/main/services/github/githubService.ts Outdated
Comment thread apps/desktop/src/main/services/ipc/registerIpc.ts Outdated
Comment thread apps/desktop/src/main/services/ipc/registerIpc.ts Outdated
Comment thread apps/desktop/src/main/services/perf/aggregator.ts Outdated
Comment thread apps/desktop/src/main/services/perf/aggregator.ts Outdated
Comment thread apps/desktop/src/renderer/perf/scenarios/lanes.ts Outdated
Comment thread apps/desktop/src/renderer/perf/webVitals.ts
Comment thread scripts/perf-launch.mjs Outdated
Comment thread scripts/perf-launch.mjs Outdated
Comment thread scripts/run-perf-scenario.mjs Outdated
@arul28
Copy link
Copy Markdown
Owner Author

arul28 commented May 12, 2026

@cursor review

@arul28
Copy link
Copy Markdown
Owner Author

arul28 commented May 12, 2026

@copilot review but do not make fixes

Comment thread apps/desktop/src/main/services/ipc/registerIpc.ts
Comment thread apps/desktop/src/main/services/perf/perfIpc.ts
arul28 and others added 6 commits May 12, 2026 06:17
Instruments ADE for an iterative perf-optimization loop driven by an
agentskills.io SKILL. Adds main-process metrics sampler (app metrics +
memory), renderer-side perfMark/perfMeasure + PerformanceObserver web
vitals, a tap into the existing IPC trace wrapper, and a JSONL +
summary aggregator that produces a single fitness score per scenario
run. Includes per-tab scenario harness (lanes + boot), launch
shortcuts (perf-launch.mjs / run-perf-scenario.mjs with --no-project
and --tab), and the ade-autoresearch / ade-perf-lanes / ade-perf-boot
skills under .agents/skills/. Smoke-verified end-to-end on the lanes
tab — summary.json populated with real signal across all six fitness
components.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* fix(app): honor hash routes in browser router

* perf(lanes): skip disabled local runtime bridge

real lanes baseline failed at lanes.idle-at-rest with V8 OOM; optimized scenarios all passed, fitness 7028.82

* perf(lanes): suppress duplicate git actions fullscreen pane

UI audit: Git Actions expand went from 2 toolbars to 1. Post-marker IPC changed from 30 calls / 223ms to 29 calls / 192ms, removing one duplicate ade.git.getSyncStatus.

* perf(lanes): fast-path disabled sync runtime

UI audit: sync IPC in local-runtime-disabled perf mode went from 4 failed calls / 1145ms to 3 successful calls / 2ms.

* perf(lanes): scope lane refreshes during UI actions

* ship: prepare lane for review

* ship: iteration 1 - address snapshot refresh review

* ship: iteration 2 - address refresh review

* ship: iteration 3 - refresh git action overlays
Squash-merge Work tab optimization lane into ade/autoresearch-d23e8745 after automate/finalize and review fixes.
@arul28 arul28 force-pushed the ade/autoresearch-d23e8745 branch from 5da119c to a5dc996 Compare May 12, 2026 10:19
@arul28
Copy link
Copy Markdown
Owner Author

arul28 commented May 12, 2026

@copilot review but do not make fixes

@arul28
Copy link
Copy Markdown
Owner Author

arul28 commented May 12, 2026

@cursor review

Comment thread apps/desktop/src/main/services/perf/perfIpc.ts
Comment thread apps/desktop/src/renderer/perf/scenarios/index.ts
Comment thread apps/desktop/src/renderer/components/app/AppShell.tsx Outdated
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: 4

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/main/services/ipc/registerIpc.ts (1)

1886-1891: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Disabled mode still leaks into generic sync error paths.

requireSyncService() is now the shared fallback for most sync IPCs, but it still throws "Sync service is not available." when ADE_DISABLE_LOCAL_RUNTIME_DAEMON=1. syncGetStatus and syncSetActiveLanePresence special-case disabled mode, while syncRefreshDiscovery, syncListDevices, syncGetTransferReadiness, pin/connect/disconnect, etc. still go through this helper. That means the renderer can successfully detect the disabled/unavailable state and then immediately hit user-visible errors on the next sync call. Fold the disabled-mode branch into the shared helper so the whole sync surface behaves consistently in this mode.

🤖 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/ipc/registerIpc.ts` around lines 1886 - 1891,
The helper requireSyncService currently throws when no service exists, which
leaks a generic error while some IPCs special-case disabled mode; change
requireSyncService to detect the disabled-local-runtime-daemon state (use the
existing ADE_DISABLE_LOCAL_RUNTIME_DAEMON check or the same utility that other
IPC handlers use) and instead return a consistent "disabled" sentinel that
implements the same interface as createSyncService (or return a well-known
DisabledSyncError instance) so all callers (e.g., syncGetStatus,
syncSetActiveLanePresence, syncRefreshDiscovery, syncListDevices,
syncGetTransferReadiness, pin/connect/disconnect) can handle disabled mode
uniformly via this single shared path rather than getting the generic "Sync
service is not available." exception.
🤖 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/main/services/perf/aggregator.ts`:
- Around line 197-198: The case "processMetrics" branch blindly casts and pushes
ev into processSamples causing a crash later when code (e.g.,
s.processes.find(...) in the aggregator/finalize flow) expects a valid processes
array; validate ev before casting by checking that ev.processes exists and is an
Array (e.g., Array.isArray((ev as any).processes)) and that its items have
expected shape (or at least non-empty), and if the check fails, skip the sample
(optionally log or increment a dropped counter) instead of pushing to
processSamples; update the case "processMetrics" handling where processSamples
is appended and use the ProcessMetricSample type only after the validation.
- Around line 144-146: The aggregate function currently uses runId directly in
join(homedir(), ".ade", "perf-runs", runId) (the dir variable) which allows path
traversal; validate or sanitize runId before building paths by either (a)
rejecting any runId containing path separators or suspicious segments (e.g.,
"..", "/", "\\"), or (b) after computing dir with path.resolve, verify that the
resolved dir startsWith the expected base directory (path.resolve(homedir(),
".ade", "perf-runs")), and throw on mismatch; apply this check before creating
eventsPath/summaryPath so aggregate rejects traversal attempts and only operates
inside the intended perf-runs directory.

In `@apps/desktop/src/preload/preload.ts`:
- Around line 941-944: The githubRemoteStatusCache created by
createShortIpcCache for IPC.githubGetRemoteStatus is global and must be cleared
when the active project changes; add calls to invalidate/clear that cache (e.g.,
githubRemoteStatusCache.clear() or the cache's provided invalidate method) from
the code paths that change the active repo—specifically after openRepo, after
switchToPath, and inside the binding-swap/active-project-change handler so the
renderer won't show stale repo/hasOrigin state.
- Around line 6645-6654: The getRemoteStatus bridge currently always calls
ipcRenderer.invoke(IPC.githubGetRemoteStatus) causing remote windows to bypass
the active project runtime; change getRemoteStatus to route through the bound
project runtime the same way getStatus does: use the active runtime (e.g.,
boundProjectRuntime or runtimeFromBoundProject) when present to invoke
IPC.githubGetRemoteStatus, preserving the githubRemoteStatusCache and
clearAround behavior on opts?.forceRefresh; if no bound runtime is available
fall back to ipcRenderer.invoke(IPC.githubGetRemoteStatus) so local mode still
works. Ensure you update the code paths that reference getRemoteStatus,
githubRemoteStatusCache, and IPC.githubGetRemoteStatus to use the bound runtime
invocation pattern.

---

Outside diff comments:
In `@apps/desktop/src/main/services/ipc/registerIpc.ts`:
- Around line 1886-1891: The helper requireSyncService currently throws when no
service exists, which leaks a generic error while some IPCs special-case
disabled mode; change requireSyncService to detect the
disabled-local-runtime-daemon state (use the existing
ADE_DISABLE_LOCAL_RUNTIME_DAEMON check or the same utility that other IPC
handlers use) and instead return a consistent "disabled" sentinel that
implements the same interface as createSyncService (or return a well-known
DisabledSyncError instance) so all callers (e.g., syncGetStatus,
syncSetActiveLanePresence, syncRefreshDiscovery, syncListDevices,
syncGetTransferReadiness, pin/connect/disconnect) can handle disabled mode
uniformly via this single shared path rather than getting the generic "Sync
service is not available." exception.
🪄 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: 5ddaf9f0-859b-4540-ac43-8e021b3190cf

📥 Commits

Reviewing files that changed from the base of the PR and between f3cd4af and a5dc996.

⛔ Files ignored due to path filters (7)
  • docs/ARCHITECTURE.md is excluded by !docs/**
  • docs/features/files-and-editor/editor-surfaces.md is excluded by !docs/**
  • docs/features/lanes/README.md is excluded by !docs/**
  • docs/features/remote-runtime/README.md is excluded by !docs/**
  • docs/features/remote-runtime/internal-architecture.md is excluded by !docs/**
  • docs/features/terminals-and-sessions/README.md is excluded by !docs/**
  • docs/perf/work-tab-action-inventory.md is excluded by !docs/**
📒 Files selected for processing (73)
  • .agents/skills/ade-autoresearch/SKILL.md
  • .agents/skills/ade-perf-boot/SKILL.md
  • .agents/skills/ade-perf-lanes/SKILL.md
  • .agents/skills/ade-perf-work/SKILL.md
  • apps/ade-cli/src/multiProjectRpcServer.test.ts
  • apps/ade-cli/src/stdioRpcDaemon.test.ts
  • apps/desktop/src/main/main.ts
  • apps/desktop/src/main/services/git/gitOperationsService.test.ts
  • apps/desktop/src/main/services/git/gitOperationsService.ts
  • apps/desktop/src/main/services/github/githubService.test.ts
  • apps/desktop/src/main/services/github/githubService.ts
  • apps/desktop/src/main/services/ipc/registerIpc.ts
  • apps/desktop/src/main/services/ipc/runtimeBridge.test.ts
  • apps/desktop/src/main/services/perf/aggregator.ts
  • apps/desktop/src/main/services/perf/metricsSampler.ts
  • apps/desktop/src/main/services/perf/perfIpc.ts
  • apps/desktop/src/main/services/perf/perfLog.ts
  • apps/desktop/src/main/services/usage/usageTrackingService.test.ts
  • apps/desktop/src/main/services/usage/usageTrackingService.ts
  • apps/desktop/src/preload/global.d.ts
  • apps/desktop/src/preload/preload.test.ts
  • apps/desktop/src/preload/preload.ts
  • apps/desktop/src/renderer/components/app/App.tsx
  • apps/desktop/src/renderer/components/app/App.workKeepAlive.test.tsx
  • apps/desktop/src/renderer/components/app/AppShell.tsx
  • apps/desktop/src/renderer/components/app/CommandPalette.test.tsx
  • apps/desktop/src/renderer/components/app/TopBar.test.tsx
  • apps/desktop/src/renderer/components/chat/AgentChatComposer.test.tsx
  • apps/desktop/src/renderer/components/chat/AgentChatMessageList.test.tsx
  • apps/desktop/src/renderer/components/chat/AgentChatMessageList.tsx
  • apps/desktop/src/renderer/components/chat/AgentChatPane.companionDrawers.test.tsx
  • apps/desktop/src/renderer/components/chat/ChatAppControlPanel.test.tsx
  • apps/desktop/src/renderer/components/chat/ChatBuiltInBrowserPanel.test.tsx
  • apps/desktop/src/renderer/components/chat/ChatGitToolbar.test.tsx
  • apps/desktop/src/renderer/components/chat/ChatIosSimulatorPanel.test.tsx
  • apps/desktop/src/renderer/components/chat/ChatIosSimulatorPanel.tsx
  • apps/desktop/src/renderer/components/chat/ChatSubagentsPanel.test.tsx
  • apps/desktop/src/renderer/components/chat/ChatTerminalDrawer.test.tsx
  • apps/desktop/src/renderer/components/chat/ChatWorkLogBlock.tsx
  • apps/desktop/src/renderer/components/chat/CursorCloudInlineLaunch.test.tsx
  • apps/desktop/src/renderer/components/files/FilesPage.test.tsx
  • apps/desktop/src/renderer/components/files/FilesPage.tsx
  • apps/desktop/src/renderer/components/lanes/CommitTimeline.test.tsx
  • apps/desktop/src/renderer/components/lanes/CommitTimeline.tsx
  • apps/desktop/src/renderer/components/lanes/LaneDiffPane.test.tsx
  • apps/desktop/src/renderer/components/lanes/LaneGitActionsPane.test.tsx
  • apps/desktop/src/renderer/components/lanes/LaneGitActionsPane.tsx
  • apps/desktop/src/renderer/components/lanes/LanesPage.test.ts
  • apps/desktop/src/renderer/components/lanes/LanesPage.tsx
  • apps/desktop/src/renderer/components/terminals/LaneCombobox.tsx
  • apps/desktop/src/renderer/components/terminals/MacosVmPanel.test.tsx
  • apps/desktop/src/renderer/components/terminals/SessionContextMenu.tsx
  • apps/desktop/src/renderer/components/terminals/SessionListPane.test.tsx
  • apps/desktop/src/renderer/components/terminals/SessionListPane.tsx
  • apps/desktop/src/renderer/components/terminals/WorkSidebar.tsx
  • apps/desktop/src/renderer/components/terminals/WorkStartSurface.test.tsx
  • apps/desktop/src/renderer/components/terminals/WorkViewArea.test.tsx
  • apps/desktop/src/renderer/components/terminals/WorkViewArea.tsx
  • apps/desktop/src/renderer/components/ui/FloatingPane.tsx
  • apps/desktop/src/renderer/lib/useGithubProjectRemote.ts
  • apps/desktop/src/renderer/main.tsx
  • apps/desktop/src/renderer/perf/harness/index.ts
  • apps/desktop/src/renderer/perf/markers.ts
  • apps/desktop/src/renderer/perf/scenarios/boot.ts
  • apps/desktop/src/renderer/perf/scenarios/index.ts
  • apps/desktop/src/renderer/perf/scenarios/lanes.ts
  • apps/desktop/src/renderer/perf/webVitals.ts
  • apps/desktop/src/renderer/state/appStore.test.ts
  • apps/desktop/src/renderer/state/appStore.ts
  • apps/desktop/src/shared/ipc.ts
  • scripts/perf-launch.mjs
  • scripts/reset-perf-pass.sh
  • scripts/run-perf-scenario.mjs
✅ Files skipped from review due to trivial changes (9)
  • .agents/skills/ade-perf-boot/SKILL.md
  • apps/desktop/src/main/services/perf/metricsSampler.ts
  • apps/desktop/src/renderer/components/terminals/WorkViewArea.tsx
  • apps/desktop/src/renderer/main.tsx
  • apps/desktop/src/renderer/components/terminals/MacosVmPanel.test.tsx
  • apps/desktop/src/renderer/components/lanes/CommitTimeline.test.tsx
  • apps/desktop/src/renderer/components/chat/ChatTerminalDrawer.test.tsx
  • .agents/skills/ade-perf-lanes/SKILL.md
  • apps/desktop/src/renderer/components/lanes/LanesPage.test.ts
🚧 Files skipped from review as they are similar to previous changes (58)
  • apps/desktop/src/renderer/components/app/App.workKeepAlive.test.tsx
  • apps/desktop/src/renderer/components/terminals/SessionListPane.test.tsx
  • apps/desktop/src/renderer/components/chat/ChatWorkLogBlock.tsx
  • apps/desktop/src/renderer/lib/useGithubProjectRemote.ts
  • apps/desktop/src/main/services/usage/usageTrackingService.test.ts
  • apps/desktop/src/renderer/components/chat/CursorCloudInlineLaunch.test.tsx
  • apps/desktop/src/renderer/components/app/TopBar.test.tsx
  • scripts/reset-perf-pass.sh
  • apps/desktop/src/renderer/components/terminals/LaneCombobox.tsx
  • apps/desktop/src/renderer/components/chat/ChatIosSimulatorPanel.tsx
  • apps/desktop/src/renderer/components/terminals/SessionContextMenu.tsx
  • apps/desktop/src/renderer/perf/scenarios/boot.ts
  • apps/desktop/src/main/services/git/gitOperationsService.ts
  • apps/desktop/src/renderer/components/terminals/WorkViewArea.test.tsx
  • apps/ade-cli/src/stdioRpcDaemon.test.ts
  • apps/desktop/src/renderer/components/chat/ChatSubagentsPanel.test.tsx
  • apps/desktop/src/main/services/git/gitOperationsService.test.ts
  • apps/desktop/src/shared/ipc.ts
  • apps/desktop/src/preload/global.d.ts
  • apps/desktop/src/renderer/components/terminals/WorkSidebar.tsx
  • apps/desktop/src/renderer/components/chat/ChatGitToolbar.test.tsx
  • apps/desktop/src/preload/preload.test.ts
  • apps/desktop/src/main/services/github/githubService.test.ts
  • apps/desktop/src/renderer/components/app/App.tsx
  • apps/desktop/src/renderer/components/chat/ChatAppControlPanel.test.tsx
  • apps/desktop/src/renderer/components/chat/ChatBuiltInBrowserPanel.test.tsx
  • apps/desktop/src/main/services/usage/usageTrackingService.ts
  • apps/desktop/src/renderer/components/lanes/CommitTimeline.tsx
  • apps/desktop/src/renderer/components/chat/AgentChatComposer.test.tsx
  • apps/desktop/src/main/services/ipc/runtimeBridge.test.ts
  • apps/desktop/src/renderer/perf/markers.ts
  • apps/desktop/src/main/services/perf/perfIpc.ts
  • apps/desktop/src/renderer/components/app/CommandPalette.test.tsx
  • apps/desktop/src/renderer/components/files/FilesPage.tsx
  • apps/desktop/src/renderer/components/lanes/LanesPage.tsx
  • apps/desktop/src/renderer/components/lanes/LaneGitActionsPane.test.tsx
  • apps/ade-cli/src/multiProjectRpcServer.test.ts
  • apps/desktop/src/main/services/perf/perfLog.ts
  • scripts/run-perf-scenario.mjs
  • apps/desktop/src/renderer/components/terminals/SessionListPane.tsx
  • apps/desktop/src/renderer/components/files/FilesPage.test.tsx
  • apps/desktop/src/renderer/components/app/AppShell.tsx
  • apps/desktop/src/renderer/components/chat/ChatIosSimulatorPanel.test.tsx
  • apps/desktop/src/renderer/perf/scenarios/lanes.ts
  • apps/desktop/src/renderer/perf/harness/index.ts
  • apps/desktop/src/renderer/components/lanes/LaneGitActionsPane.tsx
  • apps/desktop/src/renderer/components/lanes/LaneDiffPane.test.tsx
  • apps/desktop/src/renderer/components/chat/AgentChatMessageList.test.tsx
  • .agents/skills/ade-autoresearch/SKILL.md
  • apps/desktop/src/renderer/perf/scenarios/index.ts
  • apps/desktop/src/renderer/perf/webVitals.ts
  • apps/desktop/src/renderer/state/appStore.ts
  • apps/desktop/src/main/main.ts
  • apps/desktop/src/main/services/github/githubService.ts
  • apps/desktop/src/renderer/components/chat/AgentChatPane.companionDrawers.test.tsx
  • .agents/skills/ade-perf-work/SKILL.md
  • scripts/perf-launch.mjs
  • apps/desktop/src/renderer/state/appStore.test.ts

Comment thread apps/desktop/src/main/services/perf/aggregator.ts
Comment thread apps/desktop/src/main/services/perf/aggregator.ts Outdated
Comment thread apps/desktop/src/preload/preload.ts
Comment thread apps/desktop/src/preload/preload.ts Outdated
@arul28
Copy link
Copy Markdown
Owner Author

arul28 commented May 12, 2026

@cursor review

@arul28
Copy link
Copy Markdown
Owner Author

arul28 commented May 12, 2026

@copilot review but do not make fixes

Comment thread apps/desktop/src/main/services/github/githubService.ts
Comment thread apps/desktop/src/preload/preload.ts
@arul28
Copy link
Copy Markdown
Owner Author

arul28 commented May 12, 2026

@cursor review

@arul28
Copy link
Copy Markdown
Owner Author

arul28 commented May 12, 2026

@copilot review but do not make fixes

Comment thread apps/desktop/src/main/services/perf/aggregator.ts Outdated
Comment thread apps/desktop/src/main/services/github/githubService.ts
Comment thread apps/desktop/src/main/services/git/gitOperationsService.ts Outdated
@arul28
Copy link
Copy Markdown
Owner Author

arul28 commented May 12, 2026

@copilot review but do not make fixes

@arul28
Copy link
Copy Markdown
Owner Author

arul28 commented May 12, 2026

@cursor review

Comment thread apps/desktop/src/main/services/perf/perfLog.ts
@arul28
Copy link
Copy Markdown
Owner Author

arul28 commented May 12, 2026

@copilot review but do not make fixes

@arul28
Copy link
Copy Markdown
Owner Author

arul28 commented May 12, 2026

@cursor review

Copy link
Copy Markdown

@cursor cursor Bot left a comment

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes and found 2 potential issues.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit d1e0e01. Configure here.

}
if (!inOriginSection) continue;
const url = line.match(/^\s*url\s*=\s*(.+?)\s*$/);
if (url?.[1]?.trim()) return url[1].trim();
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Git config parser ignores inline comments in URL values

Low Severity

readOriginUrlFromConfig uses the regex (.+?)\s*$ to capture the URL value, which does not strip standard git config inline comments (lines containing ; or # after the value). For a config line like url = git@github.com:acme/repo.git ; my comment, url[1].trim() returns git@github.com:acme/repo.git ; my comment, which parseGitHubRepoFromRemoteUrl will fail to parse. The fallthrough to git remote get-url origin handles this correctly, so no incorrect results are produced, but the synchronous fast-path is defeated for any config with inline comments.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit d1e0e01. Configure here.

if (localRuntimeDaemonDisabled) return buildUnavailableSyncSnapshot();
throw new Error("Sync service is not available.");
}
return await service.getStatus({
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Inconsistent sync handler short-circuiting when daemon disabled

Low Severity

The syncGetStatus and syncSetActiveLanePresence handlers use resolveOptionalSyncService() before checking localRuntimeDaemonDisabled, while all other sync handlers (e.g., syncRefreshDiscovery, syncListDevices, syncConnectToBrain) short-circuit with the unavailable snapshot immediately after tryLocalRuntimeSync returns null. This inconsistency means syncSetActiveLanePresence unnecessarily resolves the sync service on every call in daemon-disabled mode, and syncGetStatus will use the real sync service if available even when daemon-disabled mode should be deterministic.

Additional Locations (1)
Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit d1e0e01. Configure here.

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