fix(pty): read the incarnation through the shared guard, not truthiness

A malformed incarnation is evidence of nothing, so it must read as "unknown"
rather than as a value that disagrees with every well-formed one — otherwise a
non-string on the payload would discard the very exits the buffer exists to
deliver. Route both the record and the comparison through the existing
isPtyIncarnationId guard.
This commit is contained in:
Neil
2026-08-28 03:00:18 -07:00
parent cc4c1f7998
commit 06fa69b63b
2 changed files with 26 additions and 4 deletions
@@ -276,6 +276,20 @@ describe('pre-handler PTY buffer', () => {
expect(hasPreHandlerPtyExit(RECYCLED_PTY_ID)).toBe(true)
})
// A malformed incarnation is evidence of nothing. Treating it as a value that disagrees with
// everything would discard the very exits the buffer exists to deliver.
it('treats a malformed incarnation as unknown rather than as a disagreement', () => {
bufferPreHandlerPtyExit(RECYCLED_PTY_ID, 4, { not: 'a string' })
discardPreHandlerPtyExitFromForeignIncarnation(RECYCLED_PTY_ID, FRESH_INCARNATION_ID)
expect(hasPreHandlerPtyExit(RECYCLED_PTY_ID)).toBe(true)
drainPreHandlerPtyExit(RECYCLED_PTY_ID, vi.fn())
clearConsumedPreHandlerPtyExit(RECYCLED_PTY_ID)
bufferPreHandlerPtyExit(RECYCLED_PTY_ID, 5, PRIOR_INCARNATION_ID)
discardPreHandlerPtyExitFromForeignIncarnation(RECYCLED_PTY_ID, 42)
expect(hasPreHandlerPtyExit(RECYCLED_PTY_ID)).toBe(true)
})
it('re-admits exits for a recycled id whose prior incarnation was consumed', () => {
consumePreHandlerPtyState(RECYCLED_PTY_ID)
@@ -1,3 +1,4 @@
import { isPtyIncarnationId } from '../../../../shared/pty-incarnation'
import { clampUtf8Tail } from './pty-eager-buffer-clamp'
import type { PtyDataMeta } from './pty-dispatcher'
@@ -81,9 +82,11 @@ function reserveBoundedPtySlot<V>(map: Map<string, V>, ptyId: string, cap: numbe
* existing id, and re-owning incarnation X is still proof that W's exit was not theirs. */
export function discardPreHandlerPtyExitFromForeignIncarnation(
ptyId: string,
incarnationId: string | undefined
incarnationId: unknown
): void {
if (!incarnationId) {
// Why the shared guard and not a truthiness check: anything that is not a well-formed incarnation
// is evidence of nothing, and must read as "unknown" rather than disagree with everything.
if (!isPtyIncarnationId(incarnationId)) {
return
}
const exit = preHandlerPtyExit.get(ptyId)
@@ -164,7 +167,11 @@ export function replayPreHandlerPtyData(ptyId: string, observer: (data: string)
}
}
export function bufferPreHandlerPtyExit(ptyId: string, code: number, incarnationId?: string): void {
export function bufferPreHandlerPtyExit(
ptyId: string,
code: number,
incarnationId?: unknown
): void {
if (consumedPreHandlerPtyExits.has(ptyId) || discardedPreHandlerPtyStates.has(ptyId)) {
return
}
@@ -172,7 +179,8 @@ export function bufferPreHandlerPtyExit(ptyId: string, code: number, incarnation
preHandlerPtyExit.set(ptyId, {
code,
sequence: nextPreHandlerPtySequence(),
...(incarnationId ? { incarnationId } : {})
// Record only a well-formed incarnation; a malformed one must not become evidence.
...(isPtyIncarnationId(incarnationId) ? { incarnationId } : {})
})
}