* fix(mobile): wall hosts that cannot serve the page instead of opening native screens
* refactor(mobile): drop the native-route state and the shell fallback prop; unknown routes say unavailable
* test(mobile): read the nullable page route refusal in its policy test
* refactor(mobile): every build is the OTA shell; drop the native/OTA build switch
The owner retired the native-screen fallback with no emergency native build, so
EXPO_PUBLIC_MOBILE_SHELL, mobileShellBuildKind, shellSwitchDecision, the Metro
cache-key suffix and both release workflows' shell input go. Every host route
mounts the shell; a route naming no screen the shell could open gets the
existing refusal screen instead of a native screen.
Claude-Session: https://claude.ai/code/session_6cea92f6
* refactor(mobile): delete native screens the OTA shell no longer reaches
With every build the OTA shell, these native siblings run nowhere: the
native-executed closure (index.js plus every non-web route) does not reach
them, and the page resolves their .web siblings. Each .web sibling takes the
plain name, its web-overrides entry goes, and the native-only rich Markdown
editor and Mermaid WebView generators and their outputs go with them.
Kept: native siblings the node test suite still renders shared screens
through (terminal WebView, browser frame path, clipboard, dictation, media
picker, live input, snapshot budget, input styles).
Claude-Session: https://claude.ai/code/session_6cea92f6
* test(mobile): drop the phone-preview comparison the deleted native sibling held
Claude-Session: https://claude.ai/code/session_6cea92f6
* refactor(mobile): drop the dead web route, flag-era comments and the one-value shell kind
The host route files no longer switch, so their comments say what they do now:
a route naming no screen shows the unavailable screen. app/h/[hostId]/web.tsx
duplicated index.tsx and nothing linked to it, so it and its stack entry go
(the bundle sweep is re-measured: 77 scripts at 15 routes). Diagnostics print
the bridge protocol on its own instead of a shell kind that has one value.
Claude-Session: https://claude.ai/code/session_6cea92f6
* refactor(mobile): one host for the rich editor, plain Text, and drop two unused native deps
MobileSelectableText was react-native's Text, so callers use Text. The rich
Markdown editor's host-facing seams (messages, URL prompt, keyboard inset) are
required now that the page is its only host; the WebView defaults go. Comments
that described deleted native components are rewritten, and
expo-image-manipulator and react-native-uitextview leave package.json and the
lockfile.
Claude-Session: https://claude.ai/code/session_6cea92f6
* fix(mobile): wall hosts that cannot serve the page instead of opening native screens
* refactor(mobile): drop the native-route state and the shell fallback prop; unknown routes say unavailable
* test(mobile): read the nullable page route refusal in its policy test
* refactor(mobile): every build is the OTA shell; drop the native/OTA build switch
The owner retired the native-screen fallback with no emergency native build, so
EXPO_PUBLIC_MOBILE_SHELL, mobileShellBuildKind, shellSwitchDecision, the Metro
cache-key suffix and both release workflows' shell input go. Every host route
mounts the shell; a route naming no screen the shell could open gets the
existing refusal screen instead of a native screen.
Claude-Session: https://claude.ai/code/session_6cea92f6
* refactor(mobile): drop the dead web route, flag-era comments and the one-value shell kind
The host route files no longer switch, so their comments say what they do now:
a route naming no screen shows the unavailable screen. app/h/[hostId]/web.tsx
duplicated index.tsx and nothing linked to it, so it and its stack entry go
(the bundle sweep is re-measured: 77 scripts at 15 routes). Diagnostics print
the bridge protocol on its own instead of a shell kind that has one value.
Claude-Session: https://claude.ai/code/session_6cea92f6
* fix(mobile): wall hosts that cannot serve the page instead of opening native screens
* refactor(mobile): drop the native-route state and the shell fallback prop; unknown routes say unavailable
* test(mobile): read the nullable page route refusal in its policy test
* chore(ci): ratchet renderer importers of the deprecated runtime-owner resolvers
getRuntimeEnvironmentIdForWorktree and resolveRuntimeOwnerForWorktree read a
worktree two hosts publish as not-a-runtime, which callers treat as local. The
62 renderer files importing them are grandfathered; the list may only shrink.
* ci: register the deprecated runtime-owner ratchet step in the preflight phase map
* fix(ssh): keep existing hosts' orca CLI control switch off after the on-by-default flip
Builds before the flip saved an explicit off as an absent value, so absent=on
silently turned on hosts the user had turned off. On the first load after this
upgrade, pin every saved host's absent switch to an explicit false and record a
one-shot marker in settings so hosts added later keep the new default.
* test(ssh): check the saved profile without a type assertion
* fix(ssh): say the CLI control switch is on by default for new hosts at the call site too
* feat(orcad): ship the mobile web page so phones paired to orcad get it
orcad never carried out/mobile-web, so it did not advertise
mobileWeb.bundle.v1 and phones paired directly to an orcad server fell
back to the app's built-in native screens.
The built page now ships in every orcad package at out/mobile-web, the
path getBundledMobileWebBundleRoot already probes under the install
root. Its manifest is a fixed artifact; its content-addressed assets
join the hashed artifact list from that manifest, as one shared copy in
the template's common files. build-orcad fails when the page is missing
or stale, and CI lanes that build orcad now build the page first.
* ci(orcad): check out mobile/ for the Windows host template build
* refactor(orcad): copy the page tree whole and build it once per CI run
The guard already refuses unlisted files, so staging copies the
bundle directory as native-chat-visuals is copied. The shipped template
takes the page the qualification lanes tested, the e2e build artifact
carries it, and the PR smoke builds it through install-mobile-dependencies
behind its own scope step. Drops tests that duplicated the guard's own.
* fix(orcad): refuse a template page asset name that escapes the slot
* feat(mobile): Resume in New CLI from a phone chat tab
A chat tab on the phone had only Close in its long-press menu, so a chat
that would not load left no way to reach its conversation. The phone now
offers Resume in New CLI, like the desktop tab menu: the chat's host
prepares the conversation and the phone opens a copy in a new terminal tab.
The resume and fork startup builders move to src/shared so the desktop and
the phone build the same command.
* test(mobile): type the fake terminal create instead of casting its calls
* fix(mobile): pass the host's fork source as is; match the quoted args in the test
* test(mobile): give the fake host client the untyped mock the other client fakes use
* style: format the shared resume builder and phone fork files
* Fix mobile chat CLI fork creation on owning host
* Fix mobile chat fork lint and split create owner helpers
* Keep chat fork beside phone anchor across tab groups
* Combine mobile terminal runtime type imports
* Repair mobile create CI contracts after chat fork extraction
* Classify chat fork preflight refusals and preserve mobile create recordings
* Keep route-owned create cleanup quiet after unmount
* Preserve typed runtime repair refusal in chat fork preflight
* Preserve checked store through mobile chat fork preflight
* Register mobile chat fork runtime fixture before test setup
* Remove unused mobile create recording value
* style(mobile): format the chat fork create files; move fork reply messages into the fork feedback module
Formatting pushed the create hook past its line limit, so the fork-only reply message, partial-success warning and host capability check now live beside the other fork messages. Same order and copy as before.
* Keep quick command mutation aligned with formatted request
* Keep mobile chat CLI forks in terminal view
* Pass mobile chat resolution its existing controller arguments
* Match the mobile create fixture mock layout
* Keep mobile chat CLI forks open when placement is unavailable
* Brace placement contract fixture checks
* test(mobile): keep the prompt controller mock under its line limit after the main merge
* Expose pane admission in the runtime test fixture type
* Complete the chat fork contract controller fixture
* Persist session parse results beyond the memory cache limit
* Expire Session History parse rows by idle time instead of a count cap
The parse cache dropped its oldest row once it held 4,096, so a scan over
more transcripts evicted its own upcoming hits and re-parsed every file on
every scan. Rows now stay until no reader has used them for 24 hours, swept
after each completed History scan and each search-index pass. Only the live
parser state keeps a 4,096 count bound; dropping it costs one whole re-read
of a file that changed, never a re-parse of one that did not.
Removes the overflow directory and the orcad persistence wiring from the
earlier revision of this change.
* Expire only rows History lists, and keep the newest transcripts under bounds
The search pass ran the idle sweep every 20 s but never re-stores History's
unchanged rows, so with search on a day away from History dropped them all
and reopening it re-parsed everything. Only a completed History scan sweeps
now, and it expires only rows History listed (on a monotonic clock). Rows
only the search index or a title lookup created keep main's 4,096 count
bound, so an index build can't grow the scanner process without limit.
Scans store newest transcripts first, so every store-order bound dropped the
newest, most active files: the saved snapshot kept the oldest ~11k rows and
the resume bound stripped the newest files' parser state. The snapshot is now
ordered by transcript mtime, and both count bounds drop the tenth of their
rows with the oldest transcripts.
* Drop the 24 h idle sweep; History rows live for the process
The sweep let whichever listing ran last decide when a row died, and
listings differ in scope: a smaller one (the phone's Agent History, a lower
desktop limit) expired rows only a larger one used, so the next Unlimited
scan re-parsed them. Rows History lists now live as long as the scanner
process; the snapshot's newest-transcripts cap bounds what survives a
reload, and rows other readers made keep the 4,096 bound.
Also clears the resume bookkeeping for a row the unlisted bound drops in the
same store.
* Skip resume bookkeeping for a row the unlisted bound just dropped
Running the resume trim first let a row the unlisted trim then deleted push
the resume set over its bound and cost 409 live resume points.
* perf(ai-vault): store the Session History parse cache one row per transcript
The parse cache was persisted as one JSON file, rewritten whole after any
change and capped at 64 MB / 1M tokens (about 11k rows). Past that cap, a
restart re-read the oldest unchanged transcripts, and an agent writing while
History was open rewrote 10-27 MB every few seconds.
The scanner child now keeps the rows in their own SQLite file. A miss in the
in-memory map reads the row by path; changes are written per row, debounced
and chunked; deletes run immediately. A used_at column, refreshed at most
daily, expires rows nothing has used for 30 days, so rows for transcripts
deleted outside Orca have a way to die. The old JSON file is imported once
and removed. Electron main only records the path for the child.
The rebuild-on-corruption open moves out of the search index into a shared
versioned cache database helper used by both.
* fix(ai-vault): import the old parse cache at open and expire rows only for missing transcripts
Review round 1 on the per-row parse cache store:
- The old JSON was imported only by a History scan or search pass, and only
into an empty database, but deleted either way. Tab-title sync writes a row
first, so the import was skipped and the first History open after updating
re-read everything. The import now runs when the database opens, before any
lookup, write or delete can reach it, keeping rows already present.
- A 30-day last-use expiry also dropped rows for transcripts that still exist,
so opening History less than monthly re-read them. Rows unparsed for 30 days
are now checked on disk and dropped only when the transcript is gone, then
the freed pages are reclaimed. The daily last-use rewrite goes away.
- 8 KB pages (a ~2.3 KB row filled a 4 KB page), busy_timeout before the WAL
switch, and lookups served from rows still waiting to be written.
- Revert accidental pnpm-lock.yaml churn.
* chore: revert accidental pnpm-lock.yaml churn
* fix(ai-vault): never stat WSL transcripts from the parse cache recheck
A stat on a \\wsl.localhost path outside the scanner's running-distro
filter and gated WSL lane could boot a stopped distro or hang on a stuck
one. WSL rows now skip the existence check and age out after 180 days
unparsed. SQLite-backed session keys use the shared OpenCode path splitter.
* fix(ai-vault): register the parse cache database with the WSL mover census
Each schema keeps its own meta table again, so the shared open helper
creates none and the search schema's SQL is unchanged. The parse cache's
tables are recorded as not moved: a cache rebuilt from transcripts.
* fix(ai-vault): a forced whole read never reuses a row a failed database delete left behind
* fix(ssh): allow a host's orca CLI to control Orca by default, and stop resetting saved choices
An absent allowRemoteCliControl now means on; only an explicit false turns the
desktop browser bridge and relay-era orchestration commands off. Removes the
one-time reset of saved true values, the consent stamp writes and the reset
notice, which would have silently turned off hosts upgraded from stable.
* fix(ssh): keep the CLI control choice when adding a host from the sidebar
* fix(ssh): read an older client's omitted CLI control switch as off over RPC
* fix(ssh): check the managed fence before routing settings in browser bridge candidates
* Persist session parse results beyond the memory cache limit
* Expire Session History parse rows by idle time instead of a count cap
The parse cache dropped its oldest row once it held 4,096, so a scan over
more transcripts evicted its own upcoming hits and re-parsed every file on
every scan. Rows now stay until no reader has used them for 24 hours, swept
after each completed History scan and each search-index pass. Only the live
parser state keeps a 4,096 count bound; dropping it costs one whole re-read
of a file that changed, never a re-parse of one that did not.
Removes the overflow directory and the orcad persistence wiring from the
earlier revision of this change.
* Expire only rows History lists, and keep the newest transcripts under bounds
The search pass ran the idle sweep every 20 s but never re-stores History's
unchanged rows, so with search on a day away from History dropped them all
and reopening it re-parsed everything. Only a completed History scan sweeps
now, and it expires only rows History listed (on a monotonic clock). Rows
only the search index or a title lookup created keep main's 4,096 count
bound, so an index build can't grow the scanner process without limit.
Scans store newest transcripts first, so every store-order bound dropped the
newest, most active files: the saved snapshot kept the oldest ~11k rows and
the resume bound stripped the newest files' parser state. The snapshot is now
ordered by transcript mtime, and both count bounds drop the tenth of their
rows with the oldest transcripts.
* Drop the 24 h idle sweep; History rows live for the process
The sweep let whichever listing ran last decide when a row died, and
listings differ in scope: a smaller one (the phone's Agent History, a lower
desktop limit) expired rows only a larger one used, so the next Unlimited
scan re-parsed them. Rows History lists now live as long as the scanner
process; the snapshot's newest-transcripts cap bounds what survives a
reload, and rows other readers made keep the 4,096 bound.
Also clears the resume bookkeeping for a row the unlisted bound drops in the
same store.
* Skip resume bookkeeping for a row the unlisted bound just dropped
Running the resume trim first let a row the unlisted trim then deleted push
the resume set over its bound and cost 409 live resume points.
* perf(dev): keep pairing web bundle after test file changes
* fix(dev): detect web input replacement within one timestamp tick
* test(dev): use real files for web replacement coverage
---------
Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>
- Specs with their own CI jobs were also being assigned to general E2E shards, so they could run twice or in the wrong job.
- planE2e now drops dedicated specs before balancing and fails if no general specs remain, instead of producing an empty plan.
- The mobile subscribe spec builds a paired runtime connection from the pairing offer before sending requests, matching the updated remote runtime helper signatures.
* Finish structured agent readiness before delivering held work
* Adapt permission fixtures to startup lifecycle
* Keep model discovery for chats before their first start
* Use one host readiness promise for provider operations
* Await provider opening in the execution host
* Await Claude initialization in host-owned open
* Observe Claude readiness through the host status
* Keep optional readback behind host readiness
* Recheck launch permission after host readiness
* Preserve the awaited child during permission revalidation
* Record failed owned opening before the first message
* Abort a matching provider open when its child exits
* Cover provider exit during an unanswered host open
* Cover explicit failure facts on a host-stopped child
* Keep startup waits cancellable and preserve operation order
* Keep live choices and delivery order while agents start
* Apply startup picks and let parked messages release operations [skip ci]
* Save a waiting startup pick after Stop ends its opening [skip ci]
* Save choices when startup ends or a replacement is still opening
* Check saved choices separately from Claude startup reports
* Align startup fixtures and preserve observed Claude exits
* Order startup picks before later messages and retain flow regressions
* Keep reviewed lifecycle contracts while pruning duplicate tests
* Save picks after a proven exit without requiring a child
* Type the startup reply fixture against the Claude control contract
* Honor the before-input callback in the startup reply fixture
* Keep startup option menus independent and show the saved choice
* Exercise startup picks across real menus and the runtime
* Keep saved effort visible when picking a model during startup
* Include the running-turn scope in repeated Stop fixture
* Use the current attach contract in import fixtures
* Align carried-message fixture with startup event contract
* Keep launch-only permission picks usable while prior writes wait
* ci: rerun on main with the locale fix
* Preserve goal retries and chat launch selections
* Give launch regression its request identity
* test(native-chat): type the provisional-launch hook's session prop
* ci: build the WSL server template once
* ci: reuse validated Windows native addons in E2E preparation
* ci: avoid the unused standalone server build in SSH E2E shards
* Refuse a chat create whose close came first, behind the layout-owner switch
With ORCA_LAYOUT_OWNER=main, main remembers a close that reaches it before
the chat's create and refuses that create, or closes the chat as it lands.
The window then tells this machine's host about a cancel instead of
keeping a cancel tombstone; a host it cannot tell still gets the
tombstone. Paired hosts keep the tombstones, whose fence module moves to
lib/launch-cancel-tombstones-compat/.
* Close a chat whose create a cancel beat, on any create outcome
The local cancel now retires the chat on main (close and host-tab close),
so a create that landed first leaves no tab behind; a tab_not_found answer
means the create had not landed. Main closes the chat for any create
answer once a close was noted, including a refusal or a throw past commit,
and publishes no tab for it. The tombstone fallback notifies listeners.
* Keep the switched-off create path byte-identical
* Log, not throw, a failed close of a chat closed before create
* Run the close-before-create test in the Node SQLite project
* ci: remove Windows build and persistence test overhead
* ci: collect unit phase diagnostics during PR checks
* test(ci): seed hook reuse fixture from the canonical prebuild
The hook reuse regression first built into a missing private --output, so it
always reached Cargo (74s warm on Windows). Build through the default output
instead, which reuses a warm canonical prebuild, then copy its executable and
fingerprint into the private fixture root.
* fix(lint): clear inherited max-lines and broad object tab parameter after main merge
Combine the duplicate shared/execution-host imports in runtime-host-services
(301 -> 300 lines) and type the overlap test's tab mapper with the
RuntimeMobileSessionClientTab owner type instead of object.
* fix(ci): align inherited runtime and workflow contracts
* test: establish changed runtime probe signatures deterministically
* test: await asynchronous daemon profile cleanup
* test: shut down owned daemons before deleting profiles
* test: clean up oracle daemons during final app close
* test: recognize verified Linux zombie exit during daemon teardown
* fix: preserve folder SSH ownership when project groups conflict
* perf(build): remove unnecessary development and config work
* feat(dev): reclaim inactive worktree Vite caches
---------
Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>
* perf: allow opting out of main source maps in development
* Use the session tab type in the inherited terminal regression test
---------
Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>
* fix(ssh): an opted-in desktop serves its browser to a managed SSH host's orca CLI again
* fix(ssh): scope bridged pages through the renderer, render them as desktop pages, and route the e2e
* fix(ssh): bridge refuses file URLs and clipboard reads, revokes on disconnect, closes desktop tabs
* fix(ssh): judge bridged paste chords as the page sees them; accept long workspace ids
* fix(ssh): bridged clipboard denial follows popups; revoked commands never leave the browser queue
* fix(ssh): refuse option-like host values bound for agent-browser argv; report undeliverable results
* fix(ssh): bound bridged error results and fall back when the host refuses one
* fix(ssh): bridge only routed host tabs, and bound bridged page scripts by a deadline and revocation
* fix(ssh): cap waiting bridged commands below the desktop socket's result capacity
* fix(ssh): bridge revokes on routing changes, checks each page's route at run time, and cancels host-driven downloads
* fix(ssh): name the bridge registry's owner type
* fix(ssh): bridge refuses pages showing a local document, and never starts a command past orcad's deadline
* fix(ssh): bridged commands may leave Chromium error pages, and stop starting before orcad's deadline
* fix(ssh): ask for a fresh opt-in to host CLI control saved before it drove the desktop browser
* test(ssh): type the bridge registry deadline test
* [CI] Keep transport-only tests outside the app store graph
* [CI] Advance the updater install clock in failure tests
* [CI] Avoid descriptor formatting in replay clip counts
* [CI] Advance native-chat test observation clocks
* [CI] Restore observation timers after failed test setup
* [CI] Clean up final headless daemons during public close
* [CI] Keep serializer test oracle out of production smoke triggers
* [CI] Keep three unit fixtures out of production smoke triggers
* [CI] Bound frozen-clock close callbacks in tests
* [CI] Avoid the app-store graph in feedback dialog tests
* [CI] Keep hidden popup fixture compositor frames active
* test(ci): reuse mobile runtime fixture for pairing listeners
* test: restore native chat clocks before rig cleanup
The execution host upgrades a remote worker's fleet/show verdict to exited
only on its own proof: the hook store retains a proven agent exit (fenced to
the attachment, cleared by a replacement agent), and after a reboot the
process table proves the PTY gone. Lost contact stays unverifiable.
Fixes#24353
Co-authored-by: Claude <noreply@anthropic.com>
linear-sdk.ts loads the SDK through createRequire, which the orcad bundle
cannot follow, and nothing staged the package beside it. Bundle the SDK
into node_modules/@linear/sdk/index.js at build time and list it as an
orcad artifact so templates and installs carry it.
Co-authored-by: thukabjj <31043997+thukabjj@users.noreply.github.com>
* fix: render Kitty Unicode placeholder images with bounded ownership
* Enable managed Claude plugin images and bounded host file transport
* Preserve isolated Claude evidence and paired restart diagnostics
* Resolve Windows workspace aliases before WSL image reads
* Handle split remote terminal identities and improve WSL file-read diagnostics
* Reuse native drive paths for WSL image files and preserve guest temporary roots
* Apply native Windows path restrictions to mapped drive image sources
* Preserve WSL participation evidence across image checks
* Keep WSL drive directory containment case-sensitive after canonicalization
* Negotiate managed Claude image support with the launching renderer
* Make image repaint checks independent of display profile and shell output
* Capture repaint diagnostics only after the pixel assertion fails
* Allow floating terminal image reads from host temporary folders
* Keep image stack independent of unrelated CI runner and mobile lint settings
* Reuse worktree argument builder for terminal viewer context
Remove duplicated mapping and keep the RPC method table within its existing line limit.
* fix: render Kitty Unicode placeholder images with bounded ownership
* Make image repaint checks independent of display profile and shell output
* Capture repaint diagnostics only after the pixel assertion fails
* ci: report final PR checks outside the shared runner queue
* ci: fix inherited mobile test file size failure
* ci: move PR analysis and ARM test shards to Blacksmith
* Qualify x64 Blacksmith capacity and reject incompatible runner images early
* Keep extracted mobile test adapters within the test-only lint scope
* Identify the runner profile overriding shell fixture homes
* Preserve fixture HOME through the Blacksmith login profile
* Make cache invalidation fixtures observable within one filesystem clock tick
* Scope Blacksmith system-profile preparation to Linux runners
* Wait for observable ctime in daemon hash cache fixtures
* Use the mobile test lint scope already merged on main
* Stabilize metadata and timer fixtures on fast CI runners
---------
Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>
* feat(layout): add the dark layout.apply channel and route pane splits through main behind ORCA_LAYOUT_OWNER
A build made with ORCA_LAYOUT_OWNER=main registers layout.apply: main applies the
window's layout command to the stored partition through the shared applyLayoutCommand,
minting new ids. A user split then waits for main's answer and mounts the pane id main
minted. Every other build substitutes 'window', so neither branch is compiled in.
* fix(layout): write only the records a layout.apply command touched
A load-then-save rewrite also normalizes records the command never touched. The command's
effect is now the diff between the save before and after it, written onto the stored
partition record by record. A split whose main reply says a paired server holds the tab
splits as today.
* fix(layout): write a load-supplied record whole and check that patched partitions reload to the command's result
* feat(layout): close, rename and equalize panes through main behind ORCA_LAYOUT_OWNER
In a layout-owner build the window's pane close, title change and equalize go through
layout.apply and the window shows main's answer. A window pane close keeps the legacy
close's rules: the tab's last pane is refused, a pane restarted since main read the close is
left alone, every partition holding the tab closes it, and the tab's remote session id follows
the remaining pane. The rules sit behind a command field only layout.apply sets.
* fix(layout): close the tab when a racing pane close leaves the window one pane
* fix(layout): leave an unmounted tab alone when main refuses its last pane
* feat(layout): move panes and save divider ratios through main behind ORCA_LAYOUT_OWNER
Claude-Session: 10569c9a-a0a1-4fc9-8469-0edfb7aed125
* test(layout): a taken pane drop whose pane closed does nothing
Claude-Session: 10569c9a-a0a1-4fc9-8469-0edfb7aed125
* test(layout): pin the window's pane move to main's saved tree, and the drops main cannot take
Claude-Session: 10569c9a-a0a1-4fc9-8469-0edfb7aed125
* fix(layout): close a user's pane even if its terminal restarted, as today
Claude-Session: 10569c9a-a0a1-4fc9-8469-0edfb7aed125
* test(layout): pin the restarted-pane close directly, not through a made-up profile state
Claude-Session: 10569c9a-a0a1-4fc9-8469-0edfb7aed125
* fix(layout): leave a split to today's path only when the workspace's row names a paired server
The split switch point asked which host to route to with a helper that falls back to the focused
server, so a focused server could claim a workspace whose row names no owner. Use the same owner
test the tab mirror uses.
* test(layout): type the split owner mock without a cast
* test(layout): type the layout-owner command mocks with the real signature
* test(layout): type the gesture command mock with the real signature
* fix(layout): close the dark path's gaps: unmounted split panes, refused or failed splits and moves, ratios during a move, release guard
- A pane main saved but the window could not mount (source pane or manager gone during the await)
is closed again in main, so it does not come back on restart.
- A split main refuses because its copy lacks the workspace, tab or pane, or a failed call, splits
as today; a refused or failed move moves as today.
- Divider ratios committed while a move is in flight wait for the window's move, then go to main.
- The orcad bundle gets the ORCA_LAYOUT_OWNER define when the switch is on.
- Packaging and the orcad template refuse ORCA_LAYOUT_OWNER=main and any output a dark build marked.
- workspace-layout-record-patch.ts: no non-null assertions; plain comment for the map depth.
* test(layout): count a split pane as mounted from the split mock's own results
* fix(layout): act as today whenever main lacks the target or the call throws, not just for splits
- sendPaneCommandToLayoutOwner runs asToday for workspace/tab/pane_not_found and a thrown call, so
close, rename and equalize fall back like split and move; a handler that throws no longer runs the
fallback too.
- A close main refuses as its last pane, while this window still has others, closes as today.
- A move whose local step throws still releases held divider ratios.
- Drop the orcad output marker: nothing read it; the template build refuses the switch itself.
* test(layout): plain names for the last-pane fallback test and the marker comment
* docs(layout): say plainly what an indeterminate write can leave behind
* fix(layout): finish the main merge with this branch's versions of the layout-owner files
* test(build): copy the layout-owner build guard into the compiler-cache checkout fixture
* fix: carry this branch's build-guard changes onto main's versions of three build files