An eviction is a set of turns
Superseded in part:
Display::Rewoundis gone, and/rewindwithdraws turns instead of truncating the table, so the 261007 note below is reversed on readability (withdrawn turns stay readable throughexarch-transcript) and on re-minting the anchor (ids stay lineage-monotone). See session-record.
Superseded in part by an-eviction-is-a-span: a cut is a run, not a set.
`evict [from: Int, to: Int, note: Str]takes one contiguous run of turns and residues, requires the note, and leaves one residue — a span[from, to]and the note, rendered as one bracketed user-voice message — in place of the marker per hole. The one edit, its authorities, the unclosed-turn guard, roles without exchanges,TURN: <id>and the[Int]read door all stand. The notes at §3, §4, §6, §7 and §8 below say what goes.
There is one context edit. It takes a set of resident turns out of the
context, wherever they lie, leaves a marker standing where they were, and
carries the model’s optional note. Every way of addressing the context and
the transcript speaks turn ids, as a list. context `evict [turns: [Int], note: Str] is the model’s spelling; /rewind <turn> and the harness’s own
pressure eviction are the same record under a different EditAuthority.
Superseding note (261007,
/rewindonly). The user’s rewind is no longer an eviction. An eviction leaves a marker that names each departed turn and invites the model to read it back throughexarch-transcript, which is the opposite of what/rewindmeans: after it the model still “saw” everything./rewind <turn>now recordsProtocol::Rewound { anchor }in the same file, and the fold truncates the table at the anchor: no row, no hole, no address, the next prompt mintedanchoragain, the prefix’s prompt cache intact; the records stay inrecord.jsonlas forensic fact. The view fold truncates its blocks likewise (Display::Rewound), so the screen shows the conversation as it was, plus one line.EditAuthority::Useris gone; point 5 below is withdrawn. The shell is not rewound.
Why
The previous vocabulary had three edits that were one operation seen through
three addresses: `evict [through: n] took a prefix, `drop [exchanges] took whole closed exchanges and left no marker, /rewind n
took a suffix by exchange. context-is-a-projection
had already named the prefix/set asymmetry as unjustified;
the-turn-is-the-atom made the
prefix edit turn-granular and left the set edit on exchanges.
What none of the three could do is the case that matters most to an agent:
a useless detour inside the live exchange. The model reads a file it did
not need at turns 41–43, knows it by turn 44, and wants those three turns
gone with a line saying why. evict would take the prompt and the good work
before them; drop could not touch the exchange being written and could not
name a turn; neither carried a note that stood where the hole is.
The decision
-
One edit, one record.
Cut { turns: Vec<u64>, note: Option<String> }is recorded asProtocol::Evicted { cut, by }, with the display twinDisplay::Evicted.ContextOp, itsDroparm,EvictionPlanandHeld::Droppedare deleted;HeldisResident | Evicted { cut }. -
The recorded set is the resolved set. The writer applies the survivor rule and records the ids that actually left; replay departs exactly those ids and judges each one resident, re-deriving nothing. This is identity is recorded from context-is-a-projection applied to the edit.
-
Two guards, one silent rule. Refused by name: a turn not recorded, a turn that has already left, and the unclosed turn — the one being written, which exists only while the protocol is mid-exchange. At
ReadyForUsernothing is unclosed, so a user rewind may empty the context. Kept silently: a user turn whose exchange still has a resident turn not in the set. Hence the invariant every resident assistant turn has its prompt resident before it, and the context opens with a user message. A set that would take nothing is refused with the reason.“The live exchange is untouchable” was never the invariant; it was the coarsest address at which the real invariant — no edit touches the unclosed turn — could be stated.
is_live_exchangeleaves the fold.Superseding note (261019). The silent rule and the emptied-set refusal are gone: two numbers cannot name an empty run, and a residue standing where its run began keeps the context opening with a user-role item by construction, so a prompt may leave under a note while its answers stay. The guard that remains beside the unclosed turn is alignment — a run that reaches into a residue must cover it whole.
-
A marker per hole, in place. A hole is a maximal run of departed turns in the table. Each renders as one bracketed user-voice message at its own position: the sentence of what left, one row per exchange fragment grouped by the cut that took it, that cut’s note beneath its rows, and how to read the turns back. The former head marker is the hole at position 0; there is no second marker kind (the-turn-is-the-atom §9). A cut adjacent to a hole joins it; a late cut with resident turns between it and the head hole leaves message 0 untouched, and the provider re-reads from the hole, not from the start.
Superseding note (261019). There are no holes and no markers. Each eviction records a residue over its span, residues nest by containment, and the context is the frontier: uncovered turns and outermost residues, in id order. A residue’s message is its sentence, its note and the read-back line — no rows — so its size does not depend on what it covers. The cache argument stands: a cut rewrites from its
from. -
The user’s rewind leaves a marker with no note.Withdrawn, see the superseding note above: a rewind leaves nothing. -
The read door speaks the same address.
transcript `read [turns: [Int]]andtranscript `grep [pattern, turns: [Int]]name turns by id;exchanges:is gone from both, as is the[from, to]pseudo-range — ral’s range primitive isrange a b, which is a list ofInt, and a turn address is a[Int]however it was built. The read answers one element per turn,[turn, exchange, messages]. What you can read, you can evict, by the same name.Superseding note (261019). The
[from, to]pair returns, as the context family’s address for a run, not as a read range:readandgrep’s narrowing stay[Int]. A grep hit names the item it lies in,[from, to, kind, role, line, text]; a model residue’s note is searchable, a turn hit beingfrom == to. -
The pressure reminder names the set.
Pressure::Overcarries the turns the planned cut would take; the reminder renders them as runs and offerscontext `evict [turns: !{range a b}, note: '…']for the model to make the same cut with a note.Superseding note (261019).
Pressure::Overcarries at most one span, the forced eviction, which may take the live prompt. The reminder names it and offers one`evict [from, to, note]for it. -
A turn has a role; there is no exchange. Every turn is
userorassistant, and that is what every row says (TurnRow { id, role, kind, label, bytes, held };kindisown,importorinherited). “The assistant turns answering prompt u” are the turns after u up to the next user turn — a function of role and order, derived where the survivor rule and the eviction plan need it, stored nowhere and shown to no one. The oldexchangecolumn was that memo leaking into the interface while the datum it derives from, the role, was smuggled asid == exchange.UserPrompt { turn }opens a user turn under its own id;Steering { text }extends the newest turn — a steering line after a tool batch, or a nudge continuing the prompt in hand — so the log says which happened rather than encoding it in a comparison with the reach;ContextMessage { id, message }names its turn alone. The marker’s sentence isTurns 1–29, 31–35 have left your context.with one role-marked row per departed turn. The survey’s receipt is[rows, total-bytes]: which turns have left istranscript `index’sheldcolumn, not a second count.Superseding note (261019). There is no marker sentence listing runs; a residue says
Turns 41–60 have left your context.over its own span. Every row is a span:SpanRow { from, to, role, kind, label, bytes, held },kindgainingresidue,heldbeingresident,under [a, b](the innermost residue over the item) orwithdrawn.TurnRowsurvives only inside the link’sLinked. Roles and the absence of exchanges stand. -
Every tool result ends with
TURN: <id>. The id of the turn it closes — the same id the survey, the index, the marker and the TUI speak. Without it the model could address the past only after a survey; with it “the last three turns were a detour” is!{range n-3 n}directly. A fixed datum per turn, so the provider’s cache is untouched.
What it costs
A mid-history hole invalidates the provider’s prompt cache from the hole
onward. For the detour case the hole is recent and the re-read small; the
docstring says so, and the survey’s bytes lets the model weigh a dead read
against one cache miss. A marker adjacent to the next prompt yields two
consecutive user-voice messages, a shape the head marker already produced.
The shell and the filesystem are untouched, as they were by /rewind. A
rewound read had no effects; where a departed turn did, the note is where
the model says what still stands. Effect-undo would be shell machinery, and
this is not that.
Supersedes
- context-is-a-projection: “model-facing rewind is out of v1”, the monotone-vocabulary asymmetry, and “a live exchange is untouchable until quiescence”.
- context-rollover: the
`droptag and exchange addressing in the read door. - the-turn-is-the-atom §2 (a cut
addressed by
through), §4 (evictions stay prefix operations; the marker at message 0) and §8 (the door speaks exchanges and a turn range).