The proposed Agent Note records the three shipped on-disk generations of
session_projcache, the read-compat and backup-and-skip decisions, the
upgrade matrix, and the rejected alternatives. The package README documents
the upgrade guarantees and requires every future schema or domain-version
change to land with archived fixtures and tests proving its upgrade story.
The storage subsystem page and the generated cordis catalog pick up the new
DomainSpec fields.
The persistence seam is now create/open/stat/list returning per-session
SessionHandles (read/append/flush/close); every log read and write flows
through the owning handle. The seam package exports only the service and
handle contracts, consumer-visible errors, and pure durable-data
validation helpers; each backend owns its complete storage runtime, and
the shared contract suites pin equivalent observable behavior. The
backend routes published sessions' live events by id into the active
write handle; agent-loop only acquires, seeds, and closes the handle.
Resume appends interruptedTurnClosers through its write handle;
session-query owns the revision-keyed cold cache. Legacy-only surfaces
are removed in the same swap: locate/readRaw/supportsRawArtifacts, the
legacy event-shape read migration, zstd torn-frame salvage,
DSH_SESSION_JSONL, and hook transcript_path population; a torn final
zstd frame is discarded whole; the session-list cold blank probe returns
on stat metadata (eventCount derived from the last physical row,
sizeBytes). The WebUI ZIP export serializes the logical log from a read
handle, so both backends export identically.
Refs #3245
Review follow-up: the per-step gate recomputed view(previous) on every
changed apply — a property read for identity-stable views, but a fresh
throwaway object per change for computing views. The registry now keeps a
WeakMap from state object to raw view: the previous state's view was
cached when that state was current, so each distinct state's view computes
exactly once (gate and snapshot share the memo) and the quiet path
allocates nothing. Unlike the earlier lastView record, an entry is keyed
by the state itself — the view of that exact state by the pure-view
contract — so no stamping discipline exists to get wrong. Primitive
states bypass the WeakMap and compute directly.
Review consensus: for the only declaring unit the token function was
literally the view function, so the extra wire member bought nothing —
default raw-view comparison produces the identical Object.is on
state.turns at the identical cost (one property read per side). The
per-step gate stays: it holds the no-stored-baseline property, and its
measured overhead (~ns per changed state) is four orders of magnitude
below the push path it guards.
Review follow-up (imccyu): comparing view(previous) recomputes the view on
every quiet change. The wire block now takes an optional viewKey(state)
declaring the cheap comparison token; the drive compares tokens across the
previous and next states and calls view only for an actual push. Default
stays the raw view output. turnOutline declares viewKey: state => state.turns,
making the identity-stable-turns convention an explicit contract; the
registration erasure forwards viewKey (dropping it silently reverted the
gate to the fallback, caught by the new view-call-counting test).
Review suggestion (imccyu): the drive holds both the previous and next
state, so the identity gate can compute view(previous) and view(next) in
the driving step and compare them directly. The stored lastView cell field
and its stamp-on-every-change rule are deleted; with no dedup memory,
nothing can go stale across listener generations by construction, and a
rebuilt cell no longer pushes an unchanged view on its first live event.
Costs one extra pure view() call per changed state.
The change feed stamped lastView only when a listener was subscribed, so a
value change during a listener-free window (HMR swap) froze the baseline
and a later transition back to the old value was silently deduplicated.
The baseline now advances on every changed state, heard or not; broadcast
still only happens with listeners. Docs, catalog, and the feature note
follow the corrected semantics.
The feed previously fired on every changed state reference of a
client-visible unit; it now also compares the raw view output against
the last delivered one and stays quiet when Object.is-identical, so a
unit can buffer working fields in state behind an identity-stable
projection. Units whose views build fresh objects per call are
unaffected.
The review's carry-over: 'each has a published backend' in the CodeRuntime
JSDoc and its projections (tool-cordis api-catalog, subsystems page) plus
'both shipped'/'backends ship' in the code-runtime README all claimed the
Python backend is released; it is private and experimental, excluded from the
release family. The wording now states the TypeScript backend is released and
the Python backend is experimental and private (not published), in the JSDoc
(api-catalog regenerated to match), the READMEs (paired), and the subsystems
page (paired).
Adding a published Python backend and reordering `flush_line` left several
owning documents stating things that are no longer true.
`src/invariant.ts` justified its empty installer with "ships only the fd-3
wire-protocol codec", which the subprocess execution path contradicts. The
reason now states the actual one: every relation this backend maintains lives
in the CPython child or on the fd-3 wire, so no same-process event sequence is
observable from a listener -- the same shape the sibling worker-thread backend
uses.
The seam's `PORTABLE_RESERVED_WORDS` and `language` JSDoc, the code-runtime
README pair, and docs/subsystems/code-runtime both said only TypeScript has a
published backend. Corrected in all four, with the generated cordis catalog
regenerated for the `language` change.
The note attributed the 12x multiple to the settlement flush holding three
copies. That stopped being true when `flush_line` was reordered to drop the
pending chunks before its push: the binding worst case is the newline path's
single near-budget write. Corrected in the note (both sides) and in the test
comment that repeated it.
The note's Testing section now registers the cases this stack added, and the
Chinese side receives the O(depth) entry it never got plus the new ones -- it
had drifted from the English.
`INTERPRETER_BASELINE_BYTES` argued 64 MiB from a RESIDENT set while RLIMIT_AS
bounds address space. It now cites the bootstrap's own measurement (30.23 MiB
of mappings for `python3 -I`), making 64 MiB roughly twice the measured
baseline.
Also: a hardcoded `(:232-235)` comment reference becomes a reference by name,
a "which now walks in O(depth) too" change narrative becomes a current-state
statement, and a stray double blank line is removed.