docs(board): measure what Ghostty 1.3.1 can actually be driven to do
The gate question before any day-plan design: can Ghostty be scripted to open a tab with two vertical panes, each in its own directory, with a command running in one? Measured against the installed 1.3.1 rather than reasoned about, because this is undocumented platform behavior. Yes, but not through the documented API. new window / new tab / split return correctly shaped objects that never spawn a shell - nine probe windows, shell count flat at 29 - and every call that needs the process fails with "Terminal surface model is not available". That is issue #12730, a 1.3.1 regression closed as not planned, and 1.3.1 is the newest release. surface configuration is collateral: all four of its useful properties ride that path. What does work is perform action on an ALREADY REALIZED surface. new_tab and new_split produce live shells (29 -> 31), and input text + send key drives them - two panes cd'd to different repos reported distinct directories and distinct PIDs. The working directory becomes a typed cd, so the broken path is not needed at all. Three constraints fell out of the measurement and are recorded because each was found by breaking something: pane targeting must diff tab ids, since last tab of w is not the new tab and a probe that trusted position typed into a tab it did not create; new tabs inherit the anchor's directory; and count of terminals is not a pane count. Recommends splitting the work - the plan here as a third rendering of the board scan, the Ghostty driver in a separate repo. The argument is the measurement: a preview API that breaks in 1.4, whose documented path is already broken, with a blast radius reaching into other repos' live sessions, does not belong behind the mailbox. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01GfDGWyyhnM26J4p93GSk2L
This commit is contained in:
parent
8e207c6c49
commit
5d9c76763d
1 changed files with 145 additions and 0 deletions
145
docs/ghostty-orchestration-measurement.md
Normal file
145
docs/ghostty-orchestration-measurement.md
Normal file
|
|
@ -0,0 +1,145 @@
|
|||
# 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.
|
||||
|
||||
### 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 <realized terminal> -> +1 live shell
|
||||
perform action "new_split:right" on <realized term> -> +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.
|
||||
Loading…
Add table
Add a link
Reference in a new issue