feat(models): agent x model cross-breakdown (--by-agent)#710
Conversation
Orchestrator setups pin different agents to different models (planner on Opus, implementers on Sonnet, scouts on Haiku). The dashboard shows "Skills & Agents" and "By Model" as separate panels, so there was no way to see which agent drove which model's spend, or catch an agent that silently switched models. `codeburn models --by-agent` closes that gap: one row per (provider, model, agent), mirroring the existing --by-task pattern across the table, markdown, CSV, and JSON renderers. The agent label comes from each Claude subagent transcript session's own agentType crossed with that session's model calls, aggregated through the same assistantCalls path --by-task uses (not modelBreakdown), so cache-tier dedup, savings, and Codex-credit logic all carry over unchanged. This is the authoritative pairing; main-session subagentTypes spawn markers are deliberately not joined to models, since a spawn marker names an agent but not the model that agent actually ran on. Ordinary main sessions (no agentType) and every non-Claude provider have no agent, so their spend buckets under the label "main" rather than being dropped or fabricated into a false agent. --by-agent is mutually exclusive with --by-task; passing both errors politely. Exposing subagentType + per-record model on `codeburn export` (so users can build custom crosses over the export) is a separate follow-up and is intentionally out of scope here.
ozymandiashh
left a comment
There was a problem hiding this comment.
Reviewed against a parallel implementation of the same issue that went through a three-model adversarial panel today, so these findings are battle-tested rather than speculative. The core design matches what we converged on independently: session-level attribution from the subagent transcript's own agentType, spawn markers deliberately unused (crossing per-call subagentTypes would bill the parent's Task-invocation tokens to the child agent). The generic third-column renderer is a nicer shape than mirroring the task path. Three findings:
-
The bare
mainsentinel collides with a real agent named "main". agentType is free-form text from the meta file, not an enum: a user-defined subagent literally namedmainmerges silently into the main-session bucket and the attribution the feature exists for is corrupted for exactly that agent. A non-slug sentinel (we used(main)with parentheses) cannot collide; worth a test pinning that a subagent namedmainstays distinct. -
The inherited
--min-cost 0.01default quietly hides sub-cent agent buckets. At task granularity that default is invisible; at agent granularity sub-cent buckets are the norm (scouts, one-shot helpers), so agents disappear from the audit without a trace. One README sentence recommending--min-cost 0for a complete agent inventory saves the support question. -
For the deferred export follow-up: when subagentType/model land on sessions.csv, append them AFTER all legacy columns — inserting mid-row shifts positions for by-index CSV consumers. We have the export half already built and panel-reviewed (records.csv with one row per call, sessions.csv with appended fields, formula-injection-safe); happy to open it as the follow-up PR so it composes with this one.
…t follow-ups (#712) Follow-ups to #710, completing issue #708's second ask plus the two review notes: - export: new records.csv with one row per call incl. subagentType and model; sessions.csv gains both fields APPENDED after all legacy columns so by-index consumers are unaffected; JSON fields additive; formula- injection escaping preserved - models --by-agent: the main-session bucket sentinel becomes '(main)' - agentType is free-form text, so a real subagent literally named 'main' no longer merges into the main bucket (test-pinned across all formats) - README: note that the --min-cost 0.01 default can hide sub-cent agent buckets; --min-cost 0 shows the complete agent inventory
Problem
The dashboard shows Skills and Agents and By Model as separate panels, so orchestrator setups with agents pinned to different models cannot see which agent drives which model's spend. Closes #708.
Fix
codeburn models --by-agent: one row per (provider, model, agent), mirroring --by-task exactly across the flag, aggregation, and all four output formats (table, markdown, csv, json). The agent dimension comes from each subagent session's own agentType joined with its calls, which is the authoritative pairing; spawn markers on main sessions are deliberately not used. Ordinary main sessions and non-Claude providers bucket under main. --by-task and --by-agent are mutually exclusive with a clear error. The renderers gained a generic third-column concept rather than a copy of the task path.
Real output from a machine with a heavy orchestrator day (the exact question from the issue, answered):
The issue's secondary ask, exposing subagentType on per-record exports, is left for a follow-up to keep this diff on one surface.
Verification