pub fn page(
messages: &[Message],
rendered: &Rendered,
dropped: usize,
dropped_thinking: i64,
before: Option<&str>,
turns: u32,
) -> PageExpand description
Slice a conversation into whole turns, newest first.
The fold is what says where a turn begins - the span render reports,
in message terms. Slicing on a count of messages instead would cut a turn
in half, which is the breakage the design exists to avoid.
The cursor errs toward OVERLAP, never toward a gap. A client asking for more may be handed a turn it already has - it keys its turns and drops the repeats - but never a HOLE, which is history it has no way to ask for again.
The cursor is a POSITION, not a name, and the reason is a fact about the
read rather than a preference. A turn’s name is its key, and the fold
builds one only from a Message::Result frame - while a conversation read
from a transcript carries none: SessionMessageKind is User | Assistant | System with no Result kind, and the replay synthesizer never emits one. So
a keyed cursor is None on every page of every transcript-derived
conversation, and a client reading None as “nothing above” stops after
the first page. The message the page’s first turn opens at is the one thing
that always names it.
dropped is how many messages the held conversation has lost off the
front, and it is what makes that position survive a drop: the held list
is renumbered by one and the conversation is not, so a page carries the
conversation’s numbering and this is the offset between the two.