Skip to content

traces get output is unbounded — no span cap, depth limit, error filter, or field selection #57

Description

@dark-sorceror

Summary

traceroot traces get takes exactly one input — the trace id — and offers no way to bound what comes back. Under --json the entire trace, every span included, is serialized as a single line (src/commands/traces/get.ts:51, one-line-per-document by design in src/output.ts:53-55). The human tree renderer recursively prints every span with no depth or count cap (src/render/tree.ts:81-105). There is no --max-spans, --depth, --errors-only, or field selection.

For a small trace this is fine. For a several-thousand-span agent trace it is a multi-megabyte single line — a context bomb for the very consumer the CLI targets: a coding agent piping traces get --json into its own context. And it gets strictly worse the moment span input/output becomes fetchable through get (the backend already supports a fields projection): today's unbounded output is span skeletons; tomorrow's is skeletons plus every prompt and completion in the trace.

Current behavior

  1. traceroot traces get <id> --json → one line containing the full trace document. Output size is linear in span count with no ceiling.
  2. Human mode renders the complete span tree; deep or wide traces flood the terminal.
  3. There is no way to ask for "just the error spans", "the top 2 levels", "at most 50 spans", or "only these fields" — the choices are everything, or the separate traces export disk round-trip.
  4. Because the JSON is one physical line, line-oriented tooling (grep, head) cannot subset it; the only recourse is jq over the whole document, which materializes all of it anyway.

Proposal

  • --max-spans <n> — cap emitted spans, with an explicit truncation marker and the true total count.
  • --depth <n> — cap tree depth in both human and JSON output.
  • --errors-only — only error-status spans plus their ancestor chain (the top debugging query).
  • Field selection — pass through the backend's fields groups so payload size is opt-in.
  • --output jsonl — one span per line (trace header first), making output streamable and greppable: traceroot traces get <id> --output jsonl | grep '"status":"error"' pays only for the spans that matter.

Impact

  • A single traces get --json on a large trace can consume a huge share of an agent's context window (or exceed a tool-output cap) for a task that needed three error spans.
  • Deep traces scroll the useful root/error information out of a human's view with no recourse.
  • Bounding is a prerequisite for shipping span I/O through get — without it, that needed feature becomes a bigger context bomb.
  • Single-line JSON defeats the shell idioms scripts reach for first; jsonl closes that cheaply.

Acceptance criteria

  • traces get supports at least a span-count cap, an errors-only filter, and field selection; truncated output explicitly marks truncation and reports the true total.
  • --output jsonl emits one span per line with a documented first-line trace header; piping to head stays EPIPE-clean and yields valid JSON per line.
  • Human tree output respects --depth/--max-spans and indicates elision (e.g. … 4,873 more spans).
  • Defaults are backward-compatible (no flags → current full output); flags documented in --help with an example.
  • Contract tests cover truncation markers and jsonl validity.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions