Scan merge proofs run with lazy fetches and transports off, so partial
clones never download blobs; direct WSL reads forward those variables.
Base and coordinator refs resolve once per repo per scan, and the squash
search lists target commits once and only patch-ids commits that touch
the branch's exact file set.
gh 2.91.0 and 2.92.0 start `gh send-telemetry` with DETACHED_PROCESS. Its
`tzutil /g` child then gets a new console, which shows a terminal window for
about 0.2s. Orca's background PR polling runs gh about once a minute, so
Windows users on those gh versions see the window every few minutes while
Orca sits idle. Default GH_TELEMETRY to false in the gh child env, next to
GH_PROMPT_DISABLED, and keep any value the user already set.
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* perf(worktrees): give each repository's worktree list one owner
The sidebar and the runtime catalog each kept their own copy of a
repository's `git worktree list` result and their own staleness rule, so
the same change was often listed twice. One owner per repository, host
and Git runtime now holds the observation, its flight and invalidation;
each reader keeps its accepted age (sidebar 5 s, runtime 30 s plus the
admin-file verifier up to the 5-minute ceiling) and shares the scan.
Settled rows now go stale on Git-level mutations too, overtaken readers
follow one trailing scan, store hygiene uses the facts captured before
the scan that produced the rows (once per observation), and a cooled-
down watcher failure defers its catalog refresh instead of dropping it.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* fix(worktrees): fence the catalog owner by runtime, single flight and hygiene claims
Sidebar reads age from scan start, so runtime verifications no longer
stretch them; probes only certify rows fingerprinted before their scan.
One flight per owner, with a single trailing scan after invalidation.
Switching a repo's Git runtime retires the old owner, and a reader whose
owner was retired mid-read returns non-authoritative rows. Only pruning
readers claim hygiene, failed scans keep the evidence gate's no-retry
claim, overflow invalidates at once, and the stopped-distro fallback
serves only current rows.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* fix(worktrees): let only the live route own a repository's catalog
The catalog owner now asks the live registration which path and Git
runtime are current instead of trusting whichever request arrives last.
A request admitted for a replaced route runs no Git, retires nothing and
answers non-authoritatively; liveness is rechecked before a flight
starts and when it publishes. Route-changing writes retire obsolete
owners after they land. Adapters re-resolve the route after the WSL
admission check, a mismatching verification probe becomes the rescan's
fingerprint, and runtime scans share the strict true-empty-aware reader.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* fix(worktrees): bound live-route checks and recheck authority before pruning
Live-route checks read the repo registry once per synchronous pass
instead of hydrating every repo per check. WSL discovery answers that
change a repo's Git route retire obsolete owners like other route
writes, and desktop and headless pruning recheck the owner's authority
for the observation after their own awaits.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* fix(worktrees): revision the live-route snapshot and carry owner authority to publication
Route checks use one registry and project pass per route-input revision,
which repo, project-membership and runtime-setting writes advance
synchronously; WSL discovery answers retire obsolete owners at once.
Repair-state and SSH routes never acquire local Git authority. Metadata
pruning rechecks owner authority after its path probes, fleet lineage
claims require a current observation, and final desktop and runtime
listings stay non-authoritative once the owner has retired their route.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* fix(worktrees): keep remote-owned repos out of local Git routes
Registrations owned by another host never get a local Git route, WSL
discovery notifies after the distro list is written and isolates its
listeners, and tests cover idle retirement on route-input and discovery
changes plus pruning that loses owner authority mid-helper.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* test(worktrees): cover catalog route authority at its production seams
Real repo-removal writer revokes the route at once, and desktop and
runtime listings never publish owner rows as authoritative after their
route was retired.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* test(worktrees): prove catalog authority at the pruning helper and Git conversion writer
The runtime listing loses route authority during its own metadata prune
and stays non-authoritative; a real held path probe deletes nothing once
the owner retires the route; a folder converted to Git is admitted as a
live route as soon as the write lands.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* fix(worktrees): keep strict true-empty listing behind the mockable lister
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* test(worktrees): start each worktree.ps case from an unobserved catalog owner
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* build(typecheck): list the WSL discovery change module in the web project
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* fix(worktrees): withdraw catalog authority from a replaced observation
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* fix(workspace-cleanup): warn before deleting a workspace's ignored local files
git worktree remove deletes ignored files such as .env and local config,
and git status never reports them, so cleanup showed "Clean git" and the
delete dialog said nothing. Read them with a bounded ls-files listing,
skip regenerable output, symlinks and byte-identical copies of the main
checkout, and surface the rest as an approval-only cleanup blocker and a
warning in the delete confirmation.
* fix(workspace-cleanup): show an unchecked warning when the ignored-files check fails
* style(workspace-cleanup): format merged fact status list
* feat(workspace-cleanup): mark merged and unchanged workspaces from git evidence
Squash-merged branches whose remote was deleted were flagged as unpushed
and needed a forced delete. The scan now reads what HEAD holds beyond the
workspace's base (ancestor, Git's squash proof, or a cached merged PR) and
reports merged/unchanged reasons; landed commits no longer count as at risk.
A gone upstream no longer hides unpushed commits. Renames idle-clean to
inactive and drops the dead tier, selectedByDefault and volatile-local-context.
* fix(workspace-cleanup): read HEAD once, skip a reused branch as its own base
Merge evidence now compares the cached PR and the base against the HEAD it
reads, not the listing's possibly stale one; a workspace opened on an
existing branch no longer counts that branch as its base; the proof
wrapper settles the WSL route before building its caches; a visit keeps
merged/unchanged reasons; landed rows stop sorting as unpushed.
* fix(workspace-cleanup): qualify base refs and trust the branch proof only for HEAD
Base refs resolve through the worktree base-ref qualifier so a tag cannot
shadow them and only the branch's own family is skipped, including the
repo default; SHA-256 pinned commits are not bases; the squash proof runs
only while the branch ref is the HEAD just read; expanded details stop
counting landed commits as unpushed.
* test(workspace-cleanup): build the repo fixture without a cast
* fix(workspace-cleanup): never take a tag as a workspace's base
* fix: retain prepared checkout cleanup ownership after timeout
* test: retain capacity for unregistered preparation leftovers
* fix: keep expired cleanup admission outside retry budget
* fix: verify original preparation registration before removal
* Retain preparation authority through bounded capacity recovery
* Exercise preparation cleanup failures through real filesystem fixtures
* Inject root mkdir failure without obstructing checkout absence proof
* Match the host filesystem path in preparation failure coverage
* Align preparation fixtures with physical cleanup ownership
* Await owned recovery in the fleet cleanup control
* Release abandoned preparation cleanup maintenance leases
* Match cleanup test identity paths on Windows
* Wait for completed preparation disposal in maintenance regression
* Await final cleanup state in maintenance controls
Cloning always went into <parent>/<repo name>, so a same-named repository
from another owner (or any non-empty folder of that name) could not be
cloned into the same parent. Before cloning, Orca now checks the target
on the clone host. If it is taken, the clone forms show a Folder name
field with a free suggestion (<repo>-<owner>, then <repo>-2...) and clone
into that instead. RPC repo.clone and `orca project setup-clone` accept
the folder name too.
* feat(runtime): tell phones when git status changes
* fix(runtime): tell phones about HEAD and packed-refs moves too
* fix(runtime): don't let a phone's status re-read join a read begun before the change
* perf(runtime): limit status-read invalidation to the checkouts an outside change touched
* fix(runtime): match a checkout's status keys under any execution host
* fix(runtime): invalidate globally while a primary checkout is unread
* test(mobile): re-record the Changes and settings goldens on the rebased main
* fix(runtime): invalidate globally while a listed checkout has no cached path
* test: follow the moved paired-client harness
* refactor(mobile): let the host list's catalog read hold the event router
* chore(lint): bump oxlint to 1.91, add perf lint rules and perf audits
* fix(lint): await promises returned inside try/catch/finally for return-await
Prepare typescript/return-await with error-handling-correctness-only: await
returns whose catch or finally was meant to cover them, and suppress the eight
sites where the catch must not re-handle the returned promise.
* chore(lint): enable return-await (error-handling-correctness-only); exempt secondary-scan directives in the changed gate
* refactor(mobile): move file preview discard modal into its own module
Keeps MobileFilePreviewScreen under the 400-line limit after merging main.
---------
Co-authored-by: m4air <m4air@m4airs-MacBook-Air.local>
* perf(main): overlap independent fs probes on git status, agent detection and orcad startup
* perf: skip per-entry copies in git change list and tab hydration; drop unused activity barrel
Adds commit actions to the Git Graph: check out a commit or branch, cherry-pick, revert, and create a branch from a commit. Conflicts hand off to Source Control with continue and abort. Works on local, WSL and remote hosts.
A folder workspace with no worktrees now offers Create worktree for each repository, named after the folder workspace. If that branch name is already taken, the host refuses instead of picking another name, and the composer opens so you can choose.
* feat(cli): one enclosing-workspace rule, --environment local, and copyable workspace addresses
current/active and cwd inference now pick the innermost workspace of either kind (worktree or
folder workspace); at equal depth the caller terminal's own workspace wins, otherwise the choice
is refused as ambiguous. --environment local names this machine's own Orca over an ambient
ORCA_ENVIRONMENT or pairing code. worktree ps/show/current print an environment: line and an
address: line (JSON environment, address, addressUnavailable, parent) that always carries its
--environment, and name a parent's environment only when it is verified.
* test: answer the folder catalog read in agent launch cwd tests
* feat(cli): worktree rm refuses current/active and names the selector to pass
Removal needs an explicit workspace, as git worktree remove needs a path. The refusal runs before
rm decides between a worktree and a folder workspace, and reports what current resolves to now
(one selector, the ambiguous choices, or none) in text and as invalid_argument data.
* fix(cli): keep the remote-environment guidance when rm refuses current
* fix(cli): surface folder-catalog failures for omitted targets; no address for a duplicated folder parent
* fix(cli): surface a worktree-list failure for omitted browser and terminal targets too
* test(cli): folder rm refuses current/active before the folder removal path
* fix(cli): refuse a cwd owner whose folder host can't be derived instead of skipping it
* fix(cli): compare cwd owners by filesystem identity, refuse every equal-path tie, surface worktree-list failures
Paths on the caller's own filesystem compare by realpath (reusing resolveRealPathSync), so /tmp
and /private/tmp aliases find the deeper owner in both the folder prefilter and ranking; other
hosts' paths stay lexical. Equal-depth owners with different repo registrations now refuse without
a caller instead of picking by catalog order. An omitted target no longer swallows worktree.list
method_not_found; only the optional folder reads fall back.
* fix(cli): read the realpath helper from shared so the packaged CLI ships it
* refactor(cli): move the catalog row type and unresolved-owner refusal out of the selection/catalog cycle
* fix(cli): resolve every caller-filesystem workspace path, not only basename matches
A workspace registered through a differently named symlink was skipped by the basename gate.
Containment now realpaths cwd and each row on the caller's own filesystem once per command,
shared by the folder prefilter and ranking; folder hosts are derived first so another host's
paths are never resolved locally.
* test: answer the folder catalog read in the ambiguous orchestration-check caller test
* feat(cli): --parent-environment names where a worktree's parent lives
worktree create and worktree set take --parent-environment <local|saved id|name>
next to --parent-worktree. The parent is resolved in that environment only, and
sent to the child as a verified cross-store ref after the child's cross-machine
capability check. Without the flag, the parent stays in the child's environment;
active/current always mean this machine's own Orca.
Preflight failures happen before any change: environment_not_paired,
environment_unavailable, LINEAGE_PARENT_NOT_FOUND / LINEAGE_PARENT_AMBIGUOUS
naming the searched environment, and incompatible_runtime with data.environment
and data.requires.
* fix(cli): parent lookup errors name and list the environment searched
LINEAGE_PARENT_AMBIGUOUS and LINEAGE_PARENT_NOT_FOUND now always carry
data.environment and a listing command for the environment the parent was
looked up in, for parents in another environment and in the child's own. Steps
inherited from the runtime listed the caller's default route instead.
* docs(cli): a local address names the Orca that printed it
* refactor(git): share the .gitattributes ancestor stamp paths
* perf(git): reuse the branch-compare changed-files list per commit pair
* perf(git): charge cached change lists for the stdout their paths retain
* perf(git): bound branch change-list reuse to five minutes for unstamped system attributes
* refactor(git): share the deadline-bounded stamp probe
* perf(git): bound branch change-list attribute stamps by a 2s deadline and 4096 paths
* refactor(git): move porcelain status streaming into its own module
* perf(git): let status use the untracked cache and expand collapsed directories
-uall never matches the untracked cache's stored mode, so every poll rescanned the whole tree. Scan in the configured mode and list only the collapsed '? dir/' rows with ls-files, reapplying the entry cap so results match -uall exactly. Ignored reads and status.showUntrackedFiles=no keep -uall.
* refactor(git): let the settled diff cache and stamp serve multi-path reads
* perf(git): reuse status numstat passes while their stamped inputs are unchanged
Each poll re-ran up to two whole-worktree numstat diffs even when nothing changed. Cache each pass in a SettledDiffCache validated by the worktree diff stamp (HEAD, index, .gitmodules, plus every unstaged working file for the unstaged pass); racy writes, mutations and submodule rows still recompute.
* perf(git): defer status while index.lock is held
A status read during an agent's commit sees a half-written state and is immediately redone. Wait (up to 1s, abortable, inside the shared lease) for the lock in the worktree's own gitdir to go away; a lock that survives a full wait is remembered as stale so later reads skip waiting.
* perf(git): pick the untracked scan from config and fall back to -uall when expansion cannot win
Read status.showUntrackedFiles and core.untrackedCache in one config call, remember worktrees that need -uall so polls do not scan twice, and rescan with -uall when rows sit outside cwd, need more than one ls-files chunk, or list empty.
* refactor(git): keep numstat cache limits beside the file diff limits
* refactor(git): stamp host settings files and resolve the common gitdir
* perf(git): read untracked-scan config from a stamped snapshot, with a rollback switch
Cache 'git config --list --show-origin' per worktree while every config file it named (plus repo, worktree and global candidates) keeps its stamp, so steady-state polls spawn no config read. WSL and UNC checkouts keep -uall: their config cannot be stamped from the host and network mtimes cannot vouch for the untracked cache. ORCA_GIT_STATUS_UNTRACKED_EXPANSION=0 restores the plain -uall scan.
* fix(git): stamp config and attributes before reusing status numstat
Numstat output also depends on Git config (including includes) and on .gitattributes, info/attributes and core.attributesFile. Stamp all of them, plus every .gitattributes that can apply to a changed path, and skip reuse under WSL. ORCA_GIT_STATUS_NUMSTAT_REUSE=0 recomputes every pass.
* fix(git): stamp include targets and onbranch HEAD in the config snapshot, and share one read
Include and includeIf targets (and HEAD for onbranch conditions) are now stamped, so a branch switch or a filled-in include re-reads config. Concurrent cold reads coalesce, both numstat passes share one config read, folders without their own gitdir skip the snapshot, aborted reads keep the cached entry, and Windows HOME follows Git's HOMEDRIVE/HOMEPATH order.
* perf(git): expand untracked directories only when the untracked cache is opted in
Git's default core.untrackedCache=keep only reuses a cache someone already built, and fresh repos have none, so status plus ls-files cost more than one -uall scan. Expand only for core.untrackedCache=true or feature.manyFiles=true without an override.
* fix(git): bound config stamps on dead mounts and share commondir resolution
Config file stats run under the same 2s deadline as local-git-config-signature, coalesced per file set, so a hung home or network mount skips reuse instead of stalling status. readHeadComponent now uses resolveGitCommonDir.
* test(git): pin the -uall truncation contract of untracked directory expansion
* fix(git): treat an unreadable commondir as unstampable config
main's resolveGitCommonDir rethrows non-missing errors; the config snapshot must fall back to -uall rather than reject the status read.
* refactor(git): share common-dir resolution and stat stamps from worktree-diff-stamp
* perf(git): cache branch-compare merge-base and ahead/behind by commit OID pair
* fix(git): only store a commit-pair answer when its scope survived the walk
* refactor(git): type commit-pair stores per answer kind and pin test mtimes
* fix(git): skip the commit-pair cache whenever replace refs, grafts or relocating env could apply
Adds a branch picker to the Source Control header for switching or creating a branch in place. Switching is disabled while an agent is working in the workspace, and results show as toasts.
* Add Git Graph view showing all branches and commit history
Adds a full-pane Git Graph tab that visualizes the whole repository —
all local branches, remote-tracking branches, tags, merges, and HEAD —
instead of only the current branch's linear history shown in the Source
Control sidebar today.
- Add an opt-in allRefs scope to the shared git-history loader so the
graph query enumerates --branches --remotes --tags HEAD; the default
current-branch path is unchanged. The flag is forwarded through the
local IPC, SSH provider, and runtime RPC boundaries so it works in all
environments.
- New git-graph tab content type with a full-pane GitGraphPane that
reuses the existing graph SVG/row components, virtualizes rows, and
opens a commit's combined diff on click. Wired into the tab strip as a
chip (activate/close/keyboard-cycle) mirroring the simulator pattern.
- Entry point: an Open Git Graph button in the Source Control commits
header (Cmd/Ctrl-click opens in a split).
Closes#4521
Co-authored-by: Orca <help@stably.ai>
* Address review: virtual-tab guards, nav-order, truncation copy
- Treat the Git Graph (and mobile emulator) tab chip as a virtual tab:
add an isVirtual contract to EditorFileTab/EditorFileTabContextMenu so
rename, copy-path, copy-relative-path, and reveal-in-finder are disabled
for tabs whose filePath is a display label, not an on-disk path. Prevents
a rename on the synthetic 'Git Graph' path running a real file operation.
- Forward gitGraphIds through the legacy reconcileTabOrder fallback in
getActiveTabNavOrder so keyboard tab navigation does not drop git-graph
tabs when no split-group layout exists yet.
- Make the truncation banner explicit: 'Showing first N commits across all
branches' (was 'of many'), across all locales.
- Tests: virtual-tab hides path actions; git-graph survives the legacy
nav-order fallback.
Co-authored-by: Orca <help@stably.ai>
* Forward allRefs over SSH relay and web runtime paths
Two boundaries re-destructured git.history params and dropped the new
allRefs flag, leaving the repo-wide Git Graph inert (linear default-branch
history) for SSH and web-runtime worktrees:
- src/relay/git-handler.ts: the relay host's git.history handler forwarded
only { limit, baseRef }. Add allRefs so the SSH host enumerates all refs.
Adds an end-to-end relay test (a feature-branch commit absent from linear
history appears only when allRefs is forwarded).
- src/renderer/src/web/web-preload-api.ts: the web preload history shim
destructured only { worktreePath, limit, baseRef }. Forward allRefs.
Also fix a request-id race in useGitGraphHistory: bump requestIdRef before
the null-context early return so an in-flight fetch from a prior worktree
can't resolve and overwrite the 'idle' state after the worktree clears.
Co-authored-by: Orca <help@stably.ai>
* Support filtered and pinned Git history
Resolve branch and tag roots on the host, keep paging tied to the chosen revision set, and echo the applied filter across Git transports.
Co-authored-by: Matheus Breguêz <matbrgz@gmail.com>
* Add the Git Graph tab and commit details
Open the all-branch view from Source Control or the command palette, preserve older clients' layouts, and keep the sidebar HEAD-only.
* Restore files the main merge left at stale versions
The earlier merge of origin/main kept older or missing copies of 61 files
unrelated to the Git Graph (CI script tests, reliability gates, shard
timings and five terminal runtime files). Take main's version of each so
the PR changes only its own files.
* Limit pinned history to explicit graph scopes
Keep All and HEAD snapshots free of redundant ref names, and filter ref searches before nested-tag peeling. Default history requests retain unpinned paging without evicting graph tokens.
Co-authored-by: Matheus Breguêz <matbrgz@gmail.com>
* Preserve canonical placement for older graph clients
Pass graph-free record deltas through without rebuilding layouts, retain existing empty groups, and translate content creation into canonical groups while preserving hidden graph tabs.
* Retain Git Graph browsing state for the tab lifetime
Own history retrieval, selection and scroll in one renderer controller that retires on close or owner/filter changes. Accumulate parent pages before publishing, share action-time workspace navigation, serialize ref searches, and scope refreshes to the workspace.
* Apply Git Graph branch selections on picker close
Keep checkbox changes local until dismissal, then normalize and apply one chosen ref set. Allow picker reads to use a qualified host scope.
* Keep Git Graph navigation bound to its tab owner
Retain the tab's qualified owner and Git connection across workspace focus changes. Newer selections and refreshes supersede pending parent navigation without losing fetched pages.
Report stale workspace switches and let the commit menu handle empty space in the Branch / Tag cell. Cover twin-owner retention, superseded navigation and picker apply-on-close with renderer regressions.
* Translate desktop layout commands against canonical state
Resolve older desktops' visible groups, tab gaps and split paths inside the canonical mutation, including detach and cross-partition moves. Keep initial admission visibility-aware and cache graph presence for binding-only frames.
* Refine Git Graph picker and owner transitions
Discard branch drafts on Escape or owner changes, follow updated tab ownership, and report workspace lookup failures for the current owner. Keep parent navigation queued behind pending history loads.
* Drop the redundant default from the exhaustive layout command switch
* Align Git Graph with changed-code quality rules
Use typed fixtures and an event guard, keep primitive styling on content wrappers, and prune loaded-commit bookkeeping after render.
* Route Git Graph opening through the existing editor action
Use workspace record selectors for stable owner inputs and remove two
loading labels orphaned by the graph changes from all locale catalogs.
* Preserve Git Graph selection and keep row content visible
Refresh and token recovery retain loaded depth and selection in one
accumulating operation. Keep the published view until it settles, and
only fall back to the first commit after history is exhausted.
Prioritize current and open-workspace refs, reserve their markers, clip
wide graphs and retain filenames when long paths need truncation.
* Bound Git Graph refresh and allow graph column resizing
Restore the loaded depth without searching for vanished selections, retain the published result on retrieval errors, and keep newer selections visible. Allow the bounded graph column to expand to every lane through the existing resize mechanism.
* Retain Git Graph history for errors without a message
Detect failed retrieval independently of its message and cover an empty-message rejection in the existing partial-failure regression.
* Retain Git Graph widths and preserve shifted selections
Separate committed and live resize widths, retaining the chosen width for the graph tab lifetime. Allow one bounded extra history page for selections shifted by new commits, and give the resize separator a translated label.
* Preserve nullable Git Graph owner fallback
* Fix Git Graph layout imports after the RPC method move
* Route the Git Graph owner connection through the source-control connection helper
* Keep the Git Graph button idle in hidden Source Control sections
* Re-probe Git Graph support when the owner's runtime status changes
---------
Co-authored-by: Orca <help@stably.ai>
Co-authored-by: Matheus Breguêz <matbrgz@gmail.com>
* Upload GitHub images when pasted or dropped
Keep image bytes out of issue and review bodies by uploading from the owning composer and inserting hosted attachment URLs.
Co-authored-by: lydiassssz <60514233+lydiassssz@users.noreply.github.com>
* Preserve GitHub image intake across editor and thread states
* Keep image placeholder boundaries and ordinary HTML paste intact
* Match the native replace overload in the image projection fixture
* Use typed element queries in the image intake fixture
* Narrow textarea elements at the fixture boundary
* Show failed GitHub draft images as filename links
* Keep GitHub draft attachment links current across updates
* Supply the repository address in the attachment preview fixture
---------
Co-authored-by: lydiassssz <60514233+lydiassssz@users.noreply.github.com>
* Keep a lifetime owner for claimed worktree preparation
* Record preparation ownership loss before retirement
* Type the preparation lock-write fixture without an assertion
* Revoke preparation cleanup when ownership changes
* fix(git): refuse unsafe branch switches in an existing workspace
The phone's Switch branch ran `git checkout` with no checks: uncommitted
files moved to the other branch, and a switch could land under a working
agent or mid-rebase. One host check now covers every client: it refuses,
with a plain reason, when files are uncommitted, a merge/rebase/
cherry-pick/revert is in progress, HEAD is detached or unborn, another
git action is running, or an agent in the checkout is working or waiting
for approval. Checks and the checkout run under a per-checkout lock.
After a switch the host clears the old branch's review links and push
target and tells every client, so a phone-only host stays correct, and
records that the user chose the branch so first-work naming never
renames it. The open Commits list follows the new branch.
* fix(git): review round 1 repairs for the guarded branch switch
- check out through the provider so the user's switch keeps the interactive git queue
- a landed switch that Windows left files behind on now tells the user instead of reporting success
- never let a Windows yes/no prompt hold the switch lock
- refuse submodule edits that submodule ignore config hides from status
- record the switch on the target host's metadata row and never fail a landed switch on bookkeeping
- read workspace paths from their ids instead of scanning the catalog on every switch
- admit agent rows like the sidebar roster (listed tab, or a live terminal by pane, tab or handle)
- move the host-side switch hooks into their own module
* test: type the new switch test fakes instead of casting them
* fix(git): second repair round for the guarded branch switch
- report files Windows left behind from git's own unlink warnings, not a status re-read, so
ordinary branch differences and submodule commit changes are never called failures
- the phone refreshes its branch and files after a switch that landed with a warning
- refuse only edits inside a submodule its ignore config hides, never a commit mismatch alone,
reading status without optional locks in the interactive queue
- read the agent process probe when a switch runs, not while the runtime is constructed
- check the agent's checkout path before terminal liveness and build the tab list once per switch
- the runtime wiring test no longer reaches real git
* fix(git): scope the switch's agent check to the checkout and keep landed-switch outcomes honest
- find candidate agents by checkout path first; build terminal liveness once, only from this
checkout's terminals and its workspaces' saved tabs; probe candidates together and re-read
the snapshot once; no work for unrelated workspaces (contract test at N and 10N)
- a checkout that fails after HEAD moved is recorded as landed; files left behind are named only
when git reports it could not replace them, otherwise git's own error is shown
- parse git's unlink warnings for paths with apostrophes
- the phone keeps the refusal or warning visible while it reloads the branch
- cover a submodule that is only at another commit
* fix(git): name git's own error when a switch lands after a failing hook; use the shared unlink parser
* fix(git): keep the switch's agent check flat for unrelated folder workspaces
- read folder workspace directories once per switch instead of a linear search per row and terminal
- re-read each candidate's current status by pane key instead of rebuilding the whole snapshot
- skip the hidden-submodule status pass in repos without submodules
- the cost contract covers folder workspaces, many in-checkout agents and PTY passes
- a real-git test covers a switch that lands before a failing post-checkout hook
* fix(git): never quote git's progress line as a landed switch's error; fixed clock in the cost test
* fix(git): decide the hidden-submodule check from the index, not a visible .gitmodules
* perf(git): read only index modes for the submodule shortcut so large repos stay under the output cap