The window is not the transcript
A block is written to user.log once, when it leaves the viewport’s window,
and a sync rebuilds only the rows the fold has moved. The two halves are one
correction: a bounded window may not double as the source of an unbounded
transcript, and a bounded window may not be re-rendered whole per record.
The window as transcript
one-seam-one-log made user.log a
regenerable render of the fold’s resident blocks, rewritten whole at flush
points. That removed the old incremental tee’s data-loss bug and reintroduced
the same shape at the other end: the render reads the resident window, so
past eviction the flush wrote a tail and called it the session. Two ways to
lose a transcript, one at each end of the same file.
It cost more than history. Viewport::evict_to_tombstone clears a dead
sub-agent’s blocks, and App::flush_logs reaches those viewports afterwards —
so the flush regenerated an empty file over the child’s own transcript, which
the tombstone’s log path then pointed the user at.
The rule now: the retired prefix only ever grows. Viewport::enforce_window_caps
renders what it drops straight into the file, so a block is durable by the time
it leaves heap, and the eviction runs on past any chrome row the dropped rows
anchored and any far half of a fold row that renders as several blocks — both
would otherwise be stranded above a window that can no longer place them, and
would leave the display without ever entering the transcript. The blocks still
resident are written past that prefix provisionally by Viewport::flush_log,
so /export reads a whole session mid-flight and the next retirement rewinds
over them rather than writing them twice. user.log therefore keeps the whole
session however long it runs, with the viewport’s caps back to what they say
they are: a bound on the screen, not on the record.
Resume appends rather than truncates, and Viewport::seed marks the replayed
window as the file’s own — the run that recorded those rows already rendered
them, so the continuation joins the transcript instead of repeating its last
thousand rows.
The window rebuilt per record
Printer::sync rebuilt every resident block from the fold on every arriving
record — a fresh markdown parse per block per record, twice over, since
estimate_rows renders a block that reflow then renders again. With nothing
ever evicted that cost grew all session; with a window it merely stopped
growing, which is not the same as being paid once.
The fold now says when a row last moved. Blocks counts its own changes and
stamps each row with that count (Block::rev) as it is opened, as the run it
holds grows, and as a result is patched onto it; a printer remembers the
revision it synced at and rebuilds from the first row past it, carrying every
block below over whole — line memo included. Two dependencies reach backwards
past that floor and are named where the floor is computed: a reasoning row’s
grain is a fact about the answer run beneath it, so a growing run reopens the
row above; and the most recent ral script an answer’s echo signal reads
against is a fact about the rows below the floor, which are not being walked.
A row the viewport’s own window has already evicted is never built again
(Viewport::evicted_through) — the fold’s window is the wider of the two, and
building five hundred rows in order to drop them was most of what the old sync
did once a session ran long.
What follows, and what does not
A block’s fidelity is now stamped by the turn that built it rather than restamped by every later sync. That is what the rail’s own doctrine says context pressure is — turn-level, inherited by every paragraph of a stressed turn — so the incremental rebuild corrects the reading rather than approximating the old one.
Two things are deliberately left. The flatten is still rebuilt whole when
stale, bounded by VIEWPORT_MAX_ROWS: it is per frame rather than per record,
and with blocks carried over it re-wraps nothing. And fidelity::context_floor
still grades a block against cumulative session input rather than the last
turn’s prompt, so a long session reads as maximum pressure throughout — a
separate wrongness, named here so it is not mistaken for this change’s doing.