Repository navigation
Conversation
…eport-only The window now says where each fresh terminal goes: the first pane of a tab sends new-tab with the tab's creation fields, a split sends the parent leaf, direction, ratio and the tree after the split, background launches send the same, and a Codex restart that rebuilds a rootless layout sends root. A reattach sends nothing. Main still only records whether placement names the tab its binding write picks; its 'absent' value now counts only new leaves without placement, so it reads the fallback mint and graft directly. Extends the shared placement type with optional row, ratio and proposedRoot, each dropped alone if malformed, so older and newer peers keep working.
…he tab lacks The adopted tab is created without launchAgent, so a row naming one would diverge from the renderer's tab once main applies placement.
…order wrapInSplit now places the new pane first itself, so the pane-created handler's proposedRoot sees the real post-split tree instead of the order before the subtree split moved it.
Core step C1 of 7. When a new terminal's binding names a tab main does not have yet, main builds the row from the creation fields the window sent; when it names a split whose parent is in the tab, main takes the window's tree (holding exactly today's leaves plus the new one) or splits at the parent with the given direction and ratio. Spawns without placement, or whose placement doesn't fit the tab's current shape, keep today's mint and root graft. The headless split reuses the same split-at-leaf op, and the agreement span now reports exactly when placement was applied.
…to topo-c1-placement-applies
…ow (no reader yet) Core step C2: the runtime owns a TerminalTopologyPublisher fed by the store's session-write hook. Each worktree is projected from its one home partition (local or ssh:, never runtime:); an owner that can't be resolved, like an ambiguous folder, keeps its last slice instead of going empty. Slices go to the window on session:terminal-topology-changed, the window can pull them with session:get-terminal-topology-slices, and close-surface and leaf-move replies name the publishSeq that carries their write.
The spawn reply now carries the publishSeq of the push holding its binding, like close-surface and leaf-move. No reader yet.
… into topo-c3-mirror-apply
Core step C3 (plan B2-4). After hydration the window subscribes to main's topology push, pulls every slice, and applies them before reconnect. The apply replaces only main-owned topology; presentation, pendingActivationSpawn, the live ptyId and ptyIdsByTabId stay. Unchanged rows and layouts keep their identity and an apply never echoes back as a session save. Flips the STA-9417 gate tests.
…'s row Main stamped the spawn's cwd on every tab it built from placement. Once main's copy is authoritative that would add startupCwd to rows the window never gave one.
…topo-c4-reconciler
…or every tab The pane reconciler ran only for web clients and remote-server tabs. It now runs for every tab, so a desktop window follows main's layout: - a leaf main dropped is detached, never killed, so a PTY main moved to another tab keeps one output owner; a pane still starting its PTY is kept until main binds it; - removals apply before insertions, and an inserted leaf with no PTY spawns through the normal mount; - divider ratios and orientation apply in place, so web and remote clients now follow desktop pane sizes; a divider mid-drag is left alone; - the write-back after insertions is gone.
…main publishes it A divider drag, divider reset, equalize or pane reorder now sends the tab's tree to main (`session:terminal-set-layout` -> commit-module `setLayout`). Main takes it only when it names exactly the tab's current panes, in every owner partition holding the tab, and refuses any other tree. The reply carries the publishSeq of the push holding the commit. Until the window has applied that push, a push with the same panes keeps the gesture's tree, so an older push no longer snaps a divider back between drag end and main's commit. A push whose tab has other panes wins at once. The layout save no longer merges live transport PTY ids into the layout's bindings; main's push owns them.
…topology Core step C5a (plan B2-6a). A tab or pane this window creates or closes is pending until main's topology catches up, so the mirror and the pane reconciler show it ahead of main without letting an older push undo it: - createTab marks the tab pending; a pane the window makes that main's layout doesn't name yet is pending. An add ends when a slice names it. - Every tab or pane close goes through commitTerminalSurfaceClose, which hides it until the mirror holds main's reply publishSeq; a refused close shows main's copy again. - The reconciler keeps pending panes and never re-inserts a closed one; this replaces the "spawn still pending" check and trackRetiredLeafIds. - Drag-out to a new tab waits for main's move and its push, then only places, activates and moves the live attachment. It no longer creates the tab or writes layouts itself, so a push that beats the reply no longer closes the new tab. A pane main doesn't hold keeps the local move. - replaceTerminalLayoutPanePtyId is deleted: leaf bindings come from main.
…o-c6-set-layout # Conflicts: # src/renderer/src/store/slices/terminals.ts # src/renderer/src/store/terminals/terminal-actions.ts # src/renderer/src/store/terminals/terminal-state.ts # src/renderer/src/store/terminals/terminal-topology-mirror.ts
…tion setLayout wrote every local/ssh partition holding the tab. A worktree has one home (the partition main's push projects), so main now resolves it with the same owner rule and writes only there; an unresolved home writes nothing and replies home_unresolved.
…ing tabs and panes One record of what the window keeps over main's pushes until main publishes it: a gesture's tree is a 'layout' entry in pendingTerminalPanes, settled like a close (by the reply's publishSeq) or at once when main's tab has other panes. Closes and gestures share one commit helper; the separate gesture map and its file are gone.
…ed multi-write helper
moveLeaf rewrote every local/ssh partition holding the source tab. A worktree has one home (the partition main's push projects), so the move now resolves it with the same rule setLayout uses and writes only there; an unresolved home writes nothing and replies home_unresolved. A stray copy in another partition is left as it is (D-core-3).
The window sends main its own sleeping-record changes (sleep, wake) through two batched IPCs; main keeps each record beside its tab in every owner partition that holds it, and its next topology push carries the result. The quit capture rides the synchronous before-unload stage, committed before the sessions are staged. Mirror applies, tab closes and pane moves send nothing: main already holds those. Co-Authored-By: Claude <noreply@anthropic.com>
… holds which PTY updateTabPtyId and the pane-level binding writers no longer write a local or SSH pane's layout binding (on spawn, handle rotation, exit or close): main records it on spawn and the mirror brings it. Their liveness half is unchanged (ptyIdsByTabId, tab.ptyId as the live attachment, the direct-SSH ledger, pendingActivationSpawn, Codex notice migration, activity bumps). A remote runtime's pane bindings stay window-written, for the push to its host. After an exit main keeps the binding (R17), so a remount does one stale reattach, then spawns fresh. A closed pane's binding is main's stale copy until main's push drops the leaf, so a late reattach reply to a closed pane is still retired.
…terminal Re-attaching an agent's live terminal that no pane claims into its pane wrote the pane's binding only in the window, which main's push overwrites, so the pane in a split started a new shell. The window now asks main to record it (session:terminal-bind-leaf -> bindLeaf, a reattach bind that never creates the pane, in the worktree's home partition); main's push carries it to the pane.
A drag made before main recorded a just-created split sent a tree naming a pane main didn't have yet; main refused it (leaves_differ) and the drag was lost, and an unrelated push could drop it sooner. The drag's pending layout now ignores panes this window added and main hasn't named, and its commit waits until main names them, then sends once.
…es; imports go through main The window no longer uploads to SSH relays. Main exports a host's workspace after any session write whose share for that host differs from what the two last agreed on, and reports each export back to the window as a push-status event. The window's snapshot import now commits its merge through main in one call (remoteWorkspace:importPeerTopology) with the pull's revision, host observation and outcome; that sets the agreement, so an import and the window's save-back of it never export, and a conflicted pull keeps the host out of exports until a whole pull agrees again. The first-connect seed of an empty host is main's export too. Co-Authored-By: Claude <noreply@anthropic.com>
The same owner rule main's push projects, exposed for writes that land in one partition. Identical to C6's hunk so the branches merge cleanly.
…ore keeps Covers inventory rows A5, A14, A19, A20, A44, A45, B16, B17, B26, B36.
Covers inventory rows A25, A40, A41, A50, A55, A56.
…tore main's slice on a refused change, keep a stale client's focus out, apply old close records on load - main reconcile and the window's apply batch read sleeping-agent records once, not once per worktree - a refused or rolled-back topology commit re-applies main's slice for the worktree at once and toasts - a headless client's stale pane tree leaves main's whole layout, as on main - close records an older build left are applied and cleared at load, as main's first write did
Covers inventory rows P6, P7, P8, P10, P11, P12, R8, C-extra3.
…, floating and web tabs Covers inventory rows B56, B57, B62, B79, B86, K2.
… does Load keeps them, as on main; the first write applies and clears them, so main's identity-migration assertion holds again unchanged.
…nes, as its rebase did
…ocal to its own partition before a window save drops them
…r start one, as on main
…ogy in a degraded boot, and report a failed SSH import Co-Authored-By: Claude <noreply@anthropic.com>
…save # Conflicts: # src/renderer/src/runtime/web-session-split-pane-retirement-fences.test.ts
…tForHost Co-Authored-By: Claude <noreply@anthropic.com>
Contributor
Author
|
Closing: the layout rework is being redesigned so the runtime holds the only copy of the layout and every window/client is a view. This branch is kept for reference. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
ELI5
Orca keeps your terminal tabs and split panes in two places: the main process (the part that starts terminals) and each window or client that shows them. Until now several of them could write the saved layout, so a window holding an older picture could overwrite the real one, and a pile of repair code tried to patch the result back. This change makes main the only writer of which tabs and panes exist. Windows, phones, the CLI and other Orca servers ask main for a change and then show what main publishes; a window still saves how things look (titles, colours, order, focus, scrollback).
This PR is the merge entry point for the whole terminal-layout core (C1–C7). Retargeted to
main, its diff is the entire core. It should land as one squash commit; the step PRs below are review aids and individual steps are not expected to pass every test alone.Steps: C1 #26151 placement applies · C2 #26279 publisher connected · C3 #26293 window mirror applies · C4 #26301 reconciler · C5a #26307 pending panes · C6 #26304 set-layout requests · C5b #26318 binding split · C5c #26317 sleep/wake · C5d #26320 SSH import/export · C7 (this) presentation-only window saves.
What Changed
What you see: terminals Orca creates itself, such as the split a setup script opens on a new worktree, no longer duplicate or vanish the first time you open that worktree (STA-9417), and saved layouts stop drifting from what is on screen after quits, restarts, sleep/resume and reconnects.
Mechanism:
Why
With one writer there is nothing for a rebase or replay to fix, so deleting them is safer than tuning them. One save entry point covers every path (including quit, which bypassed the old per-caller guards) by construction.
Known differences from main
runtime:workspaces): main no longer replays remembered terminal bindings; the server re-sends them on reconnect.mainit resumed as an extra tab on the next activation.mainsaved them in the local bucket. Orca's shared host lookup requires such folders to fail closed, the sidebar already won't open one, and saving them locally could split one workspace across two buckets.main's save merge dropped that pane, leaving its process running with no pane.Linked Issue
STA-9417 (internal).
Visual Proof
N/A — the layout parity tool captures the rendered layout and saved session for every scenario against
main; the only differences are the declared STA-9417 ones.Testing
Run on the C7 head with
main(4f4f021) merged into every step, macOS, background launch:pnpm tcon every step;pnpm run check:code-quality:changedon C7: pass.mainon this machine (browser-manager, a daemon repro and fish-shell tests; a relay test that needspg).main, all 11 scenarios: PASS, with only the declared STA-9417 differences. Sleep → quit → resume and setup split, run repeatedly: no pane swap or extra tab. After sleep → quit → resume, the split tab's saved tab-level terminal is its first pane's in 15 of 15 runs;mainsaves the second pane's in 5 of 15, and the window's own pane order varies the same way on both, so those paths are recorded as unstable onmain.Notes
Agent skill upstream boundary
Checklist
N/Awith reasonpnpm lint,pnpm typecheck,pnpm test, andpnpm buildpass (or CI will cover; local preferred)