SessionProjectionHints gains a required `kind`: `sequenced` when the Host's live registry computed the block for an attached Session, `cached` when a header-only listing viewed it from the persisted projection cache. `asOfSeq` stays the producer's own watermark in that space, so the cache read faces report the lowest served row seq again instead of the -1 sentinel. The two fields are independent: `kind` names the sequence space, `asOfSeq` the position in it.
The projection cache read-only face (cachedSnapshot, cachedPredecessorTitle) now matches the lifecycle identity a header witnesses (formatVersion, createdAt, cwd, isSeeded) and serves its rows as a cached block with asOfSeq -1. The fold face (hydratePrepared, write, coldSnapshot) keeps the full identity including the inherited cut. The session list and @ completion drop their isSeeded guards, so forked sessions keep their cached title and list metadata across a Host restart; list-children keeps its body fallback.
The cache reader preserves opaque JSON keys, but each projection still
parses the returned checkpoint through its own stateSchema before hydration.
State that ownership explicitly so documentation does not imply this reader
fix also changes projection-defined transformations or cloning behavior.
Distinguish the synthetic V7 StorageDomain regression fixture from released
cache archives. Describe its read, put and reopen path directly, and group
its JSON validation checks separately from archived-version recovery.
Translate the Chinese Agent Note headings and keep the paired docs aligned.
The note retains the borrowed-value tradeoff and links the independent
predecessor-format and lifecycle-identity decision.
Production expressions and all test assertions remain unchanged; only
contracts, fixture descriptions, test labels and grouping are clarified.
The fix does not add another persistence format or broaden migration scope.
The projection-cache writer preserves lossless JSON, including own keys named
__proto__. The z.json reader rebuilds objects and drops those keys, so opening
the same stored checkpoint can produce state different from Session replay.
This affects embedded result metadata and opaque extension payloads as well
as other projection state.
Validate with the existing isJsonValue predicate used by the writer's
snapshotJsonValue rules. It rejects non-JSON and lossy values without
rebuilding valid borrowed records. Keep the stored representation and domain
version unchanged: this is a reader correction, not a new cache format.
Exercise a fixture captured through the actual pre-fix writer with a real
StorageDomain open, rewrite, close and reopen. Preserve nested prototype-named
keys and retain negative coverage for invalid JSON values. Document why this
validation matches the storage-domain immutable borrowed-value contract.
Use neutral arbitrary JSON in the new opaque-state fixture so this reader
regression stays independent of Session message representation. Its own
prototype-named keys and nested arrays are unchanged in purpose. Confirm
the fixture matches the pre-fix StorageDomain writer output and that the
old z.json reader fails its key-preservation assertion. No released Session
recordings or historical cache fixtures are edited.
Review follow-ups: the Agent Note triplet moves to implemented/ rewritten as
shipped state (Decision/Consequences/Testing, present tense), cross-linked
both ways with the 2026-07-28 storage recovery proposal whose projcache
reset/destroy path it supersedes (that proposal stays live for authoritative
and whole-medium damage). The fixtures spec header and the note state the
fixture provenance as recorded facts of the released builds instead of
citing local tooling, and the spec JSDoc points at the note's final home.
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 session_projcache domain declares compatibleVersions: [3, 4] and
invalidRecords: 'backup-and-skip'. The two lineage identity fields become
optional — records admitted from older versions predate them, and the single
reader (identityMatches) interprets absence as the unseeded lineage: exact
for unseeded sessions, while a seeded caller fails the match and refolds
cold, so the lineage binding keeps its protection. Upgraded homes therefore
boot and serve their cached listing titles immediately, including homes
whose new tree already holds current-stamped documents without lineage
fields, and a record failing validation anyway is backed up and skipped
instead of refusing the plugin tree.
tests/fixtures/ archives the real on-disk media of every shipped generation
(v3 whole-unit file, v4 and v5 per-record documents, and the lineage-less
current-stamped shape); fixtures.spec.ts proves each recovers through the
real storage stack, rewrites to the current format on the next live write,
and that a hopeless record is salvaged without costing the boot.
cache.spec.ts polled fire-and-forget fail-soft writes with a fixed 40ms
settle(); contended runners drain the write after the window, so the
warn/row assertions flaked. Poll the observable outcome with vi.waitFor
(5s) instead, matching the file's existing cold-read write-back pattern.
The sdk-client and subagent-dsh-sdk dispose-ladder tests passed tight
confirmation budgets (disposeGraceMs 100-300ms) to real children; on a
contended runner the SIGKILL exit edge can arrive after the budget and
close() misreports a slow reap as failure. Use the product-default
budgets (disposeGraceMs 3000ms) for the real-child cases; the fake-child
negative cases in dispose.spec.ts keep the 10ms bound.