Chapter 07
Run modes
Every call to the lens declares which run mode it belongs to. The mode gates side effects, decides which events count as production history, and — most importantly — keeps test and evaluation traffic from silently polluting your production substrate. Modes are enforced fail-closed: a missing or unknown mode is rejected at the door.
What it is
A run mode is a single-string label attached to every call. Holonograph's canonical set has five values:
| Mode | Meaning | Side-effect behavior |
|---|---|---|
production | A real user's request through the real system. | Full side effects. Persists to production-state provenance. |
test | An automated test exercising the agent. | Side effects are suppressed or replaced with fixtures. Persists to exploratory-state. |
eval | A deliberate evaluation run — an operator asking "how is the system doing?" | Side effects suppressed. Persists to exploratory-state. Eligible for cohort-tag-driven grading pipelines. |
replay | Replaying prior traffic against a specific lens version or model. | Side effects suppressed. Persists to exploratory-state. Tagged so replays don't co-mingle with live evaluation. |
local_dev | A developer poking the system on their laptop. | Side effects suppressed. Persists to exploratory-state. The permissive mode you use during integration work. |
debug mode
The canonical set is exactly the five above. If you see debug anywhere in code or a sample, treat it as an error to correct — not a sixth mode.
Why it matters
Two properties fall out of enforcing modes at the wire:
- Substrate integrity. Your production history stays production. A test that forgot its mode doesn't quietly become a production event when you read it back a month later.
- Side-effect discipline. An
evalrun that touches a downstream system by accident is a serious incident. Fail-closed makes that failure loud instead of silent — the daemon rejects a call with no mode, and modes other thanproductionsuppress the side-effect channel.
How to use it
Set once, on the client
The simplest wiring: pass runMode once at client construction. Every call the client makes carries it.
const client = new HolonographClient({
endpoint: "http://127.0.0.1:8080",
runMode: process.env.NODE_ENV === "production" ? "production" : "local_dev",
lensVersion: "billing-lens/v14",
substrate: { /* ... */ }
});
Set at the wire
If you're calling POST /holonograph/messages directly, send the mode as a header on every request:
x-holonograph-run-mode: production
A missing header is rejected with a 400. This is by design — Holonograph does not guess.
Resolve per-request in a web framework
Web applications often need to resolve the mode from the incoming request — e.g. a header set by a shadow-traffic proxy, or a query flag distinguishing an internal QA session. Wire this at your framework's middleware layer, before any code path that constructs an HolonographClient:
// Express example
app.use((req, _res, next) => {
const shadow = req.header("x-shadow-traffic") === "1";
req.holonographRunMode = shadow ? "test" : "production";
next();
});
app.post("/agent", async (req, res) => {
const client = new HolonographClient({
endpoint: "http://127.0.0.1:8080",
runMode: req.holonographRunMode,
lensVersion: "billing-lens/v14",
substrate: { /* ... */ }
});
// …
});
Propagate across async and process boundaries
When work crosses a boundary the client can't reach — a queue, a background job, a message bus — carry the mode as W3C Baggage so the receiver can reconstruct it. Two keys:
| Baggage key | Carries |
|---|---|
holonograph.runmode | The run mode of the originating request. |
holonograph.correlation-id | The correlation id, so calls on the other side of the boundary bind into the same evaluation. |
The receiver extracts both keys from Baggage and constructs its HolonographClient with the run mode it inherited, then passes the correlation id through to messages.create(). See Sidecar / OTel integration for the propagation mechanism.
Reading the output
run_mode is one of the mandatory substrate columns on every event (see Substrate columns) — so every reading you do downstream is already sliceable by mode without any extra work.
Two default habits keep your reads honest:
provenancedefaults toproduction-state. That's what filters non-production modes out of dashboards. If you need them, passprovenance=exploratory-stateorprovenance=all.- Never rely on
run_modeas a substitute for side-effect gating. Modes shape which channel side effects go through, but the guarantee is Holonograph-side. Your agent code should still respect the mode when it exists in scope.
Reference
Fail-closed behavior
The daemon rejects any request that arrives without a valid mode. Concretely:
- Missing
x-holonograph-run-modeheader →400with anerrormessage indicating the run mode is required. - Unknown value (anything outside the five canonical modes) →
400with anerrormessage indicating the value is not one of the accepted modes.
This is not something you can turn off. It's a property of the daemon, not a policy on top of it.
Header
x-holonograph-run-mode: production | test | eval | replay | local_dev
Baggage keys
holonograph.runmode— the run mode.holonograph.correlation-id— the correlation id binding calls across the boundary.