test(terminal): retention-budget e2e via same-repo remote worktrees (passes docker lane)

The first draft added a second remote repo mid-session, whose pane pty
spawn misroutes to the local daemon with the remote cwd (pre-existing
multi-repo issue, reproducible without any retention override — a seeded
local repo plus one remote repo shows the same misroute). The spec now
budgets across three worktrees of the ONE connected repo, created through
the product createWorktree path (an external git-worktree-add only lands
as a detected worktree needing adoption) and polled through the relay's
transient post-connect reconnect window. Verified green on the local
Docker lane in 20.8s.

Co-authored-by: Orca <help@stably.ai>
This commit is contained in:
Jinjing
2026-07-28 13:21:54 -07:00
co-authored by Orca
parent 1bad08d9a8
commit 757aaf84ac
3 changed files with 99 additions and 60 deletions
@@ -1,40 +0,0 @@
import type { Page } from '@stablyai/playwright-test'
// Why: the retention-budget spec needs TWO remote worktrees on one connected
// target; connectDockerSshRelayTarget always mints a fresh target, so this
// replays only its addRemote+activate tail against an existing connection.
export async function addDockerSshRelayRemoteWorktree(
page: Page,
targetId: string,
remotePath: string
): Promise<{ repoId: string; worktreeId: string }> {
return page.evaluate(
async ({ targetId, remotePath }) => {
const store = window.__store
if (!store) {
throw new Error('Store unavailable')
}
const result = await window.api.repos.addRemote({
connectionId: targetId,
remotePath,
displayName: `Docker SSH Relay E2E ${remotePath}`
})
if ('error' in result) {
throw new Error(result.error)
}
await store.getState().fetchRepos()
await store.getState().fetchWorktrees(result.repo.id)
const worktree = (store.getState().worktreesByRepo[result.repo.id] ?? [])[0]
if (!worktree) {
throw new Error(`No remote worktree found for ${result.repo.path}`)
}
store.getState().setActiveWorktree(worktree.id)
if ((store.getState().tabsByWorktree[worktree.id] ?? []).length === 0) {
store.getState().createTab(worktree.id)
}
store.getState().setActiveTabType('terminal')
return { repoId: result.repo.id, worktreeId: worktree.id }
},
{ targetId, remotePath }
)
}
@@ -0,0 +1,67 @@
import type { Page } from '@stablyai/playwright-test'
import { expect } from '@stablyai/playwright-test'
// Why: the retention-budget spec needs several worktrees of ONE connected remote
// repo (adding a second repo mid-session misroutes its pty spawn to the local
// daemon — pre-existing multi-repo issue). Creation goes through the product's
// own createWorktree path so the result lands in worktreesByRepo (an external
// `git worktree add` only shows up as a detected worktree needing adoption).
// The create is polled: the relay channel can drop and reconnect shortly after
// connect, and an RPC inside that window fails with "Multiplexer disposed".
export async function createAndActivateDockerSshRelayWorktree(
page: Page,
repoId: string,
worktreeName: string
): Promise<{ worktreeId: string }> {
let worktreeId: string | null = null
await expect
.poll(
async () => {
worktreeId = await page.evaluate(
async ({ repoId, worktreeName }) => {
const store = window.__store
if (!store) {
throw new Error('Store unavailable')
}
// Why: a retried create must reuse a prior attempt's worktree
// instead of failing forever on "already exists".
const existing = (store.getState().worktreesByRepo[repoId] ?? []).find((candidate) =>
candidate.path.endsWith(`/${worktreeName}`)
)
if (existing) {
return existing.id
}
try {
const result = await store.getState().createWorktree(repoId, worktreeName)
await store.getState().fetchWorktrees(repoId)
return result.worktree.id
} catch {
return null
}
},
{ repoId, worktreeName }
)
return worktreeId
},
{
timeout: 90_000,
message: `remote worktree ${worktreeName} was not created in repo ${repoId}`
}
)
.not.toBeNull()
if (!worktreeId) {
throw new Error(`remote worktree ${worktreeName} did not resolve an id`)
}
await page.evaluate((id) => {
const store = window.__store
if (!store) {
throw new Error('Store unavailable')
}
store.getState().setActiveWorktree(id)
if ((store.getState().tabsByWorktree[id] ?? []).length === 0) {
store.getState().createTab(id)
}
store.getState().setActiveTabType('terminal')
}, worktreeId)
return { worktreeId }
}
+32 -20
View File
@@ -9,18 +9,20 @@ import {
} from './helpers/terminal'
import { waitForTabParked } from './helpers/terminal-hidden-parking'
import {
DOCKER_SSH_SECOND_HUB_REMOTE_REPO_PATH,
cleanupDockerSshRelayTarget,
startDockerSshRelayTarget,
type DockerSshRelayTarget
} from './helpers/docker-ssh-relay-target'
import { connectDockerSshRelayTarget } from './helpers/docker-ssh-relay-connection'
import { addDockerSshRelayRemoteWorktree } from './helpers/docker-ssh-relay-second-worktree'
import { createAndActivateDockerSshRelayWorktree } from './helpers/docker-ssh-relay-worktree-activation'
const RUN_DOCKER_SSH = process.env.ORCA_E2E_SSH_DOCKER === '1'
const PARKING_DELAY_MS = Number(process.env.ORCA_E2E_TERMINAL_PARKING_DELAY_MS) || 500
test.use({
// Why no seeded local repo: matching every green Docker SSH spec — the same
// mid-session repo-add misroute hits a remote repo added beside a local one.
seedTestRepo: false,
orcaAppExtraEnv: {
ORCA_E2E_TERMINAL_PARKING_DELAY_MS: String(PARKING_DELAY_MS),
// Why limit=1: two hidden un-parkable worktrees then exceed the budget while
@@ -44,14 +46,6 @@ test.describe('terminal hidden-worktree retention budget', () => {
try {
target = startDockerSshRelayTarget(testInfo)
await waitForSessionReady(orcaPage)
// The seeded local repo is the third context both remote worktrees hide behind.
const localWorktreeId = await waitForActiveWorktree(orcaPage)
// Why: with SSH view parking off, SSH ptys are not park-restorable, so both
// remote worktrees join the un-parkable class the retention budget governs.
await orcaPage.evaluate(async () => {
await window.__store?.getState().updateSettings({ terminalSshViewParking: false })
})
const older = await connectDockerSshRelayTarget(orcaPage, target)
await expect
@@ -73,12 +67,23 @@ test.describe('terminal hidden-worktree retention budget', () => {
})
.toContain(`${olderMarker}:`)
// Second remote worktree on the same connection — activating it hides the
// older one, making the older the least-recently-hidden candidate.
const newer = await addDockerSshRelayRemoteWorktree(
// Why: with SSH view parking off, SSH ptys are not park-restorable, so the
// hidden remote worktrees join the un-parkable class the budget governs.
// Written after the first terminal is live so it cannot race target setup;
// parking eligibility reads it at verdict time, not spawn time.
await orcaPage.evaluate(async () => {
await window.__store?.getState().updateSettings({ terminalSshViewParking: false })
})
// Why worktrees of ONE remote repo: the retention budget ranks worktrees,
// and a repo added mid-session misroutes its pty spawn to the local daemon
// (pre-existing multi-repo issue, independent of retention).
// Activating the second worktree hides the older one, making the older the
// least-recently-hidden candidate.
const newer = await createAndActivateDockerSshRelayWorktree(
orcaPage,
older.targetId,
DOCKER_SSH_SECOND_HUB_REMOTE_REPO_PATH
older.repoId,
'retention-newer'
)
await expect
.poll(() => waitForActiveWorktree(orcaPage), { timeout: 30_000 })
@@ -90,11 +95,18 @@ test.describe('terminal hidden-worktree retention budget', () => {
throw new Error('newer SSH terminal tab did not become active')
}
// Hide both remote worktrees behind the local context: two hidden
// un-parkable worktrees against a budget of one.
await orcaPage.evaluate((worktreeId) => {
window.__store?.getState().setActiveWorktree(worktreeId)
}, localWorktreeId)
// Third context: activating it hides BOTH earlier worktrees — two hidden
// un-parkable worktrees against a budget of one. It stays visible, so it
// is never a retention candidate itself.
const third = await createAndActivateDockerSshRelayWorktree(
orcaPage,
older.repoId,
'retention-third'
)
await expect
.poll(() => waitForActiveWorktree(orcaPage), { timeout: 30_000 })
.toBe(third.worktreeId)
await waitForActiveTerminalManager(orcaPage, 60_000)
// The older worktree must force-park (its pane managers unmount)…
await waitForTabParked(orcaPage, olderTabId, { parkDelayMs: PARKING_DELAY_MS })