fish arms `CSI ?2031h` before painting each prompt and withdraws it when it
hands the tty to a child — a ~1ms window. Orca answered that subscribe with
`CSI ?997;Nn` across a 1-3ms renderer hop, so the reply landed after the
withdrawal and was read as stdin by the next child, corrupting `brew`/`npx`
`[y/N]` prompts.
The reply is not stale by Orca's own view when written (measured
staleReplies: 0), so no suppress-the-stale-reply scheme can close this — the
information needed to suppress does not exist yet. Nothing asked for the reply
either. The Contour spec says a terminal "should only send out the DSR when the
palette has been updated"; Ghostty (Termio.zig:729 — force=true reachable only
from the ?996n DSR), iTerm2 (VT100Terminal.m:995 — flag only) and xterm.js
(InputHandler.ts:2035 — flag only) all emit nothing on the DECSET. So stop
entering the race: record the subscription, answer nothing.
Of 17 real programs measured under a pty, only fish, tmux, claude and opencode
subscribe; none block on a reply, and answering produces one redundant palette
re-query and zero rendering difference. tmux is the only one that sends `?996n`,
which Orca still answers.
- Subscribes are record-only at all four emitters (live scan, hidden-gate fact,
parked byte watcher, parked responder — the last is deleted, it only replied).
- `?996n` answers, the subscription registry, and the theme-flip push are
unchanged. `paneLastThemeMode` is still seeded at subscribe so the next
appearance re-apply is not read as a flip.
- Replay grammar carries `?2031l` alongside `?2031h`, so a late-attaching remote
client no longer registers a subscription the TUI already retired.
Also closes fish-integration gaps found alongside: `unset` (which fish lacks)
becomes `set -e` on paths parsed by the client's login shell, `config.fish` is
parsed for agent-home detection, and bracketed-paste startup delivery is made
consistent across local/daemon/relay.
Regression test drives real fish 4.7.1 under node-pty and asserts on what the
child process reads; it fails against pre-fix code with the exact payload from
the issue. CI installs fish 4 and fails loudly rather than skipping.
Closes#9993
Co-authored-by: Orca <help@stably.ai>
* test(e2e): register a real runtime host and publish the alt-screen frame as its snapshot
Two long-running scheduled-E2E failures on main were stale test setup, not
product defects.
`onboarding.spec.ts:420` seeded the Active Server by faking a runtime
environment in the renderer store and writing `activeRuntimeEnvironmentId`
through the generic `settings:set` IPC. Since #10011 that setter strips the
key, and the dedicated `settings:set-active-runtime-environment-preference`
handler resolves the id against the main-process environment store — CI
logged `RuntimeEnvironmentStoreError: Unknown environment: env-e2e` from
`runtimeEnvironments:subscribe`/`:call` alongside the assertion failure.
Register the host for real via `runtimeEnvironments:addFromPairingCode`
(offline; no live server) and write the preference through its own channel.
`terminal-tab-switch-visual-restore.spec.ts:604` wrote alt-screen frames
straight into the renderer's xterm, so those bytes never transited the PTY
and main's model could not contain them. On cycle 0 the freshly spawned
shell still has queued startup output, so hiding the pane makes main's
hidden-delivery gate drop bytes and latch a reveal restore, which repaints
main's snapshot over the fabricated frame; later cycles run against an idle
shell and survive. Arm the existing `setHiddenSnapshotOverride` seam (already
used by sibling tests in this file) with the same frame so the live-write and
restore paths render identically, and keep the `markerPresent` assertion.
Co-authored-by: Orca <help@stably.ai>
* test(e2e): keep the alt-screen restore path observable
Numbering the snapshot frame one higher than the live-written frame keeps
the marker assertion path-agnostic while leaving the frame number on
screen as the signal for which path painted. An unrecognised frame now
fails, and the per-cycle path is recorded rather than asserted because
which cycles latch a restore is load-dependent.
Frame authoring and readback move to a helper module; the additions
crossed the spec's max-lines cap.
Co-authored-by: Orca <help@stably.ai>
* Escape regex metacharacters in alt-screen marker pattern
Marker is treated as a literal string, so escape regex metacharacters
to prevent them from being interpreted as regex syntax.
---------
Co-authored-by: Orca <help@stably.ai>
The e2e terminal helpers typed `node -e ${JSON.stringify(script)}` into the
PTY. JSON.stringify emits POSIX-style \" escapes, which PowerShell does not
honor: it re-splits the program on `;` inside the payload, node throws
'Expected unicode escape' before emitting a single byte, and the OSC-title
assertions fail deterministically on Windows (default shell = PowerShell).
Stage the program in a unique temp .cjs file instead and send
`node "<forward-slash path>"` — no shell ever parses the program source, so
delivery is byte-identical on PowerShell, cmd, bash, and zsh (verified with
hexdumps: 07 1b 5d 30 3b ... 07 matches exactly across shells). Forward
slashes keep the quoted path valid in both POSIX shells and PowerShell; the
Codex startup marker moves from argv into the script body so no argument
quoting remains. macOS/Linux payload bytes are unchanged — only the delivery
mechanism differs. Test infrastructure only; no product code touched.