Skip to content

Telemetry

trkr records everything it does as OTel-shaped spans in local JSONL. Design principles:

  • Vendor-neutral — OTel-shaped data through a thin in-house API; no vendor SDK in core. Export is a future config concern, not an architectural one.
  • Local-first — spans append to .trkr/telemetry/spans.jsonl, one JSON object per line.
  • One trace per ticket turn — a ticket links its turns via attributes.
  • No sampling — head-sampling drops exactly the failures worth replaying. Keep everything.

Span vocabulary

ticket.turn                    root — ticket, turn, workflow, trigger
├── stage                      name, from/during/to columns, provider
│   ├── provider.session      command, prompt file, exit code, log path
│   └── verification          gate (review | ci | acceptance), verdict, findings count
└── gh.call                    gh operation, exit code — can parent anywhere

Events: column.moved, comment.posted, turn.kickout, turn.plateau (a stage's failing count didn't fall between turns — blocks the ticket early), ticket.blocked, pr.merged, and criterion (one per acceptance criterion or CI check, with pass=true|false).

A stage run that reaches its gate closes a verification span — gate review if the run produced a VERDICT: line (or CRITERION: lines it can infer a verdict from), else gate acceptance. (A checks-green stage parked waiting on pending CI closes its stage span with no verification.) The terminal stage records a second one at gate ci from the linked PR's check rollup, one criterion event per check.

Cross-process trace context

A ticket turn is one trace, but its stages run in separate processes. The root span's identity persists to .trkr/telemetry/context/<issue>.json (traceId, rootSpanId, turn, workflow, trigger, start time) so later stage processes reconstruct and eventually close it. Provider child processes get the context via TRKR_TRACEPARENT / TRKR_TICKET / TRKR_TURN env vars.

Failure replay

The trace is the replay manifest: stage, command, prompt file path, log pointer, findings. turn.kickout and ticket.blocked events carry the span id whose criterion events explain the failure — no separate capture mechanism.

Rotation & retention

Configured under telemetry in .trkr.json:

json
{ "telemetry": { "retain": "14d", "maxBytes": 10485760 } }

spans.jsonl rotates to a gzipped sibling when it exceeds maxBytes (default 10 MiB); rotated files older than retain (default 14d; accepts <n>d|h|m) are pruned.