Vetrix Docs

Multi-Agent Token Tracking

This document describes how the Manager rolls up per-agent Claude API token usage into a per-rc report and per-ticket comment block.

Why

Each rc run dispatches many sub-agents (Dev × N tickets, Backend CR, Frontend CR, Backend QA, Frontend QA — see §5–§10 of multi-agent-operating-instructions.md). Each agent consumes input + output + cache tokens. To keep operating cost visible across runs we need a deterministic post-run roll-up that attributes tokens to the ticket and stage that produced them.

Data source

Claude Code writes one JSONL session transcript per agent invocation, under ~/.claude/projects/<workdir-slug>/<session-uuid>.jsonl. The slug is derived from the agent's working directory (e.g. /home/rsmith/Development/vetrix-home-rsmith-Development-vetrix).

Each line is a JSON object. Lines with type == "assistant" carry a message.usage object containing input_tokens, output_tokens, cache_read_input_tokens, cache_creation_input_tokens (and a per-iteration breakdown). The first non-meta type == "user" line in a session is the agent's initial prompt.

The aggregator does not call any external API and does not require network access. It only reads local files.

Attribution model

The aggregator groups records by sessionId and inspects each session's first user message to derive:

  • Ticket — first match of Ticket ID: TCK-NNNN (preferred), else first inline TCK-NNNN reference.
  • Stage — phrase match against the agent's role label, in priority order: Backend Code ReviewerBackendCR, Frontend Code ReviewerFrontendCR, Backend QA AgentBackendQA, Frontend QA AgentFrontendQA, Dev AgentDev, ManagerManager. Anything else falls through to Unknown.

Per-stage token counters are accumulated over every assistant turn in the session.

Invocation

python3 scripts/multi-agent/aggregate-tokens.py \
    --jsonl-dir ~/.claude/projects/-home-rsmith-Development-vetrix \
    --rc-id 2026-05-05-account-transfer-exposure \
    --output-dir agent-work/manager-rc-account-transfer-exposure/reports

Flags:

  • --jsonl-dir — Directory of *.jsonl transcripts. Defaults to ~/.claude/projects/<cwd-slug>/.
  • --rc-id — Used in output filenames (<rc-id>-token-report.csv / .md).
  • --output-dir — Where the CSV + Markdown report are written. Created if missing.
  • --strict — Treat any session that lacks message.usage blocks as an error instead of warn-and-continue. Off by default.

Exit codes:

Code Meaning
0 Report produced successfully. Warnings (missing usage blocks, etc.) tolerated.
1 Fatal error: malformed JSONL, output dir unwritable, or --strict and warnings present.
2 --jsonl-dir is missing or contains no *.jsonl files.

Output

CSV

<output-dir>/<rc-id>-token-report.csv. One row per agent dispatch (session):

ticket,stage,input_tokens,output_tokens,cache_read_tokens,cache_creation_tokens,total_tokens,tool_uses,duration_ms
TCK-1234,Dev,115,85,1800,2000,4000,1,120000
TCK-2000,Dev,200,100,3000,4000,7300,0,300000
TCK-2000,BackendCR,50,25,1500,500,2075,0,120000
TCK-2000,BackendQA,80,40,2000,1000,3120,0,300000

Markdown

<output-dir>/<rc-id>-token-report.md. Same data, plus per-stage, per-ticket, and rc grand-total tables. Suitable for pasting into a PR comment.

Example excerpt:

## Per-stage totals

| Stage | Input | Output | Cache Read | Cache Create | Total | Tool Uses |
|-------|------:|-------:|-----------:|-------------:|------:|----------:|
| BackendCR | 50 | 25 | 1500 | 500 | 2075 | 0 |
| BackendQA | 80 | 40 | 2000 | 1000 | 3120 | 0 |
| Dev | 315 | 185 | 4800 | 6000 | 11300 | 1 |

## rc grand total

| Input | Output | Cache Read | Cache Create | Total | Tool Uses |
|------:|-------:|-----------:|-------------:|------:|----------:|
| 445 | 250 | 8300 | 7500 | 16495 | 1 |

How the Manager invokes it

multi-agent-operating-instructions.md §5.6 step 6.5 wires the aggregator into the rc-promotion sequence. After step 6 (tracker rollups) the Manager:

  1. Runs aggregate-tokens.py --rc-id <run-id> --output-dir <reports-dir>.
  2. Splits the resulting Markdown by ticket and posts a per-ticket Token roll-up block as part of the per-ticket merge summary comment.
  3. Posts the rc grand-total table as a comment on the rc → develop PR.

Live capture (complementary, optional)

The aggregator runs after the rc completes. For live progress visibility during the run the Manager may also inline-capture each <task-notification><usage> block emitted on agent return. That capture is informational and does not replace the post-run aggregator — only the aggregator produces the canonical artifact.

Handling corrupt / partial sessions

  • A session with no message.usage blocks (e.g., agent crashed before any assistant turn completed) emits a zero-token row and a warning to stderr. Use --strict to upgrade these warnings to a non-zero exit.
  • A *.jsonl file with malformed JSON aborts that file's processing and exits 1. Manager should investigate the offending file before re-running.
  • Unknown stages (prompt did not match any role phrase) appear as Unknown in the output. These are usually one-off CLI sessions and can be ignored or filtered by the operator.

Source files

  • Script: scripts/multi-agent/aggregate-tokens.py
  • Tests: scripts/multi-agent/aggregate_tokens_test.py
  • Test fixtures: scripts/multi-agent/testdata/

Run the tests with:

python3 -m unittest scripts/multi-agent/aggregate_tokens_test.py