Projection Reader
Turn ledger events into a deterministic read model without writing shadow state.
What You Build
Section titled “What You Build”A tiny decision-gate projection over hand-built events. The same pattern applies to app projections: durable facts are the source, projections are derived.
Prerequisites
Section titled “Prerequisites”-
Import the carrier projection:
import { DECISION_GATE_KIND, projectDecisionGate } from "@agent-os/decision-gate";import { makePreClaim } from "@agent-os/kernel/effect-claim"; -
Build the smallest event history for one gate:
const claim = makePreClaim({operationRef: "publish:subject-1",scopeRef: { kind: "artifact", scopeId: "artifact/subject-1" },effectAuthorityRef: { authorityId: "publish.subject", authorityClass: "effect" },originRef: { originId: "agent/run-1", originKind: "agent_run" },});const events = [{id: 1,kind: DECISION_GATE_KIND.REQUESTED,payload: { gateRef: "gate/1", subjectRef: "subject-1", claim },},{id: 2,kind: DECISION_GATE_KIND.DECIDED,payload: {gateRef: "gate/1",decisionRef: "decision/1",decision: "approved",decidedBy: "operator/alice",},},]; -
Project the state:
const projection = projectDecisionGate(events, "gate/1"); -
Treat the projection as read-only output. If the next state changes, append another event and fold again.
Checkpoint
Section titled “Checkpoint”The projection status is derived only from the event list:
projection.status === "approved";There is no second database table to keep in sync and no hidden repair branch. If the projection is wrong, fix the event vocabulary or the fold.
Add background work with durable trigger cancellation.