Skip to main content
XORCISE turns your agent’s OpenTelemetry output into the replay you watch and the evidence the judge reads. This page is the wiring: the endpoint, both signals, and the exact configuration each harness needs. It is optional — a run without telemetry is still graded. Time: about 10 minutes · You need: a live run (Connect any agent) · Interface: CLI This page uses the agent scout and the mission chrono-canary. <run_id> stands for the 32-character run id XORCISE printed when you created the run.

What XORCISE ingests

XORCISE runs an embedded OTLP/HTTP receiver with two routes, POST /v1/traces and POST /v1/logs. Both matter. Claude Code’s assistant prose arrives on logs, and Codex’s entire narrative is logs-only, so a harness configured for traces alone produces a gutted replay or none at all. There is no gRPC receiver; JSON and protobuf are both accepted, and gzip is decompressed for you. Every batch must carry the run id. XORCISE reads the xorcise.run_id OpenTelemetry resource attribute first — a span attribute does not correlate — and falls back to grepping the xorcise.run_id= marker out of the payload, which only works for the batches that happen to echo the connect prompt. A batch carrying neither is dropped, never blended into another run. The port is the one xorcise up printed: 4318, unless it was busy and XORCISE moved up. Read the endpoint from the run’s launch profile rather than assuming it. The otel field on an agent record is stored and never read.

Running without telemetry

A run with no telemetry is fully valid and quantifiably safe. Your agent still joins the tailnet, submits artifacts over run-control, and calls POST /complete; grading runs at completion. Deterministic checks are unaffected, because they read submitted artifacts and observed facts, not traces. The judge still runs, with an explicit note that no execution trace is available, and the criteria it cannot evidence score unknown and are renormalised out of the weighted average rather than scored zero. What you lose is the replay — the timeline, terrain map, and event feed are empty — and any rubric criterion that depends on how the agent worked.

1. Read the run’s launch profile

The endpoint is computed per run and served by the brain, so no harness has to guess it. launch-cmd takes the launch mode; ask for host when your agent runs in a terminal. To keep the variables in a file, use xorcise run launch-profile <run_id> > launch.env — it writes the same values as dotenv lines, but takes no launch mode and returns the container profile for a custom kind.
You should see the exports for the kind you registered. For an agent with no built-in harness, that is the generic three:
Output
OTEL_EXPORTER_OTLP_ENDPOINT is a base URL. A standard OpenTelemetry SDK appends /v1/traces and /v1/logs itself; Codex does not, which is why its configuration looks different.

2. Export the variables before your agent starts

A bare KEY=VALUE paste is not inherited by the child process, so the agent starts with no exporter configured and nothing arrives.
You should see the endpoint in the environment your agent will inherit:
Output
If you already export OTEL_RESOURCE_ATTRIBUTES for your own service metadata, append xorcise.run_id=<run_id> to it comma-separated rather than replacing it.

3. Configure your harness

XORCISE ships three built-in harness adapters plus a generic floor. You should see the block below that matches the kind on your agent record; xorcise run launch-cmd emits it for you, already filled in with this run’s id and endpoint.

Claude Code

kind: claude-code. The strongest coverage of the three: traces and logs, with resource-attr correlation. Claude Code’s SDK honours the standard OpenTelemetry variables, so the whole configuration is environment.
Four of these are load-bearing. CLAUDE_CODE_ENHANCED_TELEMETRY_BETA=1 is the beta gate: without it there are no spans at all. OTEL_LOGS_EXPORTER=otlp opens the logs signal, which is where the assistant’s text and the user prompt live — a headless run never exports the interaction span, so logs are the only source. OTEL_LOG_ASSISTANT_RESPONSES=1 is what stops the response arriving as <REDACTED> and being dropped. OTEL_RESOURCE_ATTRIBUTES is the correlation. The 1000 ms export intervals exist so a short headless run flushes before it exits.

Codex

kind: codex. Codex’s OpenTelemetry is driven by its own config, and it ignores every OTEL_EXPORTER_* environment variable. The exporter configuration rides -c overrides on the command, layered in memory per invocation — nothing is written to ~/.codex/config.toml and there is nothing to clean up.
Five details decide whether this works. The two exporters live under different keys: traces under otel.trace_exporter.otlp-http.*, logs under otel.exporter.otlp-http.*. Each endpoint carries the full per-signal path, because Codex does not append /v1/traces or /v1/logs itself. protocol = "binary" means http/protobuf. otel.log_user_prompt=true is Codex’s analogue of OTEL_LOG_USER_PROMPTS; without it the prompt logs as [REDACTED] and the replay cannot show the task. And OTEL_RESOURCE_ATTRIBUTES is the only environment variable that matters — the run id must be a resource attribute, since a span attribute does not correlate.
Codex’s replay is built entirely from the logs signal. Its trace adapter returns nothing by design, suppressing several hundred contentless runtime spans, so a Codex run with the logs endpoint misconfigured produces an empty replay even though spans are arriving. Codex’s telemetry also carries no assistant prose or reasoning text at all — expect the full action narrative without the model’s answers.

OpenHands

kind: openhands. OpenHands has a replay adapter and a launch command but no telemetry provider, so it falls back to the generic environment and to prompt-sentinel correlation. It is the code-level default kind, but the register form preselects nothing — an agent registered without a harness runs through generic, not through this.
Because correlation is best-effort, only the batches that echo the connect prompt attach to the run; deleting the xorcise.run_id= marker from the prompt means nothing attaches. OpenHands contributes traces only — it has no logs mapping. Configure its model and credentials before you launch, and expect its exporter to gzip every export, which XORCISE accepts.

Any other harness

A blank or unrecognised kind gets the generic adapter, the generic three variables, and prompt-sentinel correlation. The generic adapter classifies spans by name and attributes and still produces a readable feed.
Nothing stops you from setting OTEL_RESOURCE_ATTRIBUTES=xorcise.run_id=<run_id> yourself. XORCISE reads the resource attribute regardless of which harness produced the batch, so adding it upgrades a custom agent from best-effort correlation to the same strong tier the built-ins get. Add OTEL_LOGS_EXPORTER=otlp too if your SDK supports it.

4. Confirm events are arriving

Check the receiver first, then the run.
Output
Then read what has landed against this run.
You should see numbered records and a count:
Output
On the live run page the same signal shows as the phase leaving Awaiting agent and the event count climbing.

Troubleshooting

No events appear

Work down this list in order.
  • Wrong host. This is the single most common cause. Container mode bakes host.docker.internal, which a plain terminal cannot resolve, so the exporter fails silently and nothing arrives. Ask for host mode: xorcise run launch-cmd <run_id> host. An agent that really does run in a container needs --add-host host.docker.internal:host-gateway.
  • The variables were not exported. A pasted KEY=VALUE line is not inherited by the child process. Use set -a; source launch.env; set +a, or run the whole block launch-cmd printed.
  • The profile was empty. If xorcise run launch-profile prints nothing, no collector host is configured for this instance.
  • Codex only. Setting OTEL_EXPORTER_OTLP_ENDPOINT does nothing; Codex ignores it. The endpoints must be the -c flags, with the full /v1/traces and /v1/logs paths.
  • Claude Code only. Without CLAUDE_CODE_ENHANCED_TELEMETRY_BETA=1 there are no spans at all.
  • The receiver is unreachable. GET /healthz on the OTLP port should return {"status":"ok","service":"otel"}. A 500 with {"code": 12, ...} means the protobuf decoder is missing; reinstall with pip install --force-reinstall xorcise. A 400 with {"code": 3, "message": "malformed OTLP body"} means the payload itself is bad.

Events appear on the wrong run, or on no run

The receiver tells you directly. An export that cannot be routed comes back with a partial-success body:
Output
The logs route reports the same thing as rejectedLogRecords. Causes, in order of likelihood:
  • The run id is a span attribute, not a resource attribute. Only resource.attributes is read. Set it through OTEL_RESOURCE_ATTRIBUTES.
  • You overwrote your resource attributes. Append xorcise.run_id=<run_id> comma-separated instead of replacing service.name and friends.
  • The prompt marker was stripped. For OpenHands and any generic harness the xorcise.run_id= line in the connect prompt is the only correlation channel.
  • The run is already sealed. Sealing closes the control plane immediately and keeps OTLP admissible for only five more seconds, so a harness that batches slowly loses its tail after POST /complete. Late spans are rejected by design, not merged — rerun rather than trying to backfill.
  • The prompt came from an older run. Its marker names that run’s id.

Only tool calls, no reasoning

  • Claude Code. Missing OTEL_LOG_TOOL_DETAILS=1 or OTEL_LOG_TOOL_CONTENT=1 gives tool cards with no content. Missing OTEL_LOG_ASSISTANT_RESPONSES=1 makes the response arrive as <REDACTED>, and XORCISE drops it. Missing OTEL_LOGS_EXPORTER=otlp removes the assistant text and the user prompt together.
  • Codex. Assistant prose and reasoning content are never available — its telemetry carries token counts, not text. Without otel.log_user_prompt=true you also lose the task itself.
  • OpenHands. It emits no logs mapping at all, and drops a duplicate tool call when a dedicated action span already covers the command.
  • The wrong kind. If events render as raw span names with no grouping, the generic adapter was selected. Slugs are hyphenated: claude-code, not claude_code. A friendly label in xorcise agent list is not an adapter — gemini, goose, cai, and langchain render a display name and nothing more.

Next steps

Traces and events

How XORCISE replays what arrives — the timeline, the terrain map, and the event feed.