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

# traces

> View, export, and manage local SDK traces and snapshots.

# cirron traces

Read and export the local trace data the Cirron Python SDK produces when
`ci.profile()` runs. Where [`cirron spool`](/cli/commands/spool) gives you a
file-level view of the raw batch JSON on disk, `cirron traces` gives you a
**semantic** view: reconstructs the scope tree across batches, groups records
into sessions, renders flamegraphs, and exports to Parquet, OpenTelemetry,
CSV, or JSON.

Everything runs entirely against the local spool (`./.cirron/spool/`) and
snapshot directory (`./.cirron/snapshots/<span_id>/`). No network, no
platform authentication. Your traces are yours, in open formats, on your disk.

## Subcommands

* [`cirron traces view`](#view-the-scope-tree): Render a session's scope tree as a text flamegraph
* [`cirron traces list`](#list-sessions): Table of sessions with counts and sizes
* [`cirron traces export`](#export-traces): Convert to Parquet / OpenTelemetry / CSV / JSON
* [`cirron traces clear`](#clear-sessions): Delete sessions and their snapshot directories
* [`cirron traces snapshots`](#list-snapshots): Table of weight/gradient snapshots grouped by span
* [`cirron traces snapshot`](#inspect-a-snapshot): Inspect stats, histograms, and safetensors tensors for a span

## Usage

```bash theme={null}
cirron traces view      [--last <n>] [--name <substr>] [--session <id>] [--depth <n>]
                        [--min-wall <dur>] [--spool <dir>] [--no-color] [--json]

cirron traces list      [--spool <dir>] [--json]

cirron traces export    --format <parquet|otel|csv|json>
                        [--output <path>] [--session <id>] [--spool <dir>]

cirron traces clear     [--before <iso-date>] [--keep <n>] [--yes]
                        [--no-prune-orphans] [--spool <dir>]

cirron traces snapshots [<spanId>] [--session <id>] [--span <id>]
                        [--spool <dir>] [--json]

cirron traces snapshot  <spanId> [<tensorName>]
                        [--spool <dir>] [--file <path>]
                        [--preview <n>] [--tail <n>]
                        [--export <path>] [--json] [--no-color]
```

## Global Options

| Option          | Description                                              | Default                      |
| --------------- | -------------------------------------------------------- | ---------------------------- |
| `--spool <dir>` | Override the spool directory                             | `./.cirron/spool`            |
| `--json`        | Emit machine-readable JSON (`view`, `list`, `snapshots`) | —                            |
| `--no-color`    | Disable ANSI color (`view`, `snapshot`)                  | TTY-aware; honors `NO_COLOR` |

## Vocabulary

* **Session**: One `ci.profile()` lifetime, rooted at a `cirron.session` span. Typically one per training run.
* **Span**: A timed scope (epoch, step, forward pass, optimizer step). Spans nest via `parent_id`.
* **Mark**: A named scalar (`ci.mark("loss", 0.5)`) attached to the innermost open span.
* **Snapshot**: Per-tensor weight or gradient record at an epoch boundary. Stats live inline; `sampled`/`full` modes also store safetensors blobs under `./.cirron/snapshots/<span_id>/`.

Sessions reconstruct by deduplicating spans across batches (preferring records with `end_ns`) and walking parent pointers from every `cirron.session` root. Unknown top-level and per-span fields are ignored. A newer SDK can add metadata without breaking an older CLI.

***

## View the scope tree

Render one or more sessions as an indented text flamegraph.

```bash theme={null}
cirron traces view
cirron traces view --last 3
cirron traces view --name epoch
cirron traces view --session 067fc96e
cirron traces view --depth 2
cirron traces view --min-wall 1ms
cirron traces view --json
```

| Option             | Description                                                                       |
| ------------------ | --------------------------------------------------------------------------------- |
| `--last <n>`       | Show the N newest sessions (default: 1)                                           |
| `--name <substr>`  | Keep only spans whose name contains the substring (ancestors preserved)           |
| `--session <id>`   | Show a specific session. Prefix match allowed                                     |
| `--depth <n>`      | Collapse the tree below depth N (shows aggregate duration and span count)         |
| `--min-wall <dur>` | Hide spans shorter than the given duration (`ns`, `us`/`µs`, `ms`, `s`, `m`, `h`) |

### Example output

```
cirron.session — 561.7ms pid=1797 rank=0
  epoch[0] — 42.1ms
    data_load — 4.5ms data_load_ns=4541667
    forward — 5.1ms
    backward — 23.0ms
    optimizer_step — 974.0us
    ci.mark loss=0.6841
  epoch[1] — 38.9ms
    ...
```

Marks render as indented `ci.mark <name>=<value>` lines under the span they attach to. `point` marks render as-is; `summary` marks carry a `(summary)` suffix.

With `--depth 1`, subtrees collapse to `… 33.6ms (4 spans, collapsed)` aggregates. The collapsed duration sums descendants only, so it's always ≤ the parent's wall time.

***

## List sessions

Table of every session in the spool, newest first.

```bash theme={null}
cirron traces list
cirron traces list --json
```

```
Spool: /home/alice/runs/resnet/.cirron/spool  (2 sessions)
┌───────────┬──────────────────────────┬──────────┬───────┬───────┬───────────┬──────────┐
│ SESSION   │ STARTED                  │ DURATION │ SPANS │ MARKS │ SNAPSHOTS │     SIZE │
├───────────┼──────────────────────────┼──────────┼───────┼───────┼───────────┼──────────┤
│ a541b565… │ 2026-04-21T14:22:05.001Z │  561.7ms │  5244 │  5123 │         0 │ 142.3 KB │
│ ea06e583… │ 2026-04-21T15:01:33.442Z │  (live)  │    64 │    20 │        36 │  48.0 KB │
└───────────┴──────────────────────────┴──────────┴───────┴───────┴───────────┴──────────┘
```

Live sessions (root `cirron.session` without `end_ns`) show `(live)` and are protected from destructive `clear --keep` operations.

```json theme={null}
{
  "dir": "/home/alice/runs/resnet/.cirron/spool",
  "sessions": [
    {
      "id": "a541b565-b754-42e3-...",
      "started_ns": "1776486598051509000",
      "ended_ns": "1776486598613253000",
      "live": false,
      "spans": 5244,
      "marks": 5123,
      "snapshots": 0,
      "bytes": 145723,
      "sdk_version": "0.1.0"
    }
  ]
}
```

***

## Export traces

Convert local traces into an open format for DuckDB, pandas, Polars, or any OpenTelemetry backend (Jaeger, Tempo, Honeycomb, etc.).

```bash theme={null}
cirron traces export --format parquet --output ./exported/
cirron traces export --format parquet --session a541b565 --output ./one/
cirron traces export --format otel    --output ./traces.otlp.json
cirron traces export --format csv     --output ./spans.csv
cirron traces export --format json    --output ./traces.json
```

| Option            | Description                                                                                 |
| ----------------- | ------------------------------------------------------------------------------------------- |
| `--format <fmt>`  | **Required.** One of `parquet`, `otel` (alias: `otlp`), `csv`, `json`                       |
| `--output <path>` | Output path. Parquet → directory; other formats → file. Defaults to `./cirron-traces[.ext]` |
| `--session <id>`  | Export a single session (prefix match allowed). Default: all sessions                       |

### `--format parquet`

Writes three files into the output directory: `spans.parquet` (one row per span; columns mirror the platform `TraceSpan` model), `marks.parquet` (one row per mark), `snapshots.parquet` (stats inline; blobs remain on disk). All `*_ns` columns are `INT64`.

```bash theme={null}
duckdb -c "SELECT name, COUNT(*), AVG(duration_ns)/1e6 AS ms
           FROM 'exported/spans.parquet'
           GROUP BY name ORDER BY ms DESC"
```

Parquet writing uses `@dsnp/parquetjs` (pure JavaScript) so the CLI's `pkg`-built binaries work without native modules.

### `--format otel`

Emits a single [OTLP/JSON](https://github.com/open-telemetry/opentelemetry-proto/blob/main/docs/specification.md) file. **If a tool speaks OTLP, it can read this file.** No Cirron-specific reader required.

Works with Jaeger, Grafana Tempo, SigNoz, Uptrace, ClickStack, Honeycomb, Datadog, New Relic, Dynatrace, Splunk Observability, Lightstep, Elastic APM, Axiom, Azure Monitor, Google Cloud Trace, AWS X-Ray (via OTEL collector), `otelcol`, Grafana Alloy/Agent, Vector, and any OTLP/HTTP `/v1/traces` endpoint:

```bash theme={null}
cirron traces export --format otel --output traces.otlp.json
curl -X POST http://localhost:4318/v1/traces \
     -H 'Content-Type: application/json' \
     --data @traces.otlp.json
```

For backends requiring OTLP/gRPC, run the JSON through the OpenTelemetry Collector to transparently convert formats.

Encoding details: each session becomes one `resourceSpans[]` with `service.name=cirron`, `sdk.version`, and `cirron.session.id`. `traceId` (32 hex) is derived from the session id; `spanId`/`parentSpanId` (16 hex each) from Cirron span ids, derived, not just truncated, so non-hex ids stay spec-valid. `startTimeUnixNano`/`endTimeUnixNano` are string-encoded int64 per the OTLP spec. Attributes wrap as `AnyValue` with correct scalar promotion. Marks attach to their owning span as `events[]`. `Status.code` defaults `UNSET (0)`; becomes `ERROR (2)` if the span has an `error` attribute. `OK` is never set proactively (the profiler has no success/failure model).

### `--format csv`

Flat, spans-only CSV streamed to disk (bounded memory). Columns:

```
session_id, span_id, parent_id, name, index, start_ns, end_ns, duration_ns,
cpu_ns, gpu_ns, memory_peak_bytes, thread_id, pid, rank, attrs_json
```

Marks and snapshots are not in CSV. Use Parquet or JSON for full fidelity.

### `--format json`

Merged, deduplicated JSON in the same shape as a single spool batch:

```json theme={null}
{
  "schema_version": 1,
  "sdk_version": "0.1.0",
  "spans": [ ... ],
  "marks": [ ... ],
  "snapshots": [ ... ]
}
```

Dedupe is by `id` across spans, marks, and snapshots, safe to diff or re-import.

***

## Clear sessions

Delete sessions (batch files plus snapshot directories) with a confirmation prompt by default.

```bash theme={null}
cirron traces clear                         # all non-live sessions, with prompt
cirron traces clear --before 2026-04-20
cirron traces clear --keep 5
cirron traces clear --yes                   # skip confirmation
cirron traces clear --no-prune-orphans
```

| Option                | Description                                                                              |
| --------------------- | ---------------------------------------------------------------------------------------- |
| `--before <iso-date>` | Only sessions with `start_ns` before this ISO-8601 date are eligible                     |
| `--keep <n>`          | Retain the N most recent non-live sessions                                               |
| `--yes`               | Skip the confirmation prompt                                                             |
| `--no-prune-orphans`  | Don't sweep snapshot directories whose span id isn't referenced by any surviving session |

Deletes batch JSONs for eligible sessions, their `./.cirron/snapshots/<span_id>/` directories, and by default any orphan snapshot directories. Live sessions are never deleted by `--keep`; `--before` deletes them only if their start timestamp is already in the past cutoff.

Confirmation prompt:

```
? Delete 3 sessions (5,244 spans, 142.3 KB, 18 snapshot dirs)? (y/N)
```

Compare with `cirron spool clear` (file-level, deletes batch JSON only). Prefer `traces clear` for user-facing cleanup.

***

## List snapshots

Weight/gradient snapshots grouped by the span that produced them.

```bash theme={null}
cirron traces snapshots
cirron traces snapshots --session a541b565
cirron traces snapshots --span f9480382
cirron traces snapshots f9480382            # positional sugar for --span
cirron traces snapshots --json
```

```
Spool: /tmp/demo/.cirron/spool  (3 spans with snapshots)
┌───────────┬───────────┬──────────┬─────────┬───────┬───────────┐
│ SESSION   │ SPAN      │ NAME     │ TENSORS │ MODES │ WITH BLOB │
├───────────┼───────────┼──────────┼─────────┼───────┼───────────┤
│ 5da94f46… │ f9480382… │ epoch[0] │      12 │ full  │        12 │
│ 5da94f46… │ 5385ac83… │ epoch[1] │      12 │ full  │        12 │
│ 5da94f46… │ 912b0397… │ epoch[2] │      12 │ full  │        12 │
└───────────┴───────────┴──────────┴─────────┴───────┴───────────┘
```

* **TENSORS**: number of snapshot records (one per parameter and per `.grad` if gradients were captured).
* **MODES**: `stats` (inline only), `sampled` (stats + blob for some epochs), `full` (blob every epoch).
* **WITH BLOB**: records carrying a `blob_uri`.

***

## Inspect a snapshot

Deep-dive view: combines inline stats with the safetensors file header on disk, renders an ASCII histogram of tensor values, and optionally previews or exports raw tensor data.

```bash theme={null}
# Span summary: stats for every tensor + safetensors headers
cirron traces snapshot f9480382

# Focused tensor view
cirron traces snapshot f9480382 2.weight

# Preview first 10 and last 10 values
cirron traces snapshot f9480382 2.weight --preview 10 --tail 10

# Extract one tensor to a fresh single-tensor safetensors file
cirron traces snapshot f9480382 2.weight --export ./my-2weight.safetensors
cirron traces snapshot f9480382 2.weight --export ./out/

# Copy the whole span's blob(s)
cirron traces snapshot f9480382 --export ./out/
```

| Option            | Description                                                                                                                           |
| ----------------- | ------------------------------------------------------------------------------------------------------------------------------------- |
| `--file <path>`   | Read a specific safetensors file instead of auto-selecting `weights.safetensors` and `gradients.safetensors` under the span directory |
| `--preview <n>`   | Print first N values of the selected tensor (requires a tensor argument)                                                              |
| `--tail <n>`      | Print last N values                                                                                                                   |
| `--export <path>` | With tensor: write a single-tensor safetensors file. Without tensor: copy whole-span blobs                                            |
| `--json`          | Machine-readable output                                                                                                               |
| `--no-color`      | Disable ANSI color                                                                                                                    |

### Example: span summary

```
Span     f948038257e64867b6557edfd699a210
Name     epoch[0]
Session  5da94f46f9ef4db5bfc0a38511ae55b0
Records  12 snapshot records

┌───────────────┬───────┬─────────┬────────────┬──────────┬──────────┬──────┐
│ TENSOR        │ DTYPE │ SHAPE   │       MEAN │      STD │     NORM │ MODE │
├───────────────┼───────┼─────────┼────────────┼──────────┼──────────┼──────┤
│ 0.weight      │ F32   │ [32,16] │  -0.001464 │ 0.144752 │ 3.275535 │ full │
│ 2.weight      │ F32   │ [16,32] │  -0.006097 │ 0.102454 │ 2.322361 │ full │
│ ...           │       │         │            │          │          │      │
└───────────────┴───────┴─────────┴────────────┴──────────┴──────────┴──────┘

weights.safetensors  (6 tensors, 4.45 KB, file=4.85 KB)
gradients.safetensors  (6 tensors, 4.45 KB, file=4.85 KB)
```

### Example: focused tensor view

```
Tensor   2.weight  (F32, shape=[16,32], mode=full)
  mean = -0.006097
  std  = 0.102454
  min  = -0.176622
  max  = 0.175181
  norm = 2.322361

  histogram: ▇▇▇▆▇▇▆▅█▆▅▆▆█▅▇  [-1.77e-1 … 1.75e-1]  max_bucket=41

Blob     /tmp/demo/.cirron/snapshots/f948038.../weights.safetensors
  dtype=F32 shape=[16,32] bytes=2048
  first 10: [0.0179, 0.1631, -0.1491, 0.0919, ...]
  last  10: [-0.1036, 0.0101, -0.089, ...]
```

The histogram uses Unicode block characters (`▁▂▃▄▅▆▇█`) scaled to the max bucket count.

### Supported dtypes

Every standard safetensors dtype: `F64`, `F32`, `F16`, `BF16`, `I64`, `I32`, `I16`, `I8`, `U64`, `U32`, `U16`, `U8`, `BOOL`. `--preview` upcasts `F16`/`BF16` to `float32` for display; integer types print exact values (64-bit ints as BigInt).

### Export rules

**With a tensor name**: writes a fresh single-tensor safetensors file containing only the requested tensor. If `<path>` is an existing directory, the file is written as `<tensor_name>.safetensors` inside it (non-filename characters replaced with `_`).

**Without a tensor name**: copies the candidate safetensors blob(s) to the destination. File vs directory is auto-detected:

* If the path exists on disk, we use what's there (dir → dir, file → file).
* If the path ends in `.safetensors`, it's treated as a file.
* If there's exactly one blob and the path has no extension, it's treated as a file.
* Otherwise the path is treated as a directory.

Mismatches (e.g. pointing at a file path but two blobs exist) produce an error directing you to `--file` to narrow the selection.

***

## Examples

```bash theme={null}
# Terminal inspection after a training run
python train.py
cirron traces list
cirron traces view --last 1 --depth 3

# Compare two epochs' weight stats
cirron traces snapshots | head
cirron traces snapshot <span_id_epoch_0> 2.weight
cirron traces snapshot <span_id_epoch_5> 2.weight

# Export for DuckDB
cirron traces export --format parquet --output ./out/
duckdb -c "
  SELECT name, COUNT(*) AS n, AVG(duration_ns)/1e6 AS avg_ms
  FROM 'out/spans.parquet'
  GROUP BY name ORDER BY avg_ms DESC
"

# Ship to Jaeger via OTLP/HTTP
cirron traces export --format otel --output ./traces.otlp.json
curl -X POST -H 'Content-Type: application/json' \
     --data @traces.otlp.json \
     http://localhost:4318/v1/traces

# Air-gapped audit of a tensor
cirron traces snapshot <span_id> layer1.weight \
    --preview 16 --tail 16 \
    --export ./audit/layer1.weight.safetensors

# Clean up after a long training session
cirron traces clear --keep 3 --yes
```

## Troubleshooting

| Error                                                 | Resolution                                                                                                                   |
| ----------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------- |
| `No traces found`                                     | Call `ci.profile()` in your Python script before the work you want to trace, or point `--spool` at the right directory       |
| `No session found matching <prefix>`                  | Use `cirron traces list` for valid ids; prefix matching is substring-prefix, not fuzzy                                       |
| `Tensor "<name>" not found`                           | Run `cirron traces snapshot <span>` without a tensor name to list all tensors; copy names verbatim (`layer1.0.conv1.weight`) |
| `Destination is a single file but N blobs were found` | Point `--export` at a directory, or use `--file <path>` to pick one blob                                                     |
| `Invalid --before date`                               | Use ISO-8601: `2026-04-20`, `2026-04-20T12:00:00Z`                                                                           |
| Ctrl-C during clear                                   | Reports how many files/dirs were deleted before interruption; exit code `130`                                                |

## Related Commands

* [`cirron spool`](/cli/commands/spool): File-level view; required for `spool flush` to upload batches to the platform.
* [`cirron auth`](/cli/commands/auth): Authenticate before `spool flush` (not needed for any `traces` subcommand).
* [`cirron status`](/cli/commands/status): Check CLI + platform connectivity.
