Span spec
The span taxonomy behind every Swirls run. What each span represents, how the trace tree nests, and the identity that ties every span to a project.
This page defines the span names used for workflow executions and agent turns, their parent relationships, and their scope attributes. Use it with the attribute reference when inspecting or querying traces.
The trace model
A trace is one run. The root span is the run; everything the run does nests beneath it.
- A workflow execution roots its trace at
swirls.graph.execution. Nodes, agent turns, model calls, tools, and sandbox processes are spans inside it. - A root agent session (an agent reached over the API or a chat channel, with no workflow around it) roots its trace at the agent turn span. The model calls and tools it runs nest beneath that.
Spans export when they end, so a trace fills in as the run proceeds. A zero-duration swirls.trace.complete sentinel marks run completion; telemetry can arrive out of order.
Span kinds
Recognized span names receive a swirls.span.kind attribute. The five classifications are:
swirls.span.kind | Span names that classify as this kind |
|---|---|
workflow | swirls.graph.execution |
agent | gen_ai.invoke_agent, gen_ai.create_agent |
llm | gen_ai.chat, the AI SDK ai.* model spans |
tool | swirls.sandbox.exec, swirls.workflow.invoke, gen_ai.execute_tool, ai.toolCall |
internal | swirls.trace.complete |
swirls.node.execution is structural and is identified by its name rather than a kind. The kind is stamped centrally as each span starts, so spans Swirls does not author (the AI SDK ai.* and gen_ai.* spans) are classified the same way the ones it does author are.
Span types
swirls.graph.execution
The workflow run. The root span of a workflow trace, and the owner of the run's outcome. It carries the workflow identity (swirls.workflow.id, swirls.workflow.label), the trigger that started the run (swirls.trigger.type, swirls.trigger.id), and the final swirls.execution.status of completed or failed. When the run is itself a nested workflow, swirls.parent.execution.id links it to its caller. Emitted by the executor.
swirls.node.execution
One node in the workflow. Parents under the graph span, so a run reads as a tree of nodes. Carries swirls.node.id and swirls.node.name, plus the node's policy-gated input and output (swirls.node.input, swirls.node.output) and any operation metadata the node returns (swirls.node.meta). An ai node also stamps the prompt and response it sent to the model (swirls.ai.prompt, swirls.ai.response) and token usage. An agent node accumulates the agent transcript onto swirls.agent.messages when it ends. Emitted by the executor.
gen_ai.invoke_agent
One agent turn. On the workflow path it nests under the agent node; on the chat or API path it is the trace root. Carries the agent name and the requested model under the GenAI conventions, the turn's configuration (swirls.agent.max_steps, swirls.agent.output_mode, swirls.agent.tool_names), the turn's outcome rollups (swirls.agent.step_count, swirls.agent.tool_call_count), and full token usage. When a content policy is set, it also carries the durable, policy-gated LLM input and output. As a root turn it owns swirls.execution.status and emits the completion sentinel. Emitted by the agent host.
gen_ai.chat and ai.*
A single model call. Emitted by the Vercel AI SDK as part of an agent turn, one per request to the model. Carries the GenAI conventions for model, finish reason, and per-call token usage. Classified as kind llm. The AI SDK also emits gen_ai.input.messages and gen_ai.output.messages on these spans; those bodies ride a default-off path and the collector strips them, so durable message capture happens on the owning agent turn span instead.
ai.toolCall and gen_ai.execute_tool
A tool invocation. ai.toolCall is emitted by the AI SDK for each tool the model calls; gen_ai.execute_tool marks an agent invoking a workflow as a tool. Both classify as kind tool and carry the tool name and call id.
swirls.sandbox.exec
A process run inside a sandbox. Emitted by the agent host when an agent runs a command. The program name (swirls.sandbox.program), the tool name (swirls.tool.name), and the exit code (swirls.sandbox.exit_code) are always recorded. The full command text (swirls.sandbox.command) can carry secrets, so it is captured only under the project content policy.
swirls.workflow.invoke
A nested workflow called as a tool by an agent. Carries gen_ai.operation.name of execute_tool, the tool name, and swirls.child.execution.id, which links to the root span of the nested workflow's own trace. Emitted by the executor.
swirls.trace.complete
The completion sentinel. A zero-duration span emitted once per trace, as the root ends, carrying swirls.trace.complete set to true and kind internal. This records the run's completion, not a guarantee that all spans have reached the destination.
swirls.review.gate
A zero-duration span emitted when a review is requested. It is a child of the workflow root and carries the node ID and name. Its duration does not measure the time spent waiting for a person to respond.
Identity and scope
A span processor copies available tenant and execution identifiers from the active run scope. Workflow-only fields, such as swirls.workflow.id, can be absent on a standalone agent turn.
| Attribute | What it scopes the span to |
|---|---|
swirls.org.id | The organization. |
swirls.project.id | The project. This is the key the trace store routes and filters on. |
swirls.deployment.id | The deployment the run came from. |
swirls.workflow.id | The workflow definition. |
swirls.execution.id | The single run. |
These are not inherited down the tree by OpenTelemetry. A child span does not pick up its parent's attributes. Instead the active run sets a scope at the execution or agent-turn boundary, and a span processor stamps that scope onto each span as it starts. That is how a model call emitted by the AI SDK or a process emitted by a sandbox still carries swirls.project.id and swirls.execution.id, and so still lands in the project's trace store. The completion sentinel is stamped the same way so a project-scoped query never drops it.
Run status
Run outcome lives on one span per trace, never two.
- On a workflow run,
swirls.graph.executionownsswirls.execution.status. - On a root agent session, the root
gen_ai.invoke_agentturn owns it.
The value is completed or failed, the same vocabulary in both cases, so the Traces list reads one outcome per run. A non-root agent turn inside a workflow does not stamp status; the graph root already owns it.
Content policy
The spans above split into two classes of attribute. Identity, status, span kind, timings, names, and token usage are structure: they are recorded when applicable and available. Run I/O is content: node inputs and outputs, model prompts and completions, agent messages, and sandbox command text.
Content is gated by a per-project policy with three settings.
| Policy | What is captured |
|---|---|
full | The serialized value, truncated past a size cap. |
redacted | The shape only: object keys and value types, no leaf values. |
off | Nothing. |
The gate runs at capture time, not display time. Under redacted, content values are replaced with their shape; under off, content attributes are omitted. Full capture may contain sensitive values supplied by your application. Trace export can apply further redaction. The Attribute schema marks every content attribute, so you know exactly which keys the policy governs.
Correlation
Traces use W3C trace context. When the caller that triggers a run propagates traceparent into Swirls, the run's trace links to that upstream trace, so a Swirls execution sits in line with the rest of your system. A nested workflow is linked to its caller through swirls.child.execution.id on the swirls.workflow.invoke span rather than by nesting, so each execution keeps its own trace.