Files
orca/tests/e2e/local-worktree-visibility-runtime-active.spec.ts
a356b9d5c2 fix(worktrees): show CLI-created local worktrees in the sidebar while a remote runtime is active (#6628)
* fix(worktrees): refresh local worktrees in the sidebar while a remote runtime is active

When a remote runtime is active, a local `worktrees:changed` event for an
unbound repo was dropped by the renderer guard in useIpcEvents. Worktrees
created outside Orca for that repo (e.g. `orca worktree create` from a CLI or
automation flow) therefore stayed invisible in the sidebar until an app
restart, even though their sessions were already running.

The guard existed because an unbound repo's list fetch routes to the active
runtime (settingsForKnownRepoOwner's unbound fall-through), so refreshing with
local worktree ids could query — and purge against — the remote host.

Instead of dropping the event, pin the refresh to the local host
(forceLocalOwner): fetch the worktree list against the local owner and merge
additively. The merge is host-scoped and the deletion-purge is skipped on this
path, so it only ever adds local-host worktrees and never overwrites the active
runtime's worktree state. A genuinely-removed local worktree is reclaimed by
the next unguarded full refresh.

* test(e2e): regression — CLI-created worktree visible while a remote runtime is active

Drives the real `orca worktree create` path: the CLI RuntimeClient calls
`worktree.create` over the app's socket, registering a managed worktree and
firing the `worktrees:changed` IPC the renderer listens for. Stages a remote
runtime as active by injecting `activeRuntimeEnvironmentId` into the renderer
store, so no real remote host is needed. Fails on the prior behavior (the
worktree never appears while a runtime is active) and passes with this fix.

* fix(worktrees): pin local lineage refresh during runtime activity

Co-authored-by: Orca <help@stably.ai>

* review: trim comments to house style, normalize queue coalescing to booleans

* review: sweep rename-grace expiry before early returns in worktrees:changed handler

* review: document accepted workspace-space gap, drop imprecise 'additive' wording

* fix(worktrees): route duplicate local repo events locally

* fix(worktrees): tag local worktree events at origin, gate purge skip on runtime overlap

* test: pin origin-based forceLocalOwner with a no-runtime local event assertion

---------

Co-authored-by: brennanb2025 <brennankbenson@gmail.com>
Co-authored-by: Orca <help@stably.ai>
Co-authored-by: Brennan Benson <79079362+brennanb2025@users.noreply.github.com>
2026-07-22 16:10:53 -07:00

80 lines
3.6 KiB
TypeScript

/**
* Regression: a worktree created via the CLI (`orca worktree
* create`) must appear in the sidebar even while a remote runtime is active.
*
* The faithful trigger is the real CLI path — the RuntimeClient connects to the
* running app's socket and calls `worktree.create`, which registers a managed
* worktree and fires the `worktrees:changed` IPC the renderer listens for.
* Before the fix, the renderer dropped that IPC whenever a remote runtime was
* active (an unbound repo's list fetch would route to the runtime), so the
* worktree never appeared until an app restart. The "remote runtime active"
* condition is injected into the renderer store, so no real remote host is
* needed.
*/
import { test, expect } from './helpers/orca-app'
import { waitForSessionReady, waitForActiveWorktree } from './helpers/store'
import { RuntimeClient } from '../../src/cli/runtime-client'
test.describe('worktree visibility with a remote runtime active', () => {
test('a CLI-created worktree appears in the sidebar while a remote runtime is active', async ({
orcaPage,
electronApp
}) => {
await waitForSessionReady(orcaPage)
await waitForActiveWorktree(orcaPage)
const repoId = await orcaPage.evaluate(() => {
const repos = window.__store?.getState().repos ?? []
// This case reproduces only for a local-host repo — one whose execution
// host resolves to local (executionHostId unset or 'local') and which has
// no connection binding. That is the repo whose list fetch an active
// runtime would otherwise route away from local. Select it explicitly so
// a future fixture change can't silently drop coverage.
const target = repos.find(
(repo) => (repo.executionHostId ?? 'local') === 'local' && !repo.connectionId
)
if (!target) {
throw new Error('expected a seeded local-host repo')
}
return target.id
})
// The CLI talks to the running app over the socket recorded in its userData
// dir — exactly what `orca worktree create` does from a terminal.
const userDataDir = await electronApp.evaluate(({ app }) => app.getPath('userData'))
const client = new RuntimeClient(userDataDir, 30_000, null, null)
const createViaCli = async (name: string): Promise<string> => {
const response = await client.call<{ worktree: { id: string } }>('worktree.create', {
repo: `id:${repoId}`,
name,
noParent: true,
activate: false
})
return response.result.worktree.id
}
const worktreeRow = (worktreeId: string) =>
orcaPage.locator(`[data-worktree-id=${JSON.stringify(worktreeId)}]`).first()
// Guard: with no runtime active, a CLI-created worktree appears. This proves
// the create+notify path works, so the assertion below isolates the bug
// rather than masking a broken harness as a fixed regression.
const controlId = await createViaCli(`wt-control-${Date.now()}`)
await expect(worktreeRow(controlId)).toBeVisible({ timeout: 15_000 })
// Stage a remote runtime as active — the condition that triggered the drop.
await orcaPage.evaluate(() => {
window.__store?.setState((current) => ({
settings: { ...current.settings, activeRuntimeEnvironmentId: 'e2e-fake-runtime' }
}))
})
// The fix: a CLI-created worktree must still appear, with no app restart.
const targetId = await createViaCli(`wt-runtime-active-${Date.now()}`)
await expect(
worktreeRow(targetId),
'a CLI-created worktree must appear even while a remote runtime is active'
).toBeVisible({ timeout: 15_000 })
})
})