Skip to content

Review tab parallel review organization#322

Merged
arul28 merged 24 commits into
mainfrom
ade/review-tab-v1-parallel
May 20, 2026
Merged

Review tab parallel review organization#322
arul28 merged 24 commits into
mainfrom
ade/review-tab-v1-parallel

Conversation

@arul28
Copy link
Copy Markdown
Owner

@arul28 arul28 commented May 20, 2026

What changed

  • Rebuilt the Review tab around a findings-first product view with collapsed review process, artifacts, debug JSON, and transcript sections.
  • Added parallel read-only specialist reviewer runs, persisted candidate findings, deterministic adjudication, and full diff/manifest/risk-map artifacts.
  • Fixed no-limits review budget preservation, stale runtime guards, compare-target/read-only copy, and local-only publication wording.
  • Folded in ADE review findings around PR cache privacy, PR hydration, PR branch import safety, force-live refreshes, and CTO stash popping.

Validation

  • npx vitest run src/main/services/review/reviewService.test.ts src/main/services/prs/prService.test.ts src/main/rendererCsp.test.ts src/renderer/components/prs/detail/PrDetailPane.issueResolver.test.tsx src/renderer/components/prs/shared/PrTimeline.test.tsx src/renderer/components/prs/tabs/GitHubTab.test.tsx src/renderer/components/prs/state/PrsContext.test.tsx src/main/services/state/kvDb.bootstrapSql.test.ts --reporter=dot
  • npx vitest run src/renderer/components/prs/state/PrsContext.test.tsx src/renderer/components/prs/detail/PrDetailPane.issueResolver.test.tsx src/main/services/prs/prService.test.ts src/main/services/ai/tools/ctoOperatorTools.test.ts --reporter=dot
  • npm --prefix apps/desktop run typecheck
  • npm --prefix apps/desktop run lint
  • npm --prefix apps/ade-cli run typecheck
  • npm --prefix apps/ade-cli run test

Notes

  • Opened as draft while the in-product ADE review and ship-lane loop continue.

Summary by CodeRabbit

  • New Features

    • Create lanes from PR branches with preflight checks
    • In-line PR comment composer and “Copy findings” clipboard export
  • Improvements

    • Stash commands now use stash commit OIDs for reliable apply/pop/drop and CLI --oid support; branch-scoped stash UI & messaging
    • Review pipeline: parallel specialist runs, richer evidence artifacts, Codex fast-mode and unlimited-budget support
    • PR detail: snapshot prefill, background refreshes, consolidated overview behavior
  • Bug Fixes

    • More reliable stash workflows and steadier PR navigation/state handling

Greptile Summary

This PR rewrites the review system from sequential to parallel specialist sessions (diff-risk, cross-file-impact, checks-and-tests, security-data, ui-regression) via Promise.all, adds new ReviewReviewerRun / ReviewCandidateFinding DB tables, a warm cross-PR snapshot cache, and a CreateLaneFromPrBranchDialog with preflight checks.

  • Parallel review passes each get their own AI session; findings are persisted as candidates then deduplicated into the final review result.
  • pr_auto_link_ignores table prevents the backfill loop from re-linking deliberately unlinked PRs.
  • normalizeGitHubFilter migrates warm-cache entries away from the now-removed \"all\" filter option.

Confidence Score: 3/5

Needs attention before merge — a race condition can silently discard completed review results, and a React anti-pattern can corrupt snapshot timestamps in Strict Mode.

Two P1 issues were found: the cancelledRuns race condition that discards valid completed work, and the side-effect inside a React state updater. The pre-existing getReviewThreads removal (detailReviewThreads always empty) and double-truncation of the manifest prompt from the previous review also remain unresolved.

reviewService.ts (cancelledRuns race condition), PrsContext.tsx (side-effect in state updater)

Important Files Changed

Filename Overview
apps/desktop/src/main/services/review/reviewService.ts Core rewrite to parallel passes; cancelledRuns race condition and double-truncation of manifest prompt are present.
apps/desktop/src/renderer/components/prs/state/PrsContext.tsx Cross-PR snapshot warm cache added; ref mutation inside state updater is an anti-pattern that can corrupt timestamps in Strict Mode.
apps/desktop/src/main/services/prs/prService.ts createLaneFromPrBranch with multi-step preflight, pr_auto_link_ignores table, hardened null-name check in rawPullHasSameRepoHead.
apps/desktop/src/main/services/state/kvDb.ts LOCAL_ONLY_CRR_EXCLUDED_TABLES, removeExcludedCrrMetadata, new review tables, github_pr_cache drop.
apps/desktop/src/renderer/components/prs/tabs/GitHubTab.tsx CreateLaneFromPrBranchDialog, linkableLanes filter change, normalizeGitHubFilter migration, sort by updatedAt.
apps/desktop/src/renderer/components/review/ReviewPage.tsx Findings-first UI, reviewer-run cards, unlimited-budget toggle, fixed pluralization in formatPublicationOutcome.

Comments Outside Diff (4)

  1. apps/desktop/src/main/services/review/reviewService.ts, line 1960-1970 (link)

    P1 Manifest can consume entire prompt budget, silencing context artifacts and rule overlays

    manifestPrompt is truncated to maxPromptChars and then the fully assembled prompt (instructions + manifest + context artifact hints + rule overlays + validation context) is truncated again to the same maxPromptChars. Because the instructions prefix appears before the manifest in the prompt, the second truncation cuts off everything after the manifest — context artifact IDs, rule overlays, and validation signals — whenever the manifest is close to the budget limit.

    For the default bounded budget (60 k maxPromptChars) with 25 files at 1 500-char excerpts, the manifest JSON alone is ~50–70 k chars. The reviewer would silently receive a prompt missing artifact IDs (cannot cite evidence), missing rule overlays (custom review rules ignored), and missing validation context. The fix is to truncate the manifest to maxPromptChars minus the surrounding prompt skeleton size, or to place the manifest at the end of the prompt as the previous diffText was.

    Prompt To Fix With AI
    This is a comment left during a code review.
    Path: apps/desktop/src/main/services/review/reviewService.ts
    Line: 1960-1970
    
    Comment:
    **Manifest can consume entire prompt budget, silencing context artifacts and rule overlays**
    
    `manifestPrompt` is truncated to `maxPromptChars` and then the fully assembled prompt (instructions + manifest + context artifact hints + rule overlays + validation context) is truncated again to the same `maxPromptChars`. Because the instructions prefix appears before the manifest in the prompt, the second truncation cuts off everything after the manifest — context artifact IDs, rule overlays, and validation signals — whenever the manifest is close to the budget limit.
    
    For the default bounded budget (60 k `maxPromptChars`) with 25 files at 1 500-char excerpts, the manifest JSON alone is ~50–70 k chars. The reviewer would silently receive a prompt missing artifact IDs (cannot cite evidence), missing rule overlays (custom review rules ignored), and missing validation context. The fix is to truncate the manifest to `maxPromptChars` minus the surrounding prompt skeleton size, or to place the manifest at the end of the prompt as the previous `diffText` was.
    
    How can I resolve this? If you propose a fix, please make it concise.

    Fix in Claude Code

  2. apps/desktop/src/renderer/components/prs/state/PrsContext.tsx, line 1043-1060 (link)

    P1 detailReviewThreads permanently empty after first PR selection

    getReviewThreads was removed from both the primary detail fetch (the Promise.allSettled in refreshDetailSilently) and the secondary detail fetch (startSecondaryDetailFetch steps). setDetailReviewThreads is now only ever called with [] — to clear the state. The state is initialized from warmCache?.detailReviewThreads ?? [] so warm-cache data shows briefly on first mount, but as soon as the user selects a PR the clear fires and threads are never repopulated. Every consumer of detailReviewThreads will always see an empty array after the initial navigation.

    Prompt To Fix With AI
    This is a comment left during a code review.
    Path: apps/desktop/src/renderer/components/prs/state/PrsContext.tsx
    Line: 1043-1060
    
    Comment:
    **`detailReviewThreads` permanently empty after first PR selection**
    
    `getReviewThreads` was removed from both the primary detail fetch (the `Promise.allSettled` in `refreshDetailSilently`) and the secondary detail fetch (`startSecondaryDetailFetch` steps). `setDetailReviewThreads` is now only ever called with `[]` — to clear the state. The state is initialized from `warmCache?.detailReviewThreads ?? []` so warm-cache data shows briefly on first mount, but as soon as the user selects a PR the clear fires and threads are never repopulated. Every consumer of `detailReviewThreads` will always see an empty array after the initial navigation.
    
    How can I resolve this? If you propose a fix, please make it concise.

    Fix in Claude Code

  3. apps/desktop/src/main/services/review/reviewService.ts, line 1-1177 (link)

    P1 Completed run silently discarded on late cancel

    If cancelRun is called just as all passes finish their Promise.all, the guard if (cancelledRuns.has(runId)) return executes after the work is done, discarding valid results with no user-visible feedback. The cancelledRuns Set also grows unboundedly — entries are never removed after the run is discarded or completes normally.

    Prompt To Fix With AI
    This is a comment left during a code review.
    Path: apps/desktop/src/main/services/review/reviewService.ts
    Line: 1-1177
    
    Comment:
    **Completed run silently discarded on late cancel**
    
    If `cancelRun` is called just as all passes finish their `Promise.all`, the guard `if (cancelledRuns.has(runId)) return` executes after the work is done, discarding valid results with no user-visible feedback. The `cancelledRuns` Set also grows unboundedly — entries are never removed after the run is discarded or completes normally.
    
    How can I resolve this? If you propose a fix, please make it concise.

    Fix in Claude Code

  4. apps/desktop/src/renderer/components/prs/state/PrsContext.tsx, line 1-352 (link)

    P1 Side-effect inside React state updater

    The mergeDetailSnapshots callback mutates detailSnapshotLoadedAtByPrIdRef.current inside the setDetailSnapshotsByPrId updater function. React may invoke state updater functions more than once in Strict Mode (double-invoking in development), so this ref mutation can fire multiple times per state transition, corrupting the loaded-at timestamp.

    Prompt To Fix With AI
    This is a comment left during a code review.
    Path: apps/desktop/src/renderer/components/prs/state/PrsContext.tsx
    Line: 1-352
    
    Comment:
    **Side-effect inside React state updater**
    
    The `mergeDetailSnapshots` callback mutates `detailSnapshotLoadedAtByPrIdRef.current` inside the `setDetailSnapshotsByPrId` updater function. React may invoke state updater functions more than once in Strict Mode (double-invoking in development), so this ref mutation can fire multiple times per state transition, corrupting the loaded-at timestamp.
    
    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 2 code review issues. Work through them one at a time, proposing concise fixes.

---

### Issue 1 of 2
apps/desktop/src/main/services/review/reviewService.ts:1-1177
**Completed run silently discarded on late cancel**

If `cancelRun` is called just as all passes finish their `Promise.all`, the guard `if (cancelledRuns.has(runId)) return` executes after the work is done, discarding valid results with no user-visible feedback. The `cancelledRuns` Set also grows unboundedly — entries are never removed after the run is discarded or completes normally.

### Issue 2 of 2
apps/desktop/src/renderer/components/prs/state/PrsContext.tsx:1-352
**Side-effect inside React state updater**

The `mergeDetailSnapshots` callback mutates `detailSnapshotLoadedAtByPrIdRef.current` inside the `setDetailSnapshotsByPrId` updater function. React may invoke state updater functions more than once in Strict Mode (double-invoking in development), so this ref mutation can fire multiple times per state transition, corrupting the loaded-at timestamp.

Reviews (6): Last reviewed commit: "ship: bound review manifest prompts" | Re-trigger Greptile

@vercel
Copy link
Copy Markdown

vercel Bot commented May 20, 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 20, 2026 8:18am

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 20, 2026

📝 Walkthrough

Walkthrough

Adds stash OID propagation and branch-scoped stash operations; implements “create lane from PR branch” preflight/create flows and auto-link ignore persistence; refactors PR snapshot hydration and detail progressive loading; extends review service to persist per-reviewer runs and candidate findings; updates IPC, preload, types, DB migrations, UI, and many tests.

Changes

Primary Cohort

Layer / File(s) Summary
Stash OID & CLI/RPC wiring
apps/ade-cli/src/adeRpcServer.ts, apps/ade-cli/src/cli.ts, apps/ade-cli/src/services/sync/syncRemoteCommandService.ts, apps/desktop/src/shared/types/git.ts, apps/desktop/src/renderer/components/lanes/*, apps/desktop/src/main/services/git/*, apps/desktop/src/renderer/components/automations/adeActionSchemas.ts
Tool schemas, RPC handlers, CLI planning, git service, UI and types updated to add/require stashOid for stash_pop/drop and optionally accept for apply; branch-scoped stash listing and OID resolution implemented; tests updated.
PR → Lane creation APIs and IPC/preload
apps/desktop/src/main/services/prs/prService.ts, apps/desktop/src/main/services/ipc/registerIpc.ts, apps/desktop/src/preload/preload.ts, apps/desktop/src/preload/global.d.ts, apps/desktop/src/shared/types/prs.ts, apps/desktop/src/shared/ipc.ts
Adds preflight/create lane-from-PR-branch APIs and IPC channels; wiring in registerIpc and preload; types for args/results; PR auto-link-ignore persistence and related helpers.
PR snapshot hydration & prs context
apps/desktop/src/renderer/components/prs/state/PrsContext.tsx, apps/desktop/src/renderer/components/prs/detail/PrDetailPane.tsx, apps/desktop/src/renderer/components/prs/tabs/GitHubTab.tsx, apps/desktop/src/renderer/components/prs/*
Refactors warm-cache snapshot hydration per-PR, progressive primary/secondary fetch pipeline, removes standalone activity tab (map to overview), updates PR detail/tab behavior, and adds UI for create-lane/unmap flows and snapshot patching.
Review service: parallel reviewer runs & persistence
apps/desktop/src/main/services/review/reviewService.ts, apps/desktop/src/main/services/review/reviewService.test.ts, apps/desktop/src/shared/types/review.ts, DB migration files
Implements per-pass parallel specialist reviewer runs, manifest/risk-map prompting, deterministic candidate IDs, persists review_reviewer_runs and review_candidate_findings, updates adjudication/publish behavior, and extends tests and types.
DB/CRR, migrations, kvDb maintenance
apps/desktop/src/main/services/state/kvDb.ts, apps/ios/ADE/Resources/DatabaseBootstrap.sql, apps/ios/ADE/Services/Database.swift
Adds CRR-excluded tables, cleanup routines removing CRR metadata for local-only caches, drops github_pr_cache, adds pr_auto_link_ignores, review tables, and marks local-only tables on iOS.
IPC availability / PR read gating & desktop wiring
apps/desktop/src/main/services/ipc/registerIpc.ts, apps/desktop/src/main/services/adeActions/registry.ts, apps/desktop/src/shared/ipc.ts
Introduces ensurePrReadContext/ensurePrMutationContext guards, updates many PR IPC handlers to throw when services are unavailable, and extends ADE action allowlist for PR actions.
UI, helpers, and tests
multiple apps/desktop/src/renderer/* and many *.test.tsx files
Numerous UI updates (PR lists, GitHubTab, detail panes, timeline rails, review pages, copy helpers), test expansions, and adjustments across the renderer and desktop scripts.

🎯 5 (Critical) | ⏱️ ~120 minutes

  • Possibly related PRs:
    • arul28/ADE#146: overlapping edits to PrDetailPane behavior and polling.
    • arul28/ADE#183: related allowlist/registry changes for ADE actions.

Suggested labels: desktop, ios

✨ 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/review-tab-v1-parallel

@arul28 arul28 marked this pull request as ready for review May 20, 2026 06:01
@arul28
Copy link
Copy Markdown
Owner Author

arul28 commented May 20, 2026

@coderabbitai review

@arul28
Copy link
Copy Markdown
Owner Author

arul28 commented May 20, 2026

@copilot review but do not make fixes

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 20, 2026

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@capy-ai
Copy link
Copy Markdown

capy-ai Bot commented May 20, 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.

Copy link
Copy Markdown
Contributor

Copilot AI commented May 20, 2026

@copilot review but do not make fixes

Reviewed the branch through commit 6d54014. I’m not applying fixes per your request; no additional blocking issues were identified in this pass.

Copilot finished work on behalf of arul28 May 20, 2026 06:02
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: 13

Caution

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

⚠️ Outside diff range comments (3)
apps/desktop/src/main/services/prs/prService.ts (2)

3240-3264: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Persist the auto-link ignore for any local PR removal.

This guard is too narrow. When closeOnGitHub is true and the lane stays active, the row is removed locally but no ignore is recorded, and the next GitHub snapshot can backfill the same PR again because backfill reads state: "all". That breaks the new “intentionally unmapped” behavior for both successfully closed PRs and failed-close cases.

💡 Suggested fix
-    const localUnmapOnly = args.closeOnGitHub !== true && args.archiveLane !== true;
...
-    if (localUnmapOnly) {
-      rememberAutoLinkIgnore(row);
-    }
+    // Any explicit local removal should stay unmapped until the user
+    // relinks/recreates it. The ignore is cleared on explicit relink paths.
+    rememberAutoLinkIgnore(row);
🤖 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/prs/prService.ts` around lines 3240 - 3264,
The current guard uses localUnmapOnly so rememberAutoLinkIgnore(row) runs only
when closeOnGitHub is false, which misses cases where we remove the row locally
after attempting to close on GitHub; move or remove that guard so
rememberAutoLinkIgnore(row) is invoked whenever we perform the local PR
unmap/removal (i.e., unconditionally in this removal code path), keeping the
existing githubService PATCH/try-catch (githubClosed, githubCloseError) intact
and only changing the condition around rememberAutoLinkIgnore to ensure it runs
for both closeOnGitHub true and false.

5686-5736: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Don’t reuse an in-flight GitHub snapshot after repo/auth changes.

invalidateGithubSnapshotCache() only clears the published snapshot. If an old request is still sitting in githubSnapshotInFlight, the later if (!force && githubSnapshotInFlight) branch will return that stale promise, so callers can receive the previous repo/session snapshot after switching context.

💡 Suggested fix
-let githubSnapshotInFlight: { request: Promise<GitHubPrSnapshot> } | null = null;
+let githubSnapshotInFlight: {
+  request: Promise<GitHubPrSnapshot>;
+  repoKey: string | null;
+  viewerLogin: string | null;
+} | null = null;
...
-      inFlight = { request };
+      inFlight = {
+        request,
+        repoKey: repoRefKey(precheckedGithubStatus.repo),
+        viewerLogin: precheckedGithubStatus.userLogin ?? null,
+      };
       githubSnapshotInFlight = inFlight;
       return request;
     };
+
+    const currentRepoKey = repoRefKey(githubStatus.repo);
+    const currentViewerLogin = githubStatus.userLogin ?? null;
+    if (
+      githubSnapshotInFlight &&
+      (githubSnapshotInFlight.repoKey !== currentRepoKey
+        || githubSnapshotInFlight.viewerLogin !== currentViewerLogin)
+    ) {
+      githubSnapshotInFlight = null;
+    }
🤖 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/prs/prService.ts` around lines 5686 - 5736,
The in-flight snapshot promise (githubSnapshotInFlight) can be returned after
repo/auth changes because invalidateGithubSnapshotCache() only clears the
published snapshot; fix by ensuring invalidateGithubSnapshotCache also
clears/invalidates githubSnapshotInFlight (or increments
githubSnapshotCacheEpoch) so stale in-flight requests are not reused; update the
code that calls invalidateGithubSnapshotCache or the
invalidateGithubSnapshotCache implementation to set githubSnapshotInFlight =
null (and/or bump githubSnapshotCacheEpoch) so the later `if (!force &&
githubSnapshotInFlight)` branch cannot return a stale promise, keeping the logic
in startSnapshotRequest, githubSnapshotCacheEpoch, and publishGithubSnapshot
consistent.
apps/desktop/src/renderer/components/prs/state/PrsContext.tsx (1)

1372-1405: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Keep the fallback detail path from marking partial data as live.

This fallback branch still treats partial primary success as fresh detail: markPrimarySettled() keeps the PR live when primaryFulfilledCount > 0, and the per-piece success path updates detailStatePrIdRef, detailLoadedAtByPrIdRef, and detailLiveDataPrId immediately. In any environment where window.ade.prs.listSnapshots is unavailable, one successful status/checks/reviews/comments call can still mask stale sections and short-circuit later refreshes. Mirror startProgressivePrimaryFetch() here and only mark the detail cache fresh after all four primary calls succeed.

🤖 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/prs/state/PrsContext.tsx` around lines
1372 - 1405, The fallback primary-piece path (loadPrimaryPiece) is marking
partial results as fresh/live; change it to mirror startProgressivePrimaryFetch
by deferring updates that mark the cache live until all primary requests
succeed: do not set detailStatePrIdRef.current,
detailLoadedAtByPrIdRef.current[prId], or call setDetailLiveDataPrId(prId)
inside the per-piece success branch of loadPrimaryPiece; only mark those after
markPrimarySettled determines primaryFulfilledCount === primaryRequestCount (and
not rateLimited). Adjust markPrimarySettled to only treat the detail as fresh
when primaryFulfilledCount === primaryRequestCount and ensure
detailFetchInProgress.current is cleared only at the same finalization point;
keep liveDetailApplied true for tracking but do not commit the cache refs/live
flag until all four primary calls succeed.
🧹 Nitpick comments (3)
apps/desktop/src/main/services/ai/tools/ctoOperatorTools.test.ts (1)

201-216: ⚡ Quick win

Strengthen the stash-selection fixture to actually prove “latest” resolution.

This test currently passes with a single stash entry, so it doesn’t verify the selection logic when multiple stashes exist. Add at least one older/mismatched stash and assert the expected one is chosen.

🤖 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/ai/tools/ctoOperatorTools.test.ts` around
lines 201 - 216, The test for gitStashPop uses only one stash so it doesn't
prove the "latest" selection; update the fixture returned by
gitService.listStashes in the test for tools.gitStashPop.execute to include
multiple stashes (e.g., an older mismatch and the intended latest stash with
createdAt later), keep the expected latest entry having oid "oid-3" and ref
"stash@{3}", and assert that gitService.stashPop was called with that latest
stash ({ laneId, stashRef: "stash@{3}", stashOid: "oid-3" }) while still
verifying listStashes was called with { laneId: "lane-1" } and the result
matches the expected operationId.
apps/desktop/src/renderer/components/prs/detail/PrDetailPane.tsx (2)

652-656: 💤 Low value

Potential type issue with ctxReviewThreads assignment.

The condition (ctxReviewThreads?.length ?? 0) > 0 ensures the array has elements, but setReviewThreads(ctxReviewThreads) passes the potentially-undefined value without a non-null assertion. While logically safe at runtime, TypeScript may flag this.

Suggested fix
   React.useEffect(() => {
     if (ctxDetailPrId === pr.id && (ctxReviewThreads?.length ?? 0) > 0) {
-      setReviewThreads(ctxReviewThreads);
+      setReviewThreads(ctxReviewThreads!);
     }
   }, [ctxDetailPrId, ctxReviewThreads, pr.id]);
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/desktop/src/renderer/components/prs/detail/PrDetailPane.tsx` around
lines 652 - 656, The useEffect may pass an undefined value to setReviewThreads
by calling setReviewThreads(ctxReviewThreads) even though you guarded length;
update the assignment to guarantee a non-null array or assert non-null so
TypeScript won’t complain — e.g., inside the effect where you check
(ctxReviewThreads?.length ?? 0) > 0, call setReviewThreads with a safe value
(use ctxReviewThreads ?? [] or ctxReviewThreads! as appropriate) referencing
ctxReviewThreads, setReviewThreads, and the surrounding useEffect that checks
ctxDetailPrId and pr.id.

4060-4117: 💤 Low value

Duplicate modal code could use the extracted PrReviewSubmitModal component.

The review modal JSX here duplicates the PrReviewSubmitModal component defined at lines 3259-3347. Consider replacing this inline modal with the extracted component for consistency.

Suggested refactor
-      {/* ---- Review Modal ---- */}
-      {props.showReviewModal && (
-        <div style={{ position: "fixed", inset: 0, background: "rgba(0,0,0,0.7)", backdropFilter: "blur(4px)", display: "flex", alignItems: "center", justifyContent: "center", zIndex: 100 }}>
-          <div style={{ background: COLORS.cardBgSolid, border: `1px solid ${COLORS.outlineBorder}`, borderRadius: 16, padding: 24, width: 500, maxHeight: "80vh", overflow: "auto", boxShadow: "0 20px 60px rgba(0,0,0,0.5)" }}>
-            {/* ... modal content ... */}
-          </div>
-        </div>
-      )}
+      <PrReviewSubmitModal
+        open={props.showReviewModal}
+        actionBusy={actionBusy}
+        reviewBody={props.reviewBody}
+        setReviewBody={props.setReviewBody}
+        reviewEvent={props.reviewEvent}
+        setReviewEvent={props.setReviewEvent}
+        onCancel={() => props.setShowReviewModal(false)}
+        onSubmit={() => void props.onSubmitReview()}
+      />
🤖 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/prs/detail/PrDetailPane.tsx` around
lines 4060 - 4117, The modal JSX in PrDetailPane duplicates the existing
PrReviewSubmitModal component; replace the inline modal block with a single
PrReviewSubmitModal usage to avoid duplication. Locate the modal JSX in the
PrDetailPane render and remove it, then render <PrReviewSubmitModal ...> passing
the same props/state handlers used in the inline code (showReviewModal,
setShowReviewModal, reviewEvent, setReviewEvent, reviewBody, setReviewBody,
onSubmitReview, and actionBusy) so behavior and styling remain identical; ensure
imports/reference to PrReviewSubmitModal are present and remove the duplicated
styles/handlers from the inline fragment.
🤖 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/ade-cli/src/cli.ts`:
- Around line 2707-2724: The function resolveStashSelectionForCli currently only
accepts stashRef and always falls back to the first stash; change its signature
to accept both stashRef: string | null and stashOid: string | null, then resolve
the selected stash by: 1) if stashRef is provided, find stash where
asString(stash.ref) === stashRef; 2) else if stashOid is provided, find stash
where asString(stash.oid) === stashOid; 3) else use the first stash. Set
selectedRef and stashOid from the matched stash and return them, preserving the
existing CliUsageError messages when nothing matches (but use the provided
stashOid in the error message when searching by oid). Update any call sites that
invoke resolveStashSelectionForCli to pass the new stashOid argument.

In `@apps/desktop/src/main/services/ai/tools/ctoOperatorTools.ts`:
- Around line 2630-2648: The gitStashPop tool currently lets a user-supplied
stashRef that doesn't match any stash fall through and causes
deps.gitService!.stashPop to throw a low-level "stashOid is required" error;
change the logic in tools.gitStashPop (around resolveLaneId, selectedStash,
stashRef handling) to detect when a trimmed stashRef was provided but
selectedStash is undefined and throw a clear "stash not found" error (or
similar) before calling deps.gitService!.stashPop; keep existing behavior when
no stashRef is provided (use stashes[0]) and continue passing stashOid only when
selectedStash?.oid exists.

In `@apps/desktop/src/main/services/ipc/registerIpc.ts`:
- Around line 8591-8607: The handler for IPC.prsGetHealth currently fabricates a
PrHealth object when ensurePrPolling() returns falsy; instead modify the
ipcMain.handle for IPC.prsGetHealth to return an explicit unavailable value
(e.g., null) rather than a fake PrHealth, by checking ensurePrPolling() and
returning null when ctx is absent, update the handler's Promise return type
(PrHealth | null) and adjust callers to handle null accordingly; keep the
successful path calling ctx.prService.getPrHealth(arg.prId) unchanged.
- Around line 8313-8332: The emptyPrDetail and emptyPrMergeContext helpers
currently return plausible-but-empty PR objects that can be persisted and poison
the cache; change them to return an explicit unavailable sentinel (e.g., an
object with a clear flag like unavailable: true or simply null) instead of a
valid-looking payload, and update the PrMergeContext/PR detail type definitions
and all callers of emptyPrDetail and emptyPrMergeContext to handle this sentinel
(or move the fallback entirely into the renderer so these helpers are never used
for cached state). Ensure callers distinguish sentinel vs real data before
caching or rendering.
- Around line 8970-8989: The handlers for IPC.prsGetFiles / IPC.prsGetCommits /
IPC.prsGetActionRuns / IPC.prsGetActivity use ensurePrPolling() and return []
when ctx is missing, which can overwrite hydrated cached subresources with
empties; change each handler (the ipcMain.handle registrations) to return a
nullable or availability-tagged response instead of an empty array (e.g., null
or { unavailable: true }) when ensurePrPolling() yields no ctx, update the
returned Promise/return types for ctx.prService.getFiles, getCommits,
getActionRuns, getActivity callers to accept the nullable/availability shape so
callers can skip caching fallback responses rather than persisting empty lists.

In `@apps/desktop/src/main/services/review/reviewService.ts`:
- Around line 548-557: The UI regex in classifyChangedFileRisk is wrong:
`/\b(renderer|components|tsx$|css$)\b/i` uses word boundaries and treats `$`
incorrectly, so files like "foo.tsx" or "style.css" won't match. Fix the UI test
by changing the pattern in classifyChangedFileRisk to explicitly match
extensions (e.g., use `\.tsx$` and `\.css$`) and combine with the existing word
checks — for example replace that regex with one that matches either
`\b(renderer|components)\b` or the end-of-string extensions like `\.tsx$|\.css$`
(keep the `i` flag).

In `@apps/desktop/src/main/services/state/kvDb.ts`:
- Around line 537-555: The early continue conditioned on
hasClockTable/hasPksTable/triggerCount (the if (!hasClockTable && !hasPksTable
&& triggerCount === 0) continue;) can skip necessary cleanup of
crsql_master/crsql_changes rows; modify the logic so metadata-only cleanup still
runs: either remove/adjust that continue to also check for metadata rows (use
rawHasTable/rawHasColumn and/or a lightweight existence query against
crsql_master/crsql_changes) or move the runStatement deletions for
crsql_master/crsql_changes to execute before the continue; keep usages of
hasClockTable, hasPksTable, triggerCount, dropCrrTriggers, runStatement,
rawHasTable, rawHasColumn and quoteIdentifier intact while ensuring deletion of
metadata rows always happens when present.

In `@apps/desktop/src/renderer/components/prs/detail/PrDetailTimelineRails.tsx`:
- Around line 606-608: The Cmd/Ctrl+Enter onKeyDown handler in
PrDetailTimelineRails currently calls onAddComment unguarded; update the
onKeyDown handlers (the one shown and the similar handler around lines
referenced) so they check the same conditions as the submit button before
invoking onAddComment: ensure event.key === "Enter" && (event.metaKey ||
event.ctrlKey) and also !actionBusy and that the draft is non-empty (e.g.,
draft.trim().length > 0) so duplicate or empty submissions are prevented.
- Line 620: The inline style that sets outline: "none" on the composer textarea
in PrDetailTimelineRails.tsx removes the keyboard focus indicator; revert this
by removing the outline: "none" or replace it with an accessible alternative
(e.g., a visible outline/box-shadow on :focus or :focus-visible) for the
textarea element so keyboard users retain a clear focus ring; locate the
textarea/composer styling in PrDetailTimelineRails.tsx and update the inline
style or CSS class accordingly (remove outline: "none" or provide an accessible
focus style).

In `@apps/desktop/src/renderer/components/prs/state/PrsContext.tsx`:
- Around line 948-956: The effect that retries refreshCore() on every active &&
error (inside useEffect referencing active, error, refreshCore and retryTimer)
must stop unconditional retries: add local state (e.g., retryCount) and cap
retries (maxRetries) or switch to exponential backoff, increment retryCount each
scheduled retry and only schedule another timeout if retryCount < maxRetries;
when max reached, stop scheduling further retries and surface a "manual retry"
action so the user can call refreshCore() explicitly; ensure the cleanup still
clears retryTimer and reset retryCount when refreshCore succeeds or when active
becomes false.

In `@apps/desktop/src/renderer/components/prs/tabs/GitHubTab.test.tsx`:
- Around line 431-447: The test "requires confirmation before unmapping a GitHub
PR from its lane" creates a spy confirmSpy on window.confirm but never restores
it, potentially leaking into other tests; update the test to restore the spy
after use by calling confirmSpy.mockRestore() (or vi.restoreAllMocks()) at the
end of the test or in an afterEach cleanup, ensuring window.confirm is returned
to its original implementation; locate the confirmSpy declared in
GitHubTab.test.tsx and add the restore call (or global restore) so subsequent
tests are unaffected.

In `@apps/desktop/src/renderer/components/prs/tabs/GitHubTab.tsx`:
- Around line 1080-1094: The effect currently only collects linkedPrId from
filteredItems.slice(0, 8) so it refreshes only the first 8 PRs; update it to
gather linkedPrId from the actually rendered/visible items instead (e.g., use
the component's visible/rendered items array or virtualization window rather
than slice(0,8)). Specifically, replace the slice(0, 8) source with the
prop/state that represents what is currently rendered (or derive it from the
virtualization/scroll window), keep the uniquePrIds/key logic tied to
visibleLinkedHydrationKeyRef and keep the timer + onRefreshAll call unchanged so
visible rendered linked PRs get targeted refreshes.

In `@apps/desktop/src/renderer/components/review/ReviewPage.tsx`:
- Around line 392-397: The function formatPublicationOutcome incorrectly uses a
ternary that returns the same string ("completed") in both branches, losing
singular/plural distinction; update the return to pluralize the noun instead of
the adjective—for example, change the template to use publishedCount and choose
between "publication" and "publications" based on publishedCount (publishedCount
=== 1 ? "publication" : "publications") so the message becomes e.g. "1
publication completed." or "2 publications completed."; adjust this inside
formatPublicationOutcome where publishedCount is used.

---

Outside diff comments:
In `@apps/desktop/src/main/services/prs/prService.ts`:
- Around line 3240-3264: The current guard uses localUnmapOnly so
rememberAutoLinkIgnore(row) runs only when closeOnGitHub is false, which misses
cases where we remove the row locally after attempting to close on GitHub; move
or remove that guard so rememberAutoLinkIgnore(row) is invoked whenever we
perform the local PR unmap/removal (i.e., unconditionally in this removal code
path), keeping the existing githubService PATCH/try-catch (githubClosed,
githubCloseError) intact and only changing the condition around
rememberAutoLinkIgnore to ensure it runs for both closeOnGitHub true and false.
- Around line 5686-5736: The in-flight snapshot promise (githubSnapshotInFlight)
can be returned after repo/auth changes because invalidateGithubSnapshotCache()
only clears the published snapshot; fix by ensuring
invalidateGithubSnapshotCache also clears/invalidates githubSnapshotInFlight (or
increments githubSnapshotCacheEpoch) so stale in-flight requests are not reused;
update the code that calls invalidateGithubSnapshotCache or the
invalidateGithubSnapshotCache implementation to set githubSnapshotInFlight =
null (and/or bump githubSnapshotCacheEpoch) so the later `if (!force &&
githubSnapshotInFlight)` branch cannot return a stale promise, keeping the logic
in startSnapshotRequest, githubSnapshotCacheEpoch, and publishGithubSnapshot
consistent.

In `@apps/desktop/src/renderer/components/prs/state/PrsContext.tsx`:
- Around line 1372-1405: The fallback primary-piece path (loadPrimaryPiece) is
marking partial results as fresh/live; change it to mirror
startProgressivePrimaryFetch by deferring updates that mark the cache live until
all primary requests succeed: do not set detailStatePrIdRef.current,
detailLoadedAtByPrIdRef.current[prId], or call setDetailLiveDataPrId(prId)
inside the per-piece success branch of loadPrimaryPiece; only mark those after
markPrimarySettled determines primaryFulfilledCount === primaryRequestCount (and
not rateLimited). Adjust markPrimarySettled to only treat the detail as fresh
when primaryFulfilledCount === primaryRequestCount and ensure
detailFetchInProgress.current is cleared only at the same finalization point;
keep liveDetailApplied true for tracking but do not commit the cache refs/live
flag until all four primary calls succeed.

---

Nitpick comments:
In `@apps/desktop/src/main/services/ai/tools/ctoOperatorTools.test.ts`:
- Around line 201-216: The test for gitStashPop uses only one stash so it
doesn't prove the "latest" selection; update the fixture returned by
gitService.listStashes in the test for tools.gitStashPop.execute to include
multiple stashes (e.g., an older mismatch and the intended latest stash with
createdAt later), keep the expected latest entry having oid "oid-3" and ref
"stash@{3}", and assert that gitService.stashPop was called with that latest
stash ({ laneId, stashRef: "stash@{3}", stashOid: "oid-3" }) while still
verifying listStashes was called with { laneId: "lane-1" } and the result
matches the expected operationId.

In `@apps/desktop/src/renderer/components/prs/detail/PrDetailPane.tsx`:
- Around line 652-656: The useEffect may pass an undefined value to
setReviewThreads by calling setReviewThreads(ctxReviewThreads) even though you
guarded length; update the assignment to guarantee a non-null array or assert
non-null so TypeScript won’t complain — e.g., inside the effect where you check
(ctxReviewThreads?.length ?? 0) > 0, call setReviewThreads with a safe value
(use ctxReviewThreads ?? [] or ctxReviewThreads! as appropriate) referencing
ctxReviewThreads, setReviewThreads, and the surrounding useEffect that checks
ctxDetailPrId and pr.id.
- Around line 4060-4117: The modal JSX in PrDetailPane duplicates the existing
PrReviewSubmitModal component; replace the inline modal block with a single
PrReviewSubmitModal usage to avoid duplication. Locate the modal JSX in the
PrDetailPane render and remove it, then render <PrReviewSubmitModal ...> passing
the same props/state handlers used in the inline code (showReviewModal,
setShowReviewModal, reviewEvent, setReviewEvent, reviewBody, setReviewBody,
onSubmitReview, and actionBusy) so behavior and styling remain identical; ensure
imports/reference to PrReviewSubmitModal are present and remove the duplicated
styles/handlers from the inline fragment.
🪄 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: 6c0bbdd0-cf90-444d-a0f9-ce96db760a75

📥 Commits

Reviewing files that changed from the base of the PR and between 63aabb7 and 6d54014.

⛔ Files ignored due to path filters (1)
  • .ade/ade.yaml is excluded by !.ade/**
📒 Files selected for processing (59)
  • .claude/skills/plan/SKILL.md
  • apps/ade-cli/src/adeRpcServer.test.ts
  • apps/ade-cli/src/adeRpcServer.ts
  • apps/ade-cli/src/cli.test.ts
  • apps/ade-cli/src/cli.ts
  • apps/ade-cli/src/services/sync/syncRemoteCommandService.ts
  • apps/desktop/scripts/ensure-ade-cli-build.cjs
  • apps/desktop/src/main/rendererCsp.test.ts
  • apps/desktop/src/main/services/adeActions/registry.ts
  • apps/desktop/src/main/services/ai/tools/ctoOperatorTools.test.ts
  • apps/desktop/src/main/services/ai/tools/ctoOperatorTools.ts
  • apps/desktop/src/main/services/git/gitOperationsService.test.ts
  • apps/desktop/src/main/services/git/gitOperationsService.ts
  • apps/desktop/src/main/services/ipc/registerIpc.ts
  • apps/desktop/src/main/services/lanes/laneService.test.ts
  • apps/desktop/src/main/services/lanes/laneService.ts
  • apps/desktop/src/main/services/prs/prService.test.ts
  • apps/desktop/src/main/services/prs/prService.ts
  • apps/desktop/src/main/services/review/reviewService.test.ts
  • apps/desktop/src/main/services/review/reviewService.ts
  • apps/desktop/src/main/services/state/kvDb.test.ts
  • apps/desktop/src/main/services/state/kvDb.ts
  • apps/desktop/src/main/services/sync/syncRemoteCommandService.test.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/AppShell.tsx
  • apps/desktop/src/renderer/components/automations/adeActionSchemas.ts
  • apps/desktop/src/renderer/components/graph/WorkspaceGraphPage.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.tsx
  • apps/desktop/src/renderer/components/prs/PRsPage.tsx
  • apps/desktop/src/renderer/components/prs/detail/PrDetailPane.issueResolver.test.tsx
  • apps/desktop/src/renderer/components/prs/detail/PrDetailPane.tsx
  • apps/desktop/src/renderer/components/prs/detail/PrDetailTimelineRails.tsx
  • apps/desktop/src/renderer/components/prs/prsRouteState.ts
  • apps/desktop/src/renderer/components/prs/shared/PrTimeline.test.tsx
  • apps/desktop/src/renderer/components/prs/shared/PrTimeline.tsx
  • apps/desktop/src/renderer/components/prs/state/PrsContext.test.tsx
  • apps/desktop/src/renderer/components/prs/state/PrsContext.tsx
  • apps/desktop/src/renderer/components/prs/state/PrsContextWarmCache.test.tsx
  • apps/desktop/src/renderer/components/prs/tabs/GitHubTab.test.tsx
  • apps/desktop/src/renderer/components/prs/tabs/GitHubTab.tsx
  • apps/desktop/src/renderer/components/prs/tabs/IntegrationTab.tsx
  • apps/desktop/src/renderer/components/review/ReviewFindingCard.tsx
  • apps/desktop/src/renderer/components/review/ReviewPage.test.tsx
  • apps/desktop/src/renderer/components/review/ReviewPage.tsx
  • apps/desktop/src/renderer/components/review/reviewFindingCopy.ts
  • apps/desktop/src/renderer/components/review/reviewFindingLabels.test.ts
  • apps/desktop/src/renderer/components/review/reviewFindingLabels.ts
  • apps/desktop/src/renderer/components/shared/ReviewLaunchModelControls.tsx
  • apps/desktop/src/shared/ipc.ts
  • apps/desktop/src/shared/types/git.ts
  • apps/desktop/src/shared/types/prs.ts
  • apps/desktop/src/shared/types/review.ts
  • apps/ios/ADE/Resources/DatabaseBootstrap.sql
  • apps/ios/ADE/Services/Database.swift
  • scripts/dev-shared.mjs
💤 Files with no reviewable changes (1)
  • .claude/skills/plan/SKILL.md

Comment thread apps/ade-cli/src/cli.ts Outdated
Comment thread apps/desktop/src/main/services/ai/tools/ctoOperatorTools.ts
Comment thread apps/desktop/src/main/services/ipc/registerIpc.ts Outdated
Comment thread apps/desktop/src/main/services/ipc/registerIpc.ts
Comment thread apps/desktop/src/main/services/ipc/registerIpc.ts
Comment thread apps/desktop/src/renderer/components/prs/detail/PrDetailTimelineRails.tsx Outdated
Comment thread apps/desktop/src/renderer/components/prs/state/PrsContext.tsx Outdated
Comment thread apps/desktop/src/renderer/components/prs/tabs/GitHubTab.test.tsx
Comment thread apps/desktop/src/renderer/components/prs/tabs/GitHubTab.tsx Outdated
Comment thread apps/desktop/src/renderer/components/review/ReviewPage.tsx
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 20, 2026

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

Copy link
Copy Markdown
Contributor

Copilot AI commented May 20, 2026

@copilot review but do not make fixes

Reviewed through commit 125b3ff with no fixes applied. I found one additional blocking concern: in reviewService.executePass, manifestPrompt is truncated to maxPromptChars and then the full assembled prompt is truncated again to the same limit, which can drop context artifact IDs/rule overlays/validation context on large manifests. I did not confirm a blocking integrity issue for pr_auto_link_ignores FK behavior because lane deletion currently clears those rows explicitly in laneService before lane removal. CI still fails on build-win artifact validation (app.asar.unpacked exceeds size limit). No UI changes in this review-only pass, so screenshot is not applicable.

Copilot finished work on behalf of arul28 May 20, 2026 07:12
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: 1

🧹 Nitpick comments (2)
apps/desktop/src/renderer/components/prs/state/PrsContext.tsx (1)

51-52: ⚡ Quick win

Rename this constant to camelCase.

REFRESH_ERROR_RETRY_DELAYS_MS doesn't follow the repo's TS/JS variable naming convention. Please rename it to something like refreshErrorRetryDelaysMs to keep the new retry code aligned with the project standard.

As per coding guidelines, **/*.{ts,tsx,js,jsx}: Use camelCase for variable names in TypeScript/JavaScript files.

🤖 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/prs/state/PrsContext.tsx` around lines
51 - 52, Rename the constant REFRESH_ERROR_RETRY_DELAYS_MS to camelCase (e.g.,
refreshErrorRetryDelaysMs) and update every usage/reference in this file (and
any imports if it's exported) to the new name; preserve the value and the "as
const" typing so the tuple and ms suffix remain intact and any retry logic
(functions that reference REFRESH_ERROR_RETRY_DELAYS_MS) continues to work
unchanged.
apps/desktop/src/renderer/components/prs/tabs/GitHubTab.tsx (1)

1925-1932: 💤 Low value

Effect triggers on every render due to unstable virtualItems reference.

getVirtualItems() returns a new array on each render, so this effect runs every time. The parent's handleHydrationItemsChange comparison prevents infinite loops and unnecessary state updates, so this is functionally correct.

If you notice performance issues, consider memoizing the mapped items array or comparing indices instead of the array reference.

🤖 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/prs/tabs/GitHubTab.tsx` around lines
1925 - 1932, The effect runs every render because virtualizer.getVirtualItems()
returns a new array reference; fix by deriving a stable key (e.g., virtual
indices) or memoizing the mapped items before the effect: call
virtualizer.getVirtualItems() once, map to indices (virtualItems.map(v =>
v.index)) and use React.useMemo to compute the GitHubPrListItem[] from items
using those indices, then use that stable memoized array (or the indices array)
in the dependency list for the effect that calls onHydrationItemsChange; update
references to virtualizer.getVirtualItems(), virtualItems, items,
onHydrationItemsChange, and GitHubPrListItem accordingly.
🤖 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/state/kvDb.ts`:
- Around line 537-545: The DELETEs against crsql_master/crsql_changes run
unconditionally before the early-continue check and will fail on readonly DBs;
change the logic to probe for matching rows first and only call runStatement(...
"delete ...") when rows exist. Specifically, for crsql_master and crsql_changes
(use rawHasTable/rawHasColumn to guard), run a cheap existence query (e.g.,
SELECT 1 or SELECT count(1) with the same WHERE clause using tableName) and only
add the delete changes to deletedMetadataCount when that query reports rows > 0;
keep the early-continue condition using hasClockTable, hasPksTable,
triggerCount, and deletedMetadataCount unchanged.

---

Nitpick comments:
In `@apps/desktop/src/renderer/components/prs/state/PrsContext.tsx`:
- Around line 51-52: Rename the constant REFRESH_ERROR_RETRY_DELAYS_MS to
camelCase (e.g., refreshErrorRetryDelaysMs) and update every usage/reference in
this file (and any imports if it's exported) to the new name; preserve the value
and the "as const" typing so the tuple and ms suffix remain intact and any retry
logic (functions that reference REFRESH_ERROR_RETRY_DELAYS_MS) continues to work
unchanged.

In `@apps/desktop/src/renderer/components/prs/tabs/GitHubTab.tsx`:
- Around line 1925-1932: The effect runs every render because
virtualizer.getVirtualItems() returns a new array reference; fix by deriving a
stable key (e.g., virtual indices) or memoizing the mapped items before the
effect: call virtualizer.getVirtualItems() once, map to indices
(virtualItems.map(v => v.index)) and use React.useMemo to compute the
GitHubPrListItem[] from items using those indices, then use that stable memoized
array (or the indices array) in the dependency list for the effect that calls
onHydrationItemsChange; update references to virtualizer.getVirtualItems(),
virtualItems, items, onHydrationItemsChange, and GitHubPrListItem accordingly.
🪄 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: 7d608dbb-56e2-45a4-9d26-f2c857a58630

📥 Commits

Reviewing files that changed from the base of the PR and between 6d54014 and 125b3ff.

📒 Files selected for processing (17)
  • apps/ade-cli/src/cli.test.ts
  • apps/ade-cli/src/cli.ts
  • apps/desktop/scripts/after-pack-runtime-fixes.cjs
  • apps/desktop/scripts/validate-win-artifacts.mjs
  • apps/desktop/src/main/services/ai/tools/ctoOperatorTools.test.ts
  • apps/desktop/src/main/services/ai/tools/ctoOperatorTools.ts
  • apps/desktop/src/main/services/ipc/registerIpc.ts
  • apps/desktop/src/main/services/review/reviewService.test.ts
  • apps/desktop/src/main/services/review/reviewService.ts
  • apps/desktop/src/main/services/state/kvDb.test.ts
  • apps/desktop/src/main/services/state/kvDb.ts
  • apps/desktop/src/renderer/components/prs/detail/PrDetailTimelineRails.tsx
  • apps/desktop/src/renderer/components/prs/state/PrsContext.test.tsx
  • apps/desktop/src/renderer/components/prs/state/PrsContext.tsx
  • apps/desktop/src/renderer/components/prs/tabs/GitHubTab.test.tsx
  • apps/desktop/src/renderer/components/prs/tabs/GitHubTab.tsx
  • apps/desktop/src/renderer/components/review/ReviewPage.tsx

Comment thread apps/desktop/src/main/services/state/kvDb.ts Outdated
@arul28
Copy link
Copy Markdown
Owner Author

arul28 commented May 20, 2026

@coderabbitai review

@arul28
Copy link
Copy Markdown
Owner Author

arul28 commented May 20, 2026

@copilot review but do not make fixes

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 20, 2026

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

Copy link
Copy Markdown
Contributor

Copilot AI commented May 20, 2026

@copilot review but do not make fixes

Reviewed through commit 0ed973c with no fixes applied. I still see four actionable issues: (1) reviewService.executePass still double-truncates manifest + full prompt at the same maxPromptChars, which can drop context artifact IDs/rule overlays/validation context on large manifests; (2) ReviewPage computes formatReviewEvidenceLine(...) twice per render; (3) ReviewPage shows selectedRun.errorMessage as red error text even for completed partial-review runs (duplicate warning/fatal styling mismatch); (4) PrsContext.mergeDetailSnapshots mutates detailSnapshotLoadedAtByPrIdRef inside the setDetailSnapshotsByPrId updater (impure updater in StrictMode). CI: latest completed branch CI (26147194519) passed; earlier failure (26146016635) was build-win artifact size (app.asar.unpacked over limit); current run (26148259201) is still in progress.

Copilot finished work on behalf of arul28 May 20, 2026 07:36
Comment thread apps/desktop/src/main/services/git/gitOperationsService.ts
@arul28
Copy link
Copy Markdown
Owner Author

arul28 commented May 20, 2026

@copilot review but do not make fixes

@arul28
Copy link
Copy Markdown
Owner Author

arul28 commented May 20, 2026

@coderabbitai review

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 20, 2026

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

Copy link
Copy Markdown
Contributor

Copilot AI commented May 20, 2026

@copilot review but do not make fixes

Reviewed through commit 2e0f1fd with no fixes applied. Current actionable issues I still see: (1) PrsContext no longer fetches review threads (getReviewThreads absent from both primary/secondary detail fetch paths), while setDetailReviewThreads([]) still clears state, so threads stay empty after navigation; (2) reviewService.executePass still double-truncates (manifestPrompt and then full prompt) at the same maxPromptChars; (3) ReviewPage still computes formatReviewEvidenceLine(...) twice and still renders selectedRun.errorMessage as red error text for completed partial-review runs; (4) mergeDetailSnapshots still mutates detailSnapshotLoadedAtByPrIdRef inside the setDetailSnapshotsByPrId updater. The stash-pop regression called out by Greptile appears fixed in 2e0f1fd (drop failure now logs warning without rethrow after successful apply). CI: current run 26149382278 is in progress with most jobs already green; latest known branch failure remains 26146016635 (build-win artifact validation: app.asar.unpacked exceeds limit).

Copilot finished work on behalf of arul28 May 20, 2026 08:01
Comment thread apps/desktop/src/main/services/review/reviewService.ts
@arul28
Copy link
Copy Markdown
Owner Author

arul28 commented May 20, 2026

@copilot review but do not make fixes

@arul28
Copy link
Copy Markdown
Owner Author

arul28 commented May 20, 2026

@coderabbitai review

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 20, 2026

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

Copy link
Copy Markdown
Contributor

Copilot AI commented May 20, 2026

@copilot review but do not make fixes

Reviewed through commit fd67d76 with no fixes applied. Current status from my pass: (1) the unlimited-budget manifest growth concern appears addressed (MANIFEST_PROMPT_FILE_LIMIT = 100 and prompt manifest uses capped files while the artifact keeps full file list); (2) PrsContext still drops detailFetchInProgress after the first settled primary request, so overlap risk remains in the snapshot-prefill + interval path; (3) clearAutoLinkIgnore is now called after successful upsertRow in both create/link flows, so the ignore-clearing concern appears addressed; (4) GitHubTab still restricts linkableLanes to exact head-branch matches and does not show an explicit empty-state hint. Previously noted unresolved items also still appear present: getReviewThreads is still absent from detail fetch paths in PrsContext, prompt construction in executePass still double-truncates manifest+final prompt, ReviewPage still computes formatReviewEvidenceLine(...) twice and shows selectedRun.errorMessage as red text even for completed partial runs, and mergeDetailSnapshots still mutates detailSnapshotLoadedAtByPrIdRef inside a state updater. CI: current run 26150384132 is in progress; latest completed failure on this branch remains 26146016635 (build-win: app.asar.unpacked too large at 765107716 bytes, limit 629145600).

Copilot finished work on behalf of arul28 May 20, 2026 08:22
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.

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/git/gitOperationsService.ts (1)

1084-1097: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Don’t treat post-apply ref-resolution failures as a successful pop.

This catch now swallows both the stash drop failure and any failure from the second resolveCurrentBranchStashRef(). If that re-list / rev-parse step fails for an unrelated git error, ADE still reports success even though it never confirmed whether the stash was removed. Only downgrade the actual drop failure; let lookup failures bubble, or explicitly whitelist the “already gone/shifted” cases you want to tolerate.

✂️ Narrow the swallowed path to the drop call
           await runGitOrThrow(["stash", "apply", currentApplyRef], { cwd: lane.worktreePath, timeoutMs: 30_000 });
-          try {
-            const currentDropRef = await resolveCurrentBranchStashRef(lane, stash);
+          const currentDropRef = await resolveCurrentBranchStashRef(lane, stash);
+          try {
             await runGitOrThrow(["stash", "drop", currentDropRef], { cwd: lane.worktreePath, timeoutMs: 30_000 });
             invalidateStashReadCaches();
           } catch (error) {
             const message = error instanceof Error ? error.message : String(error);
             logger.warn("git.stash_pop_drop_failed", {
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/desktop/src/main/services/git/gitOperationsService.ts` around lines 1084
- 1097, The current catch block is swallowing errors from both
resolveCurrentBranchStashRef and the stash drop, so move the call to
resolveCurrentBranchStashRef(lane, stash) outside (before) the try or separate
the operations so only the runGitOrThrow(["stash","drop", currentDropRef]) is
wrapped in the catch; let resolveCurrentBranchStashRef failures bubble (or
explicitly handle only the known "already gone" rev-parse cases), and keep the
logger.warn("git.stash_pop_drop_failed", {...}) only for drop failures; ensure
invalidateStashReadCaches() still runs after a successful drop.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In `@apps/desktop/src/main/services/git/gitOperationsService.ts`:
- Around line 1084-1097: The current catch block is swallowing errors from both
resolveCurrentBranchStashRef and the stash drop, so move the call to
resolveCurrentBranchStashRef(lane, stash) outside (before) the try or separate
the operations so only the runGitOrThrow(["stash","drop", currentDropRef]) is
wrapped in the catch; let resolveCurrentBranchStashRef failures bubble (or
explicitly handle only the known "already gone" rev-parse cases), and keep the
logger.warn("git.stash_pop_drop_failed", {...}) only for drop failures; ensure
invalidateStashReadCaches() still runs after a successful drop.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 9b15e84d-98c8-4ce0-808a-dcf276a9c228

📥 Commits

Reviewing files that changed from the base of the PR and between 125b3ff and fd67d76.

📒 Files selected for processing (5)
  • apps/desktop/src/main/services/git/gitOperationsService.test.ts
  • apps/desktop/src/main/services/git/gitOperationsService.ts
  • apps/desktop/src/main/services/review/reviewService.test.ts
  • apps/desktop/src/main/services/review/reviewService.ts
  • apps/desktop/src/main/services/state/kvDb.ts

@arul28 arul28 merged commit ea7ea14 into main May 20, 2026
28 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants