HOLONOGRAPH / The Guide · The four-layer snapshot v0.23.0 all chapters

Chapter 03

The four-layer snapshot

Reflects Holonograph v0.23.0 · ~6 min read

Every evaluation Holonograph observes is written as one record — an EvaluationEvent — that binds four layers of one call into a single row you can query as history. This chapter is about that record: what it holds, how to ingest one, and how to page back through them.

What it is

An event is the atomic unit. It's what reportOutcome writes when you finish an evaluation, and it's what GET /lens/events pages back to you when you're reading history. One event = one call to one surface at one moment.

The four layers of one event:

  • Substrate — the state of your deployment at call time. Populated from the columns declared on the contract (Substrate columns). This is where prompt_hash, tool_schema_version, and any operator-declared columns land.
  • Light source — which model produced the output. Captured as vendor/model/version, so a silent vendor re-route behind a stable alias is visible in the record.
  • Lens — which evaluation configuration was in effect. Captured as lens_version. Downstream analyses use this to tell a change in your evaluation apparatus from a change in the thing being evaluated.
  • Outcome — what the evaluation reported: dimension scores, cohort tags, side effects, and (if relevant) a fixture identifier.

Alongside those four layers, every event carries the metadata that binds them together for query and correlation: eventId, surfaceId, correlationId, run_mode, provenance, and a timestamp.

Provenance vs run mode run_mode is who is calling (production, test, eval, replay, local_dev). provenance is what kind of run this is from Holonograph's point of view (production-state versus exploratory-state). Read queries default to production-state; pass provenance=all when you want the full stream.

Why it matters

Two properties fall out of binding these four layers per event:

  • Every reading can be traced to the exact deployment, model, and evaluation config that produced it. When you look at last week's numbers, you're not looking at a lossy summary — you're looking at the substrate that was in effect for each specific call.
  • Changes at any layer are attributable independently. Because each layer is captured on its own and versioned on its own schedule, downstream analyses (Chapter 13, Chapter 14) can talk about which layer changed, not just that the number moved.

How to use it

Ingest an event

Most integrations never call POST /lens/events directly — the SDK's reportOutcome() writes the event for you at the end of a call. It's documented here because a small number of ingest-only integrations (backfills, custom collectors) need the raw endpoint.

POST /lens/events
Authorization: Bearer <token>
x-holonograph-run-mode: production
Content-Type: application/json

{
  "surfaceId": "classify-intent",
  "correlationId": "corr_01H...",
  "dimensions": [{
    "dimensionId": "correctness",
    "passed": true,
    "expected": "a correct answer to the user's question",
    "actual": "offered to open a replacement order"
  }],
  "cohortTags": ["customer-tier=enterprise", "fix_tier=tool-input-schema"],
  "substrate": { "prompt_hash": "v2-sha256-...", "tool_schema_version": "3.1.0" },
  "lightSourceId": "anthropic/claude-sonnet-5",
  "timestamp": "2026-07-05T18:22:14.812Z"
}

Response:

{ "eventId": "evt_01H..." }

Read history

GET /lens/events pages events back to you. It supports the query parameters you'd expect from a time-ordered log with cohorts.

GET /lens/events?surfaceId=classify-intent&since=2026-06-01&limit=200&direction=desc
Authorization: Bearer <token>
x-holonograph-run-mode: production

Response:

{
  "events": [ { "eventId": "evt_...", "surfaceId": "classify-intent", ... }, ... ],
  "count": 200,
  "nextCursor": "cur_01H..."
}

Follow the nextCursor to page forward. When there is no more history in the window, nextCursor is absent.

Reading the output

One event is a snapshot; a query is a slice through them. Two habits keep the slice honest:

  • Filter on cohort tags at read time, not at write time. Cohort tags are the query keys — write them liberally and filter narrowly. Do the opposite and you'll be re-ingesting to recover slices you didn't foresee.
  • Default reads to production-state. When you're diagnosing something unusual, pass provenance=all — but the default filter is what keeps replay traffic and local-dev noise out of dashboards.

Reference

Endpoints

Method & pathPurpose
POST /lens/eventsIngest one EvaluationEvent. Response: { eventId }.
GET /lens/eventsQuery events. Response: { events[], count, nextCursor? }.

GET /lens/events query parameters

ParamShapeNotes
surfaceIdstring (opt)Restrict to one surface.
fixtureIdstring (opt)Restrict to events produced by one fixture (Conformance, Chapter 10).
provenanceenum (opt)production-state (default), exploratory-state, or all.
sinceISO 8601 (opt)Inclusive lower bound on timestamp.
untilISO 8601 (opt)Exclusive upper bound on timestamp.
limitnumber (opt)1–10000; default reasonable.
directionenum (opt)asc or desc; default desc. order is accepted as an alias.
cursorstring (opt)Continue from a prior page's nextCursor.

Headers

  • Authorization: Bearer <token> — when the deployment enables bearer auth.
  • x-holonograph-run-mode: <mode> — required by fail-closed mode enforcement; see Run modes.