Skip to content

Projection Reader

Turn ledger events into a deterministic read model without writing shadow state.

A tiny decision-gate projection over hand-built events. The same pattern applies to app projections: durable facts are the source, projections are derived.

  1. Import the carrier projection:

    import { DECISION_GATE_KIND, projectDecisionGate } from "@agent-os/decision-gate";
    import { makePreClaim } from "@agent-os/kernel/effect-claim";
  2. 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",
    },
    },
    ];
  3. Project the state:

    const projection = projectDecisionGate(events, "gate/1");
  4. Treat the projection as read-only output. If the next state changes, append another event and fold again.

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.