Commit Graph
945 Commits
Author SHA1 Message Date
Neil ad8e71e1a2 Split GitHub project view read path (#17266)
* Split speech session lifecycle

* Split terminal output scheduler pipeline

* Split mobile browser pane modules

* Prune resolved max-lines suppressions

* Split pane tree equalization logic

* Extract mobile troubleshoot screen styles

* Split external automation manager

* Split main window service attachments

* Split hosted review creation checks

* Split automation dispatch event handling

* Split settings navigation metadata

* Split daemon initialization lifecycle

* Split GitLab item dialog

* Split relay dispatcher layers

* Split mobile host screen

* Retarget mobile view settings source test

* Split runtime file client layers

* Split ports panel layers

* Split runtime environments pane layers

* Split local PTY provider responsibilities

* Split CDP bridge responsibilities

* Split relay Git handler responsibilities

* Track moved relay Git fetch audit

* Split Linear item drawer responsibilities

* Split telemetry event schema responsibilities

* Split resource usage status responsibilities

* Split remote terminal multiplexer responsibilities

* Split Git worktree responsibilities

* Split Codex hook service responsibilities

* Keep mirrored hook trust type private

* Split web runtime session responsibilities

* Split GitHub project view read path

* Fix F3-speech for #17123

* Fix F1-cycle for #17131

* Fix F4-navtest for #17157

* Fix F2-allowlist for #17161
2026-08-29 20:22:10 -07:00
Neil 07652e252f Split web runtime session responsibilities (#17263)
* Split speech session lifecycle

* Split terminal output scheduler pipeline

* Split mobile browser pane modules

* Prune resolved max-lines suppressions

* Split pane tree equalization logic

* Extract mobile troubleshoot screen styles

* Split external automation manager

* Split main window service attachments

* Split hosted review creation checks

* Split automation dispatch event handling

* Split settings navigation metadata

* Split daemon initialization lifecycle

* Split GitLab item dialog

* Split relay dispatcher layers

* Split mobile host screen

* Retarget mobile view settings source test

* Split runtime file client layers

* Split ports panel layers

* Split runtime environments pane layers

* Split local PTY provider responsibilities

* Split CDP bridge responsibilities

* Split relay Git handler responsibilities

* Track moved relay Git fetch audit

* Split Linear item drawer responsibilities

* Split telemetry event schema responsibilities

* Split resource usage status responsibilities

* Split remote terminal multiplexer responsibilities

* Split Git worktree responsibilities

* Split Codex hook service responsibilities

* Keep mirrored hook trust type private

* Split web runtime session responsibilities

* Fix F3-speech for #17123

* Fix F1-cycle for #17131

* Fix F4-navtest for #17157

* Fix F2-allowlist for #17161
2026-08-29 20:21:36 -07:00
Neil 1369821bad Split Codex hook service responsibilities (#17260)
* Split speech session lifecycle

* Split terminal output scheduler pipeline

* Split mobile browser pane modules

* Prune resolved max-lines suppressions

* Split pane tree equalization logic

* Extract mobile troubleshoot screen styles

* Split external automation manager

* Split main window service attachments

* Split hosted review creation checks

* Split automation dispatch event handling

* Split settings navigation metadata

* Split daemon initialization lifecycle

* Split GitLab item dialog

* Split relay dispatcher layers

* Split mobile host screen

* Retarget mobile view settings source test

* Split runtime file client layers

* Split ports panel layers

* Split runtime environments pane layers

* Split local PTY provider responsibilities

* Split CDP bridge responsibilities

* Split relay Git handler responsibilities

* Track moved relay Git fetch audit

* Split Linear item drawer responsibilities

* Split telemetry event schema responsibilities

* Split resource usage status responsibilities

* Split remote terminal multiplexer responsibilities

* Split Git worktree responsibilities

* Split Codex hook service responsibilities

* Keep mirrored hook trust type private

* Fix F3-speech for #17123

* Fix F1-cycle for #17131

* Fix F4-navtest for #17157

* Fix F2-allowlist for #17161
2026-08-29 20:21:14 -07:00
Neil 037cc2e531 Split Git worktree responsibilities (#17253)
* Split speech session lifecycle

* Split terminal output scheduler pipeline

* Split mobile browser pane modules

* Prune resolved max-lines suppressions

* Split pane tree equalization logic

* Extract mobile troubleshoot screen styles

* Split external automation manager

* Split main window service attachments

* Split hosted review creation checks

* Split automation dispatch event handling

* Split settings navigation metadata

* Split daemon initialization lifecycle

* Split GitLab item dialog

* Split relay dispatcher layers

* Split mobile host screen

* Retarget mobile view settings source test

* Split runtime file client layers

* Split ports panel layers

* Split runtime environments pane layers

* Split local PTY provider responsibilities

* Split CDP bridge responsibilities

* Split relay Git handler responsibilities

* Track moved relay Git fetch audit

* Split Linear item drawer responsibilities

* Split telemetry event schema responsibilities

* Split resource usage status responsibilities

* Split remote terminal multiplexer responsibilities

* Split Git worktree responsibilities

* Fix F3-speech for #17123

* Fix F1-cycle for #17131

* Fix F4-navtest for #17157

* Fix F2-allowlist for #17161
2026-08-29 20:20:55 -07:00
Neil b5d0e51d33 Split remote terminal multiplexer responsibilities (#17248)
* Split speech session lifecycle

* Split terminal output scheduler pipeline

* Split mobile browser pane modules

* Prune resolved max-lines suppressions

* Split pane tree equalization logic

* Extract mobile troubleshoot screen styles

* Split external automation manager

* Split main window service attachments

* Split hosted review creation checks

* Split automation dispatch event handling

* Split settings navigation metadata

* Split daemon initialization lifecycle

* Split GitLab item dialog

* Split relay dispatcher layers

* Split mobile host screen

* Retarget mobile view settings source test

* Split runtime file client layers

* Split ports panel layers

* Split runtime environments pane layers

* Split local PTY provider responsibilities

* Split CDP bridge responsibilities

* Split relay Git handler responsibilities

* Track moved relay Git fetch audit

* Split Linear item drawer responsibilities

* Split telemetry event schema responsibilities

* Split resource usage status responsibilities

* Split remote terminal multiplexer responsibilities

* Fix F3-speech for #17123

* Fix F1-cycle for #17131

* Fix F4-navtest for #17157

* Fix F2-allowlist for #17161
2026-08-29 20:20:33 -07:00
Neil 80a6bd67ec Split resource usage status responsibilities (#17244)
* Split speech session lifecycle

* Split terminal output scheduler pipeline

* Split mobile browser pane modules

* Prune resolved max-lines suppressions

* Split pane tree equalization logic

* Extract mobile troubleshoot screen styles

* Split external automation manager

* Split main window service attachments

* Split hosted review creation checks

* Split automation dispatch event handling

* Split settings navigation metadata

* Split daemon initialization lifecycle

* Split GitLab item dialog

* Split relay dispatcher layers

* Split mobile host screen

* Retarget mobile view settings source test

* Split runtime file client layers

* Split ports panel layers

* Split runtime environments pane layers

* Split local PTY provider responsibilities

* Split CDP bridge responsibilities

* Split relay Git handler responsibilities

* Track moved relay Git fetch audit

* Split Linear item drawer responsibilities

* Split telemetry event schema responsibilities

* Split resource usage status responsibilities

* Fix F3-speech for #17123

* Fix F1-cycle for #17131

* Fix F4-navtest for #17157

* Fix F2-allowlist for #17161
2026-08-29 20:20:13 -07:00
Neil 23009d9345 Split telemetry event schema responsibilities (#17235)
* Split speech session lifecycle

* Split terminal output scheduler pipeline

* Split mobile browser pane modules

* Prune resolved max-lines suppressions

* Split pane tree equalization logic

* Extract mobile troubleshoot screen styles

* Split external automation manager

* Split main window service attachments

* Split hosted review creation checks

* Split automation dispatch event handling

* Split settings navigation metadata

* Split daemon initialization lifecycle

* Split GitLab item dialog

* Split relay dispatcher layers

* Split mobile host screen

* Retarget mobile view settings source test

* Split runtime file client layers

* Split ports panel layers

* Split runtime environments pane layers

* Split local PTY provider responsibilities

* Split CDP bridge responsibilities

* Split relay Git handler responsibilities

* Track moved relay Git fetch audit

* Split Linear item drawer responsibilities

* Split telemetry event schema responsibilities

* Fix F3-speech for #17123

* Fix F1-cycle for #17131

* Fix F4-navtest for #17157

* Fix F2-allowlist for #17161
2026-08-29 20:19:52 -07:00
Neil 7cef5f49ec Split Linear item drawer responsibilities (#17231)
* Split speech session lifecycle

* Split terminal output scheduler pipeline

* Split mobile browser pane modules

* Prune resolved max-lines suppressions

* Split pane tree equalization logic

* Extract mobile troubleshoot screen styles

* Split external automation manager

* Split main window service attachments

* Split hosted review creation checks

* Split automation dispatch event handling

* Split settings navigation metadata

* Split daemon initialization lifecycle

* Split GitLab item dialog

* Split relay dispatcher layers

* Split mobile host screen

* Retarget mobile view settings source test

* Split runtime file client layers

* Split ports panel layers

* Split runtime environments pane layers

* Split local PTY provider responsibilities

* Split CDP bridge responsibilities

* Split relay Git handler responsibilities

* Track moved relay Git fetch audit

* Split Linear item drawer responsibilities

* Fix F3-speech for #17123

* Fix F1-cycle for #17131

* Fix F4-navtest for #17157

* Fix F2-allowlist for #17161
2026-08-29 20:19:29 -07:00
Neil 91a35b9257 Split relay Git handler responsibilities (#17217)
* Split speech session lifecycle

* Split terminal output scheduler pipeline

* Split mobile browser pane modules

* Prune resolved max-lines suppressions

* Split pane tree equalization logic

* Extract mobile troubleshoot screen styles

* Split external automation manager

* Split main window service attachments

* Split hosted review creation checks

* Split automation dispatch event handling

* Split settings navigation metadata

* Split daemon initialization lifecycle

* Split GitLab item dialog

* Split relay dispatcher layers

* Split mobile host screen

* Retarget mobile view settings source test

* Split runtime file client layers

* Split ports panel layers

* Split runtime environments pane layers

* Split local PTY provider responsibilities

* Split CDP bridge responsibilities

* Split relay Git handler responsibilities

* Track moved relay Git fetch audit

* Fix F3-speech for #17123

* Fix F1-cycle for #17131

* Fix F4-navtest for #17157

* Fix F2-allowlist for #17161
2026-08-29 20:19:09 -07:00
Neil f3334c1bff Split CDP bridge responsibilities (#17215)
* Split speech session lifecycle

* Split terminal output scheduler pipeline

* Split mobile browser pane modules

* Prune resolved max-lines suppressions

* Split pane tree equalization logic

* Extract mobile troubleshoot screen styles

* Split external automation manager

* Split main window service attachments

* Split hosted review creation checks

* Split automation dispatch event handling

* Split settings navigation metadata

* Split daemon initialization lifecycle

* Split GitLab item dialog

* Split relay dispatcher layers

* Split mobile host screen

* Retarget mobile view settings source test

* Split runtime file client layers

* Split ports panel layers

* Split runtime environments pane layers

* Split local PTY provider responsibilities

* Split CDP bridge responsibilities

* Fix F3-speech for #17123

* Fix F1-cycle for #17131

* Fix F4-navtest for #17157

* Fix F2-allowlist for #17161
2026-08-29 20:18:48 -07:00
Neil 5650ec8c6b Split local PTY provider responsibilities (#17206)
* Split speech session lifecycle

* Split terminal output scheduler pipeline

* Split mobile browser pane modules

* Prune resolved max-lines suppressions

* Split pane tree equalization logic

* Extract mobile troubleshoot screen styles

* Split external automation manager

* Split main window service attachments

* Split hosted review creation checks

* Split automation dispatch event handling

* Split settings navigation metadata

* Split daemon initialization lifecycle

* Split GitLab item dialog

* Split relay dispatcher layers

* Split mobile host screen

* Retarget mobile view settings source test

* Split runtime file client layers

* Split ports panel layers

* Split runtime environments pane layers

* Split local PTY provider responsibilities

* Fix F3-speech for #17123

* Fix F1-cycle for #17131

* Fix F4-navtest for #17157

* Fix F2-allowlist for #17161
2026-08-29 20:18:28 -07:00
Neil 8d3a420b7b Split runtime environments pane layers (#17198)
* Split speech session lifecycle

* Split terminal output scheduler pipeline

* Split mobile browser pane modules

* Prune resolved max-lines suppressions

* Split pane tree equalization logic

* Extract mobile troubleshoot screen styles

* Split external automation manager

* Split main window service attachments

* Split hosted review creation checks

* Split automation dispatch event handling

* Split settings navigation metadata

* Split daemon initialization lifecycle

* Split GitLab item dialog

* Split relay dispatcher layers

* Split mobile host screen

* Retarget mobile view settings source test

* Split runtime file client layers

* Split ports panel layers

* Split runtime environments pane layers

* Fix F3-speech for #17123

* Fix F1-cycle for #17131

* Fix F4-navtest for #17157

* Fix F2-allowlist for #17161
2026-08-29 20:18:02 -07:00
Neil e1166ec291 Split ports panel layers (#17194)
* Split speech session lifecycle

* Split terminal output scheduler pipeline

* Split mobile browser pane modules

* Prune resolved max-lines suppressions

* Split pane tree equalization logic

* Extract mobile troubleshoot screen styles

* Split external automation manager

* Split main window service attachments

* Split hosted review creation checks

* Split automation dispatch event handling

* Split settings navigation metadata

* Split daemon initialization lifecycle

* Split GitLab item dialog

* Split relay dispatcher layers

* Split mobile host screen

* Retarget mobile view settings source test

* Split runtime file client layers

* Split ports panel layers

* Fix F3-speech for #17123

* Fix F1-cycle for #17131

* Fix F4-navtest for #17157

* Fix F2-allowlist for #17161
2026-08-29 20:17:33 -07:00
Neil 9bdc146f13 Split runtime file client layers (#17188)
* Split speech session lifecycle

* Split terminal output scheduler pipeline

* Split mobile browser pane modules

* Prune resolved max-lines suppressions

* Split pane tree equalization logic

* Extract mobile troubleshoot screen styles

* Split external automation manager

* Split main window service attachments

* Split hosted review creation checks

* Split automation dispatch event handling

* Split settings navigation metadata

* Split daemon initialization lifecycle

* Split GitLab item dialog

* Split relay dispatcher layers

* Split mobile host screen

* Retarget mobile view settings source test

* Split runtime file client layers

* Fix F3-speech for #17123

* Fix F1-cycle for #17131

* Fix F4-navtest for #17157

* Fix F2-allowlist for #17161
2026-08-29 20:17:01 -07:00
Neil 15abc3fe26 Split mobile host screen layers (#17181)
* Split speech session lifecycle

* Split terminal output scheduler pipeline

* Split mobile browser pane modules

* Prune resolved max-lines suppressions

* Split pane tree equalization logic

* Extract mobile troubleshoot screen styles

* Split external automation manager

* Split main window service attachments

* Split hosted review creation checks

* Split automation dispatch event handling

* Split settings navigation metadata

* Split daemon initialization lifecycle

* Split GitLab item dialog

* Split relay dispatcher layers

* Split mobile host screen

* Retarget mobile view settings source test

* Fix F3-speech for #17123

* Fix F1-cycle for #17131

* Fix F4-navtest for #17157

* Fix F2-allowlist for #17161
2026-08-29 20:16:28 -07:00
Neil c6641152f1 Split relay dispatcher layers (#17174)
* Split speech session lifecycle

* Split terminal output scheduler pipeline

* Split mobile browser pane modules

* Prune resolved max-lines suppressions

* Split pane tree equalization logic

* Extract mobile troubleshoot screen styles

* Split external automation manager

* Split main window service attachments

* Split hosted review creation checks

* Split automation dispatch event handling

* Split settings navigation metadata

* Split daemon initialization lifecycle

* Split GitLab item dialog

* Split relay dispatcher layers

* Fix F3-speech for #17123

* Fix F1-cycle for #17131

* Fix F4-navtest for #17157

* Fix F2-allowlist for #17161
2026-08-29 20:15:10 -07:00
Neil 052710b677 Split GitLab item dialog (#17165)
* Split speech session lifecycle

* Split terminal output scheduler pipeline

* Split mobile browser pane modules

* Prune resolved max-lines suppressions

* Split pane tree equalization logic

* Extract mobile troubleshoot screen styles

* Split external automation manager

* Split main window service attachments

* Split hosted review creation checks

* Split automation dispatch event handling

* Split settings navigation metadata

* Split daemon initialization lifecycle

* Split GitLab item dialog

* Fix F3-speech for #17123

* Fix F1-cycle for #17131

* Fix F4-navtest for #17157

* Fix F2-allowlist for #17161
2026-08-29 20:14:44 -07:00
Neil 953114c1c1 Split daemon initialization lifecycle (#17161)
* Split speech session lifecycle

* Split terminal output scheduler pipeline

* Split mobile browser pane modules

* Prune resolved max-lines suppressions

* Split pane tree equalization logic

* Extract mobile troubleshoot screen styles

* Split external automation manager

* Split main window service attachments

* Split hosted review creation checks

* Split automation dispatch event handling

* Split settings navigation metadata

* Split daemon initialization lifecycle

* Fix F3-speech for #17123

* Fix F1-cycle for #17131

* Fix F4-navtest for #17157

* Fix F2-allowlist for #17161
2026-08-29 20:14:12 -07:00
Neil b51dfc21c6 Split settings navigation metadata (#17157)
* Split speech session lifecycle

* Split terminal output scheduler pipeline

* Split mobile browser pane modules

* Prune resolved max-lines suppressions

* Split pane tree equalization logic

* Extract mobile troubleshoot screen styles

* Split external automation manager

* Split main window service attachments

* Split hosted review creation checks

* Split automation dispatch event handling

* Split settings navigation metadata

* Fix F3-speech for #17123

* Fix F1-cycle for #17131

* Fix F4-navtest for #17157
2026-08-29 20:13:44 -07:00
Neil ca6401a08f Split automation dispatch event handling (#17155)
* Split speech session lifecycle

* Split terminal output scheduler pipeline

* Split mobile browser pane modules

* Prune resolved max-lines suppressions

* Split pane tree equalization logic

* Extract mobile troubleshoot screen styles

* Split external automation manager

* Split main window service attachments

* Split hosted review creation checks

* Split automation dispatch event handling

* Fix F3-speech for #17123

* Fix F1-cycle for #17131
2026-08-29 20:12:21 -07:00
Neil 8ff8e8a5f3 Split hosted review creation checks (#17152)
* Split speech session lifecycle

* Split terminal output scheduler pipeline

* Split mobile browser pane modules

* Prune resolved max-lines suppressions

* Split pane tree equalization logic

* Extract mobile troubleshoot screen styles

* Split external automation manager

* Split main window service attachments

* Split hosted review creation checks

* Fix F3-speech for #17123

* Fix F1-cycle for #17131
2026-08-29 20:08:36 -07:00
Neil ecee14bdbc Split main window service attachments (#17151)
* Split speech session lifecycle

* Split terminal output scheduler pipeline

* Split mobile browser pane modules

* Prune resolved max-lines suppressions

* Split pane tree equalization logic

* Extract mobile troubleshoot screen styles

* Split external automation manager

* Split main window service attachments

* Fix F3-speech for #17123

* Fix F1-cycle for #17131
2026-08-29 20:08:08 -07:00
Neil 7b5bed9550 Split external automation manager (#17149)
* Split speech session lifecycle

* Split terminal output scheduler pipeline

* Split mobile browser pane modules

* Prune resolved max-lines suppressions

* Split pane tree equalization logic

* Extract mobile troubleshoot screen styles

* Split external automation manager

* Fix F3-speech for #17123

* Fix F1-cycle for #17131
2026-08-29 20:07:46 -07:00
Neil 5f75b247b9 Extract mobile troubleshoot screen styles (#17146)
* Split speech session lifecycle

* Split terminal output scheduler pipeline

* Split mobile browser pane modules

* Prune resolved max-lines suppressions

* Split pane tree equalization logic

* Extract mobile troubleshoot screen styles

* Fix F3-speech for #17123

* Fix F1-cycle for #17131
2026-08-29 20:07:23 -07:00
Neil 11a0deb8ee Split pane tree equalization logic (#17145)
* Split speech session lifecycle

* Split terminal output scheduler pipeline

* Split mobile browser pane modules

* Prune resolved max-lines suppressions

* Split pane tree equalization logic

* Fix F3-speech for #17123

* Fix F1-cycle for #17131
2026-08-29 20:07:03 -07:00
Neil 871601b6fe Prune resolved max-lines suppressions (#17142)
* Split speech session lifecycle

* Split terminal output scheduler pipeline

* Split mobile browser pane modules

* Prune resolved max-lines suppressions

* Fix F3-speech for #17123

* Fix F1-cycle for #17131
2026-08-29 20:06:41 -07:00
Neil 692ff95dd3 Split mobile browser pane modules (#17141)
* Split speech session lifecycle

* Split terminal output scheduler pipeline

* Split mobile browser pane modules

* Fix F3-speech for #17123

* Fix F1-cycle for #17131
2026-08-29 20:04:45 -07:00
Neil e39c22c1ce Split terminal output scheduler pipeline (#17131)
* Split speech session lifecycle

* Split terminal output scheduler pipeline

* Fix F3-speech for #17123

* Fix F1-cycle for #17131
2026-08-29 20:04:35 -07:00
Neil 53eb21b448 Split speech session lifecycle (#17123)
* Split speech session lifecycle

* Fix F3-speech for #17123
2026-08-29 20:04:04 -07:00
Neil 63ff0a515d Prime native cache before E2E fanout (#17280)
* Prime E2E native cache before fanout

* Update E2E permission contract
2026-08-29 19:53:50 -07:00
Neil 7655d20f04 fix(terminal): complete OMP stale cwd recovery (#17154) 2026-08-29 14:34:50 -07:00
Neil b17f60d744 build: upgrade to pnpm 12 (#17156) 2026-08-29 14:13:26 -07:00
Neil 51ed7d4f67 Pin pnpm and rebalance scheduled E2E (#17133)
* Pin pnpm and rebalance scheduled E2E

* Give scheduled E2E failure headroom
2026-08-29 03:13:46 -07:00
Junhyeok Chae 314dcba98b feat(i18n): localize Agent Dashboard to Korean (#17121)
- Translate Agent Dashboard column headers (Needs You / Working / Idle), board title, and total count.
- Translate empty-column placeholder, "You" message badge, terminal preview actions, and error-boundary copy.
- Resolves English fallback in the Agent Dashboard (dashboardPopout) under the Korean locale; only "Done" was previously translated.
2026-08-29 02:38:32 -07:00
Neil 9db319dc06 fix(terminal): recover OMP from stale working directories (#17128)
* fix(terminal): recover OMP from stale cwd

* fix(terminal): harden OMP cwd recovery
2026-08-29 02:27:44 -07:00
Neil 92ab618a11 Repair scheduled computer-use CI (#17122)
* Repair scheduled computer-use CI

* Make Calculator E2E Windows-version neutral

* Handle classic Calculator accessibility panes

* Update Calculator E2E source contract
2026-08-29 01:50:38 -07:00
Brennan Benson 6bef6d2727 fix(ci): stop the Linux Electron probe step from starving its own probes (#17071)
* fix(ci): stop the Linux Electron probe step from starving its own probes

The package job's "Test Linux Electron lifecycle boundary" step ran five
Electron probe files under Vitest's default file parallelism, so four full
Electron stacks competed for a 4-vCPU runner. Each probe carries its own
in-process deadline (20s for WebRTC, 25s for H3), and every observed failure
was one of those deadlines expiring: exit code 2, "no result", with every
sibling file in the same run slower than its own green maximum.

Run the step with --no-file-parallelism so each probe owns the runner, and
stop each probe nesting a private `xvfb-run --auto-servernum` X server inside
the step's own xvfb-run: reuse an inherited DISPLAY, and only own one when
there is none (shards, local dev), which leaves those lanes unchanged.

Also set each Docker-SSH E2E step's Playwright output aside before the next
step starts, because Playwright empties test-results/ on every run and only
the last lane's traces survived to the artifact.

* fix(ci): route the persisted-worker probe through the same display resolver
2026-08-29 01:38:17 -07:00
Brennan Benson fd9125ea8c feat(native-chat): Codex structured native chat restructure (#16729)
* feat(native-chat): port structured Codex sessions from restructure-recovery

Rebuilds the desktop structured native-chat implementation from
brennanb2025/native-chat-restructure-recovery (tip 4e31c08db3) on top of
current main as a single commit, scoped to the local Codex path.

Ported:
- Structured agent-session core: durable record store + single-writer lease,
  canonical journal, agent-session wire host/attach/eviction/subscribers,
  `agentSession.*` RPC surface (registered via ALL_RPC_METHODS; host-side
  mobile allowlist included for wire compat), pty write gate, transcript
  additions, and the Codex app-server adapter/launch resolution.
- Renderer: NativeChatStructuredSession view/composer stack, structured
  launch path with the single-flight guard, local structured session tabs
  sync, activation gate + structured inventory (read-only
  `agentSession.handoffStatus` probe), agent-session tabs in the tab strip,
  AI-vault structured session activation, and the settings pane with the
  parent Experimental Chat UI toggle plus the nested "Use updated structured
  native chat" toggle. New sessions require both flags, agent codex, no
  prompt, and a local non-WSL, non-Windows-host execution host
  (structured-native-chat-availability).
- Fixes 72c013cea6 (verified Codex launch recovery), 8ddbaf5e3d (defer
  native terminal view switching affordances), and 4e31c08db3 (release the
  launch gate after a visibility retry) with their regression tests,
  including the third-launch-after-retry guard case.
- Cross-version agent-session wire test + CI lane, packaging entries
  (proper-lockfile, agent-tooling asar excludes), and the wire-compat doc
  section.

Deliberately not ported: mobile/ changes, the Claude structured runtime
(only the claude-transcript-branch-proof and claude-structured-owner-identity
leaf modules remain, backing the kept TUI-recovery arms), the terminal↔chat
adoption/handoff flow (`agentSession.adoptTerminal`/`requestHandoff`, the
handoff request engine, TUI adoption machinery, orca-runtime adoption
methods), renderer switching affordances and their dead leftovers, the
hook/subagent-status refactor cluster, and unrelated branch changes. The
crash-during-acquisition recovery path (restart handoff adjudication,
restore/reverse re-acquire, lease schema handoff keys) is kept because every
plain direct launch depends on it; a trimmed handoff coordinator exposes
only status/restore/close.

Branch edits that targeted files main has since split (ipc/pty.ts,
worktrees.ts, rpc/methods/terminal.ts, useIpcEvents, pty-connection,
store/slices/terminals.ts, runtime-types, web preload) were re-applied to
the split modules, preserving main's newer logic (Windows CIM fallback,
browser tab close rework, cold-restore resume flow, dispatcher threading).

Known seam: the mobile clipboard image-provenance CONSUMER gate ships
(agentSession.send refuses unproven mobile image refs with
agent_session_image_untrusted) but the producer hunk in
rpc/methods/clipboard.ts stays with the unported mobile cluster, so mobile
image sends into structured chat fail closed until that side ports.

* fix(native-chat): trust only authenticated local image uploads

* fix(build): preserve Windows process-tree patch application

* test(windows): include process creation time in addon fixture

* fix(build): run windows-process-tree node-gyp from the physical package dir

gyp expands the node-addon-api dependency by probing node, whose cwd
resolves to the package's physical directory in the store, so the emitted
target is a store-relative ../../../../node-addon-api@... hop. gyp then
resolves that hop against the rebuild cwd; from the node_modules
symlink/junction it escapes the store and configure fails with
"node_addon_api.gyp not found" (run 32999886072).

Rebuild from realpath(package dir) so both bases agree, matching how the
package manager itself runs native install scripts. The regression test
replays gyp's expansion+resolution against the planned cwd and fails
without the fix.

* fix(native-chat): keep chat tabs visible through terminal closes and empty-worktree launches

Two proven blockers in the native Codex tab contract:

closeTerminalTab pre-empted the canonical unified close. With one terminal
left it deactivated the worktree on a terminal/editor/browser-only check,
blanking a workspace that still held a renderable agent-session tab; with
two or more it pre-picked a successor from terminal entities only,
re-stamping the group active before closeUnifiedTab's MRU/neighbor repair
could land on the chat tab. Successor choice now defers to the unified
contract whenever the terminal has a unified row, and deactivation is
gated on the unified renderable count (matching leaveWorktreeIfEmpty),
with the legacy pre-pick kept only for terminals without a unified row.

A structured session created on an empty worktree was published into the
host's headless group while preserveLocalLayout froze the local layout,
leaving the tab in store but permanently off screen. A preserveLocalLayout
owner now always takes client-owned placement — repairing a rendered
leaf whose group record is missing, or materializing a rendered group on a
truly empty worktree — and applies the client-derived layout repair while
still rejecting host-authored layout.

Regression tests drive the real store through closeTerminalTab (git
worktree and folder workspace) and the real snapshot applier for the
empty-worktree adoption states; all fail without the fixes.

* fix(native-chat): close stale turns and retry rejected sends

* fix(native-chat): retire hosted rows on structured tab activation

* fix(native-chat): preserve rpc defaults across main merge

* chore: format remote wire compatibility guide

* test(native-chat): cover retry after unconfirmed send

* fix(native-chat): reload outbox on session switch

* docs(settings): disclose structured chat platform limits

* fix(native-chat): await Codex launch-home preparation

* fix(codex): align child-process allowlist with async trust bridge

* test(identity): update inventory for tab surface refactor

* fix(windows): preserve process-tree CRLF patch sources

* fix(native-chat): anchor an unmatched chat echo where it was sent (#16117)

* fix(native-chat): anchor an unmatched chat echo where it was sent

The reported symptom was old user messages replaying below every new turn, so the
conversation read as scrambled. The cause was not that the echo failed to match a
transcript row. Claude consumes a mid-turn send through a `queued_command`
attachment and writes no `type:"user"` record for it, so some echoes can never
match, and no amount of matching will change that. The cause was WHERE an
unmatched echo rendered: buildMobileNativeChatTransientData appended every pending
item after the entire transcript, so it re-read below each turn that landed
afterwards.

Render each echo directly after the transcript row it was sent against, using the
baseline the send already captures. An unmatched echo is then at worst a duplicate
in the right position rather than a scrambled one, and it stays visible. Echoes
sharing an anchor keep send order; a send with no baseline, or one whose anchor
folding dropped, still falls back to the tail.

Deliberately NOT fixed by deleting the echo. Inferring from send ordering that an
echo can never match, then removing it, loses the user's own text for a message
the agent did receive, and it cannot fire in the common case anyway - measured
drain groups are 1,017 of size 1 against 55 larger. It also escalates an existing
gap: the count pass has no baseline-tail guard, unlike the glue pass, while
`messages` is a 40-row window that head-trims, resets on reconnect and grows at
the front on loadEarlier, so a false landing there would license deleting a
DIFFERENT outstanding message.

That count-pass gap is real and left for a separate change; anchoring makes its
worst case a duplicate in place rather than a scrambled conversation.

* fix(native-chat): preserve folded echo anchors

* fix(native-chat): preserve forward-folded echo anchors

* fix(native-chat): keep leading folded echoes in place

* fix(workspace-cleanup): show git status for every row (#16690)

* fix(native-chat): refuse structured chat on every Windows execution path

canUseStructuredNativeChat only refused win32 when a project runtime
resolved, so folder-workspace keys (and other keys with no project
runtime) failed open into structured chat on Windows. Fail closed on
win32 unconditionally after the host check, matching the settings copy:
local macOS/Linux only; Windows/WSL/SSH stay on terminal chat.

* fix(native-chat): restore runtime refusals behind the win32 gate

506d375de3 replaced the project-runtime checks with a bare platform test,
so a WSL or repair-required runtime resolution would no longer refuse
structured chat off-win32. Keep the unconditional win32 refusal and
re-run the runtime resolution after it, so the gate does not depend on
the resolver's own platform guard. Tests inject WSL and repair-required
resolutions on darwin/linux and fail against the regressed gate.

* fix structured session journal durability

* fix structured tab active pointer after restart

* fix(native-chat): await optional lease renewal callbacks

* refactor(skills): extract install error messages

* fix(agent-session): harden recovery ownership

* fix(native-chat): retain panes across tab activation

* fix(native-chat): address round-one review findings

* test(native-chat): align integration coverage after main merge

* fix(native-chat): harden round-two reliability

* fix(native-chat): harden round-three reliability

* fix(native-chat): close round-four recovery gaps

* fix(native-chat): separate bounded journal key forms

* fix(native-chat): reset outbox error in render on session switch

The switch effect adjusted error state after the sessionId prop changed,
tripping react-doctor's no-adjust-state-on-prop-change on the changed-code
gate and flashing the old session's banner for a frame. Reset it with the
render-time previous-value guard instead.

* fix(native-chat): invalidate stale outbox settlements

* test(native-chat): restore settled-error session-switch regression

a6e2379bd1 replaced this test with the in-flight settlement race test,
leaving the render-time error reset unpinned: deleting the reset block
still passed the whole native-chat suite. Keep both scenarios pinned;
they are distinct (settled error clears on switch vs stale settlement
invalidated in the commit-to-passive window).

* test(wire): make release checkouts race safe

* test(wire): pin cross-process checkout single-flight and importer specifier contract

* test(wire): harden release checkout lifecycle

* fix(build): drop CR-byte residue from windows-process-tree patch

The two trailing CR bytes on the patch's deletion lines are a proven
no-op: pnpm hashes patches CRLF-normalized (both forms hash to the
lockfile's 946ffb2b) and materializes this package without applying the
patch in either form, so the load-bearing build edits come solely from
applyWindowsProcessTreeBuildFixes() (#16947), which handles both source
EOL forms. Restore byte-identity with main and repin the contract test
to the post-#16947 reality: LF-only patch bytes plus lockfile hash sync.

* fix(native-chat): skip empty startup recovery
2026-08-28 16:45:58 -07:00
Jinwoo Hong 5c10bf9001 fix(sta-5781): stop cross-client resets of workspace view preferences (#17057) 2026-08-28 15:20:21 -07:00
Jinwoo Hong 6256f3d137 Fix lost session-tab changes during initial census (#17064) 2026-08-28 14:57:48 -07:00
Neil 7bbb8adc61 fix(ssh): replay an undelivered remote PTY stop on the next handshake (#12447 item 1) (#17011)
* fix(ssh): replay an undelivered remote PTY stop on the next handshake

A pty.shutdown that dies on the transport left the remote shell running
forever: kill.ts marked liveness unverifiable and nothing retried.

Record the undelivered stop on the existing durable SshRemotePtyLease and
replay it against the authoritative host on the next handshake to that same
target, fenced by the host-minted PTY incarnation so a replay cannot kill a
later PTY that reused a recycled pty-N id. Retire the record on confirmed
delivery, on the host reporting the PTY absent, and on a bounded TTL.

No wire change: the fence reads incarnationId, already published on
pty.listProcesses. A host that does not publish it degrades to no replay.

* fix(ssh): do not leave a replayable kill order behind a reversible stop

Worktree sleep stops through stopAndWait and marks those stops reversible;
when one does not land the pane stays live and the user keeps using it. An
order recorded there would come back on a later handshake and kill that
terminal. Only killPtyFromRuntimeController — where the client gives the PTY
up for good — records one, and it skips any PTY a reversible stop owns.

* fix(ssh): cover the renderer kill route and harden the replay's evidence

pty:kill is a separate implementation from killPtyFromRuntimeController and
is the one an ordinary tab close reaches, so the record was never written on
the path #12447 describes. Extracted it out of inspect.ts (which was over the
line budget and was not what the file is named for) and wired both branches.

Also:
- finishPtyShutdown no longer retires the order. It runs on paths that asked
  the host and on paths that never did, so retiring there was a contract every
  caller had to know, and the one that forgot silently dropped a kill order.
  Retirement is the replay's, on inventory evidence only.
- A recycled relay id now expires its lease. Declining to kill was only half:
  reattach fences on paneKey/tabId, never incarnation, so an untouched lease
  bound the user's old pane to whatever now holds the id.
- Dropped isPtyAlreadyGoneError from the tombstone path. It matches message
  text a transport failure could wear; every tombstone now traces to a listing.
- TTL is owned by a durable prune that actually deletes, not by a branch that
  was unreachable behind the read filter and only looked tested.
- The replay re-reads the inventory per wave and re-checks the fence next to
  each shutdown, and can never reject into the connect path.
2026-08-28 04:08:15 -07:00
Jinwoo Hong 86b770e448 fix(release): trust Linux floor workspace (#16988)
* fix(release): trust Linux floor workspace

* test(release): ratchet workspace trust scope
2026-08-28 01:07:51 -07:00
Jinjing c4b39295c1 style: format codebase (#16935)
* style: format codebase

* style: format codebase

* refactor: extract skill install dialog footer and content

Extract footer and content sections from SkillInstallDialog and
SkillInstallManagementDialog into separate components for improved
maintainability and clarity of component responsibilities.
2026-08-28 00:59:21 -07:00
Jinwoo Hong 59515beb70 fix(release): recover immutable patch validation gates (#16984)
* fix(release): recover immutable patch validation gates

* test(e2e): locate wrapped terminal file links

* test(e2e): keep sibling file links on one terminal row
2026-08-28 00:55:45 -07:00
Brennan Benson 4bb337741c feat(terminal): weight-layer forensics for the bold-collapse bug (STA-4042) (#16868)
* feat(terminal): weight-layer forensics for the bold-collapse bug (STA-4042)

Field instrumentation to name the writer behind regular-text-renders-bold:
- metric-weight-change crumbs at the writePaneMetricOptions funnel
  (prev/next/reason; weights never change in normal operation)
- terminal-weight-parity-mismatch audit on every visibility resume
- sentinel weightProbe capture fields: live options vs atlas captured
  config vs renderer-buffer bold census
- Cmd/Ctrl+Shift+click unconditional capture (no divergence gate, no
  recovery) for states the missing-ink detector cannot see
- patched addon-webgl ctx.font readback probe: detects failed font
  assignments that rasterize glyphs at a stale weight

* fix(terminal): treat canvas weight-700-serializes-as-bold as a match in the atlas font probe

Found by live validation: Chromium's ctx.font getter normalizes numeric 700
to the keyword 'bold', which made every legitimate bold rasterization count
as a failed assignment (124 false positives in one session).

* chore: update patch hash for the font-probe normalization fix

* fix(terminal): bound bold glitch diagnostics

* fix(terminal): cover serialized WebGL probe state

* feat(settings): hidden staff toggle to arm terminal render diagnostics

Replaces the reserved hidden-experimental placeholder slot with a real
switch (Shift-click the Experimental sidebar entry to reveal). It arms
and disarms the render-desync capture sentinel live — no localStorage
incantation, no reload — for the bold-glitch investigation. The passive
probes stay always-on; only the capture gestures are gated.

* fix(settings): make render diagnostics disarm exact

* chore(settings): rename hidden group to 'Hidden experimental settings', drop its description

* feat(settings): unlock hidden experimental group via Option-click on the Experimental page title

Replaces the Shift-click-sidebar unlock with the Updates-header idiom:
Option-click the Experimental page title toggles the hidden group.
Removes the now-unused click-modifier plumbing from the settings sidebar.
2026-08-27 23:36:44 -07:00
Jinwoo Hong 8dd7d6060c fix(release): stabilize native builds across CI platforms (#16947) 2026-08-27 21:42:34 -07:00
Jinwoo Hong cb848647e5 fix(browser-preview): require explicit preview capabilities (STA-5758) (#16921)
* fix(browser-preview): require explicit preview capabilities (STA-5758)

Scope document reads to approved directories, confirm external links before opening them, revoke grants with tab lifecycle, and keep document-preview session state rollback-safe across mixed client/runtime versions.

* Harden document preview lifecycle and permissions

* Document preview DNS prefetch residual

* Make preview E2E guest focus explicit

* fix(browser-preview): entry-file-only authority for root-level docs, contained chip layout, re-issued gate paths (STA-5758)

A grant whose document directory is its own request base — a doc at the
workspace root, or outside any workspace — now reads nothing but the entry
file until the reader approves a directory, at both the lexical and the
canonical containment pass. The DNS-prefetch residual can only beacon what
the page can read, and a root-level document could previously read the
whole worktree silently.

The identity chip's host badge overflowed the chip's layout box under
squeeze (Linux CI): every row member can now shrink and truncate, verified
by a width sweep in isolated Chromium down to ~120px chips.

The Allow banner says what it grants: 'Allow folder', reading files in the
named directory, for the life of the preview.

The reliability-gate manifest command, testFiles entry, assertion refs and
dated evidence naming the deleted doc-preview-external-link-bridge.test.ts
are re-issued at doc-preview-external-link-confirmation.test.ts with a
fresh 189/189 run; the focus-gate assertion text follows the shipped gate.

* fix(browser-preview): hide the chip identity row below 24rem instead of clipping it, ellipsize the host badge, catalog the new i18n keys (STA-5758)

CI's preview pane leaves the chip ~40px: no truncation shows anything
there, so the Workspace-file label and host badge now hide whole below a
24rem container threshold sized so that visible implies contained. The
badge text gains an inner text box — text directly inside the flex pill
clipped both ends with no ellipsis. The e2e geometry oracle asserts
containment when the row shows and the threshold when it does not.

verify:localization-catalog: the hardening's new preview keys (and the
renamed allowDirectory) join en.json via sync:localization-catalog.

* feat(browser-preview): batch blocked folders into one access decision (STA-5758)

Sequential per-folder banners trained the allow reflex without adding
judgment — a reader cannot weigh assets/ against data/. The banner now
accumulates every folder a load surfaces, names them (three, then a
count, full list in the title), and grants exactly that set with one
Allow-N-folders click and one reload. Dismiss fences the whole named
set. The map lives behind a ref with a version tick so a dismissal
fences an offer landing in the same event batch.
2026-08-28 00:42:07 -04:00
Jinwoo Hong b19a397d3e feat(browser-preview): reland remote HTML document previews (STA-5758) (#16920)
Reapply the reverted remote HTML document preview implementation so remote workspace files render locally over the orca-preview scheme.
2026-08-28 00:27:01 -04:00
Neil 6f8c5888b3 Run Node 26 compatibility daily instead of per PR (#16946)
* Run Node 26 compatibility daily

* Update relocated unit workflow contracts
2026-08-27 20:17:25 -07:00
Neil 2b391652b1 fix(terminal): a close the host never heard must survive the reconnect (#16752)
An enterprise user: "Every day I open orca and it opens more tabs daily at a
linear scale." Three reports over a week, told on 08-19 that a PR had fixed it,
reported twice more after. STA-4658 (P0), GH #12447, #15136, #10342, #9585. One
install held 39 zombie tab records. The revived tab's sleeping-agent record still
holds the pre-close session id, so it boots `claude --resume <old id>` -- two
agents on one transcript.

## The chain, measured

Reproduced deterministically in `ssh-lost-kill-tab-resurrection.spec.ts`: close
an SSH tab, kill the relay daemon in the container so `pty.kill` rejects with a
transport-class error, reconnect.

    drop 2 resurrected the closed tab <id>:
      baseline=1  drop1=1  drop2=2 (closed tab returned)  drop3=1

The trigger is narrow and had to be measured rather than assumed: killed relay
daemon reproduces **6 of 6 runs**; an orderly `ssh.disconnect` **passes**. Only
an ungraceful loss -- network partition, host reboot, relay crash, a laptop
sleeping mid-session -- strands the close with the RPC rejecting on a
transport-class error. Both variants live in the spec behind one
`runResurrectionCycles` parameterized solely by the disruption, so the difference
is attributable to that single variable.

What actually carries the tab back, from the pull path
(`workspace.get` -> `getRemoteSnapshot`, `remote-workspace-relay-sync.ts:29`):

    pullSnapshot rev=3 tabs={repo:["16c4a3e1","06aba6b6"]}
    pullSnapshot rev=4 tabs={repo:["16c4a3e1","ff72768e"]}   <- ff72768e IS the resurrected tab
    pullSnapshot rev=5 tabs={repo:["16c4a3e1","ff72768e","da21b76c"]}

The client uploaded the session containing the tab; the user closed it; the kill
RPC rejected so the close never reached the host; the host's snapshot still lists
it; the client pulls it back and the merge restores it -- **correctly, by its own
rule that the host is authoritative for what it knows.** A pane then mounts,
respawns, and takes the recycled pty id.

Client-side correlation from the same run, two controls and one positive in one
run differing in exactly one variable:

| Tab | Close events observed | Resurrected? |
|---|---|---|
| `6305cc07` | `user` + `pty-exit` | No |
| `ed56f66c` | `user` + `pty-exit` | No |
| `2036e760` | `user` only | **YES** |

## The fix

`src/shared/closed-terminal-tab-tombstones.ts` (99 lines). A client-recorded
close is first-party intent and must survive until the host acknowledges it. Per
`docs/reference/ssh-execution-boundary.md` the remote verdict is `unverifiable`
-- which may not authorise declaring the process dead, but equally must not
authorise resurrecting the tab. This is SSH-v3 principle P2, "durable tombstones
with a monotonic per-scope revision", reusing the existing
`RemoteWorkspaceSnapshot.revision` rather than adding a twelfth per-tab identity
field (the codebase carries eleven, 784 refs, that SSH-v3 Phase 3 deletes).

- **Recorded** only on `closeReason === 'user'` (`terminal-tab-close.ts:69`).
- **Suppresses** a host-sourced tab only when
  `tabId in tombstones && !currentTabsById.has(tabId)` -- a live local tab always
  wins, because deleting a live pane is the one outcome the merge exists to
  avoid.
- **Retires** on positive acknowledgement:
  `!hostKnownTabIds.has(tabId) && hostRevision > observed`. Strictly newer, so a
  pull already in flight at close time cannot ack a close it predates.
- Three never-retire guards: no revision retires nothing; a worktree the snapshot
  has no row for retires nothing; the first omitting snapshot only stamps the
  watermark.
- TTL (30d) + cap (500) are **backstops** for a target the user never returns to,
  not the mechanism.
- **Client-local only** -- never crosses the wire, so there is no mixed-version
  exposure.
- Suppression is scoped to `replaceWorktreeIds`, which is what makes the
  live-tab check meaningful. A final whole-map sweep over the assembled
  `tabsByWorktree` would break that (a live tab is absent from `currentTabsById`
  outside the scope and would look suppressible); it is deliberately not there,
  and the comment at the top of the function says so.

## Evidence

The load-bearing evidence is an A/B control on one tree, not the oracle's
assertion. Flipping `isSuppressedByClose` to `false` -- one character --
reproduces the resurrection on demand:

    --repeat-each=2:
      1) drop 2 resurrected the closed tab ab0e305d-…: baseline=1 drop1=1 drop2=2
      2) drop 2 resurrected the closed tab 51533e34-…: baseline=1 drop1=1 drop2=2
      2 failed

With suppression on: **0 occurrences of "resurrected the closed tab" across five
runs plus one independent run by a second agent.** Provenance verified
positively, not by mtime: `closedTerminalTabTombstonesByTabId` appears 13x across
3 renderer chunks including `store-Do3KBvRE.js`; for every red control run
`mayCreate` appeared 0 times in `out/main/index.js`.

At the unit layer, disabling the same predicate: 3 failed | 39 passed. Restored:
42 passed; 287 across the workspace-session, terminal-store, remote-workspace,
shared-tombstone and profile suites; 24 in the four tombstone suites.

## The oracle spec: GREEN in the full lane

`ssh-lost-kill-tab-resurrection.spec.ts` passes both tests at this commit. Full
Docker-SSH lane, clean tree:

    BUILD_SHA=49bb96e0b4c   DIRTY=0
    PROVENANCE  tombstone=13  hasLocalTabsRow=2  hostAuthority=4  mayCreate=3
    14 specs / 20 tests -> 17 passed, 2 failed, 1 skipped (10.7m)

    [12/20] :178 does not resurrect tabs whose kill was lost to a killed relay
            daemon                                                      PASSED
    [13/20] :190 does not resurrect tabs closed while the host is
            disconnected                                                PASSED

    grep -c "resurrected the closed tab"  (whole lane)  -> 0

It passes WITHOUT PR 7 in the build (`mayCreate` present,
`SshPtyAbsentFromRelayError` absent), so the bug-2 fix below is not required for
it.

Test 1 fails intermittently in ISOLATED single-spec runs, where a third defect
blocks its cycle-2 setup. The resurrection assertion itself has never failed with
this fix in place -- the intermittent failure is always a setup failure, never a
resurrected tab. A reviewer running the spec alone may see it red; that is not
this fix regressing.

Three defects sit under STA-3374 and should not be conflated:

- Bug 1 -- the closed tab resurrects. Fixed here.
- Bug 2 -- `ssh-pty-session-reattach.ts:227-231` rewrites the relay's
  `PTY "pty-1" not found` into a bare `SSH_SESSION_EXPIRED`, so
  `isPtyAlreadyGoneError`'s `/PTY ".+" not found/` cannot match and
  `attachStablePaneOwner:242`'s already-correct fallback never runs. Owned by
  PR 7 (`nwparker/ssh-07-absent-from-relay`). Not required for the oracle above.
- Bug 3 -- after the daemon is killed and the client launches a replacement, the
  client's OWN SSH transport drops and does not reconnect within 60s: no
  "delay step 2/9", no handshake failure, nothing. `ssh-connection.ts:1533` only
  logs on an SSH-level close. Unfixed, its own ticket. This is what makes test 1
  intermittent in isolation.

Discriminator for bug 3, measured in the isolated runs (the lane above ran
without `ORCA_E2E_FORWARD_APP_LOGS=1`, so it was not re-confirmed there):
`[ssh-relay] Socket probe result:` reads "DEAD" on every cycle of test 1 (daemon
killed, a NEW relay must be launched) and "ALIVE" on every cycle of test 2
(daemon survived). Whenever a new daemon must be launched, the SSH transport
drops afterwards and does not recover.

An earlier reading blamed `kill.ts:82-84` for skipping `finishPtyShutdown` on a
non-already-gone error. That was eliminated by direct test: the implied fix,
`markSshRemotePtyLease(…, 'expired')` in that branch, was implemented, changed
nothing, and was reverted rather than shipped unproven. Recorded so the path is
not re-walked. The `SSH_SESSION_EXPIRED` rejection is real but fires during cycle
1 for the baseline pane, after which cycle 1 completes; the 60s silence begins
only after `Relay channel lost ..., triggering reconnect`.

The spec is claimed by the Docker-SSH lane, and that lane does not gate merges
today.

## Persistence: the tombstone must survive a relaunch

`closedTerminalTabTombstonesByTabId` is declared on `WorkspaceSessionState` but was missing from
`workspaceSessionStateSchema` (`src/shared/workspace-session-schema.ts`), which is the load boundary
for BOTH partitions -- `normalize-loaded-state-collections.ts` for `local` and
`workspace-session-partitions.ts` for `ssh:<target>`. Zod strips unknown keys and the write side does
not validate, so the map reached disk and was discarded on the next launch. Measured with the repo's
own parser:

    input : closedTerminalTabTombstonesByTabId: { 'tab-1': {...} }
    ok    = true
    tombstones after parse = undefined

That made the fix ineffective in the exact reported scenario: close an SSH tab with the transport
down, QUIT, relaunch, reconnect -- the merge runs with an empty map, the host still lists the tab,
and it resurrects. "Every day I open orca and it opens more tabs" is a claim about restarts.

Neither the green oracle nor the A/B control could see it: both run entirely inside one app process.
It also made the 30-day TTL and the 500 cap unreachable.

Fixed by adding the field with a `salvagingRecord` matching its sibling
`terminalSurfaceTombstonesByPaneKey`, so one malformed entry drops that entry rather than the map.

`workspace-session-schema.ts` was one line under its 300-line max-lines limit, so adding the field
required room rather than a suppression (the project forbids max-lines disables and per-file bumps).
Two value schemas were extracted to modules named after what they contain:
`terminal-tab-id-schema.ts` and `terminal-surface-tombstone-schema.ts`. The closed-tab tombstone's
own schema is colocated with its type in `closed-terminal-tab-tombstones.ts`, which is where it
belongs -- omitting it from the session schema is exactly the drift that caused this bug.

`workspace-session-schema-field-coverage.test.ts` is the ratchet. Two sibling tables already pin
themselves with `satisfies Record<keyof WorkspaceSessionState, ...>`; this schema had no such guard
and is the one that fell behind. The new file adds both halves -- a `satisfies` list that makes a
forgotten field a compile error, and a runtime assertion that names it -- plus a
`parseWorkspaceSession` round-trip. Without the schema entry: 3 failed. With it: 3 passed.

## A host tab the user never closed could be deleted

`tabId in closedTerminalTabTombstonesByTabId` answers true for every `Object.prototype` key even on
an EMPTY map, because the map is a plain object from `Object.fromEntries`. A host tab whose id is
`toString` was filtered from the reconciled list, blocked from the host-unknown branch, and stripped
of its layout and session id. Tab ids are validated only as non-empty and colon-free, and `createTab`
honours caller-supplied id hints, so the id is reachable rather than theoretical. This was the only
path in either direction that could delete a tab the user never closed.

Now `Object.hasOwn`, as the same file already uses elsewhere.

Suppression is also scoped structurally: `isSuppressedByClose` compares the tombstone's stored
`worktreeId`, which it already carried, so it cannot reach another workspace's tab. The two sweeps
that have no worktree in scope (`terminalLayoutsByTabId`, `remoteSessionIdsByTabId`) now consult the
set of ids this merge actually suppressed rather than re-deriving a verdict without that scope.

The scope comment at the top of the function was also wrong and is corrected. It claimed every use of
suppression sits inside `replaceWorktreeIds`; it does not -- the tabs pass walks all of
`orderedWorktreeIds` and the two sweeps cover the whole remote maps. What actually makes it safe is
that `closeTab` strips the id from every worktree row before recording the tombstone, plus the
worktree match above, plus `closeReason === 'user'` being the only writer. Real guarantee, different
from the documented one.

## Divergences from open PR #16571

#16571 implements the same concept. Three deliberate changes:

1. It never retires on acknowledgement -- TTL+cap only, so it never converges.
   Ack retirement added.
2. It crosses the wire and lets a HOST-sourced tombstone delete a LOCAL tab in a
   final whole-map sweep. After #14361 that is the wrong risk; dropped. This also
   removes the mixed-version regression its own body flags.
3. Its hydration unions rather than replaces the map -- a union resurrects every
   tombstone the merge just retired, so it never converges.

Its `activeTabId` nulling is also dropped as redundant:
`workspace-terminal-hydration.ts:99-105,126-138` already revalidates both
pointers against the tab rows it just built, and nulling twice would add a second
rule that has to stay in step with the first.

## Can a tab the user did NOT close disappear?

No, but the guarantee needs stating precisely. The only writer is
`recordClosedTerminalTabTombstone` (`terminal-tab-close.ts:69`), reachable only
on `closeReason === 'user'`; suppression additionally requires the tab not be live
locally. Reopen (`recently-closed-tabs.ts:122-166`) calls `createTab` and restores
cwd/shell/title/color/position, never the old id.

**Caveat, stated because the slogan is not literally true:** `createTab` honours a
caller-supplied id hint (`terminal-tab-creation.ts:53-65`, used by `useIpcEvents`
for host-admitted tabs), so "tab ids are uuids that never recur" does not hold in
this codebase. The guarantee rests on the `closeReason === 'user'` writer plus the
live-local-tab check, not on id uniqueness.

## Risk

Renderer-side, client-local, no wire change. The blast radius is
`mergeDirectSshRemoteWorkspaceSession` and the persisted session field. Worst case
if the ack logic were wrong in the retiring direction: a tombstone outlives its
usefulness and suppresses a host tab whose id the host re-issues -- bounded by the
live-local-tab check, the 30d TTL and the 500 cap. Worst case in the other
direction is today's behaviour. `profile-project-session-field-disposition.ts`
records the new field as `notRepoScoped` / `notTransferred` residue, bounded by
the same TTL and cap.

## Verify

    pnpm test src/shared/closed-terminal-tab-tombstones.test.ts \
      src/renderer/src/lib/workspace-session-closed-tab-tombstones.test.ts \
      src/renderer/src/store/terminals/terminal-tab-close-tombstone.test.ts \
      src/renderer/src/hooks/remote-workspace-session-merge-close-tombstones.test.ts

To reproduce the bug this fixes, set `isSuppressedByClose` to `() => false` in
`remote-workspace-session-merge.ts` and run
`pnpm test:e2e:ssh-docker -- tests/e2e/ssh-lost-kill-tab-resurrection.spec.ts --repeat-each=2`.
2026-08-27 19:47:15 -07:00