# Driving a day plan into Ghostty: what is actually possible on 1.3.1 Measured 2026-08-01/02 against the installed terminal, not reasoned about. This document exists to answer one gate question before any design: **can Ghostty be scripted to open a tab with two vertical panes, each in its own directory, with a command running in one?** Short answer: **yes, but not through the documented API** - the documented path is broken in the installed version, and the path that works is an undocumented composition of primitives that the maintainer has labelled preview. ## The environment being measured - Ghostty **1.3.1** (stable, released 2026-03-13). This is the newest release; there is no 1.3.2 and no 1.4. - Config: `working-directory = inherit`, `window-save-state = always`, `shell-integration = zsh`, `macos-titlebar-style = tabs`. - Operator's workspace: one window, 13 tabs, 2 vertical panes per tab. ## What was measured | Mechanism | Realizes a surface? | Splits? | Can start a command? | |---|---|---|---| | AppleScript `new window` / `new tab` / `split` | **No** | structure only, panes blank | no | | `ghostty +new-window` | not available on macOS | - | - | | `open -na Ghostty.app --args ...` | yes (26 shells) | yes, whole layout | **no**, and it duplicates everything | | `perform action` on a **realized** surface | **Yes** | yes | **yes** | ### 1. The documented API is broken `new window`, `new tab` and `split` return objects with the correct structure - `count of tabs` and `count of terminals` are exactly what was asked for - but no shell process is ever spawned. Nine probe windows were created across the session; the count of `login ... exec -l /bin/zsh` processes stayed at 29 throughout. The windows are visible and blank, and the tabs carry the placeholder ghost emoji as their name. Any subsequent call that needs the process fails: execution error: Ghostty got an error: Terminal surface model is not available. (-10000) This is [issue #12730](https://github.com/ghostty-org/ghostty/issues/12730), "AppleScript: new tab via 'new tab' creates empty tab without shell process (1.3.1 regression)", **closed as not planned with no maintainer comment**. The independent measurement here matched the report on every detail including the ghost emoji and the full set of failed workarounds (delays up to 4s, explicit focus, explicit `activate`, every surface-configuration variant). The likely cause is stated in the issue and is consistent with the 1.3.1 release notes: the fix for [issue #11457](https://github.com/ghostty-org/ghostty/issues/11457) (AppleScript implicitly activating the app) appears to have removed the step that also initialized the shell. **Consequence: `surface configuration` is unusable.** Its four useful properties - `initial working directory`, `command`, `initial input`, `environment variables` - all ride the broken creation path. None of them had any measurable effect: no process, no file written, no directory set. **A ghost surface can be detected in one property read, and this is the cheapest guard against the whole failure class.** `working directory` is readable on a terminal, and a surface with no shell process reports it EMPTY - that is exactly the ghost signature. So "has this surface been realized" is a machine-checkable question, not something to infer from counting `login ... exec -l /bin/zsh` processes as this measurement did. Anything driving Ghostty should wait for a new surface to report a working directory before typing into it, and refuse to type into one that never does. Contributed by `morning-driver` (2026-08-02) from the sdef plus its own measurement, after building against this document. The same property answers a question left open below: it goes empty when a shell process exits and returns when the prompt is back, so "has this pane finished and returned to a prompt" can be polled rather than guessed at with a delay. ### 2. `open -na` works and is still the wrong tool `open -na Ghostty.app --args --working-directory=...` produced 26 live shells in the correct 13-tab / 2-pane layout. It does this by launching a **second Ghostty instance** (pid 80818 alongside the operator's pid 519) which then restores the saved session, because `window-save-state = always`. That makes it useless for a day plan on two counts: it duplicates the entire existing workspace rather than opening a chosen subset, and session restore restores layout and working directories but never running programs - the panes came up as bare shells with no Claude Code. It also leaves the operator with two instances of their whole environment. ### 3. The path that works: actions on realized surfaces The AppleScript *creation commands* are broken. The AppleScript *action* mechanism is not, and `new_tab` / `new_split` are both in `ghostty +list-actions`. Performing them **on a surface that is already realized** produces surfaces that are themselves realized: perform action "new_tab" on -> +1 live shell perform action "new_split:right" on -> +1 live shell Measured: 29 -> 31 shells, tab count 13 -> 14, and the operator confirmed a live prompt in the new tab. `input text` + `send key "enter"` then works on those panes. Two panes were driven to `cd` into different repositories and report `pwd` plus their own PID; both wrote distinct directories and distinct PIDs. **This is the full chain, and it needs no `surface configuration` at all** - the working directory becomes a typed `cd`, and the command becomes typed text. ## Three constraints any implementation inherits These came out of the measurement and are not negotiable design details. 1. **Never target a pane by position.** `last tab of w` is not the tab that was just created; new tabs are inserted relative to the current tab, not appended. A probe that trusted position typed into a tab it did not create. The new tab must be captured by diffing tab `id`s before and after. This is the failure mode with the worst blast radius: the text lands in whatever program that pane is running, which may be another repo's live Claude Code session. 2. **A new tab inherits the anchor pane's working directory** (`working-directory = inherit`), so every pane must be `cd`-ed explicitly. Inheriting silently produces a plan that looks right and points at the wrong repo. 3. **`count of terminals` is not a reliable pane count.** Probe tabs reported 3 and 4 terminal objects where 2 shells existed. Panes must be addressed by the ids captured at creation, never enumerated after the fact. ## Recommendation **Both paths, split across two homes - and the split is the recommendation, not a hedge.** - **The plan itself - which repos, in what order, with which startup command - belongs here**, as a third rendering of the scan `board.sh` already performs, built with exactly the argument `--brief` was: it is a lookup over data the scan already holds, it costs zero model calls, and `route.sh` already derives the per-repo command. Printing that plan as pasteable commands is path (a), and it is complete on its own. - **Driving Ghostty belongs in a separate repo that consumes the plan.** The measurement is the argument: this is a version-pinned, undocumented composition on top of an API the maintainer calls a preview and expects to break in 1.4, whose documented path is already broken and whose regression was closed as not planned. That is a maintenance surface with its own release cadence, its own failure modes, and a blast radius that reaches into other repos' live sessions. None of that is mailbox transport, and none of it should be able to break `coord-inbox` or `board`. That `board.sh` lives here is not an argument for Ghostty automation living here. The dependency runs one way: the driver consumes the plan, the plan never knows a terminal exists. If 1.4 breaks the driver, the day plan still prints and the operator still pastes. ## Not measured - Whether `input text` is safe against a pane running a full-screen TUI. - Whether 1.3.0 is unaffected. The issue calls this a 1.3.1 regression, which implies it, but downgrading a daily driver to gain a preview API that changes in 1.4 was not evaluated as a serious option. - How "focus" should be expressed, how many tabs a day plan should propose, and whether it should remember yesterday. These were deferred until the gate question was answered; they are now open.