RetentionResult:
    | { status: "retained"; time: GraphTime; value: T }
    | { oldestRetainedTime: GraphTime; status: "evicted" }

Result of Graph.readAt: either the value retained for the requested time, or an evicted marker carrying the oldest still- retained time so callers can clamp future requests into the window.

Type Parameters

  • T

The Evicted arm is the engine's honesty about bounded retention. Snapshot history is a ring buffer with a configurable cap; a read for a time that falls outside the window cannot return a value, and dressing that case up as undefined or throwing would force every caller to invent the same handling. Returning a discriminated union forces a tag check at the call site, the same pattern used for Resource and Formula to make impossible states unrepresentable.

The clamp above terminates iff the graph opted into retention. Both caps default to 0 (see CreateCauslOptions.commitHistoryCap), and at cap 0 there is no window, so oldestRetainedTime is not its lower edge: it reports Graph.now, which is a placeholder meaning "the earliest time you could hope for if you opted in from here", and reading at it evicts too. So the obvious retry loop spins forever. MEASURED on a default-cap graph after two commits:

graph.readAt(a, graph.now)   // evicted { oldestRetainedTime: 2 }
graph.readAt(a, 2) // evicted { oldestRetainedTime: 2 }
graph.readAt(a, 2) // evicted { oldestRetainedTime: 2 } ...

Do not write the clamp as a loop that re-reads until it retains. Either treat evicted as terminal, or decide from the caps the graph was constructed with, which is the value a caller already holds:

const res = graph.readAt(node, t)
if (res.status === 'evicted') {
// Retry ONCE, and only if this graph has retention at all.
if (retentionIsOn && res.oldestRetainedTime > t) {
return graph.readAt(node, res.oldestRetainedTime)
}
return notAvailable
}