ADR-029: Session & State Window Contract (MCP Governance)¶
Status¶
Proposed (March 2026)
Context¶
Assay enforces deterministic governance for MCP tool routes. Several experiment lines demonstrated that stateful invariants (sequence/state) generalize beyond lexical checks, including tool-hopping and cross-session delayed sink attempts.
To keep the product surface audit-grade, we need a frozen contract for: - how we identify a "session" - how we define a "state window" across sessions - what we store (privacy/retention defaults) - how we produce deterministic snapshot identifiers
This ADR freezes the contract only. It does not add storage backends, enforcement modes, or workflows.
Decision¶
Introduce session_state_window_v1 as an informational contract describing: 1) Session key: stable identifiers that partition MCP activity into sessions. 2) State window: a bounded time/run window where prior actions remain relevant. 3) Privacy/retention defaults: no raw bodies, no prompt/tool argument storage by default. 4) Deterministic snapshot id: content-addressed state identifiers derived from a canonical snapshot payload.
1) Session key contract (frozen)¶
A session is identified by the tuple:
event_source(string) — normalized origin, e.g.assay://…server_id(string) — MCP server label / wrapped target identitysession_id(string) — generated per run, unique within(event_source, server_id)
Notes: - session_id MUST be opaque (no embedded PII, no timestamps required). - session_id MUST be stable for the duration of a wrapped MCP process. - Any cross-session state references MUST use state_snapshot_id (not raw content).
2) State window contract (frozen)¶
A state window is a bounded relevance horizon used by governance logic:
window_kind:session|cross_session_decay- For
session: relevance lasts only within the session. - For
cross_session_decay: relevance spans subsequent sessions, bounded by: decay_runs(integer >= 1): number of subsequent sessions where the window remains active.
No other decay semantics (time-based TTL, hybrid windows) are defined in v1.
3) Privacy/retention defaults (frozen)¶
Default data handling rules: - Raw tool arguments, raw document bodies, raw prompts are not stored in state snapshots. - State snapshots contain only: - tool names - tool classes (if known) - decision codes/reasons - hashes/refs (content-addressed ids), never the raw payload
Optional attachments MAY exist in the broader system, but are explicitly out of scope for this ADR.
4) Deterministic snapshot id (frozen)¶
state_snapshot_id is content-addressed:
snapshot_canonical_jsonis produced using canonical JSON (key-order independent).state_snapshot_id = "sha256:" + hex(sha256(snapshot_canonical_json_bytes))
This ADR freezes: - snapshot id MUST be derived from the canonical snapshot payload - snapshot id MUST be stable across runs when snapshot content is equal - snapshot id MUST NOT depend on local paths, timestamps, hostnames, or random values.
Schema¶
A JSON schema session_state_window_v1 defines the report/snapshot envelope for: - session key fields - state window parameters - snapshot id + canonicalization fields - privacy assertions (what is intentionally not present)
Consequences¶
Positive¶
- Makes session/state semantics explicit and auditable.
- Enables deterministic replay and cross-session experiments without implying storage/enforcement.
- Provides a stable interface for future runtime implementations.
Negative¶
- Some future use cases may need time-based TTL or richer provenance.
- v1 intentionally does not define backend storage or enforcement integration.
Out of scope (explicit)¶
- Any runtime/backend implementation
- Any new policy DSL
- Any workflow / CI gating changes
- Any taint tracking or dataflow labeling
- Any persistence format beyond this informational contract
Acceptance criteria (for this A-slice)¶
- ADR exists and is marked Proposed.
session_state_window_v1schema exists.- Reviewer gate enforces allowlist-only scope + workflow-ban and validates schema parses.