Skip to content

Lanes Snapshot Invalidation#711

Merged
arul28 merged 2 commits into
mainfrom
ade/start-by-running-skill-keywords-9923fd90
Jul 7, 2026
Merged

Lanes Snapshot Invalidation#711
arul28 merged 2 commits into
mainfrom
ade/start-by-running-skill-keywords-9923fd90

Conversation

@arul28

@arul28 arul28 commented Jul 6, 2026

Copy link
Copy Markdown
Owner

Summary

Describe the change.

What Changed

Key files and behaviors.

Validation

How you tested.

Risks

Anything to watch.

ADE   Open in ADE  ·  ade/start-by-running-skill-keywords-9923fd90 branch  ·  PR #711

Summary by CodeRabbit

  • New Features

    • Lane updates now trigger better app-wide refreshes, keeping lane lists and related views more up to date.
    • Rename and restore actions now emit clearer lifecycle updates, including previous lane name details.
  • Bug Fixes

    • Lane list data now stays in sync more reliably after changes, even across hidden tabs and focus switches.
    • PR lists refresh more consistently when lane activity occurs in the background.
    • Rename actions no longer show a toast notification, while archive, unarchive, and delete actions use clearer messages.

Greptile Summary

This PR improves lane lifecycle refreshes across the desktop app. The main changes are:

  • New renderer invalidation hook for lane lifecycle events.
  • Lane rename and unarchive lifecycle events from the main lane service.
  • Lane read cache invalidation for coalesced list and snapshot requests.
  • PR, graph, lanes, and run surfaces wired to refresh after lane changes.

Confidence Score: 4/5

The unarchive refresh path still needs a small fix before merging.

lane-unarchived now reaches the renderer, but the refresh options still skip git status recomputation for restored lanes. The immediate refresh and delayed follow-up use the same stale-status option.

apps/desktop/src/renderer/hooks/useLaneListInvalidation.ts

T-Rex T-Rex Logs

What T-Rex did

  • Reproduced the lane-unarchived status behavior with a focused Vitest repro against the real hook source.
  • Observed that the repro exercises useLaneListInvalidation, emits a lane-unarchived lifecycle event, and triggers both the immediate debounced refresh and the delayed refresh call refreshLanes with includeStatus false.
  • Validated the lane invalidation focused Vitest run completed with EXIT_CODE: 0 and seven test files passed, totaling 233 tests.
  • Noted a typecheck run ending with EXIT_CODE: 124, documenting a timeout blocker for typecheck completion within budget.

View all artifacts

T-Rex Ran code and verified through T-Rex

Important Files Changed

Filename Overview
apps/desktop/src/renderer/hooks/useLaneListInvalidation.ts Adds lifecycle-driven lane list invalidation, but restored lanes still skip fresh git status.
apps/desktop/src/main/services/lanes/laneService.ts Emits lane lifecycle events for rename and real unarchive transitions.
apps/desktop/src/renderer/lib/laneReadCache.ts Adds explicit invalidation for lane list and snapshot read coalescing.

Fix All in Claude Code

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

---

### Issue 1 of 1
apps/desktop/src/renderer/hooks/useLaneListInvalidation.ts:14
**Unarchived Status Still Skips**

When `lane-unarchived` reaches this helper, it still gets `includeStatus: false`. The restored lane can be fetched without fresh dirty/ahead/behind data, so it may reappear in active lane views with missing or stale git status until an unrelated full status refresh runs.

Reviews (3): Last reviewed commit: "review: address lane lifecycle refresh f..." | Re-trigger Greptile

Greptile also left 1 inline comment on this PR.

@vercel

vercel Bot commented Jul 6, 2026

Copy link
Copy Markdown

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

1 Skipped Deployment
Project Deployment Actions Updated (UTC)
ade Ignored Ignored Preview Jul 7, 2026 12:42am

@cursor

cursor Bot commented Jul 6, 2026

Copy link
Copy Markdown
Contributor

Bugbot is not enabled for your account, so this pull request was not reviewed.

Enable Bugbot in the Cursor dashboard to get automatic reviews on future PRs.

@arul28

arul28 commented Jul 6, 2026

Copy link
Copy Markdown
Owner Author

@copilot review but do not make fixes

@coderabbitai

coderabbitai Bot commented Jul 6, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@arul28, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 14 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 2f8c0a46-7414-4906-a55f-56850a26137c

📥 Commits

Reviewing files that changed from the base of the PR and between 433ef71 and 53015b4.

⛔ Files ignored due to path filters (2)
  • docs/features/lanes/README.md is excluded by !docs/**
  • docs/features/pull-requests/README.md is excluded by !docs/**
📒 Files selected for processing (16)
  • apps/desktop/src/main/services/lanes/laneService.test.ts
  • apps/desktop/src/main/services/lanes/laneService.ts
  • apps/desktop/src/preload/preload.test.ts
  • apps/desktop/src/renderer/components/app/toast/useLaneEventToasts.ts
  • apps/desktop/src/renderer/components/graph/WorkspaceGraphPage.tsx
  • apps/desktop/src/renderer/components/lanes/LanesPage.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/run/RunPage.test.tsx
  • apps/desktop/src/renderer/components/run/RunPage.tsx
  • apps/desktop/src/renderer/hooks/useLaneListInvalidation.test.tsx
  • apps/desktop/src/renderer/hooks/useLaneListInvalidation.ts
  • apps/desktop/src/renderer/lib/laneReadCache.test.ts
  • apps/desktop/src/renderer/lib/laneReadCache.ts
  • apps/desktop/src/shared/types/lanes.ts
📝 Walkthrough

Walkthrough

This PR expands the LaneLifecycleEvent type with lane-renamed and lane-unarchived events plus a previousLaneName field, has laneService broadcast these events, adds generation-based invalidation to the lane read cache, introduces a useLaneListInvalidation hook to debounce cache invalidation and lane refreshes on lifecycle/focus/visibility changes, wires this hook into WorkspaceGraphPage, LanesPage, and RunPage, updates toast messaging, and adds a debounced lifecycle-driven refresh effect in PrsContext.

Changes

Lane lifecycle events, invalidation, and consumers

Layer / File(s) Summary
LaneLifecycleEvent type expansion
apps/desktop/src/shared/types/lanes.ts
Adds "lane-renamed" and "lane-unarchived" to the event type union and an optional previousLaneName field.
Lane service emits rename/unarchive events
apps/desktop/src/main/services/lanes/laneService.ts, apps/desktop/src/main/services/lanes/laneService.test.ts
rename() and unarchive() broadcast lifecycle events with lane id, name, previous name, and color, validated by new test assertions.
Preload IPC fan-out
apps/desktop/src/preload/preload.test.ts
Verifies bridge.lanes.onLifecycleEvent fires from both direct IPC events and remote runtime events, and unsubscribe removes the listener.
Generation-based read cache invalidation
apps/desktop/src/renderer/lib/laneReadCache.ts, apps/desktop/src/renderer/lib/laneReadCache.test.ts
Adds a generation counter to in-flight entries, scopes coalesced reads by generation, and adds an exported invalidateLaneReadCache function, with new tests for stale in-flight reuse.
useLaneListInvalidation hook
apps/desktop/src/renderer/hooks/useLaneListInvalidation.ts, apps/desktop/src/renderer/hooks/useLaneListInvalidation.test.tsx
New hook debounces lifecycle-driven cache invalidation and refreshLanes calls, defers refresh while hidden, schedules follow-up and focus-based stale refreshes, covered by multiple test scenarios.
Wiring into pages and toasts
apps/desktop/src/renderer/components/graph/WorkspaceGraphPage.tsx, apps/desktop/src/renderer/components/lanes/LanesPage.tsx, apps/desktop/src/renderer/components/run/RunPage.tsx, apps/desktop/src/renderer/components/run/RunPage.test.tsx, apps/desktop/src/renderer/components/app/toast/useLaneEventToasts.ts
Wires useLaneListInvalidation into graph, lanes, and run pages; updates toast logic to skip lane-renamed and add a lane-unarchived message.
PRs context debounced lifecycle refresh
apps/desktop/src/renderer/components/prs/state/PrsContext.tsx, apps/desktop/src/renderer/components/prs/state/PrsContext.test.tsx, apps/desktop/src/renderer/components/prs/state/PrsContextWarmCache.test.tsx
Adds a debounced effect that refreshes PR data on lane lifecycle events while respecting document visibility, with mocks and tests updated accordingly.

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

Possibly related PRs

  • arul28/ADE#219: The new refreshLanes include-flag calls from useLaneListInvalidation align with prior changes to the refreshLanes signature.
  • arul28/ADE#689: Builds directly on prior lane lifecycle event plumbing and toast integration, extending it with new event types and invalidation logic.

Suggested labels: desktop

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title is concise and relevant, capturing the core lane snapshot invalidation focus of the change.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch ade/start-by-running-skill-keywords-9923fd90

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@arul28 arul28 force-pushed the ade/start-by-running-skill-keywords-9923fd90 branch from 433ef71 to 9e47ded Compare July 7, 2026 00:04
@arul28

arul28 commented Jul 7, 2026

Copy link
Copy Markdown
Owner Author

@codex review

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🧹 Nitpick comments (4)
apps/desktop/src/main/services/lanes/laneService.ts (1)

4937-4948: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

Consider guarding against no-op unarchive calls.

Unlike rename() (which early-returns when trimmed === lane.name), unarchive() broadcasts lane-unarchived unconditionally, even if row.status is already "active". Since this event now drives a user-facing toast ("Lane unarchived" in useLaneEventToasts.ts) and triggers cache invalidation/refresh in useLaneListInvalidation, a redundant call would produce a spurious toast and unnecessary refresh cascade. archive() has the same gap, so this isn't a new issue, but the newly-wired event/toast wiring makes it user-visible for the first time.

🩹 Proposed fix
     unarchive({ laneId }: { laneId: string }): void {
       const row = getLaneRow(laneId);
       if (!row) throw new Error(`Lane not found: ${laneId}`);
+      if (row.status !== "archived") return;
       db.run("update lanes set status = 'active', archived_at = null where id = ? and project_id = ?", [laneId, projectId]);
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/desktop/src/main/services/lanes/laneService.ts` around lines 4937 -
4948, Guard the no-op path in laneService.ts for unarchive() (and likewise
archive()) so lifecycle events are only emitted when the lane status actually
changes. In unarchive(), check the current row.status before updating; if it is
already active, return early without calling db.run, invalidateLaneListCache, or
broadcastLifecycleEvent("lane-unarchived"). Mirror the same status check pattern
in archive() to avoid redundant writes, refreshes, and toasts.
apps/desktop/src/renderer/lib/laneReadCache.ts (1)

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

Duplicate cache-clear logic between test helper and production invalidation.

clearLaneReadInFlightForTest and invalidateLaneReadCache share nearly identical bodies (clear in-flight maps + bump generation), differing only by the keybindingsInFlight.clear() call. Consider having the test helper call invalidateLaneReadCache() plus its own keybindingsInFlight.clear() to avoid future drift between the two.

♻️ Proposed refactor
 export function clearLaneReadInFlightForTest(): void {
-  laneListInFlight.clear();
-  laneSnapshotInFlight.clear();
   keybindingsInFlight.clear();
-  laneReadGeneration += 1;
+  invalidateLaneReadCache();
 }
🤖 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/lib/laneReadCache.ts` around lines 92 - 103, The
cache-clearing logic in clearLaneReadInFlightForTest and invalidateLaneReadCache
is duplicated and likely to drift. Refactor clearLaneReadInFlightForTest to call
invalidateLaneReadCache() for the shared laneListInFlight, laneSnapshotInFlight,
and laneReadGeneration reset behavior, then keep only the test-specific
keybindingsInFlight.clear() in the test helper. Use the existing function names
as the single source of truth so future changes to invalidation behavior stay
consistent.
apps/desktop/src/renderer/components/graph/WorkspaceGraphPage.tsx (1)

183-183: 🚀 Performance & Scalability | 🔵 Trivial | ⚡ Quick win

Verify stale-focus refresh options don't duplicate this page's dedicated refresh effects.

useLaneListInvalidation is wired with the raw store refreshLanes (not the page's refreshGraphLanes wrapper, which deliberately skips heavier phases per the comment at Lines 174-180). Per the hook's contract (apps/desktop/src/renderer/hooks/useLaneListInvalidation.ts), its stale-focus path calls runRefresh({ includeStatus: false, includeSnapshots: true, includeConflictStatus: true, includeRebaseSuggestions: true, includeAutoRebaseStatus: true }) on refocus after staleMs. This page already has dedicated focus/visibility-triggered refreshes for auto-rebase status (refreshAutoRebaseStatuses) and sync status (refreshLaneSyncStatuses) at Lines 1248-1258, plus its own risk-batch refresh cadence. The new hook's heavier stale-refresh could duplicate that work on refocus after the tab has been hidden a while.

🔍 Suggested verification

Confirm whether laneLifecycleRefreshOptions (used for lifecycle-driven refreshes) and the fixed stale-focus option set in useLaneListInvalidation are expected to overlap with this page's existing per-concern refresh effects, or whether a lighter-weight custom refreshLanes wrapper (mirroring refreshGraphLanes) should be passed 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 `@apps/desktop/src/renderer/components/graph/WorkspaceGraphPage.tsx` at line
183, The stale-focus refresh wired through useLaneListInvalidation is using the
raw refreshLanes path, which can duplicate this page’s existing dedicated
refresh effects. Review WorkspaceGraphPage’s refreshGraphLanes wrapper and the
related focus/visibility refresh handlers (including refreshAutoRebaseStatuses
and refreshLaneSyncStatuses), then either pass a lighter custom wrapper into
useLaneListInvalidation or align laneLifecycleRefreshOptions so the stale-focus
path does not re-run phases this page already refreshes separately.
apps/desktop/src/renderer/components/prs/state/PrsContext.tsx (1)

895-925: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚖️ Poor tradeoff

Duplicated debounce/visibility-defer pattern vs. useLaneListInvalidation.

This effect reimplements essentially the same "debounce lifecycle event, defer while hidden, replay on focus/visibility" pattern already encapsulated in useLaneListInvalidation (used by WorkspaceGraphPage, LanesPage, RunPage per the stack context). Since PrsContext refreshes via refreshCore() rather than a RefreshLanes-shaped function, direct reuse isn't a drop-in, but consider extracting the shared "debounce + hidden-defer + focus/visibility replay" logic into a small reusable utility/hook to avoid two independently-maintained copies of this timing logic (and two magic debounce constants: PRS_LANE_LIFECYCLE_REFRESH_DEBOUNCE_MS here vs. the hook's own debounce constant).

🤖 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
895 - 925, The `useEffect` in `PrsContext` duplicates the same
debounce/hidden-defer/focus-replay lifecycle handling already centralized in
`useLaneListInvalidation`. Extract that timing behavior into a shared utility or
hook that can accept `refreshCore()` here and the `RefreshLanes` callback used
elsewhere, then have `PrsContext` use it instead of maintaining its own
`scheduleRefresh`, `refreshPendingIfVisible`, and local debounce constant.
🤖 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.

Nitpick comments:
In `@apps/desktop/src/main/services/lanes/laneService.ts`:
- Around line 4937-4948: Guard the no-op path in laneService.ts for unarchive()
(and likewise archive()) so lifecycle events are only emitted when the lane
status actually changes. In unarchive(), check the current row.status before
updating; if it is already active, return early without calling db.run,
invalidateLaneListCache, or broadcastLifecycleEvent("lane-unarchived"). Mirror
the same status check pattern in archive() to avoid redundant writes, refreshes,
and toasts.

In `@apps/desktop/src/renderer/components/graph/WorkspaceGraphPage.tsx`:
- Line 183: The stale-focus refresh wired through useLaneListInvalidation is
using the raw refreshLanes path, which can duplicate this page’s existing
dedicated refresh effects. Review WorkspaceGraphPage’s refreshGraphLanes wrapper
and the related focus/visibility refresh handlers (including
refreshAutoRebaseStatuses and refreshLaneSyncStatuses), then either pass a
lighter custom wrapper into useLaneListInvalidation or align
laneLifecycleRefreshOptions so the stale-focus path does not re-run phases this
page already refreshes separately.

In `@apps/desktop/src/renderer/components/prs/state/PrsContext.tsx`:
- Around line 895-925: The `useEffect` in `PrsContext` duplicates the same
debounce/hidden-defer/focus-replay lifecycle handling already centralized in
`useLaneListInvalidation`. Extract that timing behavior into a shared utility or
hook that can accept `refreshCore()` here and the `RefreshLanes` callback used
elsewhere, then have `PrsContext` use it instead of maintaining its own
`scheduleRefresh`, `refreshPendingIfVisible`, and local debounce constant.

In `@apps/desktop/src/renderer/lib/laneReadCache.ts`:
- Around line 92-103: The cache-clearing logic in clearLaneReadInFlightForTest
and invalidateLaneReadCache is duplicated and likely to drift. Refactor
clearLaneReadInFlightForTest to call invalidateLaneReadCache() for the shared
laneListInFlight, laneSnapshotInFlight, and laneReadGeneration reset behavior,
then keep only the test-specific keybindingsInFlight.clear() in the test helper.
Use the existing function names as the single source of truth so future changes
to invalidation behavior stay consistent.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 13fa9c6d-a4d7-4c57-9a99-6dae46eba357

📥 Commits

Reviewing files that changed from the base of the PR and between fbbe215 and 433ef71.

⛔ Files ignored due to path filters (2)
  • docs/features/lanes/README.md is excluded by !docs/**
  • docs/features/pull-requests/README.md is excluded by !docs/**
📒 Files selected for processing (16)
  • apps/desktop/src/main/services/lanes/laneService.test.ts
  • apps/desktop/src/main/services/lanes/laneService.ts
  • apps/desktop/src/preload/preload.test.ts
  • apps/desktop/src/renderer/components/app/toast/useLaneEventToasts.ts
  • apps/desktop/src/renderer/components/graph/WorkspaceGraphPage.tsx
  • apps/desktop/src/renderer/components/lanes/LanesPage.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/run/RunPage.test.tsx
  • apps/desktop/src/renderer/components/run/RunPage.tsx
  • apps/desktop/src/renderer/hooks/useLaneListInvalidation.test.tsx
  • apps/desktop/src/renderer/hooks/useLaneListInvalidation.ts
  • apps/desktop/src/renderer/lib/laneReadCache.test.ts
  • apps/desktop/src/renderer/lib/laneReadCache.ts
  • apps/desktop/src/shared/types/lanes.ts

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

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 9e47dede2a

ℹ️ About Codex in GitHub

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

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

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

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

type RefreshLanes = AppState["refreshLanes"];

export function laneLifecycleRefreshOptions(event: LaneLifecycleEvent): Parameters<RefreshLanes>[0] {
const includeStatus = event.type === "lane-created";

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Include status for unarchived lane refreshes

When a lane-unarchived event arrives, the lane was previously absent from the active lane list/snapshots, so refreshLanes has no previous status to preserve. This branch requests includeStatus: false, which makes the service return the restored lane with the default clean/0-ahead/0-behind status, leaving its dirty/ahead/behind badges wrong until an unrelated full-status refresh. Treat lane-unarchived like lane-created here so newly reintroduced lanes are hydrated with real status.

Useful? React with 👍 / 👎.

type RefreshLanes = AppState["refreshLanes"];

export function laneLifecycleRefreshOptions(event: LaneLifecycleEvent): Parameters<RefreshLanes>[0] {
const includeStatus = event.type === "lane-created";

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Unarchived Lanes Skip Status

When lane-unarchived reaches this refresh path, the hook requests snapshots without status recomputation because only lane-created sets includeStatus. A restored lane can reappear in the active list with stale or missing git status until a later full refresh.

Artifacts

Repro: focused Vitest test source for lane-unarchived refresh options

  • Contains supporting evidence from the run (text/typescript; charset=utf-8).

Repro: verbose Vitest output showing lane-unarchived includeStatus false and assertion failure

  • Keeps the command output available without making the summary code-heavy.

View artifacts

T-Rex Ran code and verified through T-Rex

Prompt To Fix With AI
This is a comment left during a code review.
Path: apps/desktop/src/renderer/hooks/useLaneListInvalidation.ts
Line: 14

Comment:
**Unarchived Lanes Skip Status**

When `lane-unarchived` reaches this refresh path, the hook requests snapshots without status recomputation because only `lane-created` sets `includeStatus`. A restored lane can reappear in the active list with stale or missing git status until a later full refresh.

How can I resolve this? If you propose a fix, please make it concise.

Fix in Claude Code

@arul28

arul28 commented Jul 7, 2026

Copy link
Copy Markdown
Owner Author

@codex review

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

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: fc6f0e7f9e

ℹ️ About Codex in GitHub

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

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

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

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

type RefreshLanes = AppState["refreshLanes"];

export function laneLifecycleRefreshOptions(event: LaneLifecycleEvent): Parameters<RefreshLanes>[0] {
const includeStatus = event.type === "lane-created";

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Refresh status for unarchived lanes

When a lane-unarchived event arrives, this refresh runs with includeStatus: false. Unarchived lanes were previously absent from the active lane list (includeArchived: false), so the app store has no prior status to preserve; the lightweight list path returns the default lane status, leaving dirty/ahead/behind/rebase badges wrong until an unrelated full status refresh happens. Treat lane-unarchived like lane-created here so the lane re-enters the UI with a computed status.

Useful? React with 👍 / 👎.

type RefreshLanes = AppState["refreshLanes"];

export function laneLifecycleRefreshOptions(event: LaneLifecycleEvent): Parameters<RefreshLanes>[0] {
const includeStatus = event.type === "lane-created";

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Unarchived Status Skips Refresh

When a lane is restored, lane-unarchived reaches this path with includeStatus: false. The restored lane was not in the previous active-lane status maps, so it can reappear with missing or stale git status until a later full refresh.

Prompt To Fix With AI
This is a comment left during a code review.
Path: apps/desktop/src/renderer/hooks/useLaneListInvalidation.ts
Line: 14

Comment:
**Unarchived Status Skips Refresh**

When a lane is restored, `lane-unarchived` reaches this path with `includeStatus: false`. The restored lane was not in the previous active-lane status maps, so it can reappear with missing or stale git status until a later full refresh.

How can I resolve this? If you propose a fix, please make it concise.

Fix in Claude Code

@arul28 arul28 force-pushed the ade/start-by-running-skill-keywords-9923fd90 branch from fc6f0e7 to 53015b4 Compare July 7, 2026 00:42
@arul28

arul28 commented Jul 7, 2026

Copy link
Copy Markdown
Owner Author

@codex review

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

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 53015b435b

ℹ️ About Codex in GitHub

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

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

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

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

} | null>(null);
const laneSnapshots = useAppStore((s) => s.laneSnapshots);
const laneListFreshnessKey = useMemo(() => ({ lanes, laneSnapshots }), [lanes, laneSnapshots]);
useLaneListInvalidation({ active: active && Boolean(activeProjectRoot), refreshLanes, freshnessKey: laneListFreshnessKey });

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Keep lane invalidation active while Lanes is hidden

When App hides the Lanes surface it keeps LanesPage mounted with active=false, so this disables the only lifecycle invalidation subscription for the lane store. If a daemon/remote lane-created, rename, archive, or delete event arrives while the user is on another tab such as Work, the global toast can fire but the lane list cache is not invalidated/refreshed; switching back to /lanes also does not force a refresh because the hook treats activation as fresh. Keep the lifecycle listener active for the mounted lane surface or force a refresh when re-activating after missed events.

Useful? React with 👍 / 👎.

type RefreshLanes = AppState["refreshLanes"];

export function laneLifecycleRefreshOptions(event: LaneLifecycleEvent): Parameters<RefreshLanes>[0] {
const includeStatus = event.type === "lane-created";

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Refresh status for unarchived lanes

For a lane-unarchived lifecycle event this sets includeStatus to false, but archived lanes are excluded from the active lane list, so the unarchived lane has no previous status for refreshLanes to preserve. The statusless service response seeds it with the default clean/zero status, causing dirty/ahead/behind badges and graph/run summaries to be wrong until a later full status refresh. Treat unarchive like create here and request status.

Useful? React with 👍 / 👎.

@arul28 arul28 merged commit 14995c1 into main Jul 7, 2026
33 checks passed
@arul28 arul28 deleted the ade/start-by-running-skill-keywords-9923fd90 branch July 7, 2026 00:57
type RefreshLanes = AppState["refreshLanes"];

export function laneLifecycleRefreshOptions(event: LaneLifecycleEvent): Parameters<RefreshLanes>[0] {
const includeStatus = event.type === "lane-created";

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Unarchived Status Still Skips

When lane-unarchived reaches this helper, it still gets includeStatus: false. The restored lane can be fetched without fresh dirty/ahead/behind data, so it may reappear in active lane views with missing or stale git status until an unrelated full status refresh runs.

Artifacts

Repro: focused Vitest test exercising lane-unarchived helper and hook refresh options

  • Contains supporting evidence from the run (text/tsx; charset=utf-8).

Repro: verbose Vitest output showing lane-unarchived stale-status assertions passed

  • Keeps the command output available without making the summary code-heavy.

View artifacts

T-Rex Ran code and verified through T-Rex

Prompt To Fix With AI
This is a comment left during a code review.
Path: apps/desktop/src/renderer/hooks/useLaneListInvalidation.ts
Line: 14

Comment:
**Unarchived Status Still Skips**

When `lane-unarchived` reaches this helper, it still gets `includeStatus: false`. The restored lane can be fetched without fresh dirty/ahead/behind data, so it may reappear in active lane views with missing or stale git status until an unrelated full status refresh runs.

How can I resolve this? If you propose a fix, please make it concise.

Fix in Claude Code

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