A grammar's first line also compiles its regexes, which TextMate cannot
interrupt. On a slow or busy core that compile alone could outlast all
three 50ms attempts, marking the block degraded and leaving it plain for
good. Allow a grammar that has never finished a line up to ten attempts;
warm grammars keep three. Also reverts the ineffective test-timeout bump.
* fix(quick-commands): save agent prompts whole, bounded by what a chat message carries
Agent quick-command prompts were silently cut to 6,000 characters on save:
the editor accepted a long paste, settings normalization trimmed it, and
reopening showed the shortened copy. The 6,000 was a Windows launch speed
threshold, not a limit on prompts.
- Storage no longer trims. A save is refused, never trimmed, when a prompt's
chat text block is over 256 KiB of JSON (measured on its real escaped size,
the measure the chat send schema applies) or the list is too large for one
paired reply. Host and desktop store both check before writing.
- The editor shows the size against the limit and blocks Save past it, and
stays open with the text when the host refuses the save.
- terminal.quick-commands.long-prompts.v1: hosts withhold prompts over 6,000
characters from clients that do not advertise it (older clients reject a
whole list holding one); editors keep an older host's 6,000 cap.
- The phone editor no longer drops pasted text past 6,000 via maxLength.
Launch delivery of long prompts is left to the launch-prompt series
(#24257, #23962, #24080); this branch's earlier paste-after-ready delivery
duplicated it and is removed.
* fix(quick-commands): drop a stale duplicate doc comment
* fix(quick-commands): advertise long prompts to this build's own host too
* fix(quick-commands): keep submitted editor immutable until save settles
* test(quick-commands): keep storage and save lifecycle coverage focused
* fix(quick-commands): keep portalled choices within pending form ownership
* Preserve retained credentials when settings validation refuses a save
---------
Co-authored-by: OrcaWin <alpha-eng@stably.ai>
* Transfer keyboard focus when a sibling chat pane opens
* fix(native-chat): hand focus to newly opened chat composer
* fix(native-chat): keep tab bar controller within line limit
* fix(native-chat): start focus expiry after tab opens
* fix(native-chat): underline a bare file name once the chat's folder has it
An agent's reply naming a file it created in its own folder, e.g.
`decision-explainer.html`, stayed plain inline code: chat required a
slash before it would even ask the host whether the path exists. Every
chat agent runs in one known folder (the host's launch directory, which
chat already resolves its links against), so a bare name the terminal's
bare-word detector accepts now goes through the same host existence
check and links only when the file is there. Version-like names (v1.2)
and spaced names are still never asked about.
Fixes STA-10392
* test(native-chat): drop an href assertion the click test already covers
* Link whole inline-code filenames in native chat
* fix(native-chat): keep version strings in inline code plain
Bare names with a dot now pass chat's existing inline-code shape rule, so v1.2.3 and 1.0 stay plain as on main.
* refactor(native-chat): name the inline-code file rule for what it checks
* fix(mobile): open each chat at its newest message on a chat switch
The chat view stays mounted across chat tabs, so a reader's scroll in one chat
carried into the next: it opened mid-transcript and not following. Reset follow
intent and the page's history head when the chat changes, before paint, and keep
view-wide state (Tools toggle, text size).
* fix(mobile): pin a switched-to chat to its own end on the page
The page pin scrolled to the last reported content height, which after a
chat switch is still the previous chat's, so a taller chat could paint
one frame mid-history before its own height arrived. Pin to the scroller's
live height instead.
* fix(native-chat): acting on one chat no longer waits on checks of every other chat
Starting, resuming or switching a chat's agent first ran restart
reconciliation, which probed every unreconciled session and committed them
together, so one slow or failing probe for another chat delayed or failed
this chat's action. Acquisition now reconciles only the addressed session;
the explicit startup pass still covers all of them, and an owner that isn't
proven still can't be granted a writer.
Co-Authored-By: OpenAI Codex gpt-6.1-sol <noreply@openai.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* fix(native-chat): settle each restored chat on its own; register new sqlite tests
- Startup restore no longer lets one chat's failed lease check skip recovery for the others.
- Run the two new SQLite-backed tests in the Node runtime project.
- Startup reconcile runs the whole-host pass without a placeholder session id.
- Test names describe an injected failing/stalling owner check, not an SSH host.
* fix(native-chat): restore keeps one whole-host lease check per startup
The restore opens with the whole-host pass again and stops per-chat bookkeeping after the
first failure, as on main: on a failing store it makes one lease-check write, not one per
chat. User actions stay targeted to their own chat.
---------
Co-authored-by: OpenAI Codex gpt-6.1-sol <noreply@openai.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* fix(mobile): fail a shell document the view never finishes loading instead of spinning forever
A load the WebView refuses or never commits reports nothing, which is how iOS 18
left the OTA workspace page on "Opening workspace" forever (#28626). Arm a
document-load deadline whenever the view is given a document; expiry takes the
existing document-load-failed recovery (drop the cache, refetch once, then the
failure screen with Retry).
* refactor(mobile): arm both shell document deadlines from one case
* fix(ssh): never leave an SSH host unconnectable after a refused move to its managed server
* fix(ssh): keep unsaved drafts out of what Move anyway leaves behind
* test: run the stuck-host profile test in the Node runtime project
* fix(ssh): spend a "Move anyway" consent when the conversion throws
A throw left managedServerLeaveBehind set, so a later connect left saved
state behind without asking again. A resumed fence replays its journal,
never the export, so clearing is safe.
Co-Authored-By: Claude <noreply@anthropic.com>
* Revert "fix(ssh): spend a "Move anyway" consent when the conversion throws"
This reverts commit 4f63d777d9.
* docs(ssh): note why a Move anyway consent survives a conversion throw
* fix(ssh): Move anyway needs every terminal proven exited, and skips foreign tabs fully
- A bridged terminal proof (relay census live or unverifiable) now refuses a
confirmed "Move anyway" before any fence or journal, with a reason that says
to close the terminals; the host stays on its relay and connectable. The
offer itself is withheld while terminals aren't proven exited.
- A foreign tab left behind is also removed from the legacy tabsByWorktree
rows, its layout, active-tab pointer, relay session id, scrollback, and
pane-keyed resume records, so it can't reappear on the managed server.
Co-Authored-By: Claude <noreply@anthropic.com>
* fix(ssh): Move anyway drops left-behind files' tabs, and is offered only when it would work
- An editor tab whose host-bound file is left behind is removed with the
file, so the managed server never opens it as a blank pane.
- The offer is previewed against the leave-behind export first: when what
refuses is a desktop-only draft the export can't leave, no "Move anyway"
is shown and the save-or-discard reason stays.
Co-Authored-By: Claude <noreply@anthropic.com>
* fix(ssh): drop surface and closed-tab tombstones of foreign tabs Move anyway skips
* fix(ssh): match legacy pane keys when Move anyway drops a foreign tab's records
---------
Co-authored-by: m4air <m4air@Mac.localdomain>
Co-authored-by: Neil <neil@stably.ai>
Co-authored-by: Claude <noreply@anthropic.com>
* feat(worktree): show checkout progress while a worktree is created
A local create spends most of its time in the checkout `git worktree add`
performs, and the card only said "Creating worktree…" for that whole time.
Git already writes its "Updating files: N%" meter into our stderr pipe, so
the add call reads it passively (same command line and environment) and the
create card shows "Checking out files… N%" with a thin bar. All six create
labels are translated so a create never switches language mid-flight.
Rebuilt on main from the version stacked on the create-gating branch. On
main a create can also claim a prepared checkout; that path never runs
`git worktree add`, so it keeps today's "Creating worktree…" label with no
bar, and a missed claim falls back to the plain add, which shows the bar.
* test(worktree): pin that finalizing a prepared checkout reports no checkout progress
Claiming a prepared checkout never runs `git worktree add`, so the create
card keeps its label with no bar; this pins that at the finalize step,
including the retarget `reset --hard`.
* test(worktree): keep only the checkout-progress tests that pin a contract
Drops unit tests that mirror the implementation; the real-git flow, the parser
captures, the cross-version Git contract and the create card test remain.
* test(worktree): pin the checkout meter's path from git to the create card
* test(worktree): merge the narrow-terminal capture's pipe chunks
* feat(acp): ACP agents can run with a private per-process temp folder
* feat(acp): a reclamation primitive removes all but the kept managed runtime release
The installer calls it while it owns the runtime's installs and launches; it answers the entries it
could not remove so that caller can report them. Nothing calls it from adapter construction.
* Recover retired macOS daemon trees during terminal restart
* fix(daemon): never let retired-job recovery failure block restart
A wedged retired job (unkillable descendant, launchctl timeout, recycled
root) previously aborted every untargeted Restart Terminal Daemon, leaving
no way to replace the current daemon short of a reboot. Log the failure
and continue with the ordinary restart instead.
* fix(native-chat): paste clipboard images into a chat on a managed SSH host or paired server
Since SSH hosts moved onto managed Orca servers, a terminal-backed chat on one resolves
to a remote owner, which refused every clipboard image. Save the image on that server
(its own host runs the agent), as a terminal paste already does.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* fix(native-chat): let a server-saved clipboard image through the composer's remote-target guard
The composer refuses client files on a remote PTY when it starts the pending chip.
An image the paste saved on that server is not a client file, so mark the chip
server-owned, including when it settles as a file reference. Cover the flow with
the real attachment hook.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* chore(native-chat): drop an unused paste option type widening
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* fix(orcad): hand macOS opens from the browser sidecar to the desktop app
On macOS 27, LaunchServices registers orcad's Electron browser sidecar even
when orcad was started over SSH, and sends the user's opens to the first
registered instance of the bundle regardless of its activation policy.
Opening Orca on such a host showed onboarding on the sidecar's throwaway
profile instead of the user's own.
orcad now marks the sidecar with ORCA_ORCAD_SIDECAR=1. The app treats the
marker as a background launch and hands app opens, skill share links and
opened documents to a new desktop instance with `open -n` and an empty
environment, one launch per batch. An older installed app ignores the
marker and keeps its current behavior.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* fix(orcad): an automated sidecar never launches the real desktop app
---------
Co-authored-by: jbamlee <dev.jbam@gmail.com>
Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* fix(serve): ship the paired web client outside app.asar so a /tmp sweep cannot break it
* fix(serve): unpack the mobile web bundle too, which reads its assets the same way
* fix(browser): managed SSH terminal links open in the Orca Browser again
A managed server reached over SSH has no browser backend of its own, so
links from its panes were offered only the system browser. They now open
in a desktop tab whose page egresses through the SSH host, the same route
new browser tabs in that workspace already use.
Fixes#29077, #27349
* test(e2e): keep the managed workspace host selected for the Cmd/Ctrl-click step
* refactor(browser): keep owner-route checks private; split tab presentation instead
Moving the owner predicates to their own module made their callers count as
new focus reads. Keep them in workspace-browser-tab-open and move the
title/error presentation helpers out to stay under the line limit. The SSH
deployment check also reads the workspace's owner route instead of the
focus-falling-back host lookup.
* test(e2e): assert the managed SSH link page renders in the visible browser tab
* fix(browser): keep a desktop-hosted link tab owned by its runtime workspace
---------
Co-authored-by: m4air <m4air@Mac.localdomain>
Co-authored-by: Neil <neil@stably.ai>
* fix(session-tabs): keep recent worktree streams warm; skip resubscribing live ones
Switching worktrees on one SSH host tore down and reinstalled the per-worktree
session.tabs.subscribe stream on every switch (a subscribe round trip, an orphan
recovery pass and a snapshot re-apply). A hostContactEpoch bump also reinstalled
both session-tabs streams even when they never ended.
- Keep the three most recently selected worktree streams warm, keyed by
connection identity; a new runtime session, re-pair or hidden window drops them.
Only the selected worktree may seed or respawn a terminal.
- On a hostContactEpoch bump, restart only streams that ended or are waiting on
the retry backoff; live streams are untouched.
* fix(session-tabs): a warm stream seeds only when its host still owns the selected worktree
* refactor(session-tabs): inject the worktree stream installer so the focus-read count stays in the hook
* fix(session-tabs): drop a warm stream when a host that is not selected changes identity
* fix(orcad): orca serve owns its Claude and Codex accounts again (#28962)
orcad never wired account services, so every accounts.* RPC failed once
orca serve moved onto orcad. Build the same account services the desktop
builds (now in provider-account-services.ts), made Electron-free, and
install them in orcad except on a WSL host, whose desktop owns its accounts.
* test(orcad): keep account-services test off the real userData when XDG_CONFIG_HOME is set
* fix(orcad): structured Codex chats and session discovery follow this host's managed accounts
Create orcad's account services before the runtime so its structured Codex launch/read resolvers and catalog-probe prep are constructor deps, as on the desktop. A client-sent account home still wins, and a WSL host keeps them unset so the desktop-pushed CODEX_HOME applies. AI Vault session discovery now includes this host's managed Codex homes.
* fix(orcad): automatic Codex resumes follow the account home their session was recorded in
Share the desktop's resume launch prep as a factory and wire it into orcad's PTY registration and AI Vault resume, so a pane restored after an account switch resumes from its origin home. A client-sent account home keeps resuming from itself.
---------
Co-authored-by: Neil <neil@stably.ai>
* fix(ssh): retry a stalled SSH reconnect when the machine wakes or the network returns
After sleep, an SSH host could sit in reconnection-failed forever: the retry
ladder spent its nine attempts while the Mac was asleep or in dark wakes, and
nothing restarted it. Resume only re-probed managed tunnels, and the window's
online signal never reached SSH at all.
- SshConnection.retryAfterNetworkReturn restarts a spent ladder, a pending
backoff, or a background handshake older than 5s with a fresh budget.
- Resume, unlock-screen and the window's resume/online signal
(runtimeEnvironments:retryConnectionsNow) all call it via the manager and
re-probe managed tunnels, so a transport that still looks connected after a
network change is caught by the probe instead of keepalive.
Co-Authored-By: Claude <noreply@anthropic.com>
* fix(ssh): keep a superseded system-SSH probe from clearing its replacement's transport
A wake retry can now replace an in-flight handshake. Closing a system-SSH
probe only signals the process, so its late close used to reset the
transport state the replacement had just set up, leaving a connected host
whose exec failed with 'Not connected'.
Co-Authored-By: Claude <noreply@anthropic.com>
* fix(ssh): keep a superseded handshake from killing its replacement's proxy process
Co-Authored-By: Claude <noreply@anthropic.com>
---------
Co-authored-by: Claude <noreply@anthropic.com>
Chat question cards now focus the first answer automatically, let Up and Down move the highlight, and use Enter to choose it. Existing number shortcuts, Other input and explicit multiple-answer submission use the same answer path.
Preserve text-only question focus when combining with current main, and keep arrow navigation inside the focused question card so Conversation retains its keyboard focus.
Validated with 53 focused Windows tests, independent review, and remote Electron QA using a real Claude Code session.
* 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
* 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
* fix(layout): finish the main merge with this branch's versions of the layout-owner files
When the wake that adopts another version's stopped server found the host
fenced (e.g. another desktop starting it after a reboot), the connect fell
through to its own deploy, which over an active older version can only defer
on the terminal census. Report the fence as a waiting deferral instead, and
count terminals against the record the wake re-read.
Refs #28833
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* fix(remote-terminal): keep live output flowing during resync and retry an unanswered one
Co-authored-by: Claude <noreply@anthropic.com>
* fix(remote-terminal): escalate to stall recovery when a resync is never answered
Co-authored-by: Claude <noreply@anthropic.com>
---------
Co-authored-by: Claude <noreply@anthropic.com>
* feat(session-history): show the scan's progress in a strip under the search box
One line under the Session History search box says what this computer's
scan is doing: "Scanning N files", "Reading n / T files", "Scanned N
files · K agents", or "Stopped after N files" with a Refresh link, which
replaces the red timeout line. Clicking it opens one row per agent. The
strip is hidden for remote hosts, All hosts and the web client.
* fix(session-history): drop the agent count from the finished strip
* fix(session-history): match an applied list to its progress by scan ID alone
* fix(session-history): keep the scan strip's own icon uncoloured; colour stays in the per-agent rows
* fix(session-history): a push supersedes an in-flight progress fetch for its key, whatever else was pushed
A pane hidden on the host remounts onto the same live PTY, and for a moment its graph leaf has no PTY. Main dropped the handle then and minted a new one, so the paired client's next send was refused as stale and the client remounted the pane, which made the host remount it again. Main now holds the live PTY on the leaf through that gap.
Fixes#23759
* Allow worktree parentage across projects on one host
* Keep sidebar placement and project ownership consistent
* Keep project labels on phone rows outside their own section
* Derive sidebar placement and reveal from shared occurrences
* Preserve project text across workspace card styles
* Resolve saved lineage peers without rebuilding host projects
* fix: preserve independent project deletion progress
* fix: satisfy mobile formatting and test lint checks
* test: declare project fixture arrays without assertions
* Align reveal fixtures with rendered workspace occurrences
* fix: reuse project name for cross-repo child cards
* fix: satisfy mobile section lint after main merge
* fix: retain project label for nested mobile rows
* test: keep nested card owner assertion resilient
* test: assert project badge visibility across card styles
* test: drop lineage tests already covered by the whole-flow suites
The host-peer projection is exercised by the runtime and SSH listing
integration tests, and the simple phone nesting case is covered by the
deep-chain, Pinned and hidden-project flow test.
Raise the remote terminal row bound from 120 to 500 (the bound terminal.subscribe/send already accept) so a pane taller than 120 rows no longer sits over a shorter PTY that leaves stale TUI frames below row 120.
Fixes#27425
- mobile-path-search-expiry: poll full GCs until weak targets clear instead
of one fixed round, which a loaded CI worker can outlast.
- NativeChatCodeBlock: cold grammar + WASM highlighting of 400 lines can
exceed findByText's 10s on a saturated runner; wait up to 25s, under the
30s testTimeout.
* ci: publish an explicit result from the PR aggregate
* test(codex): preserve review text across OSC terminators
* perf(ci): reuse worker fixtures and validated launcher output
* fix(test): use typed orchestration database access
* fix(ci): exclude test fixtures from focus ratchet
* test(launch): pin where the two spawn preflights differ, on main
Row 3 (one spawn preflight for both launch lanes) first pins main's behaviour:
- Row 8, agents started with their workspace (from the earlier row-8 work): a work item's
"start agent" and the folder-workspace composer through the real producer and pane and
pty:spawn: draft and note on argv, a long multi-line note, PowerShell quoting under cmd.exe,
project WSL, terminal shell WSL, and a prompt-less Codex start with no shell wait.
- A deleted folder: the window starts in the workspace root and says so; the host passes the
folder on and the provider refuses it.
- A Windows plain terminal in a folder containing a WSL-project repo, and in a \\wsl$ repo with
no saved runtime: the window uses the runtime the renderer sends; the host reads only the
repo's project.
- A managed SSH Windows host: only the host lane opens the OpenSSH login shell.
- A pane's own agent launch drops the root fallback (TEMPORARY parity).
* refactor(pty): one spawn preflight step for both launch lanes
The window's pty:spawn and the host's runtime controller each kept their own copy of the
first spawn step: resolve the folder a new terminal starts in and the shell/runtime its PTY
uses. resolvePtySpawnStartupTarget now does both for both lanes. Each lane passes the two
inputs it still differs on, so nothing changes for users:
- missingCwdFallback (TEMPORARY, row 13a): only window starts fall back to the workspace root,
with a notice, when the requested folder is gone (including the WSL cwd probe).
- projectRuntimeSource (TEMPORARY, row 13b): the window uses the runtime its renderer sent;
the host reads the repo's project from the store.
The OpenSSH login shell (#9327) now resolves for both lanes; only an SSH-launched orcad turns
it on, and that process has no window, so the window lane never meets it.
Both lanes resolve after fresh-spawn recovery and session-id mint (the WSL distro reads the
session id). Admission, adoption, hidden mark, recovery and mint stay where they were.
* feat(native-chat): one queue header that becomes the pause row; no composer Resume or send dialog
The queued cards now sit under one header row on desktop and phone: "Queued"
and the count normally, and "Paused" with the count, Clear all and Resume
while the host holds the queue after a Stop. A chevron folds only the cards;
the choice is local to the view and resets when the queue empties.
The composer's Send no longer turns into a second Resume, and a message sent
into a paused queue no longer asks "Send message?": it sends, its accepted
turn lifts the pause, and the cards follow it. Clear all deletes every card
through the existing per-card delete. No wire or host change.
* test(mobile): find the background-tasks strip's toggle inside the strip, not the queue header's
* Make queued Clear all atomic across clients
* Show queued message failures in collapsed headers
* Use per-card delete for paused queue Clear all
* Join repeated paused queue Clear actions
* fix(git): keep merge-tree's throwaway trees out of the real object store
The PR conflict summary and the squash-merged branch cleanup both run
`git merge-tree --write-tree` only to read its output, but every divergent
run left unreachable loose objects behind. Run it with a per-call scratch
object directory (inside objects/, alternates pointing at the real store)
that is deleted afterwards, on native, WSL and SSH relay hosts alike.
* fix(git): keep partial-clone downloads and clean up stranded scratch stores
- A partial clone fetches missing blobs during merge-tree; those packs landed in the
scratch store and were deleted, so every check re-downloaded them. Move fetched packs
(index last) into the real store before removing the scratch dir.
- Sweep this app's own scratch dirs older than an hour, once per objects dir per process,
so a crash or failed delete cannot strand one forever in repos where gc never runs.
- Remove with Windows transient-lock retries; the shared core now owns all filesystem
work and host adapters only resolve the objects dir.
* fix(git): quote Windows alternates paths containing ';' and log scratch cleanup failures
* fix(git): sweep a merge-tree scratch dir only when its owner is provably gone
Another Orca process on the same repo (a second instance, dev and release
builds) could delete a scratch object dir still in use once its mtime passed
an hour. Each scratch dir now records its owner (pid, hostname, platform,
Linux pid namespace); the sweep removes it only when that owner shares this
pid space and has exited, or, with no checkable owner, at Git's own two-week
tmp_ expiry.
Also:
- A fetched pack whose later rename fails is rolled back, so the real store
never keeps an index-less pack; a pack already installed is left alone.
- WSL Git gets the /mnt/<drive> spelling of a drive-path common dir (Git
< 2.31 prints it relative, resolved against the C:\ repo path).
- conflict-summary tests no longer create scratch dirs in the cwd.
* fix(git): cap every merge-tree scratch dir at two weeks, live-looking owner included
A reused pid held by a long-lived process could keep a leftover scratch dir
forever in a repo where git gc never runs. The two-week age (git gc's own
tmp_ expiry) now removes any scratch dir; a younger one is still swept only
when its owner shares this pid space and has exited.
* fix(git): sweep merge-tree scratch dirs by age alone, off the check's path
- The stale sweep now removes any scratch dir at least two weeks old, Git's
own expiry for its objects/tmp_* dirs; no merge-tree runs that long. The
owner file and pid-space probe are gone, and with them the cases where a
sweeper misjudged another process's pid.
- The sweep no longer delays the user's check: it starts without being
awaited and can only touch dirs far older than the current run's.
- A fetched pack's .keep never moves into the real store, so a transient one
from an aborted lazy fetch cannot stop gc from repacking that pack.
- Tests: the legacy merge-tree fallback gets the scratch env; the local
quarantine's WSL decisions (distro from a UNC path, drvfs spelling,
host-Git linked-worktree skip, WSLENV hand-off).
* fix(git): let quarantined merge-tree see exactly the object stores Git sees without it
The scratch object store must not change what merge-tree can read, or the
conflict list and the squash-merged branch cleanup differ from main:
- An inherited GIT_ALTERNATE_OBJECT_DIRECTORIES (from Orca's or the SSH
relay's environment) was replaced; it is now kept and the real store
appended after it, as Git's own temporary object stores do.
- An inherited GIT_OBJECT_DIRECTORY is the store Git reads and writes, so the
command now runs unquarantined. On the WSL route a Windows value only
counts if WSLENV forwards it, and then the quarantine is skipped too.
- The real store's own info/alternates entries are listed before it, so an
alternates chain is linked at the same depth as without the quarantine
(a 6-deep chain no longer hits Git's nesting limit). An alternates file
that cannot be read or holds a quoted entry runs unquarantined.
* fix(git): run merge-tree unquarantined when the repo's store has its own alternates
Mirroring Git's alternates resolution could not match it in every shape: a
path back to the real store (mutual alternates, or an inherited alternates
value naming the repo itself) still linked its chain one level deeper, and a
NUL byte in info/alternates broke the spawn. When objects/info/alternates
exists, or cannot be checked, the command now runs exactly as before. Repos
cloned with --reference or --shared keep writing throwaway objects, as they
always have. Inherited alternates are still appended to, and an inherited
GIT_OBJECT_DIRECTORY still skips the quarantine.
* refactor(git): drop the stale-folder sweep and pack-move rollback from merge-tree's scratch store
A scratch folder left by a crash is a tmp_* dir inside objects/, which
Git's own gc prunes after gc.pruneExpire, as it does Git's own leftovers,
so Orca's 14-day sweep and its test hooks go.
Packs a partial clone fetched into the scratch store are moved in Git's
migration order: pack- files only, index last, existing names left in
place, stopping at the first failed rename with no rollback. A failed
move is logged and never stops the scratch dir's removal or the result.
Adds a real-Git test that a partial clone lists its conflicts offline
once an earlier check downloaded the contents.
Co-Authored-By: Claude <noreply@anthropic.com>
* fix(git): quarantine merge-tree only once the host is known to support --write-tree
Git before 2.35 cannot prune a leftover scratch folder under objects/tmp_*, so
a crash in the moment one exists makes every later `git gc` warn and stops
automatic cleanup. On Git before 2.38 merge-tree --write-tree is rejected, yet
the first check per host per session still made and removed a folder around it.
Branch cleanup and the PR conflict list now use the scratch object store only
when that host's capability cache already knows merge-tree --write-tree works.
Until then the command runs exactly as it does on main: on a Git that
rejects it no folder is ever created, and every Git that does get one is 2.38+,
whose gc prunes it. Also reword two comments in the quarantine module.
Co-Authored-By: Claude <noreply@anthropic.com>
* fix(git): learn merge-tree support from a conflicting branch-cleanup check
Branch cleanup quarantines merge-tree only once the host's capability cache
knows --write-tree works. A conflicting merge-tree exits 1, so the check threw
and support was never recorded; on a host whose only checks conflict (likely
over SSH, where the relay has no conflict summary to teach its cache) every
check kept writing loose objects into the real store.
A failure whose stdout starts with a tree ID proves --write-tree ran (old Git
that rejects the option prints nothing there), so it now resolves to the same
"not merged" result and lets the cache record support. Every other failure is
rethrown unchanged. Also shorten two comments and soften a third.
Co-Authored-By: Claude <noreply@anthropic.com>
* fix(git): put WSL Git's scratch object store on its command line
A WSL login profile that set GIT_ALTERNATE_OBJECT_DIRECTORIES replaced the
WSLENV-forwarded value, so a quarantined merge-tree lost the real store and
failed where main succeeds. The WSL runner now passes both variables as
command-line assignments after the profile, appending to its alternates.
* test(git): drop quarantine tests the real-Git and compatibility tests already pin
The cold-capability gating, scratch-folder lifecycle and WSL path spelling
are covered end to end by merge-tree-object-quarantine-real-git.test.ts, the
real-binary compatibility contract (Git 2.25/2.38/2.49) and the
localGitObjectsDirectory tests, so the unit copies only mirrored them.
---------
Co-authored-by: Claude <noreply@anthropic.com>
* fix(daemon): skip the ps process-table read on Windows
Windows foreground evidence is unverifiable whatever the table says, yet the
daemon still ran `ps` there. With Git's or MSYS2's Unix tools on PATH that
started a real ps.exe without a hidden window, so agent monitoring could flash
a console. Return the Windows verdict directly instead.
* refactor(daemon): keep all Windows inspection in one branch
Fold the ConPTY foreground confirmation into the new win32 branch, drop the
anchor clear that can never fire there, and remove test stubs for a
process-table read that no longer happens.
---------
Co-authored-by: Orca Worker <orca-worker@localhost>
* Prepare native chat CLI copies from the host record
* Apply staged-file lint and formatting checks
* Cover pairing replacement before queued preparation dispatch
* Reuse CLI failure feedback and complete the pairing test response
* Isolate Codex materialization test user data
* Preserve native chat CLI copy completion and preparation ownership
* test: consolidate native conversation fork contracts
* fix(native-chat): refuse a Codex chat fork whose saved account home has no CODEX_HOME path
* 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
* 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
* feat(layout): draw local and SSH workspace layouts from main's layout stream behind ORCA_LAYOUT_OWNER
* fix(layout): keep the frame sync out of window-owned builds' live code
* fix(layout): keep each row's live terminal binding and pane state across frames
* chore(layout): count the frame applier's layout writes and drop test casts
* fix(layout): hold frames while any save is owed, scope the view read-back to rebuilt workspaces
* fix(layout): keep the frame sync's module init out of window-owned builds
* docs(layout): name the read-back bucket and the failed-save hold
* fix(remote-terminal): reattach a healthy remote pane promptly after a worktree switch (#27178)
A remote pane whose stream ended while the host still reported its terminal
ready waited the whole 15s inventory window before reusing it, and a third end
within 60s demanded a replacement handle that never comes, parking recovery
with no timer until the ~3 min deadline. Unknown-liveness outcomes on the
attach and resubscribe paths also parked for online/resume events that a
healthy link never fires.
- Reuse a still-ready handle as soon as the host reports it, unless a
terminal.send to it is unanswered (it may still come back stale).
- Pace repeated ends on the same handle with growing delays instead of
requiring a replacement; only ends within 10s of a reattach count.
- Schedule unknown-liveness retries on the backoff ladder instead of parking;
the existing deadline still latches Reconnect for a genuinely lost link.
Co-Authored-By: Claude <noreply@anthropic.com>
* fix(remote-terminal): fence an endless same-handle flap once its pacing ladder is spent
Each paced reattach marked the pane healthy and reset the recovery deadline,
so a handle that ended after every reattach retried forever without ever
offering Reconnect. After the last rung it now falls back to the replacement
wait, whose deadline latches Reconnect.
Co-Authored-By: Claude <noreply@anthropic.com>
---------
Co-authored-by: m4air <m4air@Mac.localdomain>
Co-authored-by: Claude <noreply@anthropic.com>