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

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions