> ## Documentation Index
> Fetch the complete documentation index at: https://docs.vexlo.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Execution graph format

> The five record kinds, value encoding, and conformance fixtures for execution graph producers.

The execution graph format represents a trace as JSON records: its opening, function
calls starting and finishing, captured values, and its closing. Use the
[specification](https://github.com/vexlo-ai/vexlo/blob/main/spec/execution-graph.md)
for the complete field definitions, emission rules, and capture bounds. The
[conformance corpus](https://github.com/vexlo-ai/vexlo/tree/main/spec/conformance/execution-graph)
contains 21 fixtures with exact records, byte strings, and expected outcomes for another
implementation to match.

## Format version

The current format is **version 2**. The Python SDK writes `format_version: 2` on
`trace-open`; the other record kinds carry no version field. Version 2 adds `step-open`
to the four record kinds in version 1.

The current server accepts versions **1 and 2**. A server reports its accepted versions
in `GET /v1/capabilities`, under `limits.record_format_versions`. Ingest validates each
record independently, without cross-checking its kind against the trace's version stamp.

## Five record kinds

Every record carries `kind` and `trace_id`. The remaining fields depend on `kind`:

| `kind` | What it records | Fields |
| - | - | - |
| `trace-open` | The trace opening. | `format_version`, `service`, `instance_id`, `sdk_version`, `started_at`, `tags`. |
| `step-open` | A function call reported before it finished. | `path`, `name`, `parent_name`, `attempt`, `started_us`, `declared_retryable`. |
| `step-attempt` | One completed attempt of a function call. | The step fields above, plus `duration_us`, `status`, `error_class`, `continues_retry`, `slots`. |
| `value` | Captured bytes referenced by a call. | `content_id`, `encoding`, `bytes_count`, `truncated`, `value`. |
| `trace-close` | The root call finishing. | `records_lost`, `truncated`. |

`parent_name` is omitted only for the root step. `status` on a completed attempt is
`ok` or `error`; `error_class` is present only for `error`. `started_us` is an integer
microsecond offset from the trace's monotonic zero; `duration_us` is the attempt's
nonnegative integer duration in microseconds. The trace's `started_at` uses UTC RFC 3339
with millisecond precision.

A `step-open` must omit `duration_us`, `status`, `error_class`, `continues_retry`, and
`slots`. Ingest rejects any of those keys with the per-record code `invalid_record`,
even when the value is `0`, `false`, or an empty object. `in_flight` is derived from an
open record without a completion; producers do not write it as a status.

## Identities and paths

| Field | Wire representation |
| - | - |
| `trace_id` | 16 bytes represented by 32 lowercase hexadecimal characters. |
| `path` | A sequence of two-byte, big-endian unsigned child ordinals, concatenated and represented as lowercase hex. The root is `""`. |
| `content_id` | 32-byte SHA-256 digest represented by 64 lowercase hexadecimal characters. |

A child path extends its parent's bytes by one ordinal. Ordinals count steps, not
attempts: grouped retries reuse a path and increment the zero-based `attempt` index.
Sorting decoded path bytes produces depth-first order.

The HTTP read API returns paths in opaque base32 form, not these producer hex strings.
It also omits `content_id` from returned handles.

## Values and content hashes

A completed attempt's `slots` object holds the present `input`, `output`, `error`, and
`logs` handles. A handle describes capture and references its bytes by `content_id`;
the full bytes travel separately in a `value` record. An absent slot is an absent key.
The specification defines
[handle rules](https://github.com/vexlo-ai/vexlo/blob/main/spec/execution-graph.md),
including the empty-logs exception.

The `value` field is always a string. For `encoding: json`, it contains JSON text rather
than a nested JSON object. For `text`, it contains UTF-8 text; for `base64`, it contains
standard padded base64.

Compute `content_id` as SHA-256 over the retained bytes, after truncation: UTF-8 bytes
for `json` and `text`, or raw bytes before base64 encoding. JSON uses RFC 8785 canonical
encoding with one source-type exception: integers retain their exact decimal digits;
floats use ordinary RFC 8785 number formatting, including floats whose values are integral.

### Conformance example

The following is the complete `11-content-id-field-order.json` fixture, reproduced
verbatim. It is a conformance example, not a runtime capture. Both source objects
produce the same canonical text and hash.

```json theme={"system"}
{
  "description": "{radius_km: 5.0, zone: 'SF-94107'} fed to the probe in both field orders canonicalizes to the same bytes and the same content_id, because content_id is computed over RFC 8785 (JCS) canonical bytes with sorted keys, not over whichever order the host happened to construct the dict in. radius_km is a Python float (5.0); JCS's ECMAScript Number::toString renders an integral-valued double without a decimal point, so it prints as 5, not 5.0 -- this is JCS's ordinary float rule, not the integer exception (see case 12).",
  "source_object_order_a": {
    "radius_km": 5.0,
    "zone": "SF-94107"
  },
  "source_object_order_b": {
    "zone": "SF-94107",
    "radius_km": 5.0
  },
  "canonical_bytes_utf8": "{\"radius_km\":5,\"zone\":\"SF-94107\"}",
  "canonical_byte_length": 33,
  "content_id_sha256_hex": "421fd7e75a05079fc8872215c7b7566dbc46ddb669c104cae6b72e78aa87bcd6",
  "value_record": {
    "kind": "value",
    "trace_id": "8192a3b4c5d6e7f8091a2b3c4d5e6f70",
    "content_id": "421fd7e75a05079fc8872215c7b7566dbc46ddb669c104cae6b72e78aa87bcd6",
    "encoding": "json",
    "bytes_count": 33,
    "truncated": false,
    "value": "{\"radius_km\":5,\"zone\":\"SF-94107\"}"
  }
}
```

## Arrival order and derived fields

An attempt is identified by `(trace_id, path, attempt)`. If its open and completion
records both arrive, the completion determines the call's name, parent name, start offset
and outcome, regardless of arrival order. An open record arriving later cannot turn a
completed attempt back into one in flight. Retry declarations merge separately, as below.

`trace-close` is not an arrival barrier: the server accepts step records that arrive
after it. Its `records_lost` reports loss known when the producer built that record;
`truncated` reports that the 10,000-step cap was reached.

The server derives `attempt_grouping` from the retry flags; producers do not emit that
field. Any `declared_retryable: true` makes the step `declared`; otherwise, any
`continues_retry: true` makes it `observed_retry`; otherwise it is `single`. The
[retry rules](https://github.com/vexlo-ai/vexlo/blob/main/spec/execution-graph.md)
define when a producer reuses a path. Read-time value states `expired` and `missing` are
also absent from emitted records.

## Use the fixture corpus

The [fixture manifest](https://github.com/vexlo-ai/vexlo/blob/main/spec/conformance/execution-graph/manifest.md)
identifies the rule each case exercises. Cases 1–16 isolate path encoding, retries,
handle shapes, tags, hashes, and logs. Cases 18–21 cover open attempts and arrival order.

| Fixture | Use |
| - | - |
| [11: Content hash](https://github.com/vexlo-ai/vexlo/blob/main/spec/conformance/execution-graph/11-content-id-field-order.json) | Canonical key order and the hash reproduced above. |
| [17: Complete trace](https://github.com/vexlo-ai/vexlo/blob/main/spec/conformance/execution-graph/17-full-worked-trace.json) | One trace's complete record set. |
| [21: Invalid open records](https://github.com/vexlo-ai/vexlo/blob/main/spec/conformance/execution-graph/21-step-open-forbidden-fields.json) | Completion fields that ingest must reject on `step-open`. |

Fixture documents contain explanatory and expected-result fields alongside the records.
They are not HTTP request bodies. Transport sends an array of records to
`POST /v1/records`; the
[API specification](https://github.com/vexlo-ai/vexlo/blob/main/spec/api.md#post-v1records)
defines authentication, headers, and per-record rejection responses.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.