Summary
Auto-review created a review session overnight but the terminal never appeared — the session hung indefinitely on the "Waiting for terminal" screen. The underlying cause appears to be a failed Ghostty surface creation, after which TerminalManager happily returns the existing (broken) view on subsequent lookups instead of failing the session or retrying.
Repro
- Run latest crow on a Mac mini, leave it overnight with auto-review enabled and a watched repo that receives a "request changes" / review-requested event.
- Auto-review fires (in this case for
radiusmethod/tanzanite#155).
- In the morning the session exists in the sidebar but the terminal pane is stuck on "Waiting for terminal".
Logs (2026-04-29, ~08:24 local)
[SessionService] Cloning radiusmethod/tanzanite into /Users/dustinhilgaertner/Projects/crow-reviews/tanzanite-pr-155
[TerminalManager] trackReadiness for 06CF4874-E79D-4D01-B76C-700995FB6518
[TerminalManager] preInitialize(06CF4874-...) — creating surface in offscreen window
[GhosttyApp] Unhandled action: tag=ghostty_action_tag_e(rawValue: 41)
[Ghostty] createSurface() FAILED — surface is nil
[SessionService] Created review session 'review-tanzanite-155' for https://github.com/radiusmethod/tanzanite/pull/155
[TerminalManager] surface(for: 06CF4874-...) — returning EXISTING view
[GhosttyApp] Unhandled action: tag=ghostty_action_tag_e(rawValue: 41)
[Ghostty] createSurface() FAILED — surface is nil
After that point the only repeating log entries are [IssueTracker] refresh: lines — there are no further terminal-related events, which means there's no retry, no surfacing of the failure, and no path out of the stuck state without restarting the app.
Observations / hypotheses
Ghostty.createSurface() returned nil, but the terminal id 06CF4874-... was still registered with TerminalManager. The next surface(for:) call returned the existing broken entry rather than treating nil-surface as "needs reinit".
- The unhandled
ghostty_action_tag_e(rawValue: 41) from GhosttyApp happens immediately before each surface failure — could be related; worth confirming what action 41 corresponds to in the current Ghostty SDK build.
- The session was created (
Created review session 'review-tanzanite-155') after the first surface failure, so session creation doesn't gate on terminal readiness. That's fine in general, but combined with the lack of retry, it means the user-visible state is "session exists, terminal forever pending".
Expected behavior
At minimum one of:
createSurface() failure triggers a bounded retry on the same terminal id, with backoff.
- After N failures the session surfaces an error state in the UI ("Terminal failed to launch — retry / open logs") instead of "Waiting for terminal" forever.
TerminalManager.surface(for:) should not return an entry whose surface is nil — treat that as a miss and re-preInitialize.
Auto-review specifically should also probably mark the session as failed (or auto-retry the whole review) if the terminal can't come up within some deadline, so overnight runs don't silently no-op.
Environment
- Latest crow (running overnight on a Mac mini)
- Auto-review enabled
- Triggering event: review requested on
radiusmethod/tanzanite#155
Summary
Auto-review created a review session overnight but the terminal never appeared — the session hung indefinitely on the "Waiting for terminal" screen. The underlying cause appears to be a failed Ghostty surface creation, after which
TerminalManagerhappily returns the existing (broken) view on subsequent lookups instead of failing the session or retrying.Repro
radiusmethod/tanzanite#155).Logs (2026-04-29, ~08:24 local)
After that point the only repeating log entries are
[IssueTracker] refresh:lines — there are no further terminal-related events, which means there's no retry, no surfacing of the failure, and no path out of the stuck state without restarting the app.Observations / hypotheses
Ghostty.createSurface()returned nil, but the terminal id06CF4874-...was still registered withTerminalManager. The nextsurface(for:)call returned the existing broken entry rather than treating nil-surface as "needs reinit".ghostty_action_tag_e(rawValue: 41)fromGhosttyApphappens immediately before each surface failure — could be related; worth confirming what action 41 corresponds to in the current Ghostty SDK build.Created review session 'review-tanzanite-155') after the first surface failure, so session creation doesn't gate on terminal readiness. That's fine in general, but combined with the lack of retry, it means the user-visible state is "session exists, terminal forever pending".Expected behavior
At minimum one of:
createSurface()failure triggers a bounded retry on the same terminal id, with backoff.TerminalManager.surface(for:)should not return an entry whose surface is nil — treat that as a miss and re-preInitialize.Auto-review specifically should also probably mark the session as failed (or auto-retry the whole review) if the terminal can't come up within some deadline, so overnight runs don't silently no-op.
Environment
radiusmethod/tanzanite#155