mirror of
https://github.com/stablyai/orca.git
synced 2026-11-02 18:17:36 +00:00
8d908764dcd9c155e46bcbcab051804868ca2b30
85
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
8d908764dc |
Revert "Keep same-machine folders with matching Git remotes in separate projects (#32272)" (#34662)
* Revert "Keep same-machine folders with matching Git remotes in separate projects (#32272)"
This reverts commit
|
||
|
|
9fd9cd7362 |
test: remove 260 low-value tests (#34535)
Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com> |
||
|
|
1d2778fa99 |
test: remove 150 low-value tests (#34520)
Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com> |
||
|
|
f0eb0949d0 |
fix(windows): delete again succeeds after a held file is released (#34058)
* fix(windows): stop recursive deletes from retry-storming when a file is held open Node's async recursive rm retries every nested entry separately and orphans sibling walkers when it rejects, so deleting a workspace holding a live browser profile multiplied filesystem work per directory level until the main process hung. Delete Windows host trees with a walker that retries whole passes from one shared budget and leaves nothing running on failure. Also bound and log detached worktree removal, and fail a file transaction whose proper-lockfile lock is compromised instead of throwing from its timer. * fix(windows): retry a locked delete as whole passes instead of a custom walker Replace the hand-rolled tree walker with Node's own recursive rm and no per-entry retries. Async rm gives each nested entry its own retry counter and never stops siblings, which is what multiplied the work; with retries off each pass is one bounded walk, and a transient Windows lock repeats the whole pass from the existing 250/500/1000/2000 ms budget, checking the job's signal between passes. A failed delete now names the file another program holds open instead of Node's EBUSY/EPERM text. Drop the 30-minute background deadline. The lost-lock error no longer embeds the library's message and says the write may or may not have been saved. * test: cover bounded deletion failure and recovery contracts * test: prune unchanged cleanup coverage * fix(worktrees): give truthful failed-delete cleanup advice * test: document fake-clock retry adapter * fix(windows): preserve checkout ownership until files are removed * fix: preserve worktree ownership checks across metadata aliases * test: make checkout retry clock completion explicit * Keep failed Windows workspace deletions retryable * test: correct removal fixture imports and path formatting * fix: retain failed removal until a retry is accepted * test: identify the mocked backoff clock * test: preserve failed removal records and recreate marker fixtures * test(worktrees): preserve exact checkout bytes in refusal contract * test(worktrees): exercise current native deletion contracts * test(windows): prove long-path deletion through native checkout removal * test(windows): preserve real Git runner exports in removal fixture --------- Co-authored-by: Orca Worker <orca-worker@localhost> |
||
|
|
84494ce394 |
test: remove 338 low-value cases and 28 suites (#34234)
Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com> |
||
|
|
29a5f89b8a |
Keep same-machine folders with matching Git remotes in separate projects (#32272)
* WIP: same-host same-remote checkouts keep their own project id * WIP: claim the shared project id per checkout surface; drop the display-only clone split * WIP: index derived ids by repo in the hydration normalizer * Resolve workspace project affiliation from current checkout owners * fix: preserve concrete project ownership across catalogs and operations * fix: combine recent project selector imports * fix: enforce canonical catalog reads and preserve search continuity * fix: scope catalog lint exceptions to renderer files * Resolve project search ownership before native candidate limits * Combine duplicate session scope imports * fix: derive returned history row ownership on the host * Reconcile full session history replies by content * Require authoritative project catalogs for terminal snapshots * Preserve registered project claims and partial history results * test: check orphaned setup cleanup state without assertions * fix: align catalog callers and native search fixture contracts * test: check full repo shapes in runtime routing fixtures * Fix checked catalog inputs in compiler regression fixtures * Keep checked PTY fixture repos mutable * test: align catalog fixtures with canonical readers * test: check catalog and runtime fixture inputs * test: check catalog fixtures without outer assertions * test: follow persistence harness relocation * Adapt catalog test inputs to integrated main checks * Test History hashing across cached depth revalidation * test(settings): expect host-qualified setup removal * test: capture host split listings and scope relay recovery * ci: regenerate merge ref * test: type host-split listing diagnostics |
||
|
|
fdcf193cd4 |
fix(worktree): track explicit remote-only branch (#31645)
* fix(worktree): track explicit remote branch * test(worktree): cover remote branch path retry * fix(worktree): resolve cached remote branches canonically * fix(git): retain commit validation in bounded ref probes * fix(worktree): preserve selected remote identity during creation * test(worktree): count cached resolver Git work during path retries * Fix branch intent state in composer source fixture * Make Git and daemon regression fixtures portable * Use the selected base for existing branch creation --------- Co-authored-by: Brennan Benson <79079362+brennanb2025@users.noreply.github.com> |
||
|
|
7dbf3974df |
test: remove low-value renderer suites and redundant shared test cases (#33819)
Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com> |
||
|
|
0efc989713 |
fix(worktrees): an unqualified id two hosts share resolves to nothing, not the first copy (#33818)
A worktree id or project host setup id repeats across hosts. Lookups that named no host picked the first match, so a delete or setup mutation could land on the other host's copy. They now go through one sole-copy rule and refuse when the matches span more than one host; callers that know the host (settings Remove, ephemeral VM rollback) now name it. Co-authored-by: fettpl <38704082+fettpl@users.noreply.github.com> |
||
|
|
de93f326ea |
test: remove low-value inventories, passthroughs and redundant cases (#33796)
* test: remove low-value inventories, passthroughs and redundant cases * style: format test suites after pruning redundant cases * test: remove obsolete workflow and flaky-test references * test: prune deleted builder test from Windows shim allowlist --------- Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com> |
||
|
|
cc3ca668d8 |
test: remove low-value predicates, inventories and helper echoes (#33778)
Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com> |
||
|
|
ac003332de |
test: remove redundant configuration, shortcut and recovery coverage (#33770)
Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com> |
||
|
|
6c9c7b0fdb |
fix(worktree): start the agent when a workspace is opened during creation (#33562)
Opening a workspace while its local creation is still pending no longer seeds an idle terminal that steals focus from (or drops) the agent launch: the creating event carries the new workspace id, activation during creation shows the existing "Creating worktree…" panel, and the workspace takes over when creation finishes. A terminal the user explicitly opens is kept and the agent gets its own tab. Fixes #25807 Supersedes #25812 Co-authored-by: Eotel <49769982+Eotel@users.noreply.github.com> |
||
|
|
7d6603aa17 |
[comment audit] Complete repository-wide comment cleanup (#33249)
* [comment audit] Align legacy failure-read counts with cached display * [comment audit] Condense shared provider lifecycle and browser recovery comments [skip ci] * [comment audit] Condense shared generation, path and preview comments [skip ci] * [comment audit] Condense host, file and Git contract rationale [skip ci] * [comment audit] Tighten Git lifecycle and provider contract comments [skip ci] * [comment audit] Clarify shared host authority and launch isolation [skip ci] * [comment audit] simplify local access mobile and native chat contracts [skip ci] * [comment audit] simplify native chat presentation and state contracts [skip ci] * [comment audit] simplify native runtime and provider contracts [skip ci] * [comment audit] simplify migration and orchestration contracts [skip ci] * [comment audit] simplify plugin trust and process capture rationale [skip ci] * [comment audit] simplify terminal delivery and remote compatibility rationale [skip ci] * [comment audit] clarify remote recovery and launch wire rationale [skip ci] * [comment audit] shorten shared RPC compatibility and runtime evidence comments [skip ci] * [comment audit] simplify secure storage shell startup and sidebar rationale [skip ci] * [comment audit] clarify sidebar, skill and SSH invariants [skip ci] * [comment audit] condense structured chat delivery and timing rationale [skip ci] * [comment audit] condense terminal replay and telemetry invariants [skip ci] * [comment audit] clarify launch permission and terminal delivery rationale [skip ci] * [comment audit] clarify workspace ownership and layout contracts [skip ci] * [comment audit] wait for fitted and painted SSH terminal links * [comment audit] clarify layout persistence and session ownership [skip ci] * [comment audit] clarify host routing and workspace lifecycle contracts [skip ci] * [comment audit] clarify startup persistence and app lifetime comments [skip ci] * [comment audit] clarify recovery and activity boundaries [skip ci] * [comment audit] condense activity and artifact invariants [skip ci] * [comment audit] condense automation authority contracts [skip ci] * [comment audit] simplify automation scheduling and history contracts [skip ci] * [comment audit] clarify browser chrome and annotation lifecycles [skip ci] * [comment audit] clarify browser routing and retained guest ownership [skip ci] * [comment audit] clarify browser recovery and document ownership comments [skip ci] * [comment audit] restore hosted runners required by native IME fixtures * [comment audit] shorten preview, palette and dashboard invariants [skip ci] * [comment audit] clarify terminal preview, dashboard and retention comments [skip ci] * [comment audit] clarify diff-note recovery and editor lifecycle comments [skip ci] * [comment audit] shorten rich-text and combined-diff ownership comments [skip ci] * [comment audit] shorten autosave, draft recovery and model ownership comments [skip ci] * [comment audit] shorten save-queue, notebook and Markdown constraints [skip ci] * [comment audit] shorten Monaco, PDF and rich Markdown safeguards [skip ci] * [comment audit] clarify rich Markdown source and selection safeguards [skip ci] * [comment audit] clarify editor loading and emulator stream lifetimes [skip ci] * [comment audit] trim emulator and feature tour narration [skip ci] * [comment audit] clarify floating workspace and GitHub cache ownership [skip ci] * [comment audit] trim provider controls and Linear filter explanations [skip ci] * [comment audit] clarify pairing and chat resume lifetimes [skip ci] * [comment audit] clarify chat delivery and transcript lifetimes [skip ci] * [comment audit] clarify session options and host-owned status [skip ci] * [comment audit] clarify draft durability and live-status precedence [skip ci] * [comment audit] Trim chat send and model ownership explanations [skip ci] * [comment audit] Shorten transcript ownership and send recovery comments [skip ci] * [comment audit] Simplify native chat composer and transcript hook comments [skip ci] * [comment audit] Shorten native chat scroll and structured session explanations [skip ci] * [comment audit] Trim workspace picker and onboarding commentary [skip ci] * [comment audit] Simplify pet rendering, review cache and quick open comments [skip ci] * [comment audit] Trim sidebar and session history explanations [skip ci] * [comment audit] Condense session resume and review checks comments [skip ci] * [comment audit] Shorten checks state and file explorer refresh comments [skip ci] * [comment audit] Trim plugin bridge and source-control action rationale [skip ci] * [comment audit] Trim source-control listing and review rationale [skip ci] * [comment audit] Shorten sidebar lifecycle and explorer safeguards [skip ci] * [comment audit] Condense Settings account and appearance rationale [skip ci] * [comment audit] Trim pairing and repository Settings explanations [skip ci] * [comment audit] Condense Settings search SSH and terminal safeguards [skip ci] * [comment audit] Shorten Settings voice and provider ownership comments [skip ci] * [comment audit] Condense Settings draft persistence and host-state rationale [skip ci] * [comment audit] Trim Settings hooks and sidebar setup rationale [skip ci] * [comment audit] Condense sidebar deletion and board-filter safeguards [skip ci] * [comment audit] Condense worktree cards, host ownership and Markdown contracts [skip ci] * [comment audit] Condense sidebar drag, folder and filtering contracts [skip ci] * [comment audit] Condense sidebar parity, sleep and host ownership contracts [skip ci] * [comment audit] Prune focus-read baseline after comment cleanup * [comment audit] Clarify sidebar visibility, board drops and agent evidence [skip ci] * [comment audit] Tighten sidebar listing, drag and reveal contracts [skip ci] * [comment audit] Clarify viewport geometry and skill update safeguards [skip ci] * [comment audit] Tighten skill inventory, usage and host status comments [skip ci] * [comment audit] Clarify usage ownership and resource kill safeguards [skip ci] * [comment audit] Tighten tab selection, rename and search contracts [skip ci] * [comment audit] Tighten tab-strip geometry and drag ownership comments [skip ci] * [comment audit] Tighten Tasks recovery, pagination and mutation comments [skip ci] * [comment audit] Tighten provider loading and terminal recovery comments [skip ci] * [comment audit] Tighten completion, cadence and PTY ownership comments [skip ci] * [comment audit] Clarify terminal restore and parked-pane boundaries [skip ci] * [comment audit] Tighten PTY startup and replay explanations [skip ci] * [comment audit] Clarify PTY identity and delivery ownership [skip ci] * [comment audit] Shorten remote reconnect and input replay rationale [skip ci] * [comment audit] Condense terminal parking clipboard and file link comments [skip ci] * [comment audit] Clarify IME ownership and trim trace history [skip ci] * [comment audit] Shorten pane recovery and parked watcher explanations [skip ci] * [comment audit] Condense snapshot replay and visibility recovery rationale [skip ci] * [comment audit] Shorten terminal parking and keyboard ownership rationale [skip ci] * [comment audit] Trim renderer lifecycle and menu rationale [skip ci] * [comment audit] Condense workspace cleanup ownership and settlement notes [skip ci] * [comment audit] Clarify renderer hook evidence and source ownership [skip ci] * [comment audit] Tighten IPC ownership and reconnect explanations [skip ci] * [comment audit] Trim renderer focus, paste and attention comments [skip ci] * [comment audit] Shorten scroll, locale and agent lifecycle comments [skip ci] * [comment audit] Trim agent launch, resume and ownership commentary [skip ci] * [comment audit] Shorten account, browser and editor comments [skip ci] * [comment audit] Clarify host-mirror waits, previews and input ownership [skip ci] * [comment audit] Condense launch recovery and editor tokenization comments [skip ci] * [comment audit] Condense workspace selection and terminal pane rationale [skip ci] * [comment audit] Condense terminal fitting and rendering recovery comments [skip ci] * [comment audit] Condense terminal input and host routing explanations [skip ci] * [comment audit] Condense ownership and sleeping-session recovery rationale [skip ci] * [comment audit] Condense structured launch and terminal link explanations [skip ci] * [comment audit] Condense terminal ownership and workspace file routing rationale [skip ci] * [comment audit] Condense session persistence and workspace activation rationale [skip ci] * [comment audit] Condense workspace routing and browser ownership rationale [skip ci] * [comment audit] Condense mirror recovery and layout authority rationale [skip ci] * [comment audit] Condense runtime transport and file safety rationale [skip ci] * [comment audit] Condense runtime graph and structured session rationale [skip ci] * [comment audit] Condense browser creation and remote tab recovery rationale [skip ci] * [comment audit] Condense snapshot authority and publisher recovery rationale [skip ci] * [comment audit] Condense frame, startup and provider cache rationale [skip ci] * [comment audit] Condense host twin and agent lifecycle rationale [skip ci] * [comment audit] Condense browser authority and editor cleanup rationale [skip ci] * [comment audit] Condense editor buffer identity and review cache rationale [skip ci] * [comment audit] Condense provider lifetime and repository ownership rationale [skip ci] * [comment audit] Condense reconnect, hydration and terminal retirement rationale [skip ci] * [comment audit] Condense recovery, reconciliation and persistence rationale [skip ci] * [comment audit] Condense cleanup ownership and listing admission rationale [skip ci] * [comment audit] Condense teardown ordering and terminal ownership rationale [skip ci] * [comment audit] Condense recovery ledger and web host-boundary rationale [skip ci] * [comment audit] Condense web reconnect and credential lifecycle rationale [skip ci] * [comment audit] Condense cloud rollout and assignment safety rationale [skip ci] * [comment audit] Condense relay session and database recovery rationale [skip ci] * [comment audit] Condense relay placement and protocol compatibility rationale [skip ci] * [comment audit] Condense build and packaging constraints [skip ci] * [comment audit] Condense CI gates and validation isolation rationale [skip ci] * [comment audit] Condense catalog identity and browser validation rationale [skip ci] * [comment audit] Condense mobile browser and frame-budget rationale [skip ci] * [comment audit] Shorten mobile route and browser test explanations [skip ci] * [comment audit] Condense editor and session seam test rationale [skip ci] * [comment audit] Trim session and terminal closure history [skip ci] * [comment audit] Shorten terminal browser and census explanations [skip ci] * [comment audit] Preserve root-only E2E diagnostic baseline * [comment audit] Condense mobile seam and HTML preview test rationale [skip ci] * [comment audit] Condense preview instrumentation and render harness rationale [skip ci] * [comment audit] Condense bundle contracts and native-build constraints [skip ci] * [comment audit] Shorten native packaging and CI routing rationale [skip ci] * [comment audit] Condense release tooling and benchmark constraints [skip ci] * [comment audit] Condense packaging and platform gate rationale [skip ci] * [comment audit] Shorten mobile route and history rationale [skip ci] * [comment audit] Condense mobile browser and component rationale [skip ci] * [comment audit] Shorten drawer and workspace-form rationale [skip ci] * [comment audit] Trim mobile review and editor-document explanations [skip ci] * [comment audit] Condense editor lifecycle and Markdown explanations [skip ci] * [comment audit] Trim mobile file, diagnostics and dictation commentary [skip ci] * [comment audit] Simplify mobile host-list and workspace-view explanations [skip ci] * [comment audit] Clarify mobile shell lifetime and bridge delivery constraints [skip ci] * [comment audit] Condense bridge stream, native request and audio rationale [skip ci] * [comment audit] Reduce bridge schema and page-client commentary [skip ci] * [comment audit] Tighten mobile page bridge contracts [skip ci] * [comment audit] Clarify mobile cache and session constraints [skip ci] * [comment audit] Condense mobile route and lifecycle rationale [skip ci] * [comment audit] Tighten mobile shell hook timing rationale [skip ci] * [comment audit] Clarify route handoff and notification compatibility [skip ci] * [comment audit] Clarify push registration and dictation cleanup constraints [skip ci] * [comment audit] Condense keyboard geometry and native media ownership rationale [skip ci] * [comment audit] Tighten mobile chat scroll and composer concurrency rationale [skip ci] * [comment audit] Condense mobile reply compatibility and upload ownership rationale [skip ci] * [comment audit] Condense optimistic echo and scroll ownership rationale [skip ci] * [comment audit] Tighten stream replay and queued session ownership rationale [skip ci] * [comment audit] Condense mobile composer and reply-schema constraints [skip ci] * [comment audit] Clarify mobile transcript recovery and session ownership [skip ci] * [comment audit] Clarify mobile queue and source-control compatibility [skip ci] * [comment audit] Condense storage and task launch constraints [skip ci] * [comment audit] Clarify task schemas and terminal document ownership [skip ci] * [comment audit] Tighten terminal input and viewport rationale [skip ci] * [comment audit] Shorten terminal and RPC recorder contracts [skip ci] * [comment audit] Condense recorder engine and connection contracts [skip ci] * [comment audit] Shorten host credential and relay recovery comments [skip ci] * [comment audit] Tighten pairing recovery and bundle transport rationale [skip ci] * [comment audit] Clarify RPC delivery and workspace catalog boundaries [skip ci] * [comment audit] Condense catalog and relay entry contracts [skip ci] * [comment audit] Shorten shutdown and SSH relay delivery contracts [skip ci] * [comment audit] Condense WSL relay and E2E regression rationale [skip ci] * [comment audit] Shorten cross-version harness and renderer fixture explanations [skip ci] * [comment audit] Condense paired-host and E2E lifecycle rationale [skip ci] * [comment audit] Shorten layout oracle and regression explanations [skip ci] * [comment audit] Condense paired recovery and ownership regression contracts [skip ci] * [comment audit] Shorten restart, sidebar and SSH lifecycle contracts [skip ci] * [comment audit] Condense terminal rendering and IME regression rationale [skip ci] * [comment audit] Shorten terminal recovery and benchmark contracts [skip ci] * [comment audit] Condense tooling and Windows lifecycle proof rationale [skip ci] * [comment audit] Condense workflow routing and release rationale [skip ci] * [comment audit] Condense mobile security and installer contracts [skip ci] * [comment audit] Shorten native rationale and stylesheet workarounds [skip ci] * [comment audit] Shorten Windows visibility probe rationale [skip ci] * [comment audit] Condense embedded release and signing rationale [skip ci] * [comment audit] Retain current main test behavior in final reconciliation [skip ci] * [comment audit] Complete final comment review across current source inventory * [comment audit] Handle consolidated audit reports before final CI * [comment audit] Preserve credential storage census detail [skip ci] * [comment audit] Scale broad changed E2E selections with verified timing shards * [comment audit] Align E2E assertions with browser close and settled session metadata * [comment audit] Match existing E2Es to current resource and windowing contracts * [comment audit] Observe the shared server outage notice in reconnect coverage * [comment audit] Make existing renderer probes measure settled tab and PTY work * [comment audit] Align palette E2E selector with task search * [comment audit] Refresh remaining palette search selectors * [comment audit] Keep selector correction within selected audit specs * [comment audit] Await emoji fixture settings inside renderer evaluation * [comment audit] Cancel fixture fit retries between terminal tests --------- Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com> |
||
|
|
e7a567b178 |
fix(worktrees): explain the shallow-clone local base warning and let users stop the refresh (#33260)
* fix(worktrees): explain the shallow-clone local base warning and let users stop the refresh When a shallow clone hides the shared history, the local base fast-forward refusal now says so and shows the git fetch --unshallow command, instead of blaming local-only commits. Every refresh-failure toast also gets a "Stop updating local main" action that turns off Keep Local Main Up to Date. * feat(worktrees): add an Unshallow button that runs the fetch in a new terminal tab * feat(worktrees): rename the opt-out to Disable Check and explain it in a tooltip * fix(worktrees): harden the Unshallow launch Reuse createNewTerminalTab (owner-routed, no focus read) instead of a parallel launcher, report real host-launch outcomes before dismissing, only offer the button for shell-inert remote names, route activation by the creation host, ignore double clicks, and treat only a silent exit 1 from merge-base as a missing merge base. --------- Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com> |
||
|
|
ddfa8544a4 |
test: remove shallow capability checks and repair boundary assertions (#33224)
Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com> |
||
|
|
352b29a030 |
fix: preserve workspace review references when clearing active reviews (#32263)
Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com> |
||
|
|
57b5082404 |
feat(connectors): connector start and conversation links behind the off-by-default experimentalConnectors flag (B2b-1) (#31805)
* feat(connectors): connector seam lifecycle, conversation links and start behind experimentalConnectors (B2b-1) * feat(agent-write): opt-in launch cancel, approval wait and binding fence on the shared writer executeAgentLaunch takes an optional signal; sendTerminalAgentPrompt (and sendAgentTurn) take optional onPermission: 'wait' and expectedBinding. No existing caller sets them, so every current path is unchanged. * fix(agent-write): keep the default writer path main's; fence cancel and real submit boundary Only opted-in writes take the submit layer, so default timing and the leaf re-read are unchanged. Cancellation is rechecked before a structured-refusal terminal fallback, and an opted-in launch learns Enter from an onSubmitWritten callback at the write itself, OMP's combined frame included. * fix(connectors): pass the seam's signal to the launch, read links from each host's rows, and keep delivery ids on orchestration's durable receipts (B2b-1) * fix(agent-launch): a cancel after a failed startup spawn recovers the created workspace * fix(connectors): keep an unknown receipt once the launch published, and restore local-only metadata ownership (B2b-1) * fix(connectors): classify connector RPCs as host-owned; CLI start output claims a start only when definite |
||
|
|
92c8f3cbdc |
fix(ssh): stop floating terminal metadata from blocking SSH access links (#31963)
* fix(ssh): stop local-only workspace metadata from blocking SSH access links Unstamped floating and setup terminal metadata rows were projected onto every execution host, so the dependent-state census refused SSH access links and empty-host claims for every SSH target with orcad_migration_dependent_state. - Local-only workspace ids project only onto the local host, are stamped local on load, and their metadata writes always land on the local host. - The census counts an unstamped legacy row only when its repo is not registered solely on other hosts. - The SSH access error now names the blocking state. Builds on the approach in #31862 by Frank-zhu2000. Fixes #31498 Co-authored-by: Frank-zhu2000 <Frank-zhu2000@users.noreply.github.com> * fix(ssh): rehome remote-stamped local-only metadata, skip orphan rows, and word the SSH access error for a link * fix(persistence): keep a local or unstamped floating row over a remote copy when rehoming * fix(ssh): make the SSH access blocker copy switch exhaustive --------- Co-authored-by: Frank-zhu2000 <Frank-zhu2000@users.noreply.github.com> |
||
|
|
8967b10d41 |
chore: remove verified dead code and calibrate Knip entries (#31903)
* chore: remove verified dead code and calibrate Knip entries * fix: include mjs consumers in dead-code analysis |
||
|
|
7df3ff76d5 |
fix(sidebar): keep each host's activity, PR lane and retention apart for a same-id workspace (dark) (#31517)
* fix(sidebar): land flip-a's switch-on fixes on main, still dark (no flip) The fixes found while the sidebar ran from main's feed, without turning that on: ORCA_SIDEBAR_INPUTS_IS_MAIN stays false, and none of flip-a's stop-gaps come along. - Main stops counting a closed tab's terminal once it was asked to stop and no pane shows it; a failed or refused stop takes the request back, and a host out of contact stays unverifiable. - Main fetches a paired server's rows with the transport's normal bound, so a server slower than 5 s still lists. - A deferred unread raise flips the folder row it was decided for, and a read while the owner check is out cancels it. - Phone: no worktree.ps flash before the feed; a host's last installed feed stands in while a fresh read is out; it highlights its own last-opened workspace and a fold never scrolls; projects file under their project group with the desktop's indent; the collapse spinner shows only after 500 ms; "Reopen to update" keeps the rows on screen. - e2e helpers and journeys for the switch-on suite. * fix(sidebar): keep an owed relay stop marked, clear a thrown stop, and prune pending unread raises - A stop-and-wait that throws takes its stop request back, like one that fails. - A final stop the legacy relay still owes stays marked, since the relay retries it. - Pending unread raises are tracked only while one waits, so the map no longer grows. - The paired-server fallback poll test gives its third fetch a reply and asserts live. * fix(sidebar): key activity, review, retention and repo lookups by host, not bare id The feed published activity and PR facts by bare workspace id, so a same-id workspace on two hosts (a paired server and this desktop, or two servers) shared one host's facts. Activity and review are now keyed by row identity, retention sweeps per listed row, and the builder resolves each row's repo by its own host's registration. Bumps the feed semantics version to 3. Claude-Session: e1fdb3d9-6985-4693-84aa-9a9fad732103 * test(e2e): read a review fact by its row key Claude-Session: e1fdb3d9-6985-4693-84aa-9a9fad732103 * test(mobile): key fixture rows by row identity Claude-Session: e1fdb3d9-6985-4693-84aa-9a9fad732103 * fix(sidebar): count and label rows by their own repo registration; keep activity keyed through a failed listing Host header counts and mixed-host labels read the row's own registration, as the host filter and row now do, so one render cannot file a row under one host and count it under another. A failed listing keys activity by the last rows. Claude-Session: e1fdb3d9-6985-4693-84aa-9a9fad732103 * fix(sidebar): a slower older listing never overwrites a newer one's rows Claude-Session: e1fdb3d9-6985-4693-84aa-9a9fad732103 * test(sidebar): read the quiet-desktop PR fact by its row key Claude-Session: e1fdb3d9-6985-4693-84aa-9a9fad732103 * refactor(repos): the renderer re-exports the shared getRepoHostIdentity Claude-Session: e1fdb3d9-6985-4693-84aa-9a9fad732103 |
||
|
|
84a9f995da |
Sidebar P2: main publishes without waiting on git, and every source announces its changes (inert) (#31513)
* fix(sidebar): land flip-a's switch-on fixes on main, still dark (no flip) The fixes found while the sidebar ran from main's feed, without turning that on: ORCA_SIDEBAR_INPUTS_IS_MAIN stays false, and none of flip-a's stop-gaps come along. - Main stops counting a closed tab's terminal once it was asked to stop and no pane shows it; a failed or refused stop takes the request back, and a host out of contact stays unverifiable. - Main fetches a paired server's rows with the transport's normal bound, so a server slower than 5 s still lists. - A deferred unread raise flips the folder row it was decided for, and a read while the owner check is out cancels it. - Phone: no worktree.ps flash before the feed; a host's last installed feed stands in while a fresh read is out; it highlights its own last-opened workspace and a fold never scrolls; projects file under their project group with the desktop's indent; the collapse spinner shows only after 500 ms; "Reopen to update" keeps the rows on screen. - e2e helpers and journeys for the switch-on suite. * fix(sidebar): keep an owed relay stop marked, clear a thrown stop, and prune pending unread raises - A stop-and-wait that throws takes its stop request back, like one that fails. - A final stop the legacy relay still owes stays marked, since the relay retries it. - Pending unread raises are tracked only while one waits, so the map no longer grows. - The paired-server fallback poll test gives its third fetch a reply and asserts live. * feat(sidebar): main publishes cached snapshots and announces its own stores; detected, view and live-content domains (inert) * fix(sidebar): keep links confirmed only by a new git run, no rescan on metadata writes, no phone wait on detected checkouts * fix(sidebar): rescan the workspace rows when the Windows runtime default changes * test(sidebar): gate the 1000-workspace publish cost on its median * test(sidebar): name the git-run token type in the identity test |
||
|
|
b30a798dee |
fix(runtime): a terminal spawn names its host; the removed-project fence keys by host (R4-2) (#30798)
* fix(runtime): a terminal spawn names its host; a removed project's fence is per (project, host) acquireWorktreeTerminalSpawn takes the spawn's HostWorktreeTarget instead of a bare id plus an optional connectionId that defaulted to this machine. The removal fence records ExecutionHostIds and only counts surviving rows that run in this process, so a paired server's same-id residue row no longer readmits a removed local project's spawn. * fix(runtime): keep every surviving row in the spawn fence; normalize the spawn connection (review) |
||
|
|
8250c93f3e |
fix(sidebar): show the project name on ungrouped compact cards (#28890)
* fix(sidebar): show the project name on ungrouped compact cards Compact and new-card layouts kept the project in an icon chip, with the name only in a tooltip. The chip now shows the project name when the sidebar is not grouped by project. * fix(sidebar): show the project name on pinned chips too, muted and lowercase * docs(sidebar): describe repo identity placement, not the chip's contents * fix(sidebar): keep emoji project icons inside the chip |
||
|
|
7df5eaf5f7 |
feat(cleanup): the owning host records last terminal use for cleanup's last active (dark) (#30431)
* feat(cleanup): the owning host records last terminal use for cleanup's last active (dark) Owner decision Q2: with ORCA_SIDEBAR_INPUTS=main, the host that owns a workspace writes lastTerminalUseAt when a terminal takes input (window, phone or CLI, through the one input funnel) or a phone attaches, at most once a minute per workspace. Output never counts. Cleanup's last active reads it next to the Recent stamp, so a workspace used only from the phone never looks idle. Never a sort key. Co-Authored-By: Claude <noreply@anthropic.com> * fix(cleanup): count only typed input, skip paired-server terminals, share record routing --------- Co-authored-by: Claude <noreply@anthropic.com> |
||
|
|
a2946f0a65 |
chore(worktree): composeWorktreeId plus a ratchet on hand-built worktree ids (#30238)
Worktree ids built as `${repo.id}::${path}` by hand are how one worktree got two
spellings (#30150). New code composes them through src/shared/worktree/id.ts; the
ratchet counts the remaining hand-built ids per file (18) and may only go down. Six
byte-identical `${repo.id}::${repo.path}` sites move to getRepoMainWorktreeId.
|
||
|
|
dcfbec0377 |
refactor(execution-host): one isLocalExecutionHost in shared (#30241)
Replaces the agent-hooks copy, a private wrapper and five inline parseExecutionHostId(x)?.kind === 'local' checks. |
||
|
|
559ece08d3 |
feat(sidebar): the host that owns a workspace stamps its Recent and Smart keys (dark) (#30235)
* feat(sidebar): the owning host stamps sort keys on its workspace records (dark) * fix(sidebar): sort stamps title phase switch is exhaustive * fix(sidebar): sort stamps reconcile seeded tiers, respect host and WSL move fence, follow PTY swaps * fix(sidebar): one clock read per sort stamp event * fix(sidebar): a first idle title is not Done; the WSL fence spares SSH records |
||
|
|
9810a5cc92 |
refactor(worktree): one comparison-only worktreeIdentityKey replaces three key wrappers (#30217)
worktreeIdComparisonKey (nullable '::' key), runtimeWorktreeIdentityKey ('\0' key) and
runtimeWorktreeLookupKey (JSON tuple) all folded the same path spelling. They become one total
key in shared/worktree/id.ts; a malformed id keys as itself. Also fixes id.test.ts literals that
were missing a colon, which made the WSL/UNC parity assertions compare null to null.
|
||
|
|
b8264e860b |
Main drops the layout of workspaces the catalog no longer validates (dark, layout-owner build only) (#30177)
* feat(layout): main drops the layout of workspaces the catalog no longer validates (dark, layout-owner build only) * rename the paired-host check so the switch-off bundle keeps its names * a repo id two hosts register skips the listing purge |
||
|
|
6c45958c8c | refactor(renderer): tighten the host-identity stack (#30124) | ||
|
|
299dcb86fc | fix(worktrees): a host-scoped purge leaves a surviving twin's tabs and edits alone (#30030) | ||
|
|
2d33cd965f | refactor(identity): brand host-scoped worktree and repo keys for new code (#29990) | ||
|
|
9cc8b80817 |
fix(worktrees): a preparation another machine owns is never reclaimed as stale (#29957)
The preparation lock reason named only the owner's pid, so a host sharing the repo's disk (an NFS home) read another machine's in-progress preparation as abandoned and discarded it. The reason now carries the writer's machine identity (optional, appended after the fields older builds read); a different or unknown machine is unverifiable, and a reason without one is judged by pid only on a local disk. |
||
|
|
23b60a5d07 |
fix(worktree): offer Force Delete with a plain warning when a checkout has submodules (#28878)
* fix(worktree): remove a clean checkout that contains submodules Git refuses to remove any worktree with an initialized submodule unless --force is set, even when nothing is uncommitted. After that specific refusal, re-check the checkout including files inside the submodule and pass --force once. A dirty submodule still blocks the delete. A second --force is not added, because that would also clear a Git lock. * fix(worktree): offer Force Delete for a checkout Git refuses over submodules Drop the automatic --force retry. A forced removal deletes the submodule's repository under .git/worktrees/<id>/modules, including commits, local branches and stashes that were never pushed, and the parent's dirty check cannot see them (the retry #24602 removed for the same reason). Instead classify Git's "working trees containing submodules cannot be moved or removed" refusal as a new Force Delete reason, 'submodules', after the lock check and the force guard. The renderer classifies the error string, so older SSH and paired hosts get it too with no wire change. The toast, the delete dialog and the Space manager row show one plain warning naming what Force Delete would lose instead of Git's text. Keep --ignore-submodules=none in the pre-teardown dirty check so a user's ignore setting cannot hide submodule edits from it. Replace the retry test with one real-Git test: a clean checkout whose submodule holds a local-only commit is refused without --force, stays registered, keeps the commit, and its refusal classifies as 'submodules'. * fix(worktree): clarify the submodule Force Delete warning and hide View for it The warning now says Git deletes the workspace only with Force Delete, the submodule refusal no longer offers View (there are no changes to show), the real-Git test pins that Force Delete then succeeds, and a comment no longer describes the removed retry. * fix(worktree): name submodules when a changed submodule blocks the delete, and clear a stale delete toast With a commit inside a submodule that the parent has not recorded, the parent sees `M vendor`, so Orca's dirty check refused first with the generic "It has changed files". Force Delete then destroyed the never-pushed submodule commit without warning. On a dirty refusal the check now asks `git status --porcelain=v2` whether a changed entry is a submodule, and if so reports a refusal the app shows with the submodule Force Delete warning. The detail keeps the dirty sentence, so older apps still offer Force Delete. After the Space manager deleted a workspace (its row Force, or a later delete), the failure toast for that workspace stayed up and still offered Force Delete. A Space delete now dismisses it. * fix(worktree): warn about submodules on any refused delete of a checkout that has them Review found the warning still vanished when a submodule commit was already recorded and some other file was dirty: the parent's status shows no submodule change, yet Force Delete destroys the commit. The changed-files check now asks whether the checkout has a populated submodule (a .gitmodules path holding a .git, as Git's own refusal checks) instead of whether one changed. The refusal keeps the changed paths after the detail, and View stays available since there are changes to view. * test(worktree): mock the .gitmodules probe as missing in the preflight unit tests * refactor(worktree): attach the dirty-check status without a type assertion * fix(worktree): key the delete failure toast by host and worktree id A Space delete on one host must not dismiss another host's failure toast for a worktree with the same id. * fix(worktree): dismiss the failure toast by the store row's host after a Space row Force |
||
|
|
005f06bbc7 |
Run automated workspace setup in a dedicated Setup tab (#29819)
* Run automated workspace setup in a dedicated tab * Preserve native-chat setup route coverage after placement retirement * Preserve Setup tab design and independent review evidence * Classify activation test fixtures consistently for routing checks * Sync runtime English catalog after removing setup preferences * Update listener savings model after retiring setup action |
||
|
|
13084ba347 |
feat(mobile): long-press a workspace to see its linked PR, issues and notes (#29225)
* fix(mobile): phone server rows follow the desktop's other-client and sleeping filters A phone created through a desktop now records whom it acted for, so the desktop's Hide other-client workspaces filter keeps the user's own phone workspaces (locally and on servers) and hides only other people's. The desktop relay marks each server row its sidebar would hide, and the rows its own browser or chat tabs keep awake; the phone applies both. * test(e2e): dedupe sidebar row ids in the visibility spec * fix(mobile): address review: owner naming, old-server relayed phones, scope trim * refactor: move the sidebar's project section grouping into shared The project sections the desktop sidebar draws under Group by Project are built from the project/setup projection, the per-host catalog fold, the endpoint host stamp, and the section labelling and ordering rules. Move the pure parts of each into src/shared (no behaviour change) so the paired phone can call the same code instead of guessing the grouping. * fix(mobile): group by Project with the desktop sidebar's own sections The phone grouped Project sections by repo display name, so it merged checkouts the desktop keeps apart (several local stablyai/orca checkouts, or a server's copy) and split what the desktop merges. The phone now reads each host's repo.list, project.list and projectHostSetup.list (the desktop's own and each server's through the relay), folds them with the renderer's shared catalog fold and projection merge, and builds sections with the desktop's shared grouping, labelling and ordering code. Keys, labels and Recent order equal the sidebar's; a host that predates the project lists gets the repo-derived projection, as the desktop does. Header colour and icon come from the section's anchor repo and rows from their own repo by id, as on the desktop. The repo filter lists every catalog repo by id per host instead of merging same-named repos. Group-by labels read Project and None. project.list and projectHostSetup.list are routed for paired phones (read-only, workspace permission). * refactor(mobile): record a phone's owner as creator and fold desktop tab activity into the host flag A phone now records its owner as the creator (this host for a directly paired phone, the relaying desktop for a delegated one), so the shared rule is unchanged and onBehalfOf is gone. The relay ORs the desktop's saved browser/chat tabs into hasHostSidebarActivity, so the phone's status dot follows too, and local worktree.ps counts the same signals. The old-server fallback is removed. * test(e2e): poll the phone and the sidebar together in the visibility spec * fix(mobile): one Project-section builder for desktop and phone, with the desktop's repo order Review fixes. The sidebar's buildOrderedGroups now calls the shared buildProjectSections for Group by Project (notice and pending repos are optional inputs), so desktop and phone run one function. The phone ranks manual header order by its folded catalog order, which is each host's repo.list order with the desktop's own repos first, as the renderer loads them; only the desktop's cross-host manualRepoOrder (pairing-local) is unknown to it. The projection selector is now a memo over the shared resolver. A transient project-list failure keeps that host's last catalog; only forbidden or method_not_found falls back to the repo-derived projection. Catalog rows are checked on every member grouping reads, and the parity test answers the renderer's real catalog fetch, includes a server's own project rows, and fixes Manual and Recent orders that differ. * fix(mobile): use the repo-derived catalog until a host's project lists first answer * fix(mobile): the desktop serves its sidebar's Project catalog instead of the phone fetching it The phone fetched repo.list, project.list and projectHostSetup.list from the desktop and every relay-ready server each minute and folded them itself, so rows from offline or older servers fell back to repo sections and keys moved on cold start. The desktop renderer now publishes the inputs its own Project headers use (state.repos in sidebar order, the projection selector's projects and setups, projectOrderBy) on the runtime window graph it already sends to main, only when they change. Main keeps the last one and answers it on a new paired-desktop read, mobileRelay.projectSections. The phone reads it with its repo metadata and groups with the shared builder; a desktop that predates the read (forbidden or method_not_found) leaves the phone grouping rows by repo from the desktop's repo.list, and a transient failure keeps the last catalog. The payload carries only grouping and label fields, no hooks, credentials or uploaded icons. The phone's own catalog fetching and its project.list/projectHostSetup.list routes are removed. * fix(mobile): keep uploaded repo icons on the phone, and isolate the snapshot tests * test(e2e): the desktop window serves its sidebar's Project catalog to the phone * chore(i18n): regenerate the runtime English catalog (drift already on main) * fix(mobile): read the Project catalog as its own request, outside the recorded repo-metadata flow The extra read inside the repo-metadata refresh stalled its recorded RPC sequence. It now runs beside it with its own throttle. The repo-metadata goldens are re-recorded for the intended state change: the name-keyed colour, icon and id maps became desktopRepos. * refactor(sidebar): one shared Workspace options filter and sort evaluator The sidebar, the jump palette and the options badge each applied the Workspace options filters with their own copy of the rules. They now call one pure evaluator in src/shared/workspace-view, which reads each row through a small fact reader so the phone can call it next with the facts its host serves. The sort comparator moves there too, with each surface supplying its own Agent activity rank. No behaviour change: a parity test runs the old sidebar chain and the new evaluator over every toggle combination, and the old comparator against the new one for each sort mode. * wip: workspace view descriptors * refactor(sidebar): address review of the shared Workspace options evaluator One predicate builder instead of a per-row scope argument; one exemption-narrowing function taking the hide flag; consumers import the shared predicates directly; Agent activity ties fall to the label in the shared comparator. The activation filter lift reuses the shared predicates. The parity test now covers other-client rows, live agent attention classes and the sort-label map. * fix(mobile): fetch the Project catalog at once when the screen moves to another host The refresh window is now kept per client, so a host screen reused for another desktop doesn't wait out the previous host's 60s window before showing its Project headers. * wip: desktop option items from shared choices * refactor(sidebar): the Workspace options menu renders from one shared option list src/shared/workspace-view/workspace-view-options.ts lists every Workspace options control (Hosts, Projects, Group by, Sort by, Project order, Card layout, Show properties or Card display, and each filter) with its section, its English copy, its choices and when it shows. The desktop menu walks that list and renders each option through a table keyed on the option key, so a new option fails to compile until the desktop has a control for it; the phone sheet will do the same. The choice lists (sort modes, project order, card layout, agent activity layout, card properties) are derived from the shared ids, and a test checks the desktop's English copy matches the shared copy. The unused classic branch of the card property list is removed. No visible change. * wip: row facts * refactor(sidebar): address review of the shared Workspace options list The show rules get their own tests with fixed expectations (the cases the old filter-section test covered, plus the rest), and the menu test checks each option's own control rather than any matching text. Card display rows toggle one property each, with the folder-path copy in shared. The unused Status and Tasks copy left by the removed classic card list is deleted from the catalogs. Filter rows write through the store directly; files are named for what they hold. * feat(runtime): worktree.ps carries the Workspace options row facts Each worktree.ps row now says whether it is the project's default-branch workspace, created by an automation, created by the CLI, or on a detached HEAD, answered by the same shared predicates the desktop sidebar filters with. The phone has none of these facts today, so its copies of the filters either guess (default branch) or are missing. Additive and inert: optional fields that nothing reads yet. An older host omits them; readers must treat absence as 'not that kind', so the row stays visible. Server rows relayed through the desktop pass them through unchanged. * feat(mobile): the desktop's own phone reads and writes its other-client filter ui.get and ui.set strip the pairing-local UI fields for every paired client. That rule exists for clients with their own host catalog (a desktop paired to a server, a web client). A phone has none: it shows its desktop's view, so 'Hide other-client workspaces' must be one shared value. For the host's own phone (clientKind mobile, no relaying parent) ui.get now always answers the field and ui.set persists it; delegated phones, paired desktops and web clients keep the stripping rule. The phone's view-settings schema reads the rest of the Workspace options values it does not read yet (the automation, CLI and detached-HEAD filters, project order, card properties, agent activity layout), each salvaged to absent so an older desktop keeps the phone's local value. * feat(runtime): address review of the worktree.ps row facts Name the fact defaultBranch, and say what absence means for it: a reader keeps its own guess, because 'hide default branch' must not start hiding or showing rows an older host never described. Folder workspace rows get the facts too, so every row from a newer host carries them. The desktopOtherClient comment now says it is a verdict, not a fact. The test covers the rows where the host disagrees with the phone's current guess. * wip: phone uses the shared evaluator * feat(mobile): address review of the phone's other-client filter mirror One isHostOwnPhone predicate decides both who shares the filter value (ui.get/ui.set) and who gets the other-client row marks, so the two cannot drift. The pairing-local field list notes the exception. Tests cover a paired desktop still being stripped, and a phone write that leaves the field out not touching the host value. * feat(mobile): the phone filters and sorts with the desktop's shared Workspace options rules The phone's own filter chain and comparator are replaced by the shared evaluator, reading the facts the host stamps on each row. Phone filter state takes the desktop's setting names and carries every filter; the badge and Clear filters match the desktop's. The desktop's other-client marks become facts the phone filters with the shared value. A parity test runs the same workspaces through the desktop's reading and the phone's (facts stamped as the host stamps them) over every filter combination, folder projects included. * wip: phone view state carries project order and card props * fix(mobile): host scope in shared filters, patch-key writes, live view push Folds the phone's host filter into the shared filters so Clear and the badge agree, writes back only the touched filter keys, and pushes workspaceViewChanged so an open phone list re-reads desktop Workspace options. * fix(mobile): Clear sends only changed filters; skip a view re-read older than a local edit An older host's strict ui.set refuses a key it lacks, so Clear names only what differs. A ui.get that started before a local edit no longer overwrites it; the edit's own change event re-reads. The change publisher's field list now lives with it and derives the filter fields. * feat(mobile): the phone's Workspace options sheet, rendered from the desktop's shared option list The Filter drawer becomes the desktop's Workspace options: hosts, projects with search, group, sort, project order, card layout and properties, card display, and every filter. Each option renders through a table keyed on the shared option keys, so a new desktop option fails to compile until the phone has a control. Sort and group pickers use the desktop's words; Repo reads Project. * fix(mobile): Workspace options sheet review fixes Hosts read as on the desktop (none checked while all show; the last shown host is disabled). Project order falls back to the served value until the shared one is read. Card layout and card style changes now push to an open phone. Pick-one rows read as radio buttons. Simpler wiring: flat card state, the preset flag rides the patch, shared choices used directly. * fix(mobile): reset project order on host switch; simpler preset-flag handling * fix(workspace-options): all hosts and clear reach the desktop; one filter key list drives every surface Host visibility: the legacy host scope fallback moves to shared code; the phone reads it, and the host resets the scope whenever a client writes the host list, so All hosts and Clear stick. An older desktop that keeps the other-client filter to itself has its marks treated as hide. Badge counts, is-anything-filtered, Clear and reveal derive from the shared filter keys on the desktop sidebar and the kanban filter menu. The phone uses the desktop's group and sort values, holds its view in one state value, and reads its UI fields from one shared list. * feat(workspace-options): Status and PR grouping read the shared tables The phone groups by status with the desktop's status helpers and default catalog (its own copy had the defaults in reverse order) and by PR with a shared lane table, so its labels now read In review / In progress as the desktop's do. The desktop's PR lane decision ends in the same shared state-to-lane step. * feat(mobile): phone rows follow Card layout, Show properties, Card display and agent activity One shared helper (getWorktreeCardDetails) decides which card details show for the chosen properties and layout; the desktop card's flags and the phone row both read it. The phone row hides the PR badge, note/issue/Linear glyphs, host badge and agent rows the desktop turns off, and the Full list agent layout shows every agent row. * test(sidebar): folder rows survive restrictive filters; create grace ignores non-finite times * fix(mobile): write the host scope with the host list, and wait for the first read before forcing the other-client filter An older desktop narrows back to its legacy single-host scope when the phone writes only a null host list, so the phone now also writes the scope for a null or one-host list. The other-client fallback waits for the first ui.get so a new desktop's marks don't hide rows before its filter value arrives. Inline the clear-filters key list and note the PR state asymmetry. * test(mobile): re-record view-settings goldens for the unknown other-client state * fix(mobile): let a tall bottom drawer scroll on the page The drag wrapper around the drawer's ScrollView had no flex limit, so it grew to its content and the sheet clipped it instead of scrolling. The full Workspace options sheet is the first drawer tall enough to hit this. * fix(sidebar): reuse the palette's default host instead of reading the focus setting again * fix(sidebar): read the host options once in Workspace options and pass them to the Hosts item * fix: parse the ui.get reply into a named type, fix the row memo test's filters, prune the focus-read baseline * test(mobile): give the project catalog fixture its project order * feat(mobile): name the options chip Workspace options, as the desktop does The chip keeps the active-filter count and reads to screen readers with the desktop trigger's label. The pluralized filter count moves to shared code so both surfaces word it the same way. * fix(mobile): show the Workspace options count as a corner badge so the chip row fits "Workspace options (2)" squeezed the Sort and Group chips to ellipses at phone width. The count moves to a corner badge, as on the desktop trigger, and the chip drops its icon, which the desktop trigger does not show either. * refactor(sidebar): one switch-to-kind table, and fold the leftover helper files back - The four kind switches come from WORKSPACE_KIND_SWITCH in workspace-kinds; the shared evaluator and main's phone-push check both apply it, and the filter type derives its switch fields from it. - git-identity.ts folds back into git-identity-display.ts, as on main. - SidebarFilterState moves into sidebar-filter-actions.ts; visible-worktree-kinds.ts is gone. - buildWorkspaceRowPredicate is module-private. * feat(mobile): Workspace options sheet in the desktop menu's shape The sheet showed every choice of every option, so the filters sat screens down. It now follows the desktop menu: Hosts and Projects rows with their value, Group by as a segmented control, Sort by, Project order, Card layout and Show properties as rows that open a page of choices, and the filters as switches. Each option's control (an inline row, or a value plus a page) still comes from one table keyed by the shared option list. The segmented control is the voice settings one, extracted so both screens share it. * fix(mobile): Workspace options review fixes - A page whose option stops showing is forgotten, so it cannot reopen by itself. - Only the switch takes the tap on a filter row, so the web page writes once. - Sections split their rows in one pass; unused heading styles go. * fix(mobile): neutral filter switches on the page react-native-web paints an on-switch thumb teal unless given activeThumbColor, which React Native's types omit. MobileSwitch applies the app's switch colors, and its web sibling also sets that prop. * refactor(mobile): one MobileSwitch file, adding the web-only thumb color on web * fix(mobile): Show properties reads Hover under Compact, as the desktop does The row's value now comes from getWorkspaceCardPropertiesValue in the shared options, which the desktop trigger uses too: Hover under the compact layout, otherwise how many properties show. Card display shows no value, as on the desktop. * test(mobile): find the renamed Workspace options chip in the page drawer render check * feat(mobile): drop the Sort and Group chips; the Workspace options chip covers both The desktop has only its options button, and the sheet now holds Sort by and Group by. The chips' pickers, their screen state and the sort label go with them. * feat(mobile): option pages slide in from the right and keep the sheet's height The main sheet stays laid out under an open page, so the drawer keeps its height going in and out. The page slides in over 200 ms and back out on back (the row, or Android's back key), instantly under reduced motion. The reduced-motion hook moves to platform/ now that two screens use it. * feat(mobile): Compact rows fold into one title line like the desktop compact card The phone's Compact layout now moves repo identity into a title-row icon chip and drops the identity meta row (repo, host, branch or folder path, child badge), matching the desktop's compact card. The rule lives next to getWorktreeCardDetails as getWorktreeCardLayout, and the desktop presentation reads it too so both surfaces share one decision. * fix(mobile): keep a remote host glyph on compact rows; pass the card layout from the list Review follow-ups: compact rows show an icon-only host glyph for remote projects as the desktop compact card does, the list computes the layout once next to the card details, and a new-card-style row test. * fix(mobile): option pages fill the sheet and open at its top - The page layer grows to the pager's height, so no part of the main sheet shows below a short page. - The drawer takes a scrollResetKey; the sheet passes the open page, so a page opened from a row low on a scrolled sheet starts at the top. - The main sheet's height is measured under a page too, and the pager state lives in the sheet so it resets when the drawer closes. - A misplaced doc comment goes back to its function. * fix(mobile): phone rows show the Jira, automation and CLI glyphs; the options pager forgets a gone page in an effect * fix(mobile): Jira glyph sits before Linear as on the desktop; Jira reader tests beside it * feat(mobile): long-press a workspace to see its details The phone's long-press sheet now lists the workspace's linked PR and issues (tap to open), who created it, its notes and live ports (Copy), the phone's take on the desktop sidebar card's hover card. Folder workspaces get the sheet too, with details only. The desktop lets phones run workspacePorts.scanHost (read only). * fix(mobile): details read links the desktop's way and stay one-off Links come through the shared getWorkspaceAttachments, so references the desktop keeps apart stay apart, in the host's order with reviews first. The branch review read no longer takes the host's active-branch slot, each read re-runs only for its own inputs, a reopened sheet reads afresh, and Copy's confirmation never outlives the sheet. * fix(mobile): details clear on close without an effect; lint findings * fix(mobile): the phone leaves Ports out of Show properties and Card display * refactor(mobile): clearer names and tests for the phone's hidden card properties * feat(mobile): details sheet heading carries the origin; no ports As Jinwoo approved: the sheet heads with the workspace (primary text), its short branch, and a quiet origin line (automation name, or Orca CLI). The group below holds only linked reviews and tasks, then notes. Pin uses the pin icon. Ports are left out of the phone, so the desktop change that let phones scan them is reverted. * test(mobile): details section render helper matches its props * fix(mobile): details review read asks by branch; review merge by number; comments * fix(mobile): details rows share the action rows' text column * refactor(mobile): details sheet reads only the row The sheet no longer fetches. The host stamps two optional row fields: automationName beside automationCreated, and the branch PR's title, url and checks on linkedPR (from the GitHub cache it already reads). Older hosts omit them and the sheet shows what they send. Links match by provider, type and number; the reference label is one shared helper the desktop card uses too. Folder rows keep no long-press. One sheet file plus the pure model. * fix: review nits on the row-only details sheet * refactor(mobile): one view state and one settings read for the phone's Workspace options; tighter card glyph wiring - The phone's Card layout and card style now live in its view state; one parallel settings.get reads them with pinned placement beside ui.get. - The Card layout preset flag is a write option, no longer a view field or a change-event field. - getWorktreeCardDetails reads the raw desktop settings and resolves the layout once; the desktop card computes it once in its foundation. - The row computes its glyph presence in one place; Ports is hidden through one set. - Drops the vanished-page effect, the native-only a11y props and the web-only switch branch. * fix(mobile): the list settings read applies each half on its own, with tests; pinned placement changes announce themselves * fix(mobile): compact host glyph tests for older rows and the new card style; truer comments * fix(mobile): Card layout writes the setting before its preset; Card display names folder paths when the desktop has folder workspaces * fix(mobile): match the branch PR by repo and link, not number alone |
||
|
|
7538fa9466 |
feat(mobile): the phone's Workspace options sheet, with every desktop option (#28997)
* fix(mobile): phone server rows follow the desktop's other-client and sleeping filters A phone created through a desktop now records whom it acted for, so the desktop's Hide other-client workspaces filter keeps the user's own phone workspaces (locally and on servers) and hides only other people's. The desktop relay marks each server row its sidebar would hide, and the rows its own browser or chat tabs keep awake; the phone applies both. * test(e2e): dedupe sidebar row ids in the visibility spec * fix(mobile): address review: owner naming, old-server relayed phones, scope trim * refactor: move the sidebar's project section grouping into shared The project sections the desktop sidebar draws under Group by Project are built from the project/setup projection, the per-host catalog fold, the endpoint host stamp, and the section labelling and ordering rules. Move the pure parts of each into src/shared (no behaviour change) so the paired phone can call the same code instead of guessing the grouping. * fix(mobile): group by Project with the desktop sidebar's own sections The phone grouped Project sections by repo display name, so it merged checkouts the desktop keeps apart (several local stablyai/orca checkouts, or a server's copy) and split what the desktop merges. The phone now reads each host's repo.list, project.list and projectHostSetup.list (the desktop's own and each server's through the relay), folds them with the renderer's shared catalog fold and projection merge, and builds sections with the desktop's shared grouping, labelling and ordering code. Keys, labels and Recent order equal the sidebar's; a host that predates the project lists gets the repo-derived projection, as the desktop does. Header colour and icon come from the section's anchor repo and rows from their own repo by id, as on the desktop. The repo filter lists every catalog repo by id per host instead of merging same-named repos. Group-by labels read Project and None. project.list and projectHostSetup.list are routed for paired phones (read-only, workspace permission). * refactor(mobile): record a phone's owner as creator and fold desktop tab activity into the host flag A phone now records its owner as the creator (this host for a directly paired phone, the relaying desktop for a delegated one), so the shared rule is unchanged and onBehalfOf is gone. The relay ORs the desktop's saved browser/chat tabs into hasHostSidebarActivity, so the phone's status dot follows too, and local worktree.ps counts the same signals. The old-server fallback is removed. * test(e2e): poll the phone and the sidebar together in the visibility spec * fix(mobile): one Project-section builder for desktop and phone, with the desktop's repo order Review fixes. The sidebar's buildOrderedGroups now calls the shared buildProjectSections for Group by Project (notice and pending repos are optional inputs), so desktop and phone run one function. The phone ranks manual header order by its folded catalog order, which is each host's repo.list order with the desktop's own repos first, as the renderer loads them; only the desktop's cross-host manualRepoOrder (pairing-local) is unknown to it. The projection selector is now a memo over the shared resolver. A transient project-list failure keeps that host's last catalog; only forbidden or method_not_found falls back to the repo-derived projection. Catalog rows are checked on every member grouping reads, and the parity test answers the renderer's real catalog fetch, includes a server's own project rows, and fixes Manual and Recent orders that differ. * fix(mobile): use the repo-derived catalog until a host's project lists first answer * fix(mobile): the desktop serves its sidebar's Project catalog instead of the phone fetching it The phone fetched repo.list, project.list and projectHostSetup.list from the desktop and every relay-ready server each minute and folded them itself, so rows from offline or older servers fell back to repo sections and keys moved on cold start. The desktop renderer now publishes the inputs its own Project headers use (state.repos in sidebar order, the projection selector's projects and setups, projectOrderBy) on the runtime window graph it already sends to main, only when they change. Main keeps the last one and answers it on a new paired-desktop read, mobileRelay.projectSections. The phone reads it with its repo metadata and groups with the shared builder; a desktop that predates the read (forbidden or method_not_found) leaves the phone grouping rows by repo from the desktop's repo.list, and a transient failure keeps the last catalog. The payload carries only grouping and label fields, no hooks, credentials or uploaded icons. The phone's own catalog fetching and its project.list/projectHostSetup.list routes are removed. * fix(mobile): keep uploaded repo icons on the phone, and isolate the snapshot tests * test(e2e): the desktop window serves its sidebar's Project catalog to the phone * chore(i18n): regenerate the runtime English catalog (drift already on main) * fix(mobile): read the Project catalog as its own request, outside the recorded repo-metadata flow The extra read inside the repo-metadata refresh stalled its recorded RPC sequence. It now runs beside it with its own throttle. The repo-metadata goldens are re-recorded for the intended state change: the name-keyed colour, icon and id maps became desktopRepos. * refactor(sidebar): one shared Workspace options filter and sort evaluator The sidebar, the jump palette and the options badge each applied the Workspace options filters with their own copy of the rules. They now call one pure evaluator in src/shared/workspace-view, which reads each row through a small fact reader so the phone can call it next with the facts its host serves. The sort comparator moves there too, with each surface supplying its own Agent activity rank. No behaviour change: a parity test runs the old sidebar chain and the new evaluator over every toggle combination, and the old comparator against the new one for each sort mode. * wip: workspace view descriptors * refactor(sidebar): address review of the shared Workspace options evaluator One predicate builder instead of a per-row scope argument; one exemption-narrowing function taking the hide flag; consumers import the shared predicates directly; Agent activity ties fall to the label in the shared comparator. The activation filter lift reuses the shared predicates. The parity test now covers other-client rows, live agent attention classes and the sort-label map. * fix(mobile): fetch the Project catalog at once when the screen moves to another host The refresh window is now kept per client, so a host screen reused for another desktop doesn't wait out the previous host's 60s window before showing its Project headers. * wip: desktop option items from shared choices * refactor(sidebar): the Workspace options menu renders from one shared option list src/shared/workspace-view/workspace-view-options.ts lists every Workspace options control (Hosts, Projects, Group by, Sort by, Project order, Card layout, Show properties or Card display, and each filter) with its section, its English copy, its choices and when it shows. The desktop menu walks that list and renders each option through a table keyed on the option key, so a new option fails to compile until the desktop has a control for it; the phone sheet will do the same. The choice lists (sort modes, project order, card layout, agent activity layout, card properties) are derived from the shared ids, and a test checks the desktop's English copy matches the shared copy. The unused classic branch of the card property list is removed. No visible change. * wip: row facts * refactor(sidebar): address review of the shared Workspace options list The show rules get their own tests with fixed expectations (the cases the old filter-section test covered, plus the rest), and the menu test checks each option's own control rather than any matching text. Card display rows toggle one property each, with the folder-path copy in shared. The unused Status and Tasks copy left by the removed classic card list is deleted from the catalogs. Filter rows write through the store directly; files are named for what they hold. * feat(runtime): worktree.ps carries the Workspace options row facts Each worktree.ps row now says whether it is the project's default-branch workspace, created by an automation, created by the CLI, or on a detached HEAD, answered by the same shared predicates the desktop sidebar filters with. The phone has none of these facts today, so its copies of the filters either guess (default branch) or are missing. Additive and inert: optional fields that nothing reads yet. An older host omits them; readers must treat absence as 'not that kind', so the row stays visible. Server rows relayed through the desktop pass them through unchanged. * feat(mobile): the desktop's own phone reads and writes its other-client filter ui.get and ui.set strip the pairing-local UI fields for every paired client. That rule exists for clients with their own host catalog (a desktop paired to a server, a web client). A phone has none: it shows its desktop's view, so 'Hide other-client workspaces' must be one shared value. For the host's own phone (clientKind mobile, no relaying parent) ui.get now always answers the field and ui.set persists it; delegated phones, paired desktops and web clients keep the stripping rule. The phone's view-settings schema reads the rest of the Workspace options values it does not read yet (the automation, CLI and detached-HEAD filters, project order, card properties, agent activity layout), each salvaged to absent so an older desktop keeps the phone's local value. * feat(runtime): address review of the worktree.ps row facts Name the fact defaultBranch, and say what absence means for it: a reader keeps its own guess, because 'hide default branch' must not start hiding or showing rows an older host never described. Folder workspace rows get the facts too, so every row from a newer host carries them. The desktopOtherClient comment now says it is a verdict, not a fact. The test covers the rows where the host disagrees with the phone's current guess. * wip: phone uses the shared evaluator * feat(mobile): address review of the phone's other-client filter mirror One isHostOwnPhone predicate decides both who shares the filter value (ui.get/ui.set) and who gets the other-client row marks, so the two cannot drift. The pairing-local field list notes the exception. Tests cover a paired desktop still being stripped, and a phone write that leaves the field out not touching the host value. * feat(mobile): the phone filters and sorts with the desktop's shared Workspace options rules The phone's own filter chain and comparator are replaced by the shared evaluator, reading the facts the host stamps on each row. Phone filter state takes the desktop's setting names and carries every filter; the badge and Clear filters match the desktop's. The desktop's other-client marks become facts the phone filters with the shared value. A parity test runs the same workspaces through the desktop's reading and the phone's (facts stamped as the host stamps them) over every filter combination, folder projects included. * wip: phone view state carries project order and card props * fix(mobile): host scope in shared filters, patch-key writes, live view push Folds the phone's host filter into the shared filters so Clear and the badge agree, writes back only the touched filter keys, and pushes workspaceViewChanged so an open phone list re-reads desktop Workspace options. * fix(mobile): Clear sends only changed filters; skip a view re-read older than a local edit An older host's strict ui.set refuses a key it lacks, so Clear names only what differs. A ui.get that started before a local edit no longer overwrites it; the edit's own change event re-reads. The change publisher's field list now lives with it and derives the filter fields. * feat(mobile): the phone's Workspace options sheet, rendered from the desktop's shared option list The Filter drawer becomes the desktop's Workspace options: hosts, projects with search, group, sort, project order, card layout and properties, card display, and every filter. Each option renders through a table keyed on the shared option keys, so a new desktop option fails to compile until the phone has a control. Sort and group pickers use the desktop's words; Repo reads Project. * fix(mobile): Workspace options sheet review fixes Hosts read as on the desktop (none checked while all show; the last shown host is disabled). Project order falls back to the served value until the shared one is read. Card layout and card style changes now push to an open phone. Pick-one rows read as radio buttons. Simpler wiring: flat card state, the preset flag rides the patch, shared choices used directly. * fix(mobile): reset project order on host switch; simpler preset-flag handling * fix(workspace-options): all hosts and clear reach the desktop; one filter key list drives every surface Host visibility: the legacy host scope fallback moves to shared code; the phone reads it, and the host resets the scope whenever a client writes the host list, so All hosts and Clear stick. An older desktop that keeps the other-client filter to itself has its marks treated as hide. Badge counts, is-anything-filtered, Clear and reveal derive from the shared filter keys on the desktop sidebar and the kanban filter menu. The phone uses the desktop's group and sort values, holds its view in one state value, and reads its UI fields from one shared list. * feat(workspace-options): Status and PR grouping read the shared tables The phone groups by status with the desktop's status helpers and default catalog (its own copy had the defaults in reverse order) and by PR with a shared lane table, so its labels now read In review / In progress as the desktop's do. The desktop's PR lane decision ends in the same shared state-to-lane step. * feat(mobile): phone rows follow Card layout, Show properties, Card display and agent activity One shared helper (getWorktreeCardDetails) decides which card details show for the chosen properties and layout; the desktop card's flags and the phone row both read it. The phone row hides the PR badge, note/issue/Linear glyphs, host badge and agent rows the desktop turns off, and the Full list agent layout shows every agent row. * test(sidebar): folder rows survive restrictive filters; create grace ignores non-finite times * fix(mobile): write the host scope with the host list, and wait for the first read before forcing the other-client filter An older desktop narrows back to its legacy single-host scope when the phone writes only a null host list, so the phone now also writes the scope for a null or one-host list. The other-client fallback waits for the first ui.get so a new desktop's marks don't hide rows before its filter value arrives. Inline the clear-filters key list and note the PR state asymmetry. * test(mobile): re-record view-settings goldens for the unknown other-client state * fix(mobile): let a tall bottom drawer scroll on the page The drag wrapper around the drawer's ScrollView had no flex limit, so it grew to its content and the sheet clipped it instead of scrolling. The full Workspace options sheet is the first drawer tall enough to hit this. * fix(sidebar): reuse the palette's default host instead of reading the focus setting again * fix(sidebar): read the host options once in Workspace options and pass them to the Hosts item * fix: parse the ui.get reply into a named type, fix the row memo test's filters, prune the focus-read baseline * test(mobile): give the project catalog fixture its project order * feat(mobile): name the options chip Workspace options, as the desktop does The chip keeps the active-filter count and reads to screen readers with the desktop trigger's label. The pluralized filter count moves to shared code so both surfaces word it the same way. * fix(mobile): show the Workspace options count as a corner badge so the chip row fits "Workspace options (2)" squeezed the Sort and Group chips to ellipses at phone width. The count moves to a corner badge, as on the desktop trigger, and the chip drops its icon, which the desktop trigger does not show either. * refactor(sidebar): one switch-to-kind table, and fold the leftover helper files back - The four kind switches come from WORKSPACE_KIND_SWITCH in workspace-kinds; the shared evaluator and main's phone-push check both apply it, and the filter type derives its switch fields from it. - git-identity.ts folds back into git-identity-display.ts, as on main. - SidebarFilterState moves into sidebar-filter-actions.ts; visible-worktree-kinds.ts is gone. - buildWorkspaceRowPredicate is module-private. * feat(mobile): Workspace options sheet in the desktop menu's shape The sheet showed every choice of every option, so the filters sat screens down. It now follows the desktop menu: Hosts and Projects rows with their value, Group by as a segmented control, Sort by, Project order, Card layout and Show properties as rows that open a page of choices, and the filters as switches. Each option's control (an inline row, or a value plus a page) still comes from one table keyed by the shared option list. The segmented control is the voice settings one, extracted so both screens share it. * fix(mobile): Workspace options review fixes - A page whose option stops showing is forgotten, so it cannot reopen by itself. - Only the switch takes the tap on a filter row, so the web page writes once. - Sections split their rows in one pass; unused heading styles go. * fix(mobile): neutral filter switches on the page react-native-web paints an on-switch thumb teal unless given activeThumbColor, which React Native's types omit. MobileSwitch applies the app's switch colors, and its web sibling also sets that prop. * refactor(mobile): one MobileSwitch file, adding the web-only thumb color on web * fix(mobile): Show properties reads Hover under Compact, as the desktop does The row's value now comes from getWorkspaceCardPropertiesValue in the shared options, which the desktop trigger uses too: Hover under the compact layout, otherwise how many properties show. Card display shows no value, as on the desktop. * test(mobile): find the renamed Workspace options chip in the page drawer render check * feat(mobile): drop the Sort and Group chips; the Workspace options chip covers both The desktop has only its options button, and the sheet now holds Sort by and Group by. The chips' pickers, their screen state and the sort label go with them. * feat(mobile): option pages slide in from the right and keep the sheet's height The main sheet stays laid out under an open page, so the drawer keeps its height going in and out. The page slides in over 200 ms and back out on back (the row, or Android's back key), instantly under reduced motion. The reduced-motion hook moves to platform/ now that two screens use it. * fix(mobile): option pages fill the sheet and open at its top - The page layer grows to the pager's height, so no part of the main sheet shows below a short page. - The drawer takes a scrollResetKey; the sheet passes the open page, so a page opened from a row low on a scrolled sheet starts at the top. - The main sheet's height is measured under a page too, and the pager state lives in the sheet so it resets when the drawer closes. - A misplaced doc comment goes back to its function. * fix(mobile): phone rows show the Jira, automation and CLI glyphs; the options pager forgets a gone page in an effect * fix(mobile): Jira glyph sits before Linear as on the desktop; Jira reader tests beside it * fix(mobile): the phone leaves Ports out of Show properties and Card display * refactor(mobile): clearer names and tests for the phone's hidden card properties * refactor(mobile): one view state and one settings read for the phone's Workspace options; tighter card glyph wiring - The phone's Card layout and card style now live in its view state; one parallel settings.get reads them with pinned placement beside ui.get. - The Card layout preset flag is a write option, no longer a view field or a change-event field. - getWorktreeCardDetails reads the raw desktop settings and resolves the layout once; the desktop card computes it once in its foundation. - The row computes its glyph presence in one place; Ports is hidden through one set. - Drops the vanished-page effect, the native-only a11y props and the web-only switch branch. * fix(mobile): the list settings read applies each half on its own, with tests; pinned placement changes announce themselves * fix(mobile): Card layout writes the setting before its preset; Card display names folder paths when the desktop has folder workspaces |
||
|
|
131986aa16 |
feat(worktree): show checkout progress while a worktree is created (#24093)
* 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 |
||
|
|
da8de743eb |
feat(mobile): the phone filters and sorts with the desktop's shared Workspace options rules (#28986)
* fix(mobile): phone server rows follow the desktop's other-client and sleeping filters A phone created through a desktop now records whom it acted for, so the desktop's Hide other-client workspaces filter keeps the user's own phone workspaces (locally and on servers) and hides only other people's. The desktop relay marks each server row its sidebar would hide, and the rows its own browser or chat tabs keep awake; the phone applies both. * test(e2e): dedupe sidebar row ids in the visibility spec * fix(mobile): address review: owner naming, old-server relayed phones, scope trim * refactor: move the sidebar's project section grouping into shared The project sections the desktop sidebar draws under Group by Project are built from the project/setup projection, the per-host catalog fold, the endpoint host stamp, and the section labelling and ordering rules. Move the pure parts of each into src/shared (no behaviour change) so the paired phone can call the same code instead of guessing the grouping. * fix(mobile): group by Project with the desktop sidebar's own sections The phone grouped Project sections by repo display name, so it merged checkouts the desktop keeps apart (several local stablyai/orca checkouts, or a server's copy) and split what the desktop merges. The phone now reads each host's repo.list, project.list and projectHostSetup.list (the desktop's own and each server's through the relay), folds them with the renderer's shared catalog fold and projection merge, and builds sections with the desktop's shared grouping, labelling and ordering code. Keys, labels and Recent order equal the sidebar's; a host that predates the project lists gets the repo-derived projection, as the desktop does. Header colour and icon come from the section's anchor repo and rows from their own repo by id, as on the desktop. The repo filter lists every catalog repo by id per host instead of merging same-named repos. Group-by labels read Project and None. project.list and projectHostSetup.list are routed for paired phones (read-only, workspace permission). * refactor(mobile): record a phone's owner as creator and fold desktop tab activity into the host flag A phone now records its owner as the creator (this host for a directly paired phone, the relaying desktop for a delegated one), so the shared rule is unchanged and onBehalfOf is gone. The relay ORs the desktop's saved browser/chat tabs into hasHostSidebarActivity, so the phone's status dot follows too, and local worktree.ps counts the same signals. The old-server fallback is removed. * test(e2e): poll the phone and the sidebar together in the visibility spec * fix(mobile): one Project-section builder for desktop and phone, with the desktop's repo order Review fixes. The sidebar's buildOrderedGroups now calls the shared buildProjectSections for Group by Project (notice and pending repos are optional inputs), so desktop and phone run one function. The phone ranks manual header order by its folded catalog order, which is each host's repo.list order with the desktop's own repos first, as the renderer loads them; only the desktop's cross-host manualRepoOrder (pairing-local) is unknown to it. The projection selector is now a memo over the shared resolver. A transient project-list failure keeps that host's last catalog; only forbidden or method_not_found falls back to the repo-derived projection. Catalog rows are checked on every member grouping reads, and the parity test answers the renderer's real catalog fetch, includes a server's own project rows, and fixes Manual and Recent orders that differ. * fix(mobile): use the repo-derived catalog until a host's project lists first answer * fix(mobile): the desktop serves its sidebar's Project catalog instead of the phone fetching it The phone fetched repo.list, project.list and projectHostSetup.list from the desktop and every relay-ready server each minute and folded them itself, so rows from offline or older servers fell back to repo sections and keys moved on cold start. The desktop renderer now publishes the inputs its own Project headers use (state.repos in sidebar order, the projection selector's projects and setups, projectOrderBy) on the runtime window graph it already sends to main, only when they change. Main keeps the last one and answers it on a new paired-desktop read, mobileRelay.projectSections. The phone reads it with its repo metadata and groups with the shared builder; a desktop that predates the read (forbidden or method_not_found) leaves the phone grouping rows by repo from the desktop's repo.list, and a transient failure keeps the last catalog. The payload carries only grouping and label fields, no hooks, credentials or uploaded icons. The phone's own catalog fetching and its project.list/projectHostSetup.list routes are removed. * fix(mobile): keep uploaded repo icons on the phone, and isolate the snapshot tests * test(e2e): the desktop window serves its sidebar's Project catalog to the phone * chore(i18n): regenerate the runtime English catalog (drift already on main) * fix(mobile): read the Project catalog as its own request, outside the recorded repo-metadata flow The extra read inside the repo-metadata refresh stalled its recorded RPC sequence. It now runs beside it with its own throttle. The repo-metadata goldens are re-recorded for the intended state change: the name-keyed colour, icon and id maps became desktopRepos. * refactor(sidebar): one shared Workspace options filter and sort evaluator The sidebar, the jump palette and the options badge each applied the Workspace options filters with their own copy of the rules. They now call one pure evaluator in src/shared/workspace-view, which reads each row through a small fact reader so the phone can call it next with the facts its host serves. The sort comparator moves there too, with each surface supplying its own Agent activity rank. No behaviour change: a parity test runs the old sidebar chain and the new evaluator over every toggle combination, and the old comparator against the new one for each sort mode. * wip: workspace view descriptors * refactor(sidebar): address review of the shared Workspace options evaluator One predicate builder instead of a per-row scope argument; one exemption-narrowing function taking the hide flag; consumers import the shared predicates directly; Agent activity ties fall to the label in the shared comparator. The activation filter lift reuses the shared predicates. The parity test now covers other-client rows, live agent attention classes and the sort-label map. * fix(mobile): fetch the Project catalog at once when the screen moves to another host The refresh window is now kept per client, so a host screen reused for another desktop doesn't wait out the previous host's 60s window before showing its Project headers. * wip: desktop option items from shared choices * refactor(sidebar): the Workspace options menu renders from one shared option list src/shared/workspace-view/workspace-view-options.ts lists every Workspace options control (Hosts, Projects, Group by, Sort by, Project order, Card layout, Show properties or Card display, and each filter) with its section, its English copy, its choices and when it shows. The desktop menu walks that list and renders each option through a table keyed on the option key, so a new option fails to compile until the desktop has a control for it; the phone sheet will do the same. The choice lists (sort modes, project order, card layout, agent activity layout, card properties) are derived from the shared ids, and a test checks the desktop's English copy matches the shared copy. The unused classic branch of the card property list is removed. No visible change. * wip: row facts * refactor(sidebar): address review of the shared Workspace options list The show rules get their own tests with fixed expectations (the cases the old filter-section test covered, plus the rest), and the menu test checks each option's own control rather than any matching text. Card display rows toggle one property each, with the folder-path copy in shared. The unused Status and Tasks copy left by the removed classic card list is deleted from the catalogs. Filter rows write through the store directly; files are named for what they hold. * feat(runtime): worktree.ps carries the Workspace options row facts Each worktree.ps row now says whether it is the project's default-branch workspace, created by an automation, created by the CLI, or on a detached HEAD, answered by the same shared predicates the desktop sidebar filters with. The phone has none of these facts today, so its copies of the filters either guess (default branch) or are missing. Additive and inert: optional fields that nothing reads yet. An older host omits them; readers must treat absence as 'not that kind', so the row stays visible. Server rows relayed through the desktop pass them through unchanged. * feat(mobile): the desktop's own phone reads and writes its other-client filter ui.get and ui.set strip the pairing-local UI fields for every paired client. That rule exists for clients with their own host catalog (a desktop paired to a server, a web client). A phone has none: it shows its desktop's view, so 'Hide other-client workspaces' must be one shared value. For the host's own phone (clientKind mobile, no relaying parent) ui.get now always answers the field and ui.set persists it; delegated phones, paired desktops and web clients keep the stripping rule. The phone's view-settings schema reads the rest of the Workspace options values it does not read yet (the automation, CLI and detached-HEAD filters, project order, card properties, agent activity layout), each salvaged to absent so an older desktop keeps the phone's local value. * feat(runtime): address review of the worktree.ps row facts Name the fact defaultBranch, and say what absence means for it: a reader keeps its own guess, because 'hide default branch' must not start hiding or showing rows an older host never described. Folder workspace rows get the facts too, so every row from a newer host carries them. The desktopOtherClient comment now says it is a verdict, not a fact. The test covers the rows where the host disagrees with the phone's current guess. * wip: phone uses the shared evaluator * feat(mobile): address review of the phone's other-client filter mirror One isHostOwnPhone predicate decides both who shares the filter value (ui.get/ui.set) and who gets the other-client row marks, so the two cannot drift. The pairing-local field list notes the exception. Tests cover a paired desktop still being stripped, and a phone write that leaves the field out not touching the host value. * feat(mobile): the phone filters and sorts with the desktop's shared Workspace options rules The phone's own filter chain and comparator are replaced by the shared evaluator, reading the facts the host stamps on each row. Phone filter state takes the desktop's setting names and carries every filter; the badge and Clear filters match the desktop's. The desktop's other-client marks become facts the phone filters with the shared value. A parity test runs the same workspaces through the desktop's reading and the phone's (facts stamped as the host stamps them) over every filter combination, folder projects included. * fix(mobile): host scope in shared filters, patch-key writes, live view push Folds the phone's host filter into the shared filters so Clear and the badge agree, writes back only the touched filter keys, and pushes workspaceViewChanged so an open phone list re-reads desktop Workspace options. * fix(mobile): Clear sends only changed filters; skip a view re-read older than a local edit An older host's strict ui.set refuses a key it lacks, so Clear names only what differs. A ui.get that started before a local edit no longer overwrites it; the edit's own change event re-reads. The change publisher's field list now lives with it and derives the filter fields. * fix(workspace-options): all hosts and clear reach the desktop; one filter key list drives every surface Host visibility: the legacy host scope fallback moves to shared code; the phone reads it, and the host resets the scope whenever a client writes the host list, so All hosts and Clear stick. An older desktop that keeps the other-client filter to itself has its marks treated as hide. Badge counts, is-anything-filtered, Clear and reveal derive from the shared filter keys on the desktop sidebar and the kanban filter menu. The phone uses the desktop's group and sort values, holds its view in one state value, and reads its UI fields from one shared list. * feat(workspace-options): Status and PR grouping read the shared tables The phone groups by status with the desktop's status helpers and default catalog (its own copy had the defaults in reverse order) and by PR with a shared lane table, so its labels now read In review / In progress as the desktop's do. The desktop's PR lane decision ends in the same shared state-to-lane step. * test(sidebar): folder rows survive restrictive filters; create grace ignores non-finite times * fix(mobile): write the host scope with the host list, and wait for the first read before forcing the other-client filter An older desktop narrows back to its legacy single-host scope when the phone writes only a null host list, so the phone now also writes the scope for a null or one-host list. The other-client fallback waits for the first ui.get so a new desktop's marks don't hide rows before its filter value arrives. Inline the clear-filters key list and note the PR state asymmetry. * test(mobile): re-record view-settings goldens for the unknown other-client state * fix(sidebar): reuse the palette's default host instead of reading the focus setting again * fix(sidebar): read the host options once in Workspace options and pass them to the Hosts item * fix: parse the ui.get reply into a named type, fix the row memo test's filters, prune the focus-read baseline * refactor(sidebar): one switch-to-kind table, and fold the leftover helper files back - The four kind switches come from WORKSPACE_KIND_SWITCH in workspace-kinds; the shared evaluator and main's phone-push check both apply it, and the filter type derives its switch fields from it. - git-identity.ts folds back into git-identity-display.ts, as on main. - SidebarFilterState moves into sidebar-filter-actions.ts; visible-worktree-kinds.ts is gone. - buildWorkspaceRowPredicate is module-private. |
||
|
|
4782db1b0e |
refactor(sidebar): one shared Workspace options filter and sort evaluator (#28915)
* fix(mobile): phone server rows follow the desktop's other-client and sleeping filters A phone created through a desktop now records whom it acted for, so the desktop's Hide other-client workspaces filter keeps the user's own phone workspaces (locally and on servers) and hides only other people's. The desktop relay marks each server row its sidebar would hide, and the rows its own browser or chat tabs keep awake; the phone applies both. * test(e2e): dedupe sidebar row ids in the visibility spec * fix(mobile): address review: owner naming, old-server relayed phones, scope trim * refactor: move the sidebar's project section grouping into shared The project sections the desktop sidebar draws under Group by Project are built from the project/setup projection, the per-host catalog fold, the endpoint host stamp, and the section labelling and ordering rules. Move the pure parts of each into src/shared (no behaviour change) so the paired phone can call the same code instead of guessing the grouping. * fix(mobile): group by Project with the desktop sidebar's own sections The phone grouped Project sections by repo display name, so it merged checkouts the desktop keeps apart (several local stablyai/orca checkouts, or a server's copy) and split what the desktop merges. The phone now reads each host's repo.list, project.list and projectHostSetup.list (the desktop's own and each server's through the relay), folds them with the renderer's shared catalog fold and projection merge, and builds sections with the desktop's shared grouping, labelling and ordering code. Keys, labels and Recent order equal the sidebar's; a host that predates the project lists gets the repo-derived projection, as the desktop does. Header colour and icon come from the section's anchor repo and rows from their own repo by id, as on the desktop. The repo filter lists every catalog repo by id per host instead of merging same-named repos. Group-by labels read Project and None. project.list and projectHostSetup.list are routed for paired phones (read-only, workspace permission). * refactor(mobile): record a phone's owner as creator and fold desktop tab activity into the host flag A phone now records its owner as the creator (this host for a directly paired phone, the relaying desktop for a delegated one), so the shared rule is unchanged and onBehalfOf is gone. The relay ORs the desktop's saved browser/chat tabs into hasHostSidebarActivity, so the phone's status dot follows too, and local worktree.ps counts the same signals. The old-server fallback is removed. * test(e2e): poll the phone and the sidebar together in the visibility spec * fix(mobile): one Project-section builder for desktop and phone, with the desktop's repo order Review fixes. The sidebar's buildOrderedGroups now calls the shared buildProjectSections for Group by Project (notice and pending repos are optional inputs), so desktop and phone run one function. The phone ranks manual header order by its folded catalog order, which is each host's repo.list order with the desktop's own repos first, as the renderer loads them; only the desktop's cross-host manualRepoOrder (pairing-local) is unknown to it. The projection selector is now a memo over the shared resolver. A transient project-list failure keeps that host's last catalog; only forbidden or method_not_found falls back to the repo-derived projection. Catalog rows are checked on every member grouping reads, and the parity test answers the renderer's real catalog fetch, includes a server's own project rows, and fixes Manual and Recent orders that differ. * fix(mobile): use the repo-derived catalog until a host's project lists first answer * fix(mobile): the desktop serves its sidebar's Project catalog instead of the phone fetching it The phone fetched repo.list, project.list and projectHostSetup.list from the desktop and every relay-ready server each minute and folded them itself, so rows from offline or older servers fell back to repo sections and keys moved on cold start. The desktop renderer now publishes the inputs its own Project headers use (state.repos in sidebar order, the projection selector's projects and setups, projectOrderBy) on the runtime window graph it already sends to main, only when they change. Main keeps the last one and answers it on a new paired-desktop read, mobileRelay.projectSections. The phone reads it with its repo metadata and groups with the shared builder; a desktop that predates the read (forbidden or method_not_found) leaves the phone grouping rows by repo from the desktop's repo.list, and a transient failure keeps the last catalog. The payload carries only grouping and label fields, no hooks, credentials or uploaded icons. The phone's own catalog fetching and its project.list/projectHostSetup.list routes are removed. * fix(mobile): keep uploaded repo icons on the phone, and isolate the snapshot tests * test(e2e): the desktop window serves its sidebar's Project catalog to the phone * chore(i18n): regenerate the runtime English catalog (drift already on main) * fix(mobile): read the Project catalog as its own request, outside the recorded repo-metadata flow The extra read inside the repo-metadata refresh stalled its recorded RPC sequence. It now runs beside it with its own throttle. The repo-metadata goldens are re-recorded for the intended state change: the name-keyed colour, icon and id maps became desktopRepos. * refactor(sidebar): one shared Workspace options filter and sort evaluator The sidebar, the jump palette and the options badge each applied the Workspace options filters with their own copy of the rules. They now call one pure evaluator in src/shared/workspace-view, which reads each row through a small fact reader so the phone can call it next with the facts its host serves. The sort comparator moves there too, with each surface supplying its own Agent activity rank. No behaviour change: a parity test runs the old sidebar chain and the new evaluator over every toggle combination, and the old comparator against the new one for each sort mode. * refactor(sidebar): address review of the shared Workspace options evaluator One predicate builder instead of a per-row scope argument; one exemption-narrowing function taking the hide flag; consumers import the shared predicates directly; Agent activity ties fall to the label in the shared comparator. The activation filter lift reuses the shared predicates. The parity test now covers other-client rows, live agent attention classes and the sort-label map. * fix(mobile): fetch the Project catalog at once when the screen moves to another host The refresh window is now kept per client, so a host screen reused for another desktop doesn't wait out the previous host's 60s window before showing its Project headers. * test(sidebar): folder rows survive restrictive filters; create grace ignores non-finite times * fix(sidebar): reuse the palette's default host instead of reading the focus setting again * refactor(sidebar): one switch-to-kind table, and fold the leftover helper files back - The four kind switches come from WORKSPACE_KIND_SWITCH in workspace-kinds; the shared evaluator and main's phone-push check both apply it, and the filter type derives its switch fields from it. - git-identity.ts folds back into git-identity-display.ts, as on main. - SidebarFilterState moves into sidebar-filter-actions.ts; visible-worktree-kinds.ts is gone. - buildWorkspaceRowPredicate is module-private. |
||
|
|
446cedc0e9 |
fix(notifications): hidden workspaces stop notifying unless you opt in (#28870)
* refactor(sidebar): one Filters-switch predicate, kind predicates in shared
The sidebar's Filters switches were applied as five separate filters plus a
sleeping sweep inside computeVisibleWorktrees. They now go through one
isHiddenBySidebarSwitch predicate, and the workspace-kind predicates
(default branch, automation, CLI, detached HEAD) move to
shared/worktree/workspace-kinds so main can apply the same rules. The git
identity display moves to shared with them. No behaviour change.
* feat(notifications): hidden workspaces send no notifications unless opted in
A workspace hidden by a switch in the sidebar's Filters menu (sleeping,
default branch, automation, CLI, other-client, detached HEAD) now raises
no unread marker, banner, sound, tray dot or phone push. A new setting,
Settings > Notifications > "Notify about hidden workspaces", defaults off
and restores today's behaviour when on.
The renderer gate sits first in resolveAgentAttention, which every
terminal completion, bell and structured-chat edge passes through before
the unread write and the notification request; the bell's own early
marker write checks the same predicate. For local structured chat, main
pushes phones itself, so that push applies the kind switches main can
evaluate from persisted UI and workspace records. Anything unknown,
unhydrated, or a hidden parent the sidebar still shows notifies.
* fix(notifications): hidden-workspace review fixes
- Main's phone push now honours "Hide other-client workspaces" for this
host's worktrees and folder workspaces, through a shared
isCreatedByAnotherClient that the sidebar's no-environment branch uses too.
- The renderer mute fails open until terminals reconnect: before then Hide
sleeping reads every restored workspace as asleep.
- The settings row stays usable when notifications are off (it also governs
unread dots), uses Title Case, and its copy names the Hide switches.
- The mute check moves to attention/, and the switch list behind its fast
path is a full Record so a new switch cannot be skipped.
- A notification field the settings file predates is no longer a repair,
so upgrades do not rewrite every profile.
* refactor(notifications): simplify missing-field repair check
* refactor(notifications): ask the sidebar pipeline whether a workspace is hidden
- The renderer mute check now runs computeVisibleWorktreeIds with the
project and host filters lifted instead of re-walking lineage by hand,
so lineage parents and same-id rows on other hosts follow the sidebar
exactly. The pure pipeline moves to compute-visible-worktrees.ts so the
store's attention slice can call it without an import cycle.
- Drops the switch-list shortcuts in favour of sidebarHasActiveFilters
(renderer) and none (main), and the folder one-caller wrapper.
- Main reads notifyHiddenWorkspaces once, in deliver, next to its other
eligibility checks; isStructuredAttentionWorkspaceHidden only answers
whether Filters hide the workspace, and isWorkspaceHidden is required.
- Reverts the settings-repair generalisation; a one-time rewrite on
upgrade is fine.
* test(notifications): wire the hidden-workspace check into e2e fixtures, and an e2e for the flow
The structured-attention e2e unit fixtures built the main phone-push
delivery without the now-required isWorkspaceHidden, so deliver threw and
no prompt reached the relay; they now pass () => false and carry the
notifyHiddenWorkspaces field. A new Playwright spec rings a real BEL in the
seeded primary checkout with Settings open and checks the unread dot and
the main-process notification request: present while visible, absent under
Hide default branch, and back with Notify About Hidden Workspaces on.
* test(e2e): type the hidden-workspace dispatch log as a declared global
* fix(notifications): exempt a hidden lineage parent only while a child is shown
* perf(notifications): answer the mute check per row before the full sidebar pass
A row the Filters switches show is visible, and a hidden row that no
lineage record names as a parent cannot be shown above a child, so only a
hidden lineage parent needs computeVisibleWorktreeIds. At 1,000 workspaces
a mute check drops from ~0.2-0.3 ms to ~6 us.
* Revert "perf(notifications): answer the mute check per row before the full sidebar pass"
This reverts commit
|
||
|
|
29aa2a4f0f |
Refresh Source Control only for the worktree whose git index changed (#26946)
* Refresh Source Control only for the worktree whose git index changed Index and HEAD-log writes under <common>/worktrees/<entry>/ now carry the checkout they belong to (resolved through the head-identity memo), and the renderer skips the status nudge when that checkout is a known sibling of the active one. Unattributable changes (shared config, bound upstream ref, overflow, SSH watches, unknown admin dirs) still refresh as before. * Pass the stable worktree-path getter as an effect dependency instead of a render-time ref * Fan out sibling head moves to every checkout's Source Control A commit to a shared branch (e.g. main) from a sibling worktree can move the active checkout's local upstream, whose ref leaf is not polled. Its logs/HEAD append now stays unattributed so ahead/behind refreshes immediately; index-only churn keeps its per-worktree attribution. --------- Co-authored-by: Neil <neil@stably.ai> |
||
|
|
701e9f2bdf |
feat(new-workspace): add "None" agent option that opens only the setup script (#25617)
* feat(new-workspace): add "None" agent option that opens only the setup script New workspaces previously required choosing an agent or a blank terminal, so a setup script that launches its own agent always ran next to an extra shell. The quick-create agent picker and Settings > Agents now offer "None (setup script only)": when setup runs, the workspace opens with just the Setup tab focused; without a setup script it falls back to a blank terminal. The default is stored as defaultTuiAgent 'blank' plus a new optional newWorkspaceSetupOnly flag, so clients and servers that only know 'blank' degrade to a blank terminal instead of rejecting an unknown value. Closes stablyai/orca#25247 * fix(new-workspace): honor the "None" default for CLI-created workspaces "None (setup script only)" only took effect in the new-workspace dialog. A workspace created with `orca worktree create` still opened a blank terminal first and left it selected, because the main process builds those tabs and never read the setting. The main-process tab provisioning now skips the blank terminal when the default is "None" and a setup script runs in its own tab, so Setup is the only tab. The same rule applies to CLI creates that activate the workspace, and to dialog creates the user navigated away from before they finished. A split setup or repo default tabs still get their shell. The setup-only check moved to src/shared so main and renderer share it. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> * fix(new-workspace): register the CLI activation caller and stay under the line limit The CLI activation path now passes providesInitialSurface for the "None" default, so it has to appear in the test that lists every caller using that option; the full test run failed without it. Merging main pushed worktree-creation-flow-execute.ts to 301 lines against the 300-line limit. Naming the background-create condition brings it back to 300 with no behavior change. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> * fix(new-workspace): open a blank terminal when the setup-only Setup tab fails to spawn The "None" default skips the blank shell because the Setup tab will be the only surface. If that spawn fails, the workspace was left with no terminal. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> * style: format the setup-only fallback terminal call Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> * fix(workspace): save None through host settings and honor per-create choices * fix(settings): clear None when legacy clients choose a blank terminal * fix(settings): clarify None with split or skipped setup --------- Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com> Co-authored-by: Neil <neil@stably.ai> |
||
|
|
7abee44e78 |
feat(mobile): show SSH host health on the workspace row badge (#26415)
* feat(mobile): show SSH host health on the workspace row host badge
Rows on an SSH host the desktop cannot reach looked healthy on the phone. The badge now
adds the desktop sidebar's own word ("devbox · Disconnected"), derived by the same shared
function from the connection status ssh.listTargetSummaries already sends; old desktops keep today's badge.
Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb
* feat(mobile): name an unhealthy SSH host even in a single-host list
The desktop card shows SSH status on any host count, so the phone now reads target summaries
whenever a repo is on SSH and badges an unhealthy host alone too. Labels and health share one
target-to-host keying, and a hook-level test pins the reply-to-row wiring.
Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb
* fix(mobile): clear stale SSH host badges and announce them without the dot
A catalog that no longer spans hosts or holds an SSH repo now clears the health and label maps,
so a host that left cannot keep badging rows. The badge gets a screen-reader label ("devbox,
Disconnected") instead of reading the middle dot.
Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb
* test(mobile): type the single-host health fixtures inline so the test tsc passes
Fixtures hoisted into consts widened their health maps to string, which the now-required
hostHealthById rejects under the tests-typecheck ratchet; passing them inline keeps the union.
Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb
* test(mobile): re-record the repo-metadata goldens for SSH host health
The settled metadata state now carries hostHealthById, and an SSH-only catalog now reads the
target summaries so a lone unhealthy host can be badged. Scoped re-record via rpc-recording.mts.
Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb
|
||
|
|
3c71318d5b |
[CI] Overlap independent process waits and remove two static tests (#27198)
* [CI] Overlap independent process waits and remove two static tests * [CI] Use the declared conditional Vitest chain |
||
|
|
b41e2730db | Persist multiple workspace references with atomic edits | ||
|
|
c5a1c0ebc9 | Add confirmed deletion of nested worktrees (#25668) | ||
|
|
328caa2160 |
fix(git): reduce queries and preserve data across execution hosts (#24602)
* fix(git): reduce queries and preserve data across execution hosts * fix(ci): exercise pinned Git and serialize mobile dependency entrypoints * fix(relay): preserve fresh diff retries after hung shared reads * test(git): wait for fetch barrier before canceling preparation * fix(i18n): describe index-preserving discard in every locale * fix(git): retain clone diagnostics and allow WSL policy startup * test(git): refresh default-base and branch-safety fixtures |
||
|
|
b5869eeaae |
fix(worktrees): a worktree delete git fails partway stays listed and can be retried (#23952)
* fix(worktrees): delete removed checkouts in git, not in Orca's file pool Local worktree removal renamed the checkout into a sibling trash root and deleted it in the background with a recursive fs.rm in the main process. That queued one request per entry on libuv's shared 4-thread file pool, so for minutes every other async fs call in the main process (the agent-session store behind chat sends, file explorer reads) waited behind the delete. `git worktree remove` now deletes the checkout inline in git's own process again, so the card stays in its Deleting state for the length of the delete while Orca's file pool stays free. No timeout applies to the call, so a large delete is never killed halfway. If git reports success but the path still exists (Git for Windows leaves junctions and their parent directories in place), the leftover is deleted with the existing removeHostTree; WSL checkouts stay with the distro. Nothing creates trash any more: the scheduling queue, rename/restore helpers and the trash_rename span are gone. The startup sweep stays to drain entries older releases left behind, and now removes each emptied trash root so the obligation ends. * fix(worktrees): let Git delete Windows checkouts with long paths enabled Removal now always runs Git's own recursive delete, and worktree creation checks out with core.longpaths on Windows, so a deep checkout Orca created could fail to delete with "Filename too long" (#6433). The Windows recovery then finishes the delete but keeps the branch. Pass the same command-scoped core.longpaths option to `git worktree remove` so Git can delete what it created. Also point the CI shard timing entry at the renamed real-git removal suite. * fix(worktrees): keep an inherited GIT_ASK_YESNO out of the worktree delete Git for Windows asks $GIT_ASK_YESNO whether to retry when a file stays locked during a recursive delete. Orca's git env inherits the user's environment, so an inherited value would run an arbitrary prompt program in the middle of a removal. Drop it for the removal call only. * perf(worktrees): run worktree deletes under their own limit, outside git admission `git worktree remove` now deletes the whole checkout in Git's own process, which takes 20-35 s on a large tree. It took a general git admission slot at status tier for that whole time, and that cap is as small as two slots on a machine with six or fewer cores, so two deletes blocked every status read. Deletes now skip general admission and queue under their own limit of two per host instead: two concurrent deletes already saturate one disk, and more only slow each other down. Leftover cleanup runs inside the same slot. * fix(worktrees): delete removed checkouts in the background and mark them removing Since the checkout is deleted by `git worktree remove` in Git's own process, a large delete takes 20-35 s. Answering the request only after that made web and mobile (30 s), paired desktop (60/180 s) and the CLI (60 s) report a failure for a delete that was still going, and mobile silently re-showed the row. The request now does everything that can refuse (lock, cleanliness, archive hook, watcher/terminal gate, terminal stop, shared-link unlink), records the removal in an in-memory table on the host and answers `removing: true`. The delete, branch cleanup and metadata purge run after it in the same order as before, and the watcher/terminal gate stays held until they finish. - Listings mark rows in the table `removing` for clients that advertise `worktree.background-removal.v1` (the desktop renderer, paired desktop and web), and leave them out for everyone else (older clients, mobile, the CLI), which already dropped the row when the request answered. - The outcome (removed, with any preserved branch, or the error) rides the existing worktrees-changed event as an optional field, sent after the row has left the table. - A repeat delete while Git runs joins it. A create at the same path or with the same branch is refused with "Cleanup is pending; try again shortly"; create's name search skips the path, so generated names move on. - Nothing is persisted: after a quit or crash Git still lists the checkout and it can be deleted again. WSL checkouts still delete inline. - `orca worktree rm` says the checkout is still being deleted. * fix(worktrees): keep the existing Deleting card until the host's Git finishes The host now answers a local worktree delete on acceptance and deletes in the background. The renderer keeps the existing delete state set until the host publishes how it ended: - The delete that asked waits for the outcome on the worktrees-changed event (local IPC or the paired runtime's client event), then runs the same teardown, preserved-branch toast and card error an inline delete did. If that event is lost to a dropped connection, a listing that shows the row gone after it was marked removing finishes the wait, and one that shows it back without the marker fails it. - Any other renderer (a reload, a paired desktop, web) sets the same delete state from the host's `removing` marker and clears it when the marker goes. A failure the host publishes lands on that card's existing error. - Web advertises `worktree.background-removal.v1` so the host sends it the marker; paired desktop does through the Electron capability list. No new component, style or state: the card reads the delete state it always did. A host that predates this answers when done without `removing`, and the renderer takes that as finished, as before. * test(worktrees): type the removal harness and projection for the node typecheck * fix(worktrees): don't fail a delete retry with an earlier attempt's buffered failure A background removal's outcome that reached this renderer with no waiter (another client's delete, a host-marked card, or one already settled from listings) was buffered for 60 s and consumed by the next delete of the same workspace, so retrying a failed delete failed at once with the old error while the host was deleting. Drop the buffered outcome before sending the request; only an outcome that arrives after it can belong to it. * fix(worktrees): let only a gap in host events settle a background delete from listings Git unlists the checkout before the host deletes the branch, cleans the push target and purges metadata, and the worktree-directory watcher refetches within 250 ms. The renderer read the missing row as a finished delete, so the waiter resolved without the preserved branch (no toast) and a failure in those last steps showed as success; the real outcome was then dropped. The listing fallback exists only for a lost outcome event, so it now applies only after this host's event stream had a gap: a new subscription or a replay after reconnect. * perf(worktrees): let a bulk delete start each same-repo checkout delete once the host accepts the last A bulk delete ran one worktree at a time per repo (#2259, for packed-refs and ref-lock races in branch cleanup). With Git now deleting each checkout for 20-35 s before the request settles, N worktrees in one repo took N times that. The renderer now queues same-repo deletes only until the host accepts each one; a parent still waits for its nested children to finish. The host serializes the branch cleanup step per repo itself, which also covers removals started by different clients. * test(worktrees): pin the host platform in the mocked removal suites so they pass on Windows Removal now passes -c core.longpaths=true on Windows, so the exact-argv assertions and command-keyed mocks never matched there (17 failures on a Windows host). Pin darwin as the add-worktree suites already do, and drive the one Windows-specific case through the same spy. * test(worktrees): type the blocked git remove result instead of a broad object The anti-slop static-analysis gate rejects `object` parameters. * test(worktrees): clear the changed-code quality gate in the removal suites Merge the duplicate node:fs import, build the mock child without a cast, read worktrees:list rows through one typed helper, and give the remaining casts a SAFETY line. * fix(worktrees): record each background delete durably and finish it after a quit or crash A quit mid-delete left git to finish the checkout on its own while the branch delete and metadata purge never ran; a crash left a normal-looking row. Each accepted local removal now writes a record beside the profile state before git starts, clears it on success or failure, and the host runs the same delete again for any record left at startup, re-deriving what remains from git and disk. An orderly quit stops the checkout delete without waiting for it. * test(worktrees): type the interrupted-removal assertions for the node typecheck * fix(worktrees): finish an interrupted delete that already removed the checkout's .git file Quit stops git worktree remove mid-delete, and Git deletes the checkout's .git file wherever it falls in directory order. Git then refuses the checkout ("validation failed ... .git does not exist") on every retry, so the startup finish failed and the row could never be deleted from Orca. A registered checkout this record owns that has lost its .git file now finishes like an unregistered one: leftover files, prune, then the branch. * fix(worktrees): let Git finish an interrupted delete, and never take a different checkout A quit or crash that stops `git worktree remove` after it deleted the checkout's .git file left a registered checkout Git refuses to remove. The previous fix deleted that leftover inside Orca's process, which is the bulk delete this change exists to avoid (and on Windows the leftover can be most of the checkout). The startup finish now rewrites the missing .git file from Git's own admin entry for that path and lets `git worktree remove --force` delete it. `git worktree repair` is not used: it also re-points every other registered path, including a checkout another repository now owns there. Orca deletes the leftover itself only when no admin entry claims the path. The startup finish forces, so it now leaves the path alone when the checkout there is not the one recorded: a registered worktree on a different branch or head, or a `.git` at a path Git already unregistered. The record is dropped and the card shows why. The record write before Git starts is now bounded (2 s, logged when exceeded) so a stalled disk cannot hold the delete, and the outcome is published before the record's clear reaches disk. * test(worktrees): compare worktree paths by value and tear down with Windows lock retries Git prints forward slashes in `git worktree list` on Windows, so the real-Git removal suites never found a joined path there: positive checks failed and negative ones passed without proving anything. They now compare Git's parsed rows by value. Teardown uses the shared retrying removeTree, since Windows can hold the deleted checkout busy for a moment after Git exits. Adds a relative-path worktree case for the .git restore (skipped before Git 2.48). * fix(worktrees): reply to a worktree delete when it has finished, not on a broadcast event A current client's delete request now waits for the host's background delete and gets its real result (removed, a preserved branch, or the error) as the reply, the way it did before the delete moved off the request. A request that arrives while the delete runs joins it and gets the same result. Every other view keeps reading the host's `removing` marker: the row leaving means the delete finished, and the row listed again without the marker shows "The delete did not finish. Try again." on a card that view had marked Deleting. A request whose reply is lost (a timeout or a dropped connection) settles the same way from a fresh listing instead of reporting a failure. Clients without the background-removal capability (mobile, the CLI, older desktops) are still answered on acceptance and have rows under removal left out of their listings. This removes the outcome on worktreesChanged and everything it needed: the renderer's outcome waiters, early-outcome buffer and TTL, per-host event-gap generations, the request pre-registration, and the accept callback bulk delete used. Bulk delete runs same-repo deletes in parallel only on this machine, whose host serializes branch cleanup per repo; SSH and paired hosts stay serialized. * test(worktrees): type the pending-removal host id in the background-removal suite * fix(worktrees): answer a delete request even when a concurrent removal of the same worktree replaced its record The desktop app's removal and the runtime removal (CLI, paired clients) coalesce separately, so both can be accepted for one worktree. The second replaced the first's record, and the first delete then finished without resolving the request waiting on it, leaving the desktop card on Deleting indefinitely. Each delete now settles the request it was started for. * fix(worktrees): run same-repo removal archive hooks and teardown one at a time on the host Local bulk delete now sends same-repo removals in parallel, so their archive hooks, terminal teardown and preflight ran at once; a hook that writes refs can race the repo's ref locks (#2259). The host now serializes each local removal up to acceptance per repo, for every client; Git's checkout delete still runs in parallel under the delete limit. * fix(runtime): keep waiting worktree deletes out of a host's foreground call slots worktree.rm now replies only after Git deletes the checkout (up to minutes), so on paired desktop and web each waiting delete held one of the host's 8 foreground call slots, and a bulk delete queued listing refreshes and every other foreground call behind it. Deletes now run in their own lane with the same bound; the 2-slot background lane stays for status polls. * fix(worktrees): join a same-worktree delete accepted while a removal waited its repo turn The desktop app and the runtime (CLI, paired clients, web) check for a running delete before they queue for the repo's acceptance turn. A delete of the same worktree from the other path, accepted while this one queued, was missed: this request re-ran the archive hook, stopped the terminals again and started a second `git worktree remove` on the directory Git was deleting. The queued acceptance now re-checks and joins the running delete. * fix(worktrees): fence a resumed delete's checkout from startup, and drop rows a listing read before the delete finished A delete a quit or crash interrupted took its terminal and file-watcher gate only when the resume job ran, after the first window was shown; session restore could open a shell or watcher inside the half-deleted checkout first, and on Windows that handle can fail the resumed git delete. Loading the records now fences each recorded path, and the resumed job takes the fence over in the same tick it takes its own gate. A listing that read git's registration before a delete finished, and replied after the removal record cleared, returned the row unmarked, so other views briefly showed "The delete did not finish". Listings now capture the pending removals before reading git and leave out a row whose delete finished successfully since; a row whose delete failed stays listed as before. * test(worktrees): keep git's auto-maintenance out of the real-git removal suite CI's Git 2.55 failed the file-pool test in teardown with ENOTEMPTY on the scratch repo's objects/pack after the test body passed: the 3,000-file commit's detached auto-maintenance was still writing a pack. The scratch repo now disables auto-maintenance and auto-gc. * fix(worktrees): one archive-hook approval covers a same-repo bulk delete again Local same-repo deletes now start together, so each queued its trust prompt with a state snapshot taken before the first prompt was answered; approving the first still showed the same prompt once per remaining worktree. The queued check now reads the store when its turn comes. * fix(worktrees): a delete Git fails partway stays listed with its error; Delete retries it `git worktree remove --force` drops the checkout's registration even when it cannot delete a file (root-owned files, `chflags uchg`, a read-only Windows directory). Orca lists workspaces from Git, so the row vanished after the error, leaving the checkout, the branch and Orca's metadata with no way to retry. - A background delete that fails with the checkout still on disk, unregistered, and still the removed checkout's own leftover keeps its durable removal record with the error (`failure`) instead of clearing it. Every other failure clears it as before. - Local listings (desktop list/list-all/detected, runtime list/ps/detected) add a row for each such record, carrying `removalError`, and for a pending removal whose checkout Git no longer lists (shown as removing). - Delete on that row (desktop IPC and runtime worktree.rm) runs the recorded removal again: terminal teardown, then the leftover, prune, branch and metadata, under the per-host delete limit. - The record ends on a successful retry, when the checkout is gone (listing or startup), when a different checkout takes the path, or on forget-local. Startup never retries a failed record. - The finish's unregistered-path rule accepts a `.git` file naming the admin entry Git removed (the leftover's own) and still refuses any other `.git`. The removal table and listing projection move out of the background removal module into worktree-removal-table.ts and worktree-removal-listing.ts. * fix(renderer): show a failed delete's host error on its card A row the host lists with removalError gets the existing delete-state error (no new element), cleared when the host stops listing it failed. A row this view marked Deleting that comes back failed, and a lost delete reply settled from the listing, report the host's error instead of the generic one. * test(worktrees): type the failed-removal listing and refresh mocks * fix(worktrees): a failed delete's retry never removes a checkout Git registers at the path again The retry replays the recorded choices (force, branch deletion) that were made for the unregistered leftover. If the user removed the leftover and `git worktree add`ed the same branch at the path, the new checkout matched the record's branch and head, so Delete force-removed it with its uncommitted files, skipping the normal delete's cleanliness check. The retry now refuses a registered checkout and lets the record go, so the next Delete takes the normal path. * fix(worktrees): a failed delete's record ends at startup once its repo is removed from Orca * fix(renderer): a failed delete's row offers Remove from Orca * test(worktrees): type the failed-removal IPC test's module mocks without casts * fix(worktrees): Delete picks retry or a normal delete from Git's current listing * fix(worktrees): a failed delete's retry checks the leftover again right before deleting it * fix(worktrees): Remove from Orca reaches paired clients and matches the failed row's own host * fix(worktrees): a failed delete's retry re-lists only its own repo's authorized roots * fix(worktrees): a second Delete joins a retry already running, and the startup finish keeps its last-resort delete * fix(renderer): a failed delete's card says it failed, and Delete keeps the error for its dialog * fix(renderer): a failed delete's dialog shows the host's error, and its card label keeps the full error a hover away * style(worktrees): import the removal result types in one statement * test(worktrees): a runtime listing right after a failed delete shows the failed row, not the cached scan * fix(worktrees): pass the runtime retry's PTY-stop waiver in the shape the waiver invariant pins * fix(worktrees): drop Remove from Orca from failed-delete rows A failed delete stays listed with its error and Delete retries it; the separate forget item, its dialog copy and forget's failed-record clearing are removed. The startup clear for repos no longer in Orca keeps matching the local copy only. * test(worktrees): wait for the dropped record's write before the failed-removal suite tears down |