Skip to content

fix(desktop): pty resume backfill + persist per-project route#199

Merged
arul28 merged 1 commit intomainfrom
release/v1.1.5-pty-route-fix
Apr 25, 2026
Merged

fix(desktop): pty resume backfill + persist per-project route#199
arul28 merged 1 commit intomainfrom
release/v1.1.5-pty-route-fix

Conversation

@arul28
Copy link
Copy Markdown
Owner

@arul28 arul28 commented Apr 25, 2026

Bug fix bundled into v1.1.5 release.

  • pty: backfill resume target on launch (was warn-only)
  • AppShell: persist last-visited route per project to localStorage

Summary by CodeRabbit

  • New Features

    • Per-project route persistence: The app now remembers your last visited location within each project and restores it when you switch between projects.
  • Bug Fixes

    • Improved Claude session resumption with automatic target recovery.

Greptile Summary

This PR fixes two independent bugs: (1) the PTY service now actively backfills the resume target (session ID) before spawning the PTY instead of only logging a warning, and (2) AppShell persists the last-visited route per project to localStorage and restores it on project switch.

Confidence Score: 4/5

Safe to merge; only P2 findings present.

Both fixes are well-scoped and backed by tests. The two P2 findings (allowedRoots inconsistency and potential flag-dropping on backfill) don't cause regressions but are worth addressing in a follow-up.

apps/desktop/src/renderer/components/app/AppShell.tsx – verify /review and /cto route handling; apps/desktop/src/main/services/pty/ptyService.ts – confirm --permission-mode is not needed in the backfilled command.

Important Files Changed

Filename Overview
apps/desktop/src/main/services/pty/ptyService.ts Promotes resume-target backfill from warn-only to an active async call before PTY spawn; adds "resume-launch" reason variant. One P2: backfilled command silently drops original launch flags (e.g. --permission-mode).
apps/desktop/src/main/services/pty/ptyService.test.ts Adds a targeted test for the new backfill-on-resume-launch path; covers happy-path assertion of both setResumeCommand and pty.write. Good coverage of the core change.
apps/desktop/src/renderer/components/app/AppShell.tsx Adds per-project route persistence via localStorage; logic is sound, but allowedRoots in serializeLocationRoute includes /review and /cto which are absent from primaryTabPath, creating a minor inconsistency.

Sequence Diagram

sequenceDiagram
    participant Caller
    participant PtyService
    participant SessionService
    participant tryBackfillResumeTarget

    Caller->>PtyService: create({ sessionId, startupCommand: "claude --resume" })
    PtyService->>SessionService: get(sessionId) → existingSession
    alt existingSession && no targetId
        PtyService->>tryBackfillResumeTarget: await tryBackfillResumeTarget("resume-launch")
        tryBackfillResumeTarget->>SessionService: readTranscriptTail()
        tryBackfillResumeTarget->>SessionService: setResumeCommand(sessionId, "claude --resume <id>")
        tryBackfillResumeTarget-->>PtyService: true
        PtyService->>SessionService: get(sessionId) → updatedSession
        PtyService->>PtyService: startupCommand = "claude --resume <id>"
    end
    PtyService->>PtyService: spawn shell PTY
    PtyService->>PtyService: pty.write("claude --resume <id>\r")
Loading

Fix All in Claude Code

Prompt To Fix All With AI
This is a comment left during a code review.
Path: apps/desktop/src/renderer/components/app/AppShell.tsx
Line: 75-92

Comment:
**`allowedRoots` diverges from `primaryTabPath` roots**

`serializeLocationRoute` adds `/review` and `/cto` to its allow-list, but `primaryTabPath` (defined just above) does not include them. Routes under those paths will therefore be persisted and later restored, but the sidebar tab won't be activated (since `primaryTabPath` won't match them). If `/review` and `/cto` are real top-level pages that should be restored, they should also be added to `primaryTabPath`; otherwise they should be dropped from `allowedRoots`.

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

---

This is a comment left during a code review.
Path: apps/desktop/src/main/services/pty/ptyService.ts
Line: 1160-1172

Comment:
**Backfilled command drops original launch flags**

When `tryBackfillResumeTarget` succeeds, `startupCommand` is overwritten with the backfilled command (e.g. `claude --resume <id>`). However, the original `requestedStartupCommand` may contain flags such as `--permission-mode default` that are not preserved in the backfilled command. The PTY therefore launches without those flags.

This may be intentional if permission mode is meant to come from session metadata at the tool level, but the test's expected invocation (`"claude --resume claude-session-123\r"`) silently drops `--permission-mode default` that was in the startup command. Worth verifying the tool CLI picks up the correct permission mode without the explicit flag on resume.

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

Reviews (1): Last reviewed commit: "fix(desktop): backfill pty resume target..." | Re-trigger Greptile

Greptile also left 2 inline comments on this PR.

…ect route

- ptyService: when relaunching a tracked CLI session whose resumeMetadata
  has no targetId, run tryBackfillResumeTarget("resume-launch") before
  spawning so the new pty starts on the right resume command instead of
  cold-starting. Replaces the old warn-only branch.
- AppShell: persist the last-visited route per project to localStorage
  and restore it on project switch, instead of always slamming /work.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@vercel
Copy link
Copy Markdown

vercel Bot commented Apr 25, 2026

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

1 Skipped Deployment
Project Deployment Actions Updated (UTC)
ade Ignored Ignored Apr 25, 2026 7:55pm

@arul28 arul28 merged commit a35195a into main Apr 25, 2026
6 checks passed
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Apr 25, 2026

Caution

Review failed

The pull request is closed.

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: cb2505fa-6551-4d9f-b0c7-6bd4b74e01aa

📥 Commits

Reviewing files that changed from the base of the PR and between 49a93ee and d15bdb7.

📒 Files selected for processing (3)
  • apps/desktop/src/main/services/pty/ptyService.test.ts
  • apps/desktop/src/main/services/pty/ptyService.ts
  • apps/desktop/src/renderer/components/app/AppShell.tsx

Disabled knowledge base sources:

  • Linear integration is disabled

You can enable these sources in your CodeRabbit configuration.


📝 Walkthrough

Walkthrough

Three interconnected changes: a new unit test for resuming tracked Claude sessions with resume command backfilling, implementation of resume target backfill logic during PTY creation to update session metadata when missing target IDs, and addition of localStorage-based per-project "last visited" route persistence in the AppShell component with fallback to "/work".

Changes

Cohort / File(s) Summary
PTY Resume Service Test
apps/desktop/src/main/services/pty/ptyService.test.ts
New unit test for the create path when resuming tracked Claude sessions, verifying backfill of resume commands and session metadata updates when target ID is missing.
PTY Resume Service Implementation
apps/desktop/src/main/services/pty/ptyService.ts
Implements resume target backfill logic during PTY creation; adds shouldBackfillResumeTarget check that calls tryBackfillResumeTarget for sessions lacking resumeMetadata.targetId, updates session-derived commands/metadata on success, and replaces previous warning-only behavior with active backfill-and-update flow.
AppShell Route Persistence
apps/desktop/src/renderer/components/app/AppShell.tsx
Adds localStorage-based per-project "last visited" route tracking; serializes outgoing location scoped to previous project root, restores stored route for incoming project root (fallback to "/work"), and persists current location continuously for active project via ref-guarded effect.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

Suggested labels

desktop

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch release/v1.1.5-pty-route-fix

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 and usage tips.

Comment on lines +75 to +92
const allowedRoots = [
"/project",
"/lanes",
"/files",
"/work",
"/graph",
"/prs",
"/review",
"/history",
"/automations",
"/missions",
"/cto",
"/settings",
];
if (!allowedRoots.some((root) => pathname === root || pathname.startsWith(`${root}/`))) {
return null;
}
return route;
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 allowedRoots diverges from primaryTabPath roots

serializeLocationRoute adds /review and /cto to its allow-list, but primaryTabPath (defined just above) does not include them. Routes under those paths will therefore be persisted and later restored, but the sidebar tab won't be activated (since primaryTabPath won't match them). If /review and /cto are real top-level pages that should be restored, they should also be added to primaryTabPath; otherwise they should be dropped from allowedRoots.

Prompt To Fix With AI
This is a comment left during a code review.
Path: apps/desktop/src/renderer/components/app/AppShell.tsx
Line: 75-92

Comment:
**`allowedRoots` diverges from `primaryTabPath` roots**

`serializeLocationRoute` adds `/review` and `/cto` to its allow-list, but `primaryTabPath` (defined just above) does not include them. Routes under those paths will therefore be persisted and later restored, but the sidebar tab won't be activated (since `primaryTabPath` won't match them). If `/review` and `/cto` are real top-level pages that should be restored, they should also be added to `primaryTabPath`; otherwise they should be dropped from `allowedRoots`.

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

Fix in Claude Code

Comment on lines +1160 to +1172
const shouldBackfillResumeTarget =
existingSession
&& isTrackedCliToolType(toolTypeHint)
&& !existingSession.resumeMetadata?.targetId?.trim();
if (shouldBackfillResumeTarget) {
const backfilled = await tryBackfillResumeTarget(sessionId, toolTypeHint, "resume-launch", cwd);
const updatedSession = backfilled ? sessionService.get(sessionId) : null;
if (updatedSession?.resumeCommand?.trim()) {
initialResumeCommand = updatedSession.resumeCommand.trim();
initialResumeMetadata = updatedSession.resumeMetadata ?? initialResumeMetadata;
startupCommand = initialResumeCommand;
}
}
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 Backfilled command drops original launch flags

When tryBackfillResumeTarget succeeds, startupCommand is overwritten with the backfilled command (e.g. claude --resume <id>). However, the original requestedStartupCommand may contain flags such as --permission-mode default that are not preserved in the backfilled command. The PTY therefore launches without those flags.

This may be intentional if permission mode is meant to come from session metadata at the tool level, but the test's expected invocation ("claude --resume claude-session-123\r") silently drops --permission-mode default that was in the startup command. Worth verifying the tool CLI picks up the correct permission mode without the explicit flag on resume.

Prompt To Fix With AI
This is a comment left during a code review.
Path: apps/desktop/src/main/services/pty/ptyService.ts
Line: 1160-1172

Comment:
**Backfilled command drops original launch flags**

When `tryBackfillResumeTarget` succeeds, `startupCommand` is overwritten with the backfilled command (e.g. `claude --resume <id>`). However, the original `requestedStartupCommand` may contain flags such as `--permission-mode default` that are not preserved in the backfilled command. The PTY therefore launches without those flags.

This may be intentional if permission mode is meant to come from session metadata at the tool level, but the test's expected invocation (`"claude --resume claude-session-123\r"`) silently drops `--permission-mode default` that was in the startup command. Worth verifying the tool CLI picks up the correct permission mode without the explicit flag on resume.

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