For the complete documentation index, see llms.txt. This page is also available as Markdown.

REA Accounting

The Resource-Event-Agent event ledger that underpins scoring and contribution tracking

REA stands for Resource–Event–Agent, a well-known accounting model from the work of William E. McCarthy: every economic phenomenon is captured as an event that moves resources between agents. The @holons/core/rea domain is the Holons ecosystem's implementation of that model.

Every action that should "count"—a task being completed, an appreciation being given, an expense being logged, a library item being borrowed, an offer being made—produces an REA event. Those events are what the scoring system reads when computing contribution equity, and what federated holons exchange when sharing recognition across membrane boundaries.

REA is the substrate layer of the contribution stack. Most users never interact with it directly; they create tasks, give appreciations, log expenses—and the corresponding REA events are recorded as a side effect.

Why REA

The alternative to REA is to keep separate, ad-hoc records for tasks, appreciations, expenses, and so on, and then re-aggregate them for scoring. That works briefly but breaks down as soon as a holon wants to:

  • recognize a new kind of contribution without rewriting the scoring pipeline,

  • federate recognition data across holons that track different things,

  • audit "what happened in this holon last month" as a single ordered stream.

REA solves these by making the event the unit of record, with a loose enough shape that new event types don't require schema migrations.

The event shape

interface REAEvent {
  id: string;
  timestamp: number;
  resource?: {
    type?: string;
    quantity?: number;
    unit?: string;
    resourceId?: string | number;
    [key: string]: any;
  };
  provider?: { id?: string | number; type?: string; name?: string; [key: string]: any };
  receiver?: { id?: string | number; type?: string; name?: string; [key: string]: any };
  context?: {
    holonId?: string;
    questId?: string | null;
    itemId?: string | number;
    expenseId?: string;
    note?: string | null;
    [key: string]: any;
  };
  eventType?: string;
  status?: string;
  [key: string]: any;
}

The shape is deliberately loose. The required fields are id and timestamp; everything else is optional. Each domain that emits events adds the fields it cares about under resource, context, etc. Forward compatibility comes for free.

Storage

Events are persisted in the rea_events lens of the holon they belong to:

Querying is an in-memory filter pass over the lens contents, supporting:

This keeps the storage layer simple and the query layer flexible—the same semantics as the original Telegram-bot implementation, now lifted into shared core.

The Event Factory

REAEventFactory is a static class with one method per kind of action that should produce an event. Callers don't construct raw REA events; they call factory methods like:

  • questCreated(...) / questCompleted(...)

  • appreciationGiven(...)

  • expenseEvents(expense) — produces both the paid-event and the per-participant share-events

  • timeLogged(...)

  • itemBorrowed(item, borrower) / itemReturned(item, returner)

  • offerMade(...) / wantMade(...)

  • creditIssued(...)

The factory ensures every event has:

  • a unique ID (generateId(holonId) produces ${holonId}_${timestamp}_${rand}),

  • properly shaped provider / receiver agents (createUserAgent, createHolonAgent, createExternalAgent),

  • the correct eventType discriminator for downstream aggregators to dispatch on.

Output matches the original JavaScript factory exactly, so existing stored events keep aggregating correctly after the TS migration.

Where it plugs in

REA is the integration point between several otherwise-independent domains:

  • Council emits events when proposals reach agreement (recognition for participation).

  • Library emits itemBorrowed and itemReturned events, with optional deposit accounting.

  • Expenses emits a expense:paid event plus one share-event per participant.

  • Tasks emits quest lifecycle events.

  • Scoring consumes all of them through the REAEventStoreLike interface to compute contribution points.

This is why a "value equation" in the protocol sense can be reconfigured purely by changing weights: the underlying event stream is the same regardless of what you choose to value.

MCP tool surface

REA itself is not heavily exposed as MCP tools—the goal is for events to be a byproduct of domain operations, not something agents have to manage directly. When a Claude agent calls task_complete or appreciation_give via the MCP server, the corresponding REA events are emitted automatically.

The holosphere MCP domain provides escape-hatch reads against the rea_events lens for tooling that wants to inspect the event stream directly.

See also

Last updated

Was this helpful?