Skip to main content

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.

cirron traces

Read and export the local trace data the Cirron Python SDK produces when ci.profile() runs. Where cirron 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 under cirron traces runs entirely against the local spool (./.cirron/spool/) and snapshot directory (./.cirron/snapshots/<span_id>/). No network is required; no platform authentication is involved. This is the “no lock-in” surface — your traces are yours, in open formats, on your disk.

Subcommands

Usage

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

OptionDescriptionDefault
--spool <dir>Override the spool directory./.cirron/spool
--jsonEmit machine-readable JSON (supported on view, list, snapshots)
--no-colorDisable ANSI color output (supported on view, snapshot)color when stdout is a TTY and NO_COLOR is unset

Sessions, Spans, Marks, Snapshots

Before diving into subcommands, a quick vocabulary:
  • Session — One ci.profile() lifetime in a single process. Identified by the root cirron.session span. Typically one session per training run.
  • Span — A timed scope: epoch, step, forward pass, optimizer step, etc. Spans nest via parent_id, forming the scope tree.
  • Mark — A named scalar value (ci.mark("loss", 0.5)) attached to the innermost open span.
  • Snapshot — A per-tensor weight or gradient record captured at an epoch boundary. Stats (mean/std/min/max/norm/histogram) live inline in the spool JSON; for sampled and full modes, the tensor values themselves are stored as safetensors files under ./.cirron/snapshots/<span_id>/.
cirron traces reconstructs sessions by deduplicating spans across batches (preferring the record with end_ns set when a span spans two flush intervals) and walking parent pointers from every cirron.session root.

View the scope tree

Render one or more sessions as an indented text flamegraph. This is the primary “what happened?” view.
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 --spool ./other/.cirron/spool
cirron traces view --no-color
cirron traces view --json

Options

OptionDescription
--last <n>Show the N newest sessions (default: 1)
--name <substr>Keep only spans whose name contains the substring — ancestors are preserved so the tree stays readable
--session <id>Show a specific session. Prefix match allowed
--depth <n>Collapse the tree below depth N. Collapsed subtrees show an aggregate duration and span count
--min-wall <dur>Hide spans shorter than the given duration. Accepts 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 are rendered as indented ci.mark <name>=<value> lines under the span they attach to. point marks (the default) render as-is; summary marks carry a (summary) suffix.

Collapsed output

With --depth 1:
cirron.session — 561.7ms pid=1797 rank=0
  epoch[0] — 42.1ms
    … 33.6ms (4 spans, collapsed)
  epoch[1] — 38.9ms
    … 31.2ms (4 spans, collapsed)
The collapsed duration sums only descendants, so it’s always ≤ the parent’s own wall time.

Filtered output

With --name transform:
cirron.session — 7.3ms pid=96540 rank=0
  fit — 6.2ms estimator=Pipeline
    fit_transform — 803.1us estimator=StandardScaler
  predict — 191.0us estimator=Pipeline
    transform — 66.8us estimator=StandardScaler

Empty spool

No traces found. Run ci.profile() from Python to produce some.

List sessions

Table of every session found in the spool, newest first.
cirron traces list
cirron traces list --spool ./other/.cirron/spool
cirron traces list --json

Example output

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 — those whose root cirron.session has no end_ns yet because the SDK is still running or the process exited mid-flush — show (live) instead of a duration and are protected from destructive clear --keep operations.

JSON output

{
  "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 so you can analyze them in DuckDB, pandas, Polars, or ship them to an OpenTelemetry backend like Jaeger, Tempo, or Honeycomb.
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

Options

OptionDescription
--format <fmt>Required. One of parquet, otel (alias: otlp), csv, json
--output <path>Output path. For Parquet this is a directory; for other formats it’s a file. Defaults to ./cirron-traces[.ext]
--session <id>Export a single session by id (prefix match allowed). Without this flag, all sessions are exported together

--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 — one row per snapshot record (stats inline; blobs remain on disk)
All *_ns columns are INT64. Use DuckDB or pandas to query:
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 file in the OTLP/JSON wire format. If a tool speaks OTLP, it can read this file. No Cirron-specific reader, schema, or plugin is required on the receiving side.

Where it works

Anything on the OpenTelemetry traces receive-side. A non-exhaustive list:
CategoryExamples
Open-source backendsJaeger, Grafana Tempo, SigNoz, Uptrace, ClickStack
Commercial backendsHoneycomb, Datadog, New Relic, Dynatrace, Splunk Observability, Lightstep, Elastic APM, Axiom
Cloud-nativeAzure Monitor, Google Cloud Trace, AWS X-Ray (via OTEL collector)
Collectors / proxiesotelcol (OpenTelemetry Collector — core and contrib), Grafana Alloy, Grafana Agent, Vector

How to ship it

The simplest path is an HTTP POST to any OTLP/HTTP /v1/traces endpoint:
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
That’s the OTLP/HTTP contract verbatim — no Cirron-side involvement. For backends that require OTLP/gRPC (Protobuf), run the JSON through the OpenTelemetry Collector, which transparently converts formats:
# otelcol config.yaml
receivers:
  otlp:
    protocols:
      http: {}
exporters:
  otlp:
    endpoint: your-backend.example.com:4317
service:
  pipelines:
    traces:
      receivers: [otlp]
      exporters: [otlp]

Encoding details

  • Each session becomes one resourceSpans[] entry with service.name=cirron, sdk.version, and cirron.session.id on the Resource.attributes.
  • Each span becomes an OTLP span. traceId is derived from the session id (32 hex chars), spanId/parentSpanId from Cirron span ids (16 hex chars each) — derived, not just truncated, so non-hex ids (the SDK uses UUIDs) still produce spec-valid fields.
  • startTimeUnixNano and endTimeUnixNano are string-encoded int64, per the OTLP spec’s 64-bit integer rule. Values are Unix-epoch nanoseconds from the SDK’s time.time_ns().
  • Attributes are wrapped as AnyValue (stringValue / intValue / doubleValue / boolValue / arrayValue / kvlistValue) with correct scalar promotion.
  • Marks attach to their owning span as events[] with timeUnixNano, name, attributes.value, and attributes.kind (point or summary).
  • Status.code defaults to UNSET (0); becomes ERROR (2) when a span carries an error attribute. We do not set OK (1) proactively — the profiler has no success/failure model, and marking everything OK would paint spans artificially green in UIs like Jaeger.

--format csv

Flat, spans-only CSV streamed to disk (memory usage stays bounded even on million-span spools). One row per span; 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 aren’t in the CSV — use Parquet or JSON if you need full fidelity.

--format json

Merged, deduplicated JSON in the same shape as a single spool batch:
{
  "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 their snapshot directories) with a confirmation prompt by default.
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

Options

OptionDescription
--before <iso-date>Only sessions whose start_ns is before this ISO-8601 date are eligible
--keep <n>Retain the N most recent non-live sessions
--yesSkip the confirmation prompt (for CI/scripting)
--no-prune-orphansDo not sweep snapshot directories whose span id isn’t referenced by any surviving session

What gets deleted

  1. Every batch JSON under the spool that belongs to an eligible session.
  2. Every ./.cirron/snapshots/<span_id>/ directory for spans in those sessions.
  3. By default, any orphan snapshot directory whose span id isn’t referenced by any surviving session (common if cirron spool clear was used previously and left snapshots dangling).
Live sessions are never deleted by --keep; they only participate in --before deletion 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)

Behavior vs. cirron spool clear

cirron spool clear is file-level: it deletes every batch JSON under the spool directory and stops there. cirron traces clear is session-level: it deletes batch JSONs and the matching snapshot directories, honors live sessions, and can scope by date or count. Both coexist; prefer traces clear for user-facing cleanup.

List snapshots

Table of weight/gradient snapshots grouped by the span that produced them. Useful when you want to know which epochs have recorded weights.
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

Example output

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, if gradients were captured, per parameter’s .grad).
  • MODESstats (inline stats only), sampled (stats + blob for a subset of epochs), full (blob every epoch).
  • WITH BLOB — number of records that carry a blob_uri (i.e., have a safetensors payload on disk or on the platform).

Inspect a snapshot

The 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.
# Span summary: stats table for every tensor + safetensors headers
cirron traces snapshot f9480382

# Focused view on one tensor
cirron traces snapshot f9480382 2.weight

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

# Extract just this 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) to disk (no tensor arg)
cirron traces snapshot f9480382 --export ./out/

Options

OptionDescription
--file <path>Read a specific safetensors file instead of auto-selecting weights.safetensors and gradients.safetensors under the span directory
--preview <n>Print the first N values from the selected tensor (requires a tensor argument)
--tail <n>Print the last N values from the selected tensor
--export <path>With a tensor: write a single-tensor safetensors file. Without a tensor: copy the whole-span blobs. See Export rules
--jsonMachine-readable output
--no-colorDisable ANSI color (consistent with cirron traces view)

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 │
│ 0.bias        │ F32   │ [32]    │   0.022988 │ 0.151547 │ 0.867083 │ full │
│ 2.weight      │ F32   │ [16,32] │  -0.006097 │ 0.102454 │ 2.322361 │ full │
│ 0.weight.grad │ F32   │ [32,16] │ -2.0163e-4 │ 0.011099 │ 0.251187 │ full │
│ ...           │       │         │            │          │          │      │
└───────────────┴───────┴─────────┴────────────┴──────────┴──────────┴──────┘

weights.safetensors  (6 tensors, 4.45 KB, file=4.85 KB)
┌──────────┬───────┬─────────┬─────────┐
│ NAME     │ DTYPE │ SHAPE   │   BYTES │
├──────────┼───────┼─────────┼─────────┤
│ 0.weight │ F32   │ [32,16] │ 2.00 KB │
│ 2.weight │ F32   │ [16,32] │ 2.00 KB │
│ 4.weight │ F32   │ [4,16]  │   256 B │
│ 0.bias   │ F32   │ [32]    │   128 B │
│ 2.bias   │ F32   │ [16]    │    64 B │
│ 4.bias   │ F32   │ [4]     │    16 B │
└──────────┴───────┴─────────┴─────────┘

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

Example: focused tensor view

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

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, -0.0841, -0.1328, -0.16, -0.111, -0.1585, -0.1334]
  last  10: [-0.1036, 0.0101, -0.089, -0.1747, 0.1161, -0.0149, -0.0135, -7.113e-4, 0.1747, -0.0621]
The histogram uses Unicode block characters (▁▂▃▄▅▆▇█) scaled to the max bucket count. Range is the bin edges reported by the SDK.

Supported dtypes

The safetensors reader understands 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

When --export is supplied: 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 a clear error directing you at --file to narrow the selection.

How It Works

  • Spool format. Batch files under .cirron/spool/ use the name pattern <created_ns>-<batch_id>.json. The CLI sorts by the nanosecond prefix and streams each file through a parser that tolerates unknown fields (spec schema_version: 1 forward-compat rule).
  • Session reconstruction. For each batch, spans are deduped by id; the record with end_ns set wins so a span that straddled a flush boundary appears as a single closed span. Marks and snapshots dedupe by id, last-write-wins (the SDK may re-emit on flush retries).
  • Session roots. A session is rooted at every span named cirron.session with parent_id === null. Every other span is attached to a session by walking parent_id up. Orphan spans (parent id not resolvable) are bucketed into a synthetic (orphan spans) session only when no real sessions exist — so the common case stays clean.
  • Snapshot layout. Safetensors live under .cirron/snapshots/<span_id>/{weights,gradients}.safetensors. The tensor name from the snapshot record is used verbatim as the key inside the safetensors container.
  • Forward compatibility. Unknown top-level and per-span fields are ignored; known fields outside their documented type (e.g. start_ns as a string instead of a number) are coerced through BigInt safely. This means a newer SDK can add metadata without breaking an older CLI.

Error Handling

No traces found

No traces found. Run ci.profile() from Python to produce some.
Solution: Ensure your training or inference script calls ci.profile() before the work you want to trace, or point --spool at the directory where the spool actually lives.

Session not found

Error: No session found matching <prefix>
Solution: Check cirron traces list for valid session ids. Prefix matching is substring-prefix, not fuzzy.

Tensor not found

Error: Tensor "<name>" not found in <file>. Available: <names>
Solution: Run cirron traces snapshot <span> without a tensor name to see the full list. Copy the tensor name verbatim — they commonly contain dots (layer1.0.conv1.weight).

Export path ambiguity

Error: Destination <path> is a single file but N blobs were found (weights, gradients).
Pass a directory, or narrow the selection with --file.
Solution: Either point --export at a directory, or pass --file <path> to pick one blob.

Empty --before / bad ISO date

Error: Invalid --before date: <value> (expected ISO-8601)
Solution: Use ISO-8601: 2026-04-20, 2026-04-20T12:00:00Z, etc.

Interrupted clear

If you hit Ctrl-C mid-clear, the command stops deleting as soon as the signal arrives and reports how many files/dirs made it to disk before interruption. Exit code is 130.

Examples

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 analysis

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 traces to Jaeger

cirron traces export --format otel --output ./traces.otlp.json

# If your collector exposes the OTLP HTTP ingest endpoint:
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 list
cirron traces clear --keep 3 --yes
  • cirron spool — File-level view; required for uploading batches to the platform with spool flush.
  • cirron auth — Authenticate before a spool flush (not needed for any traces subcommand).
  • cirron status — Check CLI + platform connectivity.