Avoid repeated runtime imports and recovery fixture seeding (#25155)

This commit is contained in:
Neil
2026-10-04 00:18:19 -07:00
committed by GitHub
parent b1b78b4d20
commit ffd23fe25c
6 changed files with 230 additions and 231 deletions
+43
View File
@@ -1941,6 +1941,49 @@ remained red. Its review recognized all five shards as a complete reference
(10,608 files, 8,965,977 worker-ms). This was again a full fallback with
`selectionEvaluated: false`, not evidence for enabling selected tests.
## October 4 runtime imports and recovery fixtures
Three helper-only tests now import the existing terminal modules directly rather
than initializing the runtime service. Ten copied-loop cases never exercised
runtime memoization: they passed with its cache, timestamp update or prune
invalidation disabled. Two actual helper checks remain. The existing runtime
prune suite now exercises real leaf cache reuse, split prompt timestamps,
ordinary output, fresh prompts and detection after retained-history eviction.
Each of those three production faults fails a real runtime assertion.
Recovery tests now seed three exact fixture variants once, after the seed child
has closed. Each crash still receives an independent byte-for-byte copy of the
entire database/WAL family and remapped paths. Buffer.equals retains exact byte
comparison without recursive matcher overhead. All 46 original crash boundaries
and retries remain. Four additional copy-isolation/WAL checks run, and teardown
requires that all seed bytes remain unchanged after the full suite.
Three alternating one-worker hosted ARM pairs in
[37182181976](https://github.com/stablyai/orca/actions/runs/37182181976)
measured these complete invocations:
| Cohort | Baseline seconds | Candidate seconds | Median saving |
| --- | --- | --- | --- |
| Three imports only, same 15 tests | 19.257 / 19.167 / 19.363 | 1.769 / 1.768 / 1.768 | 90.8% |
| Final four-file runtime cohort | 22.312 / 22.122 / 21.969 | 13.494 / 13.793 / 13.601 | 38.5% |
| Recovery crash boundaries | 24.082 / 24.075 / 24.814 | 8.061 / 8.105 / 9.074 | 66.3% |
The final runtime cohort has seven real cases versus 16 including the copied
loops; its new runtime case is included in candidate timing. Recovery has 50
passes versus the original 46. Hosted Node typecheck passed. Recovery faults for
last-byte database/WAL corruption, shared database paths, missing WAL copies and
accepted/unaccepted seed collision failed the intended assertions. These are
focused workload savings, not measured whole-shard or queue-delay gains.
An independent local cache screen left both caches disabled. Across 14 unchanged
files and 92 cases, a warm Vitest transform cache reduced median invocation time
3.090 to 1.948 seconds, excluding archive costs; its cold arm increased time to
3.281 seconds. Node compilation caching showed no gain. Controls reproduced stale
transforms after TypeScript configuration or plugin-option changes, so persisted
reuse requires a complete transform-input stamp and hosted net-cost evidence.
A separate 130,000-pane leaf-collection optimization was restored: its complete
migration-file timing stayed within noise. The regression fixture remains.
## October 3 removal fixture cleanup ordering
[37105566358](https://github.com/stablyai/orca/actions/runs/37105566358)
@@ -1,4 +1,5 @@
import {
cpSync,
existsSync,
mkdirSync,
mkdtempSync,
@@ -8,7 +9,7 @@ import {
writeFileSync
} from 'node:fs'
import { tmpdir } from 'node:os'
import { basename, dirname, join } from 'node:path'
import { basename, dirname, join, relative } from 'node:path'
import { afterAll, afterEach, beforeAll, beforeEach, describe, expect, it, vi } from 'vitest'
import { setSecretStore } from '../../../shared/secret-store'
import { profileStateStorage } from '../../orca-profiles/profile-project-state-file'
@@ -103,13 +104,25 @@ afterEach(() => {
rmSync(root, { recursive: true, force: true })
}
})
afterAll(() => rmSync(suiteRoot, { recursive: true, force: true }))
afterAll(() => {
try {
for (const seed of seededFixtures.values()) {
for (const [suffix, bytes] of seed.originalFamily) {
expect(readFileSync(`${seed.databasePath}${suffix}`).equals(bytes)).toBe(true)
}
expect(readFileSync(seed.backupPath).equals(seed.backupBytes)).toBe(true)
expect(readFileSync(seed.exportPath, 'utf8')).toBe(selectedJson)
}
} finally {
rmSync(suiteRoot, { recursive: true, force: true })
}
})
type Fixture = RecoveryCrashOptions & { backupBytes: Buffer; originalFamily: Map<string, Buffer> }
const seededFixtures = new Map<string, Fixture>()
async function fixture(kind: 'json' | 'sqlite', accepted: boolean): Promise<Fixture> {
const root = mkdtempSync(join(suiteRoot, 'profile-'))
fixtureRoots.push(root)
async function seedFixture(kind: 'json' | 'sqlite', accepted: boolean): Promise<Fixture> {
const root = mkdtempSync(join(suiteRoot, 'seed-'))
const directory = join(root, 'profiles', profileId)
mkdirSync(directory, { recursive: true })
writeFileSync(
@@ -173,6 +186,39 @@ async function fixture(kind: 'json' | 'sqlite', accepted: boolean): Promise<Fixt
return { ...options, backupBytes: readFileSync(backupPath), originalFamily }
}
function cloneFixture(seed: Fixture): Fixture {
const root = mkdtempSync(join(suiteRoot, 'profile-'))
fixtureRoots.push(root)
cpSync(seed.root, root, { recursive: true })
const options = {
...seed,
root,
dataFile: join(root, relative(seed.root, seed.dataFile)),
databasePath: join(root, relative(seed.root, seed.databasePath)),
exportPath: join(root, relative(seed.root, seed.exportPath)),
backupPath: join(root, relative(seed.root, seed.backupPath)),
markerPath: join(root, relative(seed.root, seed.markerPath))
}
const originalFamily = new Map(
['', '-wal', '-shm', '-journal'].map((suffix) => [
suffix,
readFileSync(`${options.databasePath}${suffix}`)
])
)
return { ...options, backupBytes: readFileSync(options.backupPath), originalFamily }
}
async function fixture(kind: 'json' | 'sqlite', accepted: boolean): Promise<Fixture> {
const key = `${kind}/${accepted}`
let seed = seededFixtures.get(key)
if (!seed) {
// The seed child's close event has fired before its WAL family is copied.
seed = await seedFixture(kind, accepted)
seededFixtures.set(key, seed)
}
return cloneFixture(seed)
}
function readSqlite(path: string): unknown {
const opened = openProfileStateDatabaseReadOnly(path, profileId)
try {
@@ -192,10 +238,12 @@ function assertQuarantine(profile: Fixture): void {
const directory = join(dirname(profile.databasePath), quarantine)
// Check exact family bytes before opening the copied WAL snapshot.
for (const [suffix, bytes] of profile.originalFamily) {
expect(readFileSync(join(directory, `profile-state.db${suffix}`))).toEqual(bytes)
expect(readFileSync(join(directory, `profile-state.db${suffix}`)).equals(bytes)).toBe(true)
}
expect(readFileSync(join(directory, basename(profile.exportPath)), 'utf8')).toBe(selectedJson)
expect(readFileSync(join(directory, basename(profile.backupPath)))).toEqual(profile.backupBytes)
expect(
readFileSync(join(directory, basename(profile.backupPath))).equals(profile.backupBytes)
).toBe(true)
expect(readSqlite(join(directory, 'profile-state.db'))).toEqual(oldState)
}
@@ -346,7 +394,7 @@ describe('SQLite recovery process death', () => {
const profile = await fixture('sqlite', true)
await killRecoveryAt(bundle, profile, stage(profile, boundary))
assertQuarantine(profile)
expect(readFileSync(profile.backupPath)).toEqual(profile.backupBytes)
expect(readFileSync(profile.backupPath).equals(profile.backupBytes)).toBe(true)
const expected = [
'marker-invalidated',
'selected-export',
@@ -365,7 +413,7 @@ describe('SQLite recovery process death', () => {
: 'refused'
assertRestart(profile, expected)
retry(profile)
expect(readFileSync(profile.backupPath)).toEqual(profile.backupBytes)
expect(readFileSync(profile.backupPath).equals(profile.backupBytes)).toBe(true)
})
it.skipIf(process.platform === 'win32')(
@@ -378,3 +426,38 @@ describe('SQLite recovery process death', () => {
}
)
})
describe('seeded recovery fixture copies', () => {
it.each([
['json', false],
['json', true],
['sqlite', true]
] as const)(
'isolates %s/accepted=%s through corruption and WAL reopen',
async (kind, accepted) => {
const first = await fixture(kind, accepted)
const second = await fixture(kind, accepted)
const seed = seededFixtures.get(`${kind}/${accepted}`)
if (!seed) {
throw new Error('Seed fixture was not retained')
}
expect(new Set([first.root, second.root, seed.root]).size).toBe(3)
for (const [suffix, bytes] of seed.originalFamily) {
expect(readFileSync(`${first.databasePath}${suffix}`).equals(bytes)).toBe(true)
writeFileSync(`${first.databasePath}${suffix}`, 'corrupted-copy')
expect(readFileSync(`${second.databasePath}${suffix}`).equals(bytes)).toBe(true)
expect(readFileSync(`${seed.databasePath}${suffix}`).equals(bytes)).toBe(true)
}
const third = await fixture(kind, accepted)
expect(readSqlite(third.databasePath)).toEqual(oldState)
expect(readFileSync(seed.backupPath).equals(seed.backupBytes)).toBe(true)
expect(readFileSync(seed.exportPath, 'utf8')).toBe(selectedJson)
}
)
it('rejects an incomplete copied WAL family before a recovery child starts', async () => {
const incomplete = await fixture('json', false)
rmSync(`${incomplete.databasePath}-wal`)
expect(() => cloneFixture(incomplete)).toThrow(/ENOENT/)
})
})
@@ -1,124 +1,7 @@
import { describe, expect, it, vi } from 'vitest'
import {
appendNormalizedToTailBuffer,
buildPreview,
computeTerminalTailWaitState,
tailGainedNewerBlockedReason,
type TerminalTailWaitState
} from './orca-runtime'
import { computeTerminalTailWaitState } from './terminal-wait-tail-state'
// These tests pin the onPtyData wait-detection memoization: caching the
// post-append wait state and reusing it as the next chunk's pre-append state
// must produce byte-for-byte the same waitBlockedAt stamping as recomputing the
// wait-state scan on both sides of every chunk (the pre-memoization behavior),
// while doing roughly half the state computations.
type RedrawCursor = ReturnType<typeof appendNormalizedToTailBuffer>['redrawCursor']
type TailSim = {
tailBuffer: string[]
tailPartialLine: string
tailRedrawCursor: RedrawCursor
preview: string
waitBlockedAt: number | null
tailWaitState?: TerminalTailWaitState
}
function newSim(): TailSim {
return {
tailBuffer: [],
tailPartialLine: '',
tailRedrawCursor: null,
preview: '',
waitBlockedAt: null
}
}
type Compute = typeof computeTerminalTailWaitState
// Mirrors the memoized onPtyData tail loop: reuse the cached tail-derived state
// as the previous state; only recompute it on a preview-fallback (empty tail).
function stepMemoized(sim: TailSim, chunk: string, at: number, compute: Compute): void {
const previousWaitState =
sim.tailWaitState?.fromTail === true
? sim.tailWaitState
: compute(sim.tailBuffer, sim.tailPartialLine, sim.preview)
const nextTail = appendNormalizedToTailBuffer(
sim.tailBuffer,
sim.tailPartialLine,
chunk,
sim.tailRedrawCursor
)
const nextWaitState = compute(nextTail.lines, nextTail.partialLine, sim.preview)
if (tailGainedNewerBlockedReason(previousWaitState, nextWaitState, chunk)) {
sim.waitBlockedAt = at
}
sim.tailWaitState = nextWaitState
sim.tailBuffer = nextTail.lines
sim.tailPartialLine = nextTail.partialLine
sim.tailRedrawCursor = nextTail.redrawCursor
sim.preview = buildPreview(nextTail.lines, nextTail.partialLine)
}
// Reference: the pre-memoization behavior — recompute the previous state fresh
// from the current tail on every chunk (no cache).
function stepReference(sim: TailSim, chunk: string, at: number, compute: Compute): void {
const previousWaitState = compute(sim.tailBuffer, sim.tailPartialLine, sim.preview)
const nextTail = appendNormalizedToTailBuffer(
sim.tailBuffer,
sim.tailPartialLine,
chunk,
sim.tailRedrawCursor
)
const nextWaitState = compute(nextTail.lines, nextTail.partialLine, sim.preview)
if (tailGainedNewerBlockedReason(previousWaitState, nextWaitState, chunk)) {
sim.waitBlockedAt = at
}
sim.tailBuffer = nextTail.lines
sim.tailPartialLine = nextTail.partialLine
sim.tailRedrawCursor = nextTail.redrawCursor
sim.preview = buildPreview(nextTail.lines, nextTail.partialLine)
}
function runBoth(chunks: string[]): { memoized: (number | null)[]; reference: (number | null)[] } {
const memoSim = newSim()
const refSim = newSim()
const memoized: (number | null)[] = []
const reference: (number | null)[] = []
chunks.forEach((chunk, index) => {
const at = index + 1
stepMemoized(memoSim, chunk, at, computeTerminalTailWaitState)
stepReference(refSim, chunk, at, computeTerminalTailWaitState)
memoized.push(memoSim.waitBlockedAt)
reference.push(refSim.waitBlockedAt)
})
return { memoized, reference }
}
const SCENARIOS: Record<string, string[]> = {
'plain output never blocks': ['building...\n', 'compiled ok\n', 'watching for changes\n'],
'blocked prompt in one chunk': ['Update available! Press Enter to continue.\n'],
'blocked prompt split across chunks': ['Update available!\n', 'Press Enter to continue.\n'],
'blocked then plain output stays blocked': [
'Update available! Press Enter to continue.\n',
'still here\n',
'more logs\n'
],
'partial lines without newline then completion': [
'Update ava',
'ilable! Press Enter ',
'to continue.\n'
],
'empty and whitespace chunks': ['', ' ', '\n', 'ok\n', ''],
'ready header after stale blocked prompt': [
'Update available! Press Enter to continue.\n',
'OpenAI Codex\n',
'model: gpt\n',
'directory: /repo\n'
]
}
describe('onPtyData tail wait memoization', () => {
describe('terminal tail wait state', () => {
it('computeTerminalTailWaitState reports fromTail and blocked signals', () => {
const empty = computeTerminalTailWaitState([], '', '')
expect(empty.fromTail).toBe(false)
@@ -151,92 +34,4 @@ describe('onPtyData tail wait memoization', () => {
lastIndexOf.mockRestore()
}
})
for (const [name, chunks] of Object.entries(SCENARIOS)) {
it(`memoized stamping matches recompute reference: ${name}`, () => {
const { memoized, reference } = runBoth(chunks)
expect(memoized).toEqual(reference)
})
}
it('stays equivalent across tail eviction beyond the retained cap', () => {
const chunks: string[] = []
for (let i = 0; i < 2600; i += 1) {
chunks.push(`line ${i} of streaming build output that keeps the tail busy\n`)
}
// Introduce a real blocked prompt well past the eviction boundary.
chunks.push('Update available! Press Enter to continue.\n')
chunks.push('trailing log after prompt\n')
const { memoized, reference } = runBoth(chunks)
expect(memoized).toEqual(reference)
// The prompt must actually be detected (guards against a vacuous match).
expect(memoized.at(-1)).not.toBeNull()
})
it('recomputes correctly after a transcript prune empties the tail (prune-then-resume)', () => {
// pruneDisconnectedPtyTranscript empties the tail AND clears tailWaitState;
// model that here and assert the memoized stamping still tracks a fresh
// recompute across the reset (a stale cache would desync the first resumed
// chunk, since the pre-prune tail held a blocked prompt).
const prune = (sim: TailSim): void => {
sim.tailBuffer = []
sim.tailPartialLine = ''
sim.tailRedrawCursor = null
sim.preview = ''
sim.waitBlockedAt = null
sim.tailWaitState = undefined
}
const memoSim = newSim()
const refSim = newSim()
const memoOut: (number | null)[] = []
const refOut: (number | null)[] = []
let at = 0
const feed = (chunk: string): void => {
at += 1
stepMemoized(memoSim, chunk, at, computeTerminalTailWaitState)
stepReference(refSim, chunk, at, computeTerminalTailWaitState)
memoOut.push(memoSim.waitBlockedAt)
refOut.push(refSim.waitBlockedAt)
}
// Pre-prune: leave a stale blocked prompt in the tail.
;['building\n', 'Update available! Press Enter to continue.\n', 'more log\n'].forEach(feed)
prune(memoSim)
prune(refSim)
// Resume: a fresh blocked prompt must be stamped, not masked by stale cache.
;['fresh start\n', 'Update available! Press Enter to continue.\n', 'after\n'].forEach(feed)
expect(memoOut).toEqual(refOut)
expect(memoSim.waitBlockedAt).not.toBeNull()
})
it('does roughly half the wait-state computations of the recompute reference', () => {
const chunks: string[] = []
for (let i = 0; i < 500; i += 1) {
chunks.push(`streaming line ${i}\n`)
}
let memoCalls = 0
const countingMemo: Compute = (lines, partial, preview) => {
memoCalls += 1
return computeTerminalTailWaitState(lines, partial, preview)
}
let refCalls = 0
const countingRef: Compute = (lines, partial, preview) => {
refCalls += 1
return computeTerminalTailWaitState(lines, partial, preview)
}
const memoSim = newSim()
const refSim = newSim()
chunks.forEach((chunk, index) => {
stepMemoized(memoSim, chunk, index + 1, countingMemo)
stepReference(refSim, chunk, index + 1, countingRef)
})
// Reference recomputes both sides every chunk: 2 per chunk.
expect(refCalls).toBe(chunks.length * 2)
// Memoized reuses the cached previous state on every non-empty-tail chunk:
// 1 per chunk plus a single first-chunk cold miss.
expect(memoCalls).toBe(chunks.length + 1)
})
})
@@ -1,14 +1,9 @@
/**
* Regression: pruneDisconnectedPtyTranscript empties a disconnected PTY's
* retained tail. The onPtyData wait-scan memoization caches the tail's wait
* state on the record (tailWaitState) and reuses it as the next chunk's
* "previous" state — so the prune MUST also clear that cache, or a record that
* resumes output after adoption/reattach would reuse a stale (pre-prune,
* possibly blocked) wait state and mis-stamp waitBlockedAt on its first chunk.
*/
import { describe, expect, it } from 'vitest'
// A disconnected transcript must not keep a wait-scan cache from before its history was pruned.
import { describe, expect, it, vi } from 'vitest'
import { OrcaRuntimeService } from './orca-runtime'
import type { TerminalTailWaitState } from './orca-runtime'
import { MAX_TAIL_LINES } from './terminal-tail-limits'
import * as terminalWaitTailState from './terminal-wait-tail-state'
import type { TerminalTailWaitState } from './terminal-wait-tail-state'
type PtyRecord = {
connected: boolean
@@ -20,9 +15,33 @@ type RuntimeInternals = {
pruneDisconnectedPtyTranscript: (pty: PtyRecord) => void
}
function onlyRuntimeLeaf(runtime: unknown) {
if (typeof runtime !== 'object' || runtime === null || !('leaves' in runtime)) {
throw new Error('Runtime has no leaves')
}
const leaves = runtime.leaves
if (!(leaves instanceof Map) || leaves.size !== 1) {
throw new Error('Expected exactly one runtime leaf')
}
const leaf: unknown = leaves.values().next().value
if (
typeof leaf !== 'object' ||
leaf === null ||
!('tailBuffer' in leaf) ||
!Array.isArray(leaf.tailBuffer) ||
!('tailLinesTotal' in leaf) ||
typeof leaf.tailLinesTotal !== 'number' ||
!('waitBlockedAt' in leaf)
) {
throw new Error('Runtime leaf has no terminal tail state')
}
return leaf
}
describe('pruneDisconnectedPtyTranscript clears the wait-scan cache', () => {
it('empties the tail and drops tailWaitState so resume recomputes', () => {
const runtime = new OrcaRuntimeService()
// oxlint-disable-next-line typescript/consistent-type-assertions -- SAFETY: both protected methods are declared on OrcaRuntimeService's inheritance chain.
const internals = runtime as unknown as RuntimeInternals
const pty = internals.recordPtyWorktree('pty-1', 'wt-1', { connected: true })
@@ -40,4 +59,65 @@ describe('pruneDisconnectedPtyTranscript clears the wait-scan cache', () => {
expect(pty.tailBuffer).toEqual([])
expect(pty.tailWaitState).toBeUndefined()
})
it('reuses the real leaf wait scan and stamps each newly arriving blocked prompt', async () => {
const runtime = new OrcaRuntimeService()
const ptyId = 'pty-memo'
const leafId = '11111111-1111-4111-8111-111111111111'
runtime.attachWindow(1)
runtime.syncWindowGraph(1, {
tabs: [{ tabId: 'tab-1', worktreeId: 'wt-1', title: '', activeLeafId: leafId, layout: null }],
leaves: [
{ tabId: 'tab-1', worktreeId: 'wt-1', leafId, paneRuntimeId: 1, ptyId, paneTitle: null }
]
})
const leaf = onlyRuntimeLeaf(runtime)
// Different retained history exercises the leaf scan rather than the shared PTY-tail path.
leaf.tailBuffer = ['leaf-only history']
leaf.tailLinesTotal = 1
const scan = vi.spyOn(terminalWaitTailState, 'computeTerminalTailWaitState')
const leafScans = () =>
scan.mock.calls.filter(([lines]) => lines.includes('leaf-only history')).length
try {
runtime.onPtyData(ptyId, 'first plain line\n', 1_000)
expect(leafScans()).toBe(2)
expect('tailWaitState' in leaf ? leaf.tailWaitState : undefined).toMatchObject({
fromTail: true,
signal: null
})
runtime.onPtyData(ptyId, 'second plain line\n', 2_000)
expect(leafScans()).toBe(3)
expect(leaf.waitBlockedAt).toBeNull()
runtime.onPtyData(ptyId, 'Update ava', 3_000)
expect(leafScans()).toBe(4)
expect(leaf.waitBlockedAt).toBeNull()
runtime.onPtyData(ptyId, 'ilable! Press Enter to continue.\n', 4_000)
expect(leafScans()).toBe(5)
expect(leaf.waitBlockedAt).toBe(4_000)
runtime.onPtyData(ptyId, 'ordinary log after the prompt\n', 5_000)
expect(leafScans()).toBe(6)
expect(leaf.waitBlockedAt).toBe(4_000)
runtime.onPtyData(ptyId, 'Update available! Press Enter to continue.\n', 6_000)
expect(leafScans()).toBe(7)
expect(leaf.waitBlockedAt).toBe(6_000)
runtime.onPtyData(
ptyId,
Array.from({ length: MAX_TAIL_LINES + 1 }, (_, index) => `streaming line ${index}\n`).join(
''
),
7_000
)
expect(leaf.tailBuffer).toHaveLength(MAX_TAIL_LINES)
expect(leaf.tailBuffer).not.toContain('leaf-only history')
expect('tailWaitState' in leaf ? leaf.tailWaitState : undefined).toMatchObject({
fromTail: true,
signal: null
})
runtime.onPtyData(ptyId, 'Update available! Press Enter to continue.\n', 8_000)
expect(leaf.waitBlockedAt).toBe(8_000)
} finally {
scan.mockRestore()
await runtime.onPtyExit(ptyId, 0)
}
})
})
@@ -1,8 +1,6 @@
import { describe, expect, it } from 'vitest'
import {
appendNormalizedToTailBuffer,
appendNormalizedToMultilineTailBufferUnwindowed
} from './orca-runtime'
import { appendNormalizedToTailBuffer } from './terminal-tail-buffer'
import { appendNormalizedToMultilineTailBufferUnwindowed } from './terminal-tail-redraw-buffer'
// Differential guard for the windowed redraw tail path: the public
// appendNormalizedToTailBuffer routes vertical-control chunks through a
+1 -1
View File
@@ -1,7 +1,7 @@
import { describe, expect, it } from 'vitest'
import type { TerminalCursorContext } from '../../shared/terminal-composer-draft'
import type { HeadlessEmulator } from '../daemon/headless-emulator'
import { projectTerminalTailLines } from './orca-runtime'
import { projectTerminalTailLines } from './orca-runtime-terminal-projection'
describe('projectTerminalTailLines', () => {
it('does not splice a scrolled viewport into unrelated tail rows', () => {