deepseek-harness/.agents/notes/implemented/architecture/2026-08-19-session-projection-state-and-client-views.md

3.1 KiB

Agent Note: Separate session projection state from client views

Status: implemented

English | 中文

Problem

The projection registry persisted each unit's internal fold state without a runtime schema, while SessionProjectionMap described the client value returned by view. This left restored state unvalidated and made the same type table appear to describe two values that may differ. Host consumers also needed the current folded state without serializing every registered client view or exposing internal-only state through the client protocol.

Decision

SessionProjectionStateMap is the merge-extensible table for host fold states. Every ProjectionDefinition key belongs to this table and supplies a stateSchema; cached rows are validated before they seed a fold. SessionProjectionMap retains its existing meaning and name as the sole table of client-visible whole values, preserving existing client data structures such as title: string | null.

A unit whose key also appears in SessionProjectionMap supplies wire.viewSchema and wire.view. Client-visible units are always checkpointed. A host-only unit omits wire and is checkpointed only when persist is true. Carrier reads use wireOnly so internal states do not enter API payloads. Host code reads one current state through stateOf(session, key); the returned reference is borrowed and must not be mutated.

Consequences

Projection state and client values are independently typed and validated without introducing a second client DTO vocabulary. A unit may expose a compact or compatibility-preserving client value while retaining richer host state. Malformed cached state cannot seed viewCheckpoint; restore rejects malformed state and the cache's existing full-read fallback rebuilds it from the log. Host consumers can replace private log scans with the same incremental fold used by carriers.

The original session-projection proposal now records this split. The earlier subagent identity projection and projected token usage decisions remain current; their domain folds move to the state table without changing their user-facing values.

Alternatives considered

  • Rename the existing map to a state table and introduce a new client map — rejected because it changes the established client type name and invites unnecessary client payload migrations.
  • Keep one table for both state and client values — rejected because a richer fold state and a compatibility-preserving client value then cannot be represented accurately.
  • Persist every host-only unit — rejected because persistence is a cold-read optimization with storage cost; an internal unit opts in only when its consumers need cold reconstruction.
  • Return copied state from stateOf — rejected because cloning every host read adds work without protecting a boundary; the method documents a readonly borrowed-reference obligation for typed same-process callers.