Skip to content

ca TUI: fix the railway harness launch hang, ⇧esc release, and the shift+enter newline - #1079

Merged
codyde merged 2 commits into
masterfrom
railway/ca-answer-da1-query
Aug 11, 2026
Merged

ca TUI: fix the railway harness launch hang, ⇧esc release, and the shift+enter newline#1079
codyde merged 2 commits into
masterfrom
railway/ca-answer-da1-query

Conversation

@codyde

@codyde codyde commented Aug 11, 2026

Copy link
Copy Markdown
Collaborator

Three fixes in the railway ca session pane. All three are the pane failing to speak the terminal's side of a protocol.

1. Fix: railway harness hung on launch — pane showed only the relay banner

  • railway-agent-tui writes its startup burst in one write — bracketed paste, focus reporting, ESC[?u, ESC[c, ESC[6n — then waits.
  • crossterm uses DA1 (ESC[c) as the sentinel in supports_keyboard_enhancement. The pane answered ESC[?u but never DA1, so the harness waited on a reply that never came and never reached the cursor query the pane already answered.
  • Regression from adding the kitty responder. Before it, nothing was answered, crossterm's 2s timeout expired, and the harness drew.
  • Fix: answer DA1 with ESC[?62;22c.
  • Measured against the real binary under a pty: without DA1, 23 bytes then stuck indefinitely; with it, full UI in 0.07s. Also retires the old 2s startup delay.

2. Fix: ⌥esc no longer released the session pane

  • macOS Option is a compose key unless the terminal maps it to Meta. The other ⌥ chords survive that by arriving as the character they compose to — ⌥f as ƒ, ⌥[ as a curly quote — which alt_chord already matches.
  • Escape composes to nothing, so ⌥esc arrives as a bare Escape with no modifier to distinguish it from the one meant for the agent.
  • ⌥esc does work where the terminal maps Option to Alt/Meta and the kitty protocol is live (CSI 27;3u); it is dead where Option composes, which is the default in Ghostty and Terminal.app.
  • History: feat(code): coding agents on cloud agent VMs, and railway ca to drive them #1013 shipped this chord as ⇧esc, TUI: replace the wizard-replay ⌥s with a proper settings card #1069 swapped it to ⌥esc.
  • Fix: accept ⇧esc alongside ⌥esc; the kitty protocol reports it as CSI 27;2u. ⌥esc, ^] and ^o are untouched, so terminals where ⌥esc already worked are unaffected. Hints read ⌥/⇧esc / ^].

3. Partial fix: shift+enter did not insert a newline in the harness

  • The kitty protocol exempts Enter, Tab and Backspace from disambiguate mode by design, so a stock terminal reports shift+enter as a bare \r with no modifier on it. There was nothing for encode_key_for to act on — the CSI-u encoding added earlier could never fire.
  • Fix: modified Enter falls back to ESC CR instead of a bare \r when the remote has not pushed the kitty protocol. A bare \r submits the half-written prompt; ESC CR is the legacy newline chord, and the bytes Claude Code's /terminal-setup binds shift+enter to.
  • Still needs the terminal to report the key. Either /terminal-setup (or an equivalent keybind sending ESC CR), or Option-as-Meta plus ⌥enter, which already worked. REPORT_ALL_KEYS_AS_ESCAPE_CODES would lift the exemption, but crossterm cannot pair it with the associated text those events carry — composed ⌥ characters, dead keys and IMEs would all break. Recorded in a comment on setup_terminal.

Tests

  • DA1 scanner: both spellings, no false positives on ESC[?62;22c replies or other c-terminated sequences, and the real startup burst gets all three answers in the order asked.
  • Modified-Enter encodings for shift / alt / ctrl, with and without the kitty push.
  • ⇧esc added to the release-chord test; the maximized-release test now uses it.

3 pre-existing auth_sim failures on master, unrelated to this branch.

Launching the railway harness left a pane holding nothing but the relay's
banner. `railway-agent-tui` opens by writing its whole startup burst in one go
— bracketed paste, focus reporting, the kitty keyboard query, DA1, then the
cursor-position query — and then waits.

crossterm uses DA1 as the sentinel in `supports_keyboard_enhancement`: it
writes the kitty query and `ESC[c` immediately after, then reads until one of
them answers, because a terminal too old to know the kitty query will still
answer DA1. Answering the kitty query while ignoring DA1 is therefore the one
combination that hangs outright — the harness is told the protocol exists and
then waits on a reply that never comes, never reaching the cursor query the
pane was already answering. Before the pane learned `ESC[?u` it answered
neither, crossterm's two-second timeout expired, and the harness drew; adding
half the handshake turned a slow launch into a stuck one.

So answer DA1 too. Measured against the real binary under a pty: no DA1 reply
and it emits 23 bytes and stops forever; with one it draws in full in 0.07s,
which also retires the two-second timeout that was there before any of this.
@codyde codyde added the release/patch Author patch release label Aug 11, 2026
@codyde codyde changed the title ca TUI: answer the DA1 query, so the railway harness draws at all ca TUI: fix the railway harness launch hang, ⇧esc release, and the shift+enter newline Aug 11, 2026
⌥esc stopped releasing the session pane because macOS cannot send it. Option
is a compose key unless the terminal maps it to Meta, and the other ⌥ chords
survive that by arriving as the character they compose to — ⌥f as `ƒ`, ⌥[ as a
curly quote, which `alt_chord` already matches. Escape composes to nothing, so
⌥esc arrives as a bare Escape with no modifier left to tell it apart from the
one meant for the agent. Shift is never composed and the kitty protocol reports
a shifted Escape as `CSI 27;2u`, so ⇧esc is taken alongside it and is what the
hints now name. ⌥esc, `^]` and `^o` all keep working.

Modified Enter falls back to `ESC CR` rather than a bare `\r` when the remote
side has not pushed the kitty protocol. A bare `\r` submits the half-written
prompt, which is the one outcome shift+enter exists to prevent; `ESC CR` is the
legacy newline chord harnesses have always taken, and the exact bytes Claude
Code's own `/terminal-setup` binds shift+enter to.

Worth recording why the CSI-u path alone was not enough: the kitty protocol
exempts Enter, Tab and Backspace from disambiguate mode by design, so a stock
terminal reports shift+enter as a bare `\r` with no modifier on it and there is
nothing for `encode_key_for` to act on. Lifting that needs
REPORT_ALL_KEYS_AS_ESCAPE_CODES, which crossterm cannot yet pair with the
associated text those events carry — composed ⌥ characters, dead keys and IMEs
would all break — so reporting shift+enter stays the terminal's to opt into.
@codyde
codyde force-pushed the railway/ca-answer-da1-query branch from 67b9694 to 0aa2c1e Compare August 11, 2026 07:55
@codyde
codyde merged commit 30cc5cc into master Aug 11, 2026
7 checks passed
@codyde
codyde deleted the railway/ca-answer-da1-query branch August 11, 2026 07:58
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

release/patch Author patch release

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant