Copied fork messages keep their parent seqs, but a fresh checkpoint's
baseline_seq starts near zero, so nothing folded: forks received a new
full baseline plus every stale context-update message from the parent,
and copied compaction rows triggered a spurious rebaseline. Inheriting
the parent row keeps the fold horizon consistent with the copied
transcript.
Also asserts the checkpoint reset on committed reverts and deletes the
unused SessionStore.runnerContext / SessionHistory.loadForRunner
surface.
Builtins and instruction context become ordinary Location-scoped
services exposing load(), the same shape as skill, reference, and MCP
guidance. The runner composes all six producers explicitly in
loadSystemContext, so the full context roster is readable at one call
site. Dynamic registration and scoped deregistration had no consumers.
One prepare call per turn, before input promotion: it creates the
checkpoint when missing, rebaselines after completed compaction, and
otherwise narrates drift as a chronological update. The initialize/
prepare two-step and its redundant queries are gone, and context update
messages now precede the promoted user message.
An undecodable stored applied record heals by treating every source as
new instead of failing the drain; ContextSnapshotDecodeError is deleted.
Renames: SessionContextEpoch -> SessionContextCheckpoint. The SQL table
name and wire event type are unchanged.
Reconcile never rewrites the baseline; only rebaseline (compaction) and
initialize (first turn) produce baseline text. The durable Applied record
tracks what the model was last told, per source.
- Incompatible stored values re-announce the source baseline as an update
instead of forcing a full replacement
- Sources removed without removal text retain the model's belief silently;
entries self-clean at the next rebaseline
- Rebaseline restates unobservable sources from their last-applied values
instead of blocking; ReplacementBlocked and ReplacementReady are gone
- Rename Snapshot to Applied and Generation to Baseline {text, applied}
Drains now uniformly start one tick after wake or run, removing the
ready-deferred release that ran the first drain synchronously inside
the caller. The synchronous start was observable only by tests that
asserted state immediately after fire-and-forget calls, coupling them
to scheduler internals; those tests now synchronize explicitly through
session.wait or stream-start signals.
Remove the steer promotion cutoff: a turn boundary now promotes every
steer pending at promotion time instead of snapshotting the inbox at a
captured sequence, so late-arriving steers ride along into the turn.
Delete legacy projected-prompt synthesis from projectPrompted. The
event-sourced session input migration wiped pre-inbox event history,
so every Prompted event follows an admitted inbox row and a missing or
divergent row on replay is an invariant violation.
Inline the prompt equivalence predicate and update tests that seeded
history with bare Prompted events to publish PromptAdmitted first.
Restructure runner turn settlement: replace TurnTransitionError defect
control flow with tagged turn results and a plain restart loop, name the
settlement evidence, and merge duplicated interrupt branches.
Split settled tool-fiber failures: tool implementation defects keep
surfacing as model-visible tool errors and the turn continues, while
typed ToolOutputStore errors now fail the assistant and the drain
instead of being silently absorbed.
Add SessionTitle service that generates a session title from the
session's sole user message via the customizable "title" agent, then
renames the session. Runs once per session, gated on durable history
having exactly one user message (no default-title string comparison).
Wire it into SessionRunner as a background fork after the first turn's
prompt promotion makes the user message visible; forking before
promotion caused the first-message lookup to see zero rows and silently
no-op.
Closes#34364