mirror of
https://github.com/stablyai/orca.git
synced 2026-09-23 16:02:24 +00:00
* perf(terminal): activate splits before cwd resolution * test(terminal): prove split focus before cwd publish * fix(terminal): release stale split cwd fence * test(terminal): add visible split activation latency benchmark * docs(reliability): clarify split benchmark provenance * fix: preserve deferred split handoffs across remounts * fix: fence late deferred split closes * docs(reliability): record exact split benchmark runs * test(reliability): fail benchmark on artifact write errors * test(reliability): attribute split activation phases * docs(reliability): record schema-v2 split benchmark * refactor(terminal): collapse duplicated split-handoff and write-queue paths - Drop the discardDeferredSplitPaneHandoff alias for its identical clear twin. - Fold the deferred-cwd resolve/reject settle handlers into one applier. - Extract settlePaneCwdDeferredSpawn for the repeated read-clear-write pattern. - Share one head-index FIFO primitive between the ordinary and reply queues. * fix(terminal): stop retaining a promise reaction per acknowledged write Racing every accepted write against one queue-lifetime cancel promise kept a reaction record alive until that promise settled: 200k acknowledged writes retained 88.6MB, now 0.1MB. Give each in-flight write its own cancel, and split the shared FIFO primitive into its own module. Also sanitize the split-latency benchmark report at its single serialization point so shared artifacts no longer carry the machine-local repo path or unbounded cleanup error text. * fix(terminal): settle deferred split input when the spawn is abandoned An abandoned deferred spawn returns before transport.connect(), so nothing drained the pre-connect buffer: sendInputAccepted's promise never settled and a paste into that pane hung forever. Clear the buffer on the abandon fence. Also re-derive the pre-connect retention cap from the clipboard-paste ceiling rather than the 16MB single-write ceiling; it is held twice per pane across up to 64 deferred splits, so 5.59M code units guarded the wrong thing. * fix(terminal): release the deferred cwd fence on a rejected reattach A daemon createOrAttach can turn an apparent fresh spawn into a reattach; when that reattach is refused the spawn ends with deferredSplitSpawn/pendingCwd still set, permanently arming the pre-bind detach refusal. The release no-ops when a PTY did bind, so it only fires where the fence would otherwise leak. The stale-generation return above is deliberately left alone: a newer connect already owns the pane there, and the fence is not generation-scoped.
193 lines
5.3 KiB
TypeScript
193 lines
5.3 KiB
TypeScript
import { describe, expect, it } from 'vitest'
|
|
import { buildBenchmarkReport, summarizeSamples } from './terminal-split-activation-latency-report'
|
|
import {
|
|
createSplitLatencySample,
|
|
mergeSplitLatencyMainProbeEvents,
|
|
type RendererPhaseStamps,
|
|
type SplitLatencyMainProbeEvent
|
|
} from './terminal-split-activation-latency-phases'
|
|
|
|
function createRendererStamps(): RendererPhaseStamps {
|
|
return {
|
|
marker: 'echo-marker',
|
|
sourcePaneId: 1,
|
|
sourcePtyId: 'pty-source',
|
|
newPaneId: 2,
|
|
newPtyId: 'pty-child',
|
|
rendererTimeOriginEpochMs: 1_000,
|
|
keydownAtMs: 10,
|
|
focusAtMs: 20,
|
|
cwdRequestAtMs: null,
|
|
cwdSettledAtMs: null,
|
|
ptySpawnRequestAtMs: null,
|
|
ptySpawnResultAtMs: null,
|
|
ptyBoundAtMs: 82,
|
|
fixtureUnlockRequestedAtMs: 83,
|
|
fixtureUnlockIpcWriteAtMs: null,
|
|
fixtureUnlockIpcWriteChannel: null,
|
|
fixtureReadyParsedAtMs: 100,
|
|
inputAtMs: 101,
|
|
firstEchoAtMs: 103
|
|
}
|
|
}
|
|
|
|
function createMainProbeEvents(): SplitLatencyMainProbeEvent[] {
|
|
return [
|
|
{
|
|
kind: 'cwd-request',
|
|
operationId: 99,
|
|
atEpochMs: 1_010,
|
|
ptyId: 'pty-other',
|
|
writeChannel: null
|
|
},
|
|
{
|
|
kind: 'cwd-request',
|
|
operationId: 1,
|
|
atEpochMs: 1_011,
|
|
ptyId: 'pty-source',
|
|
writeChannel: null
|
|
},
|
|
{
|
|
kind: 'cwd-settled',
|
|
operationId: 1,
|
|
atEpochMs: 1_060,
|
|
ptyId: 'pty-source',
|
|
writeChannel: null
|
|
},
|
|
{
|
|
kind: 'pty-spawn-request',
|
|
operationId: 2,
|
|
atEpochMs: 1_061,
|
|
ptyId: null,
|
|
writeChannel: null
|
|
},
|
|
{
|
|
kind: 'pty-spawn-result',
|
|
operationId: 3,
|
|
atEpochMs: 1_070,
|
|
ptyId: 'pty-other',
|
|
writeChannel: null
|
|
},
|
|
{
|
|
kind: 'pty-spawn-result',
|
|
operationId: 2,
|
|
atEpochMs: 1_080,
|
|
ptyId: 'pty-child',
|
|
writeChannel: null
|
|
},
|
|
{
|
|
kind: 'pty-write-cr',
|
|
operationId: null,
|
|
atEpochMs: 1_081,
|
|
ptyId: 'pty-other',
|
|
writeChannel: 'pty:write'
|
|
},
|
|
{
|
|
kind: 'pty-write-cr',
|
|
operationId: null,
|
|
atEpochMs: 1_084,
|
|
ptyId: 'pty-child',
|
|
writeChannel: 'pty:writeAccepted'
|
|
}
|
|
]
|
|
}
|
|
|
|
describe('terminal split activation latency report', () => {
|
|
it('attributes main-process phases to the matching source and child PTYs', () => {
|
|
const stamps = mergeSplitLatencyMainProbeEvents(createRendererStamps(), createMainProbeEvents())
|
|
const sample = createSplitLatencySample({
|
|
phase: 'measured',
|
|
iteration: 0,
|
|
stamps,
|
|
completedWithinTimeout: true,
|
|
paneCountAfterProbe: 2,
|
|
ptyExitObserved: true,
|
|
cleanupError: null
|
|
})
|
|
|
|
expect(sample).toMatchObject({
|
|
cwdRequestAtMs: 11,
|
|
cwdSettledAtMs: 60,
|
|
ptySpawnRequestAtMs: 61,
|
|
ptySpawnResultAtMs: 80,
|
|
fixtureUnlockIpcWriteAtMs: 84,
|
|
fixtureUnlockIpcWriteChannel: 'pty:writeAccepted',
|
|
shortcutToCwdRequestMs: 1,
|
|
cwdLookupMs: 49,
|
|
cwdSettleToPtySpawnRequestMs: 1,
|
|
ptySpawnRequestToResultMs: 19,
|
|
ptySpawnResultToBindMs: 2,
|
|
fixtureUnlockRequestToIpcWriteMs: 1,
|
|
fixtureUnlockIpcWriteToReadyParseMs: 16,
|
|
success: true,
|
|
missing: []
|
|
})
|
|
})
|
|
|
|
it('embeds revision identity and summarizes the attributed phases', () => {
|
|
const stamps = mergeSplitLatencyMainProbeEvents(createRendererStamps(), createMainProbeEvents())
|
|
const sample = createSplitLatencySample({
|
|
phase: 'measured',
|
|
iteration: 0,
|
|
stamps,
|
|
completedWithinTimeout: true,
|
|
paneCountAfterProbe: 2,
|
|
ptyExitObserved: true,
|
|
cleanupError: null
|
|
})
|
|
const revision = { headSha: 'a'.repeat(40), dirty: false }
|
|
const result = buildBenchmarkReport({
|
|
label: 'candidate',
|
|
revision,
|
|
headfulRun: true,
|
|
windowState: { browserWindowVisible: true, windowCount: 1 },
|
|
documentVisibility: 'visible',
|
|
testRepoPath: '/tmp/repo',
|
|
warmupSamples: [{ ...sample, phase: 'warmup' }],
|
|
measuredSamples: [sample],
|
|
abortError: null,
|
|
config: {
|
|
warmupCycles: 1,
|
|
measuredCycles: 1,
|
|
maxMeasuredCycles: 200,
|
|
testTimeoutMs: 30_000,
|
|
splitChord: 'Meta+d',
|
|
closeChord: 'Meta+w',
|
|
sampleTimeoutMs: 15_000,
|
|
cleanupTimeoutMs: 15_000,
|
|
processCwdCacheExpiryWaitMs: 1_650
|
|
}
|
|
})
|
|
|
|
expect(result.report).toMatchObject({
|
|
schemaVersion: 2,
|
|
revision,
|
|
status: 'passed',
|
|
valid: true
|
|
})
|
|
expect(result.measuredSummary.distributions.cwdLookupMs.p50).toBe(49)
|
|
expect(result.measuredSummary.distributions.ptySpawnRequestToResultMs.p50).toBe(19)
|
|
expect(result.measuredSummary.distributions.fixtureUnlockIpcWriteToReadyParseMs.p50).toBe(16)
|
|
})
|
|
|
|
it('invalidates a sample when the actual fixture-unlock IPC write is missing', () => {
|
|
const stamps = mergeSplitLatencyMainProbeEvents(
|
|
createRendererStamps(),
|
|
createMainProbeEvents().filter((event) => event.kind !== 'pty-write-cr')
|
|
)
|
|
const sample = createSplitLatencySample({
|
|
phase: 'measured',
|
|
iteration: 0,
|
|
stamps,
|
|
completedWithinTimeout: true,
|
|
paneCountAfterProbe: 2,
|
|
ptyExitObserved: true,
|
|
cleanupError: null
|
|
})
|
|
|
|
expect(sample.success).toBe(false)
|
|
expect(sample.missing).toContain('fixture-unlock-ipc-write')
|
|
expect(summarizeSamples([sample], 1).counts.missingEvents.fixtureUnlockIpcWrite).toBe(1)
|
|
})
|
|
})
|