test(runtime): pin the retraction boundary against a stale inventory omission

Mutation testing left a survivor: writing the boundary unconditionally, instead
of only when it advances the ledger, passed the whole runtime suite. It is not
inert. A visibility-resume inventory reserves its received frame before it
lists, so an omission it reports can be older than a stream frame that landed
meanwhile; without the guard that stale omission rewinds the ledger, forgetting
the stream frame's version, and a delayed list reserved in between is then
readmitted instead of outranked. This pins that ordering.

The one remaining survivor is the boundary's `snapshotVersion`, and it is inert:
the ledger's version is read at exactly two sites, both reachable only when the
incoming frame's epoch equals the stored one, and a retraction epoch never
equals a live publication.
This commit is contained in:
Neil
2026-09-11 01:19:26 -07:00
parent a6f354ad8f
commit 113515f25a
@@ -2,10 +2,14 @@ import { beforeEach, describe, expect, it, vi } from 'vitest'
import type { RuntimeMobileSessionTabsResult } from '../../../shared/runtime-types'
import { decideWebSessionTabsSnapshot } from './web-session-tabs-sync'
import {
recordReceivedWebSessionTabsRemoval,
recordReceivedWebSessionTabsSnapshot,
shouldApplyRecoveredWebSessionTabsSnapshot
} from './web-session-tabs-sync/tracking'
import { nextReceivedSessionTabsFrame } from './web-session-tabs-sync/state'
import {
nextReceivedSessionTabsFrame,
VISIBILITY_INVENTORY_REMOVAL_EPOCH
} from './web-session-tabs-sync/state'
import { resetWebSessionTabsSyncTestState } from './web-session-tabs-sync-test-harness'
vi.mock('../store', () => ({ useAppStore: { setState: vi.fn() } }))
@@ -133,6 +137,41 @@ describe('a removal frame must not retire the publisher that is still live', ()
expect(admits(delayed, delayedReceived)).toBe(false)
})
/**
* The boundary is evidence, so a retraction may only ever advance it. A visibility-resume
* inventory reserves its received frame before it lists, so an omission it reports can be older
* than a stream frame that landed meanwhile. Letting that stale omission rewind the ledger would
* forget the stream frame's version and readmit a delayed frame the ledger had already outranked.
*/
it('does not let an inventory omission older than the last stream frame rewind the boundary', () => {
const inventoryReceived = nextReceivedSessionTabsFrame()
const delayedReceived = nextReceivedSessionTabsFrame()
const streamReceived = recordReceivedWebSessionTabsSnapshot(ENVIRONMENT_ID, liveFrame(3))
expect(delayedReceived).toBeGreaterThan(inventoryReceived)
expect(streamReceived).toBeGreaterThan(delayedReceived)
// The inventory sweep finally reports this worktree missing, on its older frame.
recordReceivedWebSessionTabsRemoval(
ENVIRONMENT_ID,
WORKTREE,
inventoryReceived,
VISIBILITY_INVENTORY_REMOVAL_EPOCH
)
// A list reserved before the stream frame lands last, carrying a genuinely stale version.
const delayed = liveFrame(1)
recordReceivedWebSessionTabsSnapshot(
ENVIRONMENT_ID,
delayed,
delayedReceived,
undefined,
'bootstrap'
)
expect(
shouldApplyRecoveredWebSessionTabsSnapshot(ENVIRONMENT_ID, delayed, delayedReceived)
).toBe(false)
})
// Why this stays fenced: a genuinely superseded generation is retired by a *successor's*
// publication, which is a handover. Only the removal path must stop retiring.
it('still fences a predecessor generation that a successor replaced', () => {