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.