Repository navigation
Conversation
… they spawn Co-Authored-By: Claude <noreply@anthropic.com>
…roup writer for the switch Co-Authored-By: Claude <noreply@anthropic.com>
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info
📝 Walkthrough
Priority: ➖ Normal Merge Risk: ⚪ Minimal · up to Runtime-created terminals now join the requested tab group, or the first group when none is given. Tests cover persistence across restart. No merge-blocking risk was found in the supplied changes. Pre-merge checks |
|
A phone, CLI or orchestration create on a headless host with two tab groups was written into the first group, so after a restart the tab moved groups. The phone's targetGroupId, else the host's focused group, now reaches the pane admission. Co-Authored-By: Claude <noreply@anthropic.com>
Defaulting to the host's focused group moved a CLI-created tab to the right group after a restart and in the desktop window, while main and the live view keep it in the first group. Only the phone's explicit targetGroupId now picks the group. Co-Authored-By: Claude <noreply@anthropic.com>
ELI5
When something other than the desktop window opens a terminal (the
orcaCLI, an orchestration worker, a phone with no desktop window open, or worktree setup), Orca now saves the new tab in the tab bar and its tab group before the terminal process starts. It used to save only a bare terminal record after the process started, and never the tab-bar entry or group, so on a headlessorca servethe saved tab bar was simply missing.What Changed
Before: a runtime-started terminal spawned first. When it reported back, the binding step "minted" a minimal terminal row (or grafted a new pane as a vertical split) into the saved session. It never wrote a tab-bar entry or group. Headless servers therefore saved terminal rows with no tab bar, and later headless closes and group moves saved partial or mismatched tab bars.
After: one runtime function,
spawnInAdmittedPane(src/main/runtime/runtime-pane-admission.ts), handles every runtime-originated create and split:createTerminalTab/splitPane→ Serializer (terminal-pane-admission.ts, committed by the terminal-topology commit module).mayCreate: false, so it never mints a tab or grafts a pane. If the pane was closed or moved while the terminal started, the bind is refused and that process is discarded.Pane state in this PR is
startingonly.failedandwaiting_for_hostarrive withlayout.startPaneand published pane status in the switch (PR 6), where a view can draw them. Old RPC methods keep their contracts:terminal.createstill replies with theptyIdonce the process runs.Council item J (start-attempt identity) is not in this PR. No flow in PR 4 starts the same pane twice; close and move while starting are covered by the refused bind and tested. J lands with
layout.startPanein PR 6.Tab group. A tab the phone opens is written into the group the phone asked for (
targetGroupId). Before, the write put it in the first group, so on a headless server with two groups the phone's tab moved to the first group after a server restart. A tab opened with no group named (CLI, orchestration) still goes to the first group, as on main and in what clients see live. A split stays in its parent's group.The headless group writer now also keeps the tab bar consistent: each entry's group comes from the group that lists it, and a reorder writes the group order. It is marked as deleted by the switch.
Deleted:
hostAdmittedMembership,persistHeadlessTerminalSplit,persistHeadlessTerminalTabOrder, the phone create's after-spawn view-mode write, and main's threependingActivationSpawnstamps (renderer hydration always re-sets that flag, so the saved one was never read). The mint and graft inapplyPtyBindingstay: window-originated spawns still rely on them until PR 6. The Loader keeps itsrow.pendingActivationSpawnrule, because profiles saved by older builds still carry the flag.Why
This is design row 4, and the first rework PR that changes behaviour. Writing the pane first through the same model and Serializer the runtime will own after the switch removes the "minimal tab mint" class of half-saved tabs, without a second write path. The alternative, teaching the binding step to also invent tab-bar entries and groups, would add another hand-written copy of tab order. The rework exists to remove those copies.
Linked Issue
Workspace tab layout rework, PR 4 (design row 4).
Visual Proof
N/A. A new tab from the CLI, a phone or setup looks exactly as before. The change is in what is saved, and the layout oracle measures that.
Testing
Layout oracle in record mode, local macOS; the SSH lane ran against a Docker host. Specs were run on the base commit and on this branch.
workspace-layout-oracle-headless(orcad and Electron; 16 runs on base, 18 on the branch with the new group scenario)workspace-layout-oracle(window, 20)workspace-layout-oracle-ssh(2)Known-on-main entries:
tab_bar_missingtab_lists_disagree(close saved a partial tab bar)group_lists_missing_tabfor split and move, on orcad and Electrontab_order_disagrees. A headless reorder saves rows through the session merge door, which keeps the stored row order. This already happened before this PR, and the order a client sees after the reorder and across a restart is the same; the oracle's group-order and restart checks pass on both. It was hidden because the rules stopped at "no tab bar". It is fixed in PR 6 together withclient-reorder-tabs.Unit tests:
New and rewritten tests: runtime-admitted tabs survive a stale window save; they are written with their tab-bar entry and group; a pane closed, or moved to another tab, while its terminal starts is not bound; a projected-only split source is refused before spawning; a failed split takes its pane back.
A phone create into the second group stays there, and a CLI create stays in the first group, across a cold restart and in the desktop window (unit test, a new headless oracle scenario on orcad and Electron, and a before/after run against the real app).
The seeded topology model test passes at 200 seeds, with the new write racing stale window saves.
pnpm tc,pnpm lintandcheck:code-quality:changedpass.The full
src/mainandsrc/sharedrun fails only the live-shell tests (zsh, fish, [Bug]: Anexecin user rc files skips the wrapper'sZDOTDIRrestore — shell-ready marker never emitted, ~15s added to every spawn #13767), which fail the same way on the base commit.I manually tested these changes locally
Automated tests added/updated
Review
Agent skill upstream boundary
Notes
ssh:<target>partition, the same partition its binding lands in.Checklist
N/Awith reasonpnpm lint,pnpm typecheck,pnpm test, andpnpm buildpass (or CI will cover; local preferred)