Files
orca/src/main/runtime/runtime-mobile-session-result-finalization.ts
T
Neil da836faeef fix: preserve terminal retirement proof across renderer publications (#19002)
* fix: preserve terminal retirement proof across renderer publications

* refactor: share the live-surface filter between retirement proof preservation and projection

The publication projection already dropped proofs whose surface is live;
reuse that as one helper instead of a second inline scan.

* fix: emit stored retirement proofs from host-authored snapshot writes

Three callers built a snapshot, stored it, then emitted the pre-store object. Storing grafts on the preserved proofs, so those frames carried the stored snapshotVersion without the proofs; subscribers dedupe on version and never saw them.

* fix: send terminal retirement proofs once per stream and fence them by occupant

Proofs are pinned per worktree for the host's lifetime, so every snapshot publication — including a 50ms title tick — re-shipped up to 64 proofs (~17 KB on realistic ids) to every paired client.

Negotiate session-tabs.retirement-proof-delta.v1: the host projects each session-tabs stream to send a proof only the first time that stream carries it, and a capable renderer keeps the union in a ledger keyed by (environment, worktree) with the same 64-entry bound and the same live-surface drop rule as the host, reset on removed frames and on a new connection generation. Legacy clients keep receiving the full list; CLI and mobile do not advertise the capability.

Also inherit worktreeInstanceId onto identity-less host writes so a host write between two renderer occupants can no longer launder one occupant's proofs into the next.

* fix: keep an empty proof delta distinguishable from a proof-less host

A negotiated stream now sends retiredTerminalSurfaces: [] when nothing is new instead of omitting the field. Absence is the host's "I hold no proofs" signal — which is also what a recreated worktree's fresh host entry publishes — so the client ledger forgets on absence and a successor occupant never inherits its predecessor's proofs, even when the removed frame was missed.

* test: pin ledger visibility against a legacy full-list host

An old host sends the full proof list whenever it holds any and omits the field when it holds none. Prove the new client ledger shows exactly what a legacy client would see across that sequence, so forgetting on absence is verified not to regress the mixed-version case.
2026-09-07 19:28:17 -07:00

54 lines
2.1 KiB
TypeScript

import type { RuntimeMobileSessionTabsResult } from '../../shared/runtime-types'
import { dropRetirementProofsForLiveSurfaces } from './mobile-session-terminal-retirement-proof'
import type {
RuntimeMobileSessionProjectionHost,
RuntimeMobileSessionProjectionInput
} from './runtime-mobile-session-projection-contract'
export function finalizeRuntimeMobileSessionTabsResult(
{ snapshot, tabs }: RuntimeMobileSessionProjectionInput,
host: RuntimeMobileSessionProjectionHost
): RuntimeMobileSessionTabsResult {
const active =
tabs.find((tab) => tab.isActive && tab.id === snapshot.activeTabId) ??
tabs.find((tab) => tab.isActive) ??
(snapshot.activeTabId ? (tabs[0] ?? null) : null)
const normalizedTabs =
active && !tabs.some((tab) => tab.isActive)
? tabs.map((tab) => (tab.id === active.id ? { ...tab, isActive: true } : tab))
: tabs
const tabGroups = host.sanitizeGroups(snapshot.tabGroups, normalizedTabs)
const validGroupIds = new Set(tabGroups?.map((group) => group.id) ?? [])
const tabGroupLayout =
snapshot.tabGroupLayout === undefined
? undefined
: host.pruneGroupLayout(snapshot.tabGroupLayout, validGroupIds)
const activeGroupId =
snapshot.activeGroupId && validGroupIds.has(snapshot.activeGroupId)
? snapshot.activeGroupId
: (tabGroups?.find((group) =>
active ? group.tabOrder.some((tabId) => host.collectTabIds([active]).has(tabId)) : false
)?.id ??
tabGroups?.[0]?.id ??
null)
return {
worktree: snapshot.worktree,
publicationEpoch: snapshot.publicationEpoch,
snapshotVersion: snapshot.snapshotVersion,
activeGroupId,
activeTabId: active?.id ?? null,
activeTabType: active?.type ?? null,
...(tabGroups ? { tabGroups } : {}),
...(snapshot.tabGroupLayout !== undefined ? { tabGroupLayout } : {}),
...(snapshot.retiredTerminalSurfaces
? {
retiredTerminalSurfaces: dropRetirementProofsForLiveSurfaces(
snapshot.retiredTerminalSurfaces,
snapshot.tabs
)
}
: {}),
tabs: normalizedTabs
}
}