Skip to content

[shared-state 8/9] AEP confluence: semantic action stream as evidence log #141

Description

@telleroutlook

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

  • Lives in a dedicated subpath (@wasmagent/core/shared-state/aep); the base shared-state module has no @wasmagent/aep import (verified by a dependency test).
  • Attaching a sink records agent-sourced state changes as AEP actions on the provided emitter.
  • include predicate defaults to agent-sourced writes only (source === "agent") — human edits are not misattributed to the agent.
  • sideEffectClass / recordingMode mappers let the app classify actions (mirrors the reference app's per-tool full/delta/validation policy).
  • Replay proof: a test shows replayActions(model, recordedActions) reproduces the live state — demonstrating the evidence stream is complete and faithful.
  • Tests: dispatch → emitter received the action; detach stops recording; base module import graph excludes aep.

Out of scope

  • Building/signing the AEP record (the app already does that with AEPEmitter); this only feeds actions in.

Metadata

Metadata

Assignees

No one assigned

    Labels

    aepAgent Evidence ProtocolclaudeBot: auto-implement this issueenhancementNew feature or requestneeds-splitIssue needs to be split into sub-issuesrfcRequest for Comments — design proposal

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions