test(terminal): retention-budget force-park e2e with a retentionLimit override (C1 6b)

ORCA_E2E_TERMINAL_RETENTION_LIMIT flows preload → e2e-config →
getTerminalParkingPolicyOverrides (exposeStore-gated, positive-integer
only) so a spec can shrink the force-park budget to 1. The Docker-gated
spec opens two remote worktrees on one relay target (second pre-seeded
remote repo), disables terminalSshViewParking to make both un-parkable,
hides both behind the local context, and proves the older one force-parks
while the last-active exemption spares the newest; re-activating the
evicted worktree restores the marker tail via relay replay.

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 b8f08f8750
commit 1bad08d9a8
6 changed files with 260 additions and 14 deletions
+2 -1
View File
@@ -23,5 +23,6 @@ export const preloadE2EConfig = createE2EConfig({
userDataDir: process.env.ORCA_E2E_USER_DATA_DIR ?? null,
// Why: Number('') is 0 and Number(undefined) is NaN; both coerce to null so
// only a real positive override reaches the renderer parking policy.
terminalParkingDelayMs: Number(process.env.ORCA_E2E_TERMINAL_PARKING_DELAY_MS) || null
terminalParkingDelayMs: Number(process.env.ORCA_E2E_TERMINAL_PARKING_DELAY_MS) || null,
terminalRetentionLimit: Number(process.env.ORCA_E2E_TERMINAL_RETENTION_LIMIT) || null
})
@@ -1,6 +1,10 @@
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
type MockE2EConfig = { exposeStore: boolean; terminalParkingDelayMs: number | null }
type MockE2EConfig = {
exposeStore: boolean
terminalParkingDelayMs: number | null
terminalRetentionLimit: number | null
}
let mockE2EConfig: MockE2EConfig
@@ -21,6 +25,8 @@ type TerminalParkingE2EOverridesModule = {
coldParkDelayMs?: number
hotRetainMs?: number
hotRetainLimit?: number
retentionTtlMs?: number
retentionLimit?: number
}
registerTerminalParkingDebugHandle: () => void
}
@@ -32,7 +38,11 @@ async function importOverridesModule(): Promise<TerminalParkingE2EOverridesModul
describe('getTerminalParkingPolicyOverrides', () => {
beforeEach(() => {
mockE2EConfig = { exposeStore: false, terminalParkingDelayMs: null }
mockE2EConfig = {
exposeStore: false,
terminalParkingDelayMs: null,
terminalRetentionLimit: null
}
delete (globalThis as { window?: unknown }).window
})
@@ -41,13 +51,17 @@ describe('getTerminalParkingPolicyOverrides', () => {
})
it('ignores the delay override outside e2e (exposeStore off)', async () => {
mockE2EConfig = { exposeStore: false, terminalParkingDelayMs: 500 }
mockE2EConfig = {
exposeStore: false,
terminalParkingDelayMs: 500,
terminalRetentionLimit: null
}
const { getTerminalParkingPolicyOverrides } = await importOverridesModule()
expect(getTerminalParkingPolicyOverrides()).toEqual({})
})
it('maps the e2e delay to coldParkDelayMs, hotRetainMs, and retentionTtlMs', async () => {
mockE2EConfig = { exposeStore: true, terminalParkingDelayMs: 500 }
mockE2EConfig = { exposeStore: true, terminalParkingDelayMs: 500, terminalRetentionLimit: null }
const { getTerminalParkingPolicyOverrides } = await importOverridesModule()
expect(getTerminalParkingPolicyOverrides()).toEqual({
coldParkDelayMs: 500,
@@ -57,13 +71,48 @@ describe('getTerminalParkingPolicyOverrides', () => {
})
it('returns no overrides when no delay is configured', async () => {
mockE2EConfig = { exposeStore: true, terminalParkingDelayMs: null }
mockE2EConfig = {
exposeStore: true,
terminalParkingDelayMs: null,
terminalRetentionLimit: null
}
const { getTerminalParkingPolicyOverrides } = await importOverridesModule()
expect(getTerminalParkingPolicyOverrides()).toEqual({})
})
it('maps the e2e retention limit independently of the delay', async () => {
mockE2EConfig = { exposeStore: true, terminalParkingDelayMs: null, terminalRetentionLimit: 1 }
const { getTerminalParkingPolicyOverrides } = await importOverridesModule()
expect(getTerminalParkingPolicyOverrides()).toEqual({ retentionLimit: 1 })
})
it('combines the delay trio with the retention limit when both are set', async () => {
mockE2EConfig = { exposeStore: true, terminalParkingDelayMs: 500, terminalRetentionLimit: 1 }
const { getTerminalParkingPolicyOverrides } = await importOverridesModule()
expect(getTerminalParkingPolicyOverrides()).toEqual({
coldParkDelayMs: 500,
hotRetainMs: 500,
retentionTtlMs: 500,
retentionLimit: 1
})
})
it('ignores the retention limit outside e2e (exposeStore off)', async () => {
mockE2EConfig = { exposeStore: false, terminalParkingDelayMs: null, terminalRetentionLimit: 1 }
const { getTerminalParkingPolicyOverrides } = await importOverridesModule()
expect(getTerminalParkingPolicyOverrides()).toEqual({})
})
it('rejects non-integer or non-positive retention limits', async () => {
mockE2EConfig = { exposeStore: true, terminalParkingDelayMs: null, terminalRetentionLimit: 1.5 }
const overridesModule = await importOverridesModule()
expect(overridesModule.getTerminalParkingPolicyOverrides()).toEqual({})
mockE2EConfig = { exposeStore: true, terminalParkingDelayMs: null, terminalRetentionLimit: 0 }
expect(overridesModule.getTerminalParkingPolicyOverrides()).toEqual({})
})
it('registers window.__terminalParkingDebug on import under exposeStore', async () => {
mockE2EConfig = { exposeStore: true, terminalParkingDelayMs: 500 }
mockE2EConfig = { exposeStore: true, terminalParkingDelayMs: 500, terminalRetentionLimit: null }
const testWindow: {
__terminalParkingDebug?: { parkDelayMs: number; parkedTabIds: () => string[] }
} = {}
@@ -74,7 +123,11 @@ describe('getTerminalParkingPolicyOverrides', () => {
})
it('does not register the debug handle outside e2e', async () => {
mockE2EConfig = { exposeStore: false, terminalParkingDelayMs: null }
mockE2EConfig = {
exposeStore: false,
terminalParkingDelayMs: null,
terminalRetentionLimit: null
}
const testWindow: { __terminalParkingDebug?: unknown } = {}
;(globalThis as { window?: unknown }).window = testWindow
await importOverridesModule()
@@ -10,11 +10,21 @@ import { getParkedTerminalWatcherTabIds } from './terminal-parked-tab-watchers'
// in the hot-retain working set for 5 minutes and never park within a test
// run. Gated on exposeStore so packaged builds ignore stray env vars.
export function getTerminalParkingPolicyOverrides(): TerminalColdParkPolicyOverrides {
const delayMs = e2eConfig.exposeStore ? e2eConfig.terminalParkingDelayMs : null
return typeof delayMs === 'number' && Number.isFinite(delayMs) && delayMs > 0
? // Why retentionTtlMs too: the retention-budget e2e needs its 45min TTL shrunk the same way.
{ coldParkDelayMs: delayMs, hotRetainMs: delayMs, retentionTtlMs: delayMs }
: {}
if (!e2eConfig.exposeStore) {
return {}
}
const delayMs = e2eConfig.terminalParkingDelayMs
const retentionLimit = e2eConfig.terminalRetentionLimit
return {
...(typeof delayMs === 'number' && Number.isFinite(delayMs) && delayMs > 0
? // Why retentionTtlMs too: the retention-budget e2e needs its 45min TTL shrunk the same way.
{ coldParkDelayMs: delayMs, hotRetainMs: delayMs, retentionTtlMs: delayMs }
: {}),
// Why: limit=1 lets a spec force-park with only two hidden un-parkable worktrees (production floor is 12).
...(typeof retentionLimit === 'number' && Number.isInteger(retentionLimit) && retentionLimit > 0
? { retentionLimit }
: {})
}
}
export function registerTerminalParkingDebugHandle(): void {
+13 -1
View File
@@ -6,6 +6,9 @@ export type E2EConfig = {
/** Test-only override (ORCA_E2E_TERMINAL_PARKING_DELAY_MS) shrinking the
* terminal hidden-view parking delays. null means use production timing. */
terminalParkingDelayMs: number | null
/** Test-only override (ORCA_E2E_TERMINAL_RETENTION_LIMIT) shrinking the
* hidden un-parkable worktree force-park budget. null means production (12). */
terminalRetentionLimit: number | null
}
type E2EConfigInput = {
@@ -13,6 +16,7 @@ type E2EConfigInput = {
exposeStore?: boolean
userDataDir?: string | null
terminalParkingDelayMs?: number | null
terminalRetentionLimit?: number | null
}
export function createE2EConfig(input: E2EConfigInput): E2EConfig {
@@ -25,12 +29,20 @@ export function createE2EConfig(input: E2EConfigInput): E2EConfig {
input.terminalParkingDelayMs > 0
? input.terminalParkingDelayMs
: null
// Why: a worktree count — only a positive integer is a meaningful budget.
const terminalRetentionLimit =
typeof input.terminalRetentionLimit === 'number' &&
Number.isInteger(input.terminalRetentionLimit) &&
input.terminalRetentionLimit > 0
? input.terminalRetentionLimit
: null
return {
enabled: headless || exposeStore || userDataDir !== null,
headless,
exposeStore,
userDataDir,
terminalParkingDelayMs
terminalParkingDelayMs,
terminalRetentionLimit
}
}
@@ -0,0 +1,40 @@
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 }
)
}
+130
View File
@@ -0,0 +1,130 @@
import type { TestInfo } from '@stablyai/playwright-test'
import { test, expect } from './helpers/orca-app'
import { getActiveTabId, waitForActiveWorktree, waitForSessionReady } from './helpers/store'
import {
getTerminalContent,
sendToTerminal,
waitForActivePanePtyId,
waitForActiveTerminalManager
} 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'
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({
orcaAppExtraEnv: {
ORCA_E2E_TERMINAL_PARKING_DELAY_MS: String(PARKING_DELAY_MS),
// Why limit=1: two hidden un-parkable worktrees then exceed the budget while
// the last-active exemption still spares exactly one — the smallest live proof.
ORCA_E2E_TERMINAL_RETENTION_LIMIT: '1'
}
})
// C1 slice B: hidden worktrees ordinary parking can never evict (here: SSH with
// slice A's terminalSshViewParking off) force-park beyond the retention budget,
// least-recently-hidden first, and reveal restores content from the relay replay.
test.describe('terminal hidden-worktree retention budget', () => {
test.skip(!RUN_DOCKER_SSH, 'Set ORCA_E2E_SSH_DOCKER=1 to run Docker-backed SSH tests.')
test.skip(process.platform === 'win32', 'Docker SSH parking uses POSIX SSH tooling.')
test('force-parks the older hidden un-parkable worktree and spares the newest', async ({
orcaPage
}, testInfo: TestInfo) => {
test.setTimeout(240_000)
let target: DockerSshRelayTarget | null = null
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
.poll(() => waitForActiveWorktree(orcaPage), { timeout: 30_000 })
.toBe(older.worktreeId)
await waitForActiveTerminalManager(orcaPage, 60_000)
const olderPtyId = await waitForActivePanePtyId(orcaPage, 60_000)
const olderTabId = await getActiveTabId(orcaPage)
if (!olderTabId) {
throw new Error('older SSH terminal tab did not become active')
}
// Why the ':' terminator: match the exact echoed line, not the typed command.
const olderMarker = `RETENTION_OLD_${Date.now()}`
await sendToTerminal(orcaPage, olderPtyId, `echo "${olderMarker}:"\r`)
await expect
.poll(() => getTerminalContent(orcaPage, 20_000), {
timeout: 30_000,
message: 'older worktree marker did not render before hiding'
})
.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(
orcaPage,
older.targetId,
DOCKER_SSH_SECOND_HUB_REMOTE_REPO_PATH
)
await expect
.poll(() => waitForActiveWorktree(orcaPage), { timeout: 30_000 })
.toBe(newer.worktreeId)
await waitForActiveTerminalManager(orcaPage, 60_000)
await waitForActivePanePtyId(orcaPage, 60_000)
const newerTabId = await getActiveTabId(orcaPage)
if (!newerTabId) {
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)
// The older worktree must force-park (its pane managers unmount)…
await waitForTabParked(orcaPage, olderTabId, { parkDelayMs: PARKING_DELAY_MS })
// …while the newest hidden worktree keeps its mounted panes (last-active exemption).
const newerStillMounted = await orcaPage.evaluate(
(tabId) => window.__paneManagers?.get(tabId) !== undefined,
newerTabId
)
expect(newerStillMounted).toBe(true)
// Reveal the evicted worktree: with SSH parking disabled the model paint is
// off, so the relay replay must restore the marker tail — never a blank pane.
await orcaPage.evaluate(
({ worktreeId, tabId }) => {
const state = window.__store?.getState()
state?.setActiveWorktree(worktreeId)
state?.setActiveTab(tabId)
state?.setActiveTabType('terminal')
},
{ worktreeId: older.worktreeId, tabId: olderTabId }
)
await waitForActiveTerminalManager(orcaPage, 60_000)
await expect
.poll(() => getTerminalContent(orcaPage, 20_000), {
timeout: 60_000,
message: 'revealed evicted worktree did not restore the marker via relay replay'
})
.toContain(`${olderMarker}:`)
} finally {
cleanupDockerSshRelayTarget(target)
}
})
})