fix(memory): bound host mirror gap verdicts

This commit is contained in:
m4air
2026-09-19 15:45:58 -07:00
parent 0f09c4b7ba
commit b39b2a057a
2 changed files with 23 additions and 0 deletions
@@ -244,4 +244,16 @@ describe('handle-gap verdict map, all rules on one tree', () => {
expect(countHostMirrorHandleGapVerdictsForTests()).toBe(0)
expect(vi.getTimerCount()).toBe(0)
})
it('bounds permanently orphaned verdicts across distinct environments', () => {
for (let round = 0; round < 600; round += 1) {
const environmentId = `env-orphan-${round}`
setRuntimeEnvironmentConnectionGenerationForTests(environmentId, 1)
const tabId = `orphan-${round}`
setLiveTabs([tabId], { [tabId]: `remote:${environmentId}@@term_${round}` })
parkAndExpire(environmentId, tabId)
}
expect(countHostMirrorHandleGapVerdictsForTests()).toBeLessThanOrEqual(512)
})
})
@@ -106,6 +106,7 @@ type ExpiredHandleGapVerdict = {
/** Sorted environment-minted PTY ids the tab's leaves held AT PARK TIME; '' when none. */
paneBinding: string
}
const MAX_EXPIRED_HANDLE_GAP_VERDICTS = 512
const expiredGenerationByPane = new Map<string, ExpiredHandleGapVerdict>()
let unsubscribeStore: (() => void) | null = null
@@ -199,6 +200,16 @@ function recordExpiredWait(environmentId: string, key: string): void {
// gate stops recording anything at all rather than admitting ''. It pins a different property
// (reconnect-void, host-mirror-handle-gap-resume.test.ts). Both are load-bearing, for different
// reasons — do not collapse them as redundant.
// Eviction is conservative: a missing verdict makes the pane wait once more, never resume early.
if (!expiredGenerationByPane.has(key)) {
while (expiredGenerationByPane.size >= MAX_EXPIRED_HANDLE_GAP_VERDICTS) {
const oldest = expiredGenerationByPane.keys().next()
if (oldest.done) {
break
}
expiredGenerationByPane.delete(oldest.value)
}
}
expiredGenerationByPane.set(key, {
generation,
paneBinding: waitersByPane.get(key)?.paneBinding ?? ''