Skip to content

fix: route PWD action to correct TabManager per tabId#2147

Open
anthhub wants to merge 1 commit intomanaflow-ai:mainfrom
anthhub:fix/session-restore-cwd
Open

fix: route PWD action to correct TabManager per tabId#2147
anthhub wants to merge 1 commit intomanaflow-ai:mainfrom
anthhub:fix/session-restore-cwd

Conversation

@anthhub
Copy link
Contributor

@anthhub anthhub commented Mar 25, 2026

Summary

Fixes #2125 — session restore does not recover working directories.

  • Root cause: GHOSTTY_ACTION_PWD used AppDelegate.shared?.tabManager to update the current working directory. The tabManager property only returns the key window's TabManager. In multi-window scenarios, cwd updates from non-key windows were dispatched to the wrong TabManager, leaving panelDirectories[panelId] empty at save time and causing a fallback to $HOME on restore.
  • Fix: Replace tabManager? with tabManagerFor(tabId: tabId)?, which looks up the TabManager that actually owns the tab by ID — routing the update to the correct window regardless of which window currently has focus.
  • Single-window safety: When only one window exists, tabManagerFor(tabId:) returns the same manager as tabManager, so there is no behavioral change for single-window users.

Changed files

  • Sources/GhosttyTerminalView.swift — 1-line change in GHOSTTY_ACTION_PWD handler

Test plan

  • Open cmux with multiple windows, cd into different project directories in each window's workspaces
  • Quit cmux (Cmd+Q)
  • Relaunch cmux
  • Verify each window/workspace restores to its correct working directory (not $HOME)
  • Verify the single-window case still restores working directories correctly

🤖 Generated with Claude Code


Summary by cubic

Fixes session restore so each window/tab keeps its correct working directory. Routes the PWD action to the TabManager that owns the tab instead of the key window’s manager. Fixes #2125.

  • Bug Fixes
    • In GHOSTTY_ACTION_PWD, replace AppDelegate.shared?.tabManager with tabManagerFor(tabId:) to update the right TabManager in multi-window setups; single-window behavior unchanged.

Written for commit 6736e8e. Summary will update on new commits.

Summary by CodeRabbit

  • Bug Fixes
    • Improved handling of working directory updates to ensure changes are correctly applied to the specific tab, preventing cross-tab directory synchronization issues.

Previously, GHOSTTY_ACTION_PWD used AppDelegate.shared?.tabManager to
update the current working directory, which only returns the key window's
TabManager. In multi-window scenarios, cwd updates from non-key windows
were written to the wrong TabManager, causing panelDirectories to be
empty at save time and falling back to $HOME on restore.

Fix by using tabManagerFor(tabId:) to precisely route the update to
whichever window owns the tab, ensuring all windows persist their
working directories correctly.

Fixes manaflow-ai#2125
@vercel
Copy link

vercel bot commented Mar 25, 2026

@anthhub is attempting to deploy a commit to the Manaflow Team on Vercel.

A member of the Team first needs to authorize it.

@coderabbitai
Copy link

coderabbitai bot commented Mar 25, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 9fe6e9c3-e75f-46fa-94a1-19f2f03c6399

📥 Commits

Reviewing files that changed from the base of the PR and between 99ca3c9 and 6736e8e.

📒 Files selected for processing (1)
  • Sources/GhosttyTerminalView.swift

📝 Walkthrough

Walkthrough

Updated the GHOSTTY_ACTION_PWD action handler to route directory updates through a tab-specific TabManager retrieved via AppDelegate.shared?.tabManagerFor(tabId:) instead of using the global tabManager, ensuring updates target the correct manager instance.

Changes

Cohort / File(s) Summary
Directory Update Routing
Sources/GhosttyTerminalView.swift
Changed GHOSTTY_ACTION_PWD action to dispatch directory updates to the tab-specific TabManager associated with the given tabId rather than the global TabManager instance.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~10 minutes

Possibly related PRs

Poem

🐰 A pwd found its way through the right door,
No more lost in the global store,
Tab by tab, each terminal knows,
Where it came from, where it goes! ✨

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately describes the main change: routing PWD action to correct TabManager per tabId, which is the core fix for the multi-window directory restore issue.
Description check ✅ Passed The description comprehensively covers the root cause, fix explanation, safety considerations, and test plan, though testing items are marked unchecked rather than completed.
Linked Issues check ✅ Passed The code change directly addresses issue #2125 by replacing tabManager? with tabManagerFor(tabId:)?, ensuring PWD updates route to the correct TabManager for multi-window scenarios.
Out of Scope Changes check ✅ Passed The single-line change in GhosttyTerminalView.swift is narrowly scoped to fixing the PWD action routing without introducing unrelated modifications.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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.

Copy link

@cubic-dev-ai cubic-dev-ai bot left a comment

Choose a reason for hiding this comment

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

No issues found across 1 file

@greptile-apps
Copy link

greptile-apps bot commented Mar 25, 2026

Greptile Summary

This PR fixes a multi-window session-restore regression (#2125) where working directories were not persisted correctly. The root cause was that GHOSTTY_ACTION_PWD used AppDelegate.shared?.tabManager — which always returns the key window's TabManager — to record the current directory. In a multi-window setup, PWD events from non-focused windows were therefore dispatched to the wrong TabManager, leaving panelDirectories[panelId] empty and causing a fallback to $HOME on restore.

The one-line fix replaces tabManager? with tabManagerFor(tabId: tabId)?, which iterates mainWindowContexts to find the TabManager that actually owns the tab, regardless of which window currently has focus. This pattern is already in use elsewhere in GhosttyTerminalView.swift (e.g. the GHOSTTY_ACTION_DESKTOP_NOTIFICATION handler), so the change is consistent with established conventions.

Key points:

  • Fix is minimal, targeted, and correct — no unintended behavioural change for the single-window case.
  • tabManagerFor(tabId:) is implicitly @MainActor (via AppDelegate's class annotation) and is called inside DispatchQueue.main.async, so there are no concurrency issues.
  • If the tab has already been closed by the time the async block runs, optional chaining silently drops the update — the correct behaviour.
  • The adjacent split-action handlers (GHOSTTY_ACTION_GOTO_SPLIT, GHOSTTY_ACTION_RESIZE_SPLIT, GHOSTTY_ACTION_EQUALIZE_SPLITS, GHOSTTY_ACTION_TOGGLE_SPLIT_ZOOM) still use the key-window tabManager; they carry the same class of routing bug but are pre-existing and out of scope here.
  • No automated test for the multi-window PWD routing path is added; a unit test verifying that updateSurfaceDirectory is called on the correct TabManager would strengthen coverage.

Confidence Score: 5/5

  • Safe to merge — the change is a correct, minimal one-liner with no risk of regression for single-window users and a clear correctness win for multi-window setups.
  • The fix is logically correct, consistent with existing patterns in the same file, carries no concurrency risk, and has no impact on the single-window path. The only open items (missing test, similar bugs in split handlers) are pre-existing and non-blocking.
  • No files require special attention.

Important Files Changed

Filename Overview
Sources/GhosttyTerminalView.swift One-line fix in GHOSTTY_ACTION_PWD handler replacing tabManager? (key-window TabManager) with tabManagerFor(tabId:)? (correct per-tab lookup); change is logically sound and consistent with the pattern used in the GHOSTTY_ACTION_DESKTOP_NOTIFICATION handler. Pre-existing split-action handlers nearby still use the key-window TabManager, but those are out of scope.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A[GHOSTTY_ACTION_PWD fires] --> B{Lookup TabManager}

    B -->|Before fix| C[AppDelegate.tabManager\nkey window only]
    B -->|After fix| D[AppDelegate.tabManagerFor tabId\nscans all window contexts]

    C --> E{Tab found in key window?}
    E -->|No - different window| F[panelDirectories not updated\nfallback to HOME on restore]
    E -->|Yes - single window| G[panelDirectories updated correctly]

    D --> H[contextContainingTabId iterates\nmainWindowContexts]
    H --> I[Returns owning TabManager]
    I --> J[updateSurfaceDirectory called\non correct manager]
    J --> K[panelDirectories updated\ncwd restored correctly]
Loading

Comments Outside Diff (1)

  1. Sources/GhosttyTerminalView.swift, line 2357-2391 (link)

    P2 Similar routing gap in adjacent split-action handlers

    The four handlers directly below the fixed GHOSTTY_ACTION_PWD case still use AppDelegate.shared?.tabManager (the key-window TabManager) instead of tabManagerFor(tabId: tabId):

    • GHOSTTY_ACTION_GOTO_SPLIT (line 2357)
    • GHOSTTY_ACTION_RESIZE_SPLIT (line 2368)
    • GHOSTTY_ACTION_EQUALIZE_SPLITS (line 2381)
    • GHOSTTY_ACTION_TOGGLE_SPLIT_ZOOM (line 2390)

    All four have tabId in scope at that point. In a multi-window session, a split command issued from a non-focused window will be routed to the focused window's TabManager instead of the correct one, which would either silently no-op or mutate the wrong tab layout.

    These are pre-existing issues and outside the stated scope of this PR, but since you're already touching this area it might be worth fixing them in a follow-up (or here, if you can easily verify the behaviour). The pattern to use would match the fix applied to GHOSTTY_ACTION_PWD:

    guard let tabManager = AppDelegate.shared?.tabManagerFor(tabId: tabId) ?? AppDelegate.shared?.tabManager else { return false }

Reviews (1): Last reviewed commit: "fix: route PWD action to correct TabMana..." | Re-trigger Greptile

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.

Session restore does not recover working directories — all workspaces reopen at $HOME

1 participant