Eight proposals grouped by category, each with problem statement, concrete plan, and risks: property-based testing over the protocol-shaped core (chunk streams, event logs, schema DSL); mutation testing as the counterweight to the 100%-coverage gate; deterministic tests + a universal replay-invariant fixture + nightly race stress; architectural conformance (dependency-cruiser rules and the LlmAdapter conformance kit); runtime arg validation at the model boundary with a structured error taxonomy and dev-mode invariants; doc-sync enforcement (typechecked doc snippets, API reports); supply-chain checks and nightly vendor-drift verification against the manifest; and deep-readonly public surfaces (logged-vs-in-flight mutability boundary). AGENTS.md points at docs/adr and docs/rfc.
1.9 KiB
RFC 008: Deep-readonly public surfaces
Status: proposed
Problem
The session log is append-only by contract, but session.events returns
readonly SessionEvent[] whose elements are mutable: a plugin can reach in
and rewrite history (events[0].data.content.push(...)), silently breaking
replay equivalence and the derived-history guarantee. The same applies to
derived messages and prompt assemblies passed through waterfalls — mutation
is sometimes the intended idiom (waterfall middleware mutates the request)
and sometimes corruption (mutating a logged event), and the types don't
distinguish.
Proposal
Make immutability part of the type where mutation is corruption:
SessionEventdata becomesDeepReadonlyon the way OUT of a session (events,session/eventlisteners);append()keeps taking plain mutable input. ADeepReadonly<T>utility type lands in dsh-llm next to the brand/never helpers.deriveMessages()returns deep-readonly messages; the loop clones before handing a mutable request to theagent/requestwaterfall (mutation there is sanctioned — the clone makes the boundary explicit and cheap, once per step).PromptAssemblystays mutable through its waterfall (sanctioned) but the registry's internal section list is cloned per assembly (already true).- Optionally, dev-mode
Object.freezeof event data behind the RFC 005 invariants flag, so sanctioned-mutation violations throw in tests rather than corrupting silently.
Plan
Introduce DeepReadonly, flip the session read paths, fix resulting
compile errors in consumers (expected: a handful in tests), add the
freeze-in-dev option alongside RFC 005's invariants plugin.
Risks
DeepReadonly types can produce noisy errors at waterfall boundaries where
mutation IS the API — keep the mutable/readonly boundary exactly at "logged
vs in-flight" and document it in the session README.