Part of #133 (shared-state epic)
Depends on: #135 (and, at attach time, @wasmagent/aep)
Blocks: none
Part of: the shared-state epic (link added on creation)
Tier: advanced (M2)
Labels: also aep
Motivation
Because actions are semantic and the reducer is pure, the entire state is replayable from the action stream (replayActions, from STATE_MODEL). That means the same action stream that syncs the UI can serve as a provenance / audit log. Today, apps record tool calls and UI changes separately; unifying them makes UI actions first-class AEP evidence and links the state layer (@wasmagent/core) to the evidence layer (@wasmagent/aep) into a single chain.
This is the killer confluence: the UI sync stream and the evidence stream become the same stream.
Resolved design decision (from the epic, #3)
Opt-in sink, zero hard dependency. SharedStateStore MUST NOT import @wasmagent/aep. Evidence emission is attached via a separate helper so the base store stays dependency-free and tree-shakable.
Proposed API
// @wasmagent/core/shared-state/aep (separate subpath — only this file imports @wasmagent/aep)
import type { AEPEmitter } from "@wasmagent/aep"
export function aepEvidenceSink<S, A extends { type: string }>(
store: SharedStateStore<S, A>,
emitter: AEPEmitter,
opts?: {
include?: (action: A, evt: ChangeEvent<S>) => boolean // default: agent-sourced writes only
sideEffectClass?: (action: A) => string // action -> AEP side_effect_class
recordingMode?: (action: A) => "validation" | "delta" | "full"
},
): () => void // returns a detach fn
Behavior: subscribes to the store; for each qualifying change, calls emitter.addAction({...}) recording the semantic action as evidence (state-changing flag, side_effect_class, recording_mode, timestamp).
Acceptance criteria
Out of scope
- Building/signing the AEP record (the app already does that with
AEPEmitter); this only feeds actions in.
Depends on: #135 (and, at attach time,
@wasmagent/aep)Blocks: none
Part of: the shared-state epic (link added on creation)
Tier: advanced (M2)
Labels: also
aepMotivation
Because actions are semantic and the reducer is pure, the entire state is replayable from the action stream (
replayActions, from STATE_MODEL). That means the same action stream that syncs the UI can serve as a provenance / audit log. Today, apps record tool calls and UI changes separately; unifying them makes UI actions first-class AEP evidence and links the state layer (@wasmagent/core) to the evidence layer (@wasmagent/aep) into a single chain.This is the killer confluence: the UI sync stream and the evidence stream become the same stream.
Resolved design decision (from the epic, #3)
Opt-in sink, zero hard dependency.
SharedStateStoreMUST NOT import@wasmagent/aep. Evidence emission is attached via a separate helper so the base store stays dependency-free and tree-shakable.Proposed API
Behavior: subscribes to the store; for each qualifying change, calls
emitter.addAction({...})recording the semantic action as evidence (state-changing flag,side_effect_class,recording_mode, timestamp).Acceptance criteria
@wasmagent/core/shared-state/aep); the baseshared-statemodule has no@wasmagent/aepimport (verified by a dependency test).includepredicate defaults to agent-sourced writes only (source === "agent") — human edits are not misattributed to the agent.sideEffectClass/recordingModemappers let the app classify actions (mirrors the reference app's per-toolfull/delta/validationpolicy).replayActions(model, recordedActions)reproduces the live state — demonstrating the evidence stream is complete and faithful.Out of scope
AEPEmitter); this only feeds actions in.