With spans you ask what was slow. With an execution graph you ask why this list came back
empty, and the answer sits on a step’s recorded output.
Two rows are worth reading twice. A retry is several sibling spans with nothing in the data
relating them, while an execution graph keeps the attempts under the one step they
belong to. It labels the group
declared where the step was marked retries=True, and
observed_retry where the SDK inferred the grouping itself. And sampling here applies to
values rather than to requests: an attempt that raised keeps its input and error values at any
depth, while one roll per trace decides whether an ok trace’s values are kept at all.
Sampling never touches the record of what ran.
Why not log every function’s inputs and outputs
Because you would have all of the values and none of the structure. Nothing in a log stream says which call caused which, so you rebuild the tree from request ids, timestamps, and thread ids, and that reconstruction is the work that costs incident time. The values themselves arrive flattened into strings by whatever formatter wrote them, so a list of five objects and a list of none are two strings you have to parse back. You would also pay the full price on every request, because a log line is written whether anyone reads it or not, and there is no way to ask for less of one. An execution graph is the same information arrived at already assembled, and separable: what ran is one thing to read and the captured bytes are another, each fetched on its own.What reading less actually costs
Those costs are measured rather than asserted. The MCP server’s tool results were counted with@anthropic-ai/tokenizer 0.0.4, Anthropic’s published BPE, over the text an AI agent receives, on
a server seeded with the execution graph spec’s 17-step conformance trace, cloned to 60 traces.
vexlo_search_traces over its default page of 10 traces measures 1,473 tokens. The same search at
the API’s own default of 50 measures 7,127, which is why the tool asks for 10. That tokenizer is
the Claude 2 generation’s and no current model’s is published, so the error of these counts
against a current model is unmeasured in both size and direction. The full table is on
token costs; the per-rung costs of one trace are on
the execution graph.
Where the vocabulary comes from
Where OpenTelemetry already has a word for something, Vexlo uses that word: the service name,trace_id, and status with ok and error, with tags carrying what span attributes carry.
Where the concept has no equivalent there, it gets its own name on purpose. A step is a
marked function rather than a span, and an attempt, one execution of a
step, is Vexlo’s own addition. The borrowing is vocabulary only, so the data stays legible to
anyone who knows OpenTelemetry’s terms without being mistaken for its data.
The execution graph defines the primitive those words name, and reads
one real trace down to its captured values.