Ghostty focus is not fail-closed: a stale terminal ID falls back to cwd/AXDocument and focuses the wrong tab; app-level `terminals` enumeration is also unreliable
#116 opened on Jul 16, 2026
Repository metrics
- Stars
- (759 stars)
- PR merge metrics
- (PR metrics pending)
Description
Follow-up to #113/#114/#115, same setup (macOS Tahoe, Ghostty 1.3.1, ~12 concurrent Claude Code sessions, most sharing one cwd). Two related findings from a week of running a locally patched build, with the fix we've verified in production.
1. The focus chain silently degrades to "wrong tab" instead of "no-op"
focusGhosttyWindowWithOptions tries exact terminal-ID focus, then falls back to cwd matching, then to window-level AXDocument + activateByPID. Every step down that ladder loses precision:
- A stale terminal ID (session resumed via
claude --resume, or a handed-off session) fails the ID step, then cwd matching can't disambiguate shared-cwd tabs, and the AXDocument fallback raises an arbitrary same-cwd window. - A headless session (cron/CI, or a session living on another machine) has no tab at all — the click still activates the app and lands on whatever tab is frontmost.
From the user's perspective both cases are "the notification lied to me": the click confidently focused a tab that has nothing to do with the notification. A click that does nothing is strictly better than one that focuses the wrong session.
Fix we run locally (happy to PR):
- When a terminal ID was provided and by-ID focus fails → return the error; no cwd fallback, no AXDocument, no app activation.
- At send time, when no trustworthy terminal ID exists for the session → build the notification without any click action (no
-execute, no-activate).
2. App-level terminals enumeration misses windows; iterate windows→tabs→terminals
ghosttyFocusByIDAppleScript and the cwd-matching script iterate the app-level terminals element. We observed states where that enumeration returned a small subset of actual terminals (in the extreme, 1 of 12 — notably while a second Ghostty process instance was alive, e.g. spawned via ghostty -e …; with two processes sharing the bundle ID, all Apple events route to one arbitrary instance). Explicit nesting is more robust and costs nothing:
repeat with w in windows
repeat with tb in tabs of w
repeat with t in (terminals of tb)
...
Also worth adding an explicit activate after focus t — focus alone selects the tab but doesn't reliably bring the app forward when Ghostty is backgrounded or the tab lives on another Space.
Related operational note for other multi-session users who land here: if clicks suddenly stop working across all sessions, check for a second Ghostty process (ps -axo pid=,comm= | grep Ghostty.app). Any ghostty -e-style helper instance hijacks AppleScript for the whole bundle ID until it exits. (macOS pgrep -f is a trap for this check — it matches environment strings.)
We've been running all of the above (fail-closed focus + explicit iteration + activate) for a fleet of ~12 sessions with zero wrong-tab focuses since. Can open a PR for whichever parts you'd take.