Import from your observability stack
You'll end up with your existing agent traces — from OpenTelemetry or Langfuse — turned into ponens reasoning traces you can visualize, check against policies, and gate on. ponens sits on top of observability: it doesn't replace your spans, it adds the governance layer over them.
Both bridges are one command and produce a full trace — named phases, a lineage DAG, per-action
timestamps — that ponens trace check grades like any other.
OpenTelemetry → ponens
Point it at an OTLP-JSON span export (full resourceSpans, a {"spans":[…]}
object, or a bare span list):
ponens otel import spans.json -o trace.json
ponens trace check trace.json --json The OTel model maps straight onto the trace model:
| OpenTelemetry | ponens trace |
|---|---|
| leaf span | action (name → type/label, attributes → rationale, status → result) |
| span with children | meta-action — the phase grouping its actions |
| parent span tree | meta nesting (parent_id) + each action's meta_action_id |
| span start / end | action timestamp (+ duration in the label) |
resource service.name / gen_ai.agent.name | action agent (actor) |
| span id | evidence (OtelSpan ref) |
Langfuse → ponens
Point it at a Langfuse trace export — a trace with nested observations (a single trace,
an API {"data":[…]} list, or a bare observation list):
ponens langfuse import trace.json -o trace.json
ponens trace check trace.json --json | Langfuse | ponens trace |
|---|---|
| leaf observation | action |
| observation with children | meta-action (phase) |
parentObservationId tree | meta nesting + meta_action_id |
startTime / endTime | action timestamp |
type GENERATION / SPAN / EVENT | action type — a GENERATION is an LLM call → Compute (+ model in label) |
level=ERROR / statusMessage | result_summary (its phase → partial) |
userId | action agent |
Emit lineage: the ponens.inputs/outputs convention
Spans and observations don't natively say which artifacts an action consumed or produced — so the data-flow DAG is empty unless you say so. Add two attributes (OTel) / metadata keys (Langfuse) naming the artifact ids, and the bridge wires the lineage for you:
// OpenTelemetry span attributes
{ "key": "ponens.outputs", "value": { "stringValue": "sizing-plan" } }
{ "key": "ponens.inputs", "value": { "stringValue": "snapshot" } }
// Langfuse observation metadata
"metadata": { "ponens.inputs": "snapshot", "ponens.outputs": "sizing-plan" } Now each output becomes an artifact whose derived_from is the action's inputs — so
snapshot → sizing-plan → … → order renders as a real dependency graph. Optional
ponens.rationale / ponens.intent / ponens.agent keys refine the
rest. Without them you still get actions, phases, and timing — just no artifact DAG.
Then govern it
The imported trace is an ordinary ponens trace. Attach a pack and check it — offline, in CI, or as a runtime gate:
ponens registry update
ponens policies add agent_identity_resolved --into trace.json
ponens trace check trace.json --json # per-policy verdicts + evidence See Use ponens programmatically for the JSON API and the live compliance loop, and the policy packs for what to check against — including the FIX AI WG agentic-governance pack.