Once you use Claude Code or Codex for real work, a total usage number stops being enough. You want to know which change consumed it. I did not build agent-cost because I had missed the existing token and cost trackers. I knew about multi-agent reporting CLIs, local dashboards, and OpenTelemetry-style observability stacks. I had even built a similar view in Notion before. The problem appeared when I tried to use that kind of reporting in an operational workflow. I needed agent logs to stay on the machine. I wanted a small runtime dependency surface, custom metrics I could audit, and a machine-readable result that another tool could consume. Most importantly, I needed session measurement and task attribution to remain two different claims. I did not need another universal dashboard. I needed a boundary underneath the dashboard that could answer: is this number supported well enough to enter task accounting? A measurement layer below the UI Different tools optimize for different jobs. A broad CLI such as ccusage is useful when coverage across agents matters. Local interfaces such as token-tracker or AgentMeter are a better fit for visual exploration of projects, sessions, subagents, and tools. An OpenTelemetry stack is the natural choice for fleet-level metrics, logs, and traces. Those are not inferior versions of agent-cost . They serve different use cases and trust models. The layer I wanted looked like this: local observations -> auditable normalized facts -> explicit pricing status -> caller-selected sessions -> task-attribution policy -> optional dashboard / Notion / spec-lane agent-cost reads logs that Claude Code and Codex CLI have already written locally. It normalizes each usage event into a fact with a model, token kind, timestamp, and count. At runtime it makes no network calls and declares no Python runtime dependencies. Its price catalog has a version and SHA-256 digest, both carried into machine-readable output. That “zero-network” claim is deliberately limited to runtime behavior. Installing from PyPI still means trusting a registry, installer, build backend, Python runtime, and operating system. The tool also needs access to the source logs. The design narrows runtime data egress and dependency surface; it does not make the supply chain disappear. A session is observable. A task is another claim. There is an attractive shortcut when building task-level cost reports: Measure usage in a time window. Find the issues or branches active during that window. Apportion the total by working time or commit count. This always produces numbers that add up. But the agreement comes from the allocation rule, not from an observation. One session can cover several tasks. One task can span several sessions. A branch can stay unchanged while the operator investigates a different issue or reviews someone else's work. Elapsed time does not describe the computational weight of prompts and tool calls. The invariant I wanted was: Session usage is observable. Session-to-task attribution is a separate claim. agent-cost measure accepts only session IDs selected by its caller: agent-cost measure \ --session-id \ --session-id \ --format json It does not infer a task from a branch, pull request, or timestamp. A workflow that already owns the task-to-session binding passes the corresponding session set. For example, the spec-lane adapter invokes agent-cost as a subprocess and checks the JSON, the measure/v1 protocol version, the schema, and forbidden personal dimensions. agent-cost does not learn what the task is. The caller that knows the task selects the sessions. If a session crosses tasks and there is no defensible way to split it, I would rather leave that usage unattributed than manufacture a precise-looking allocation. Unknown is pending evidence, not zero. The same rule applies to prices agent-cost carries uncertainty instead of smoothing it away. An unknown model is unpriced . A Claude cache write without a TTL breakdown is priced at the cheaper five-minute rate and labeled lower_bound . Codex logs do not expose cache-write tokens, so the tool does not invent a zero-valued cache-write row. Malformed events, unreadable files, and decreasing cumulative counters remain visible in data_quality . “Fail closed” does not mean every imperfect input crashes the command. It means unsupported pricing or attribution does not quietly become a confirmed value downstream. On August 23, 2026, I reran the published coding-agent-cost 0.1.0 package in temporary uvx directories. Its doctor command found the local sources and loaded catalog version 2026-07-29 . The explicit unknown-model path still rejected a made-up model: $ uvx --refresh --from coding-agent-cost \ agent-cost rates show --model model-not-in-catalog [unpriced] no rate entry for 'model-not-in-catalog' A numeric zero next to an unpriced row is not a claim that the usage was free. Consumers must inspect pricing_status and unpriced_tokens , then choose a policy: exclude the value from a headline, stop the workflow, or supply a verified catalog. The output field is estimated_cost_usd , not a bill. Allowances, contracts, credits, and batch usage are not fully recoverable from local logs. The number is a list-price estimate attached to observed tokens. What the smaller boundary buys There are intentional limits. agent-cost alone will not label a session as belonging to an issue. Local execution does not remove installation-time supply-chain risk or the need to trust local log access. In exchange, each layer has a narrower claim: Local logs support session usage facts. A versioned catalog supports an estimated price. Unsupported prices remain unpriced or lower_bound . The caller owns task binding as separate evidence. Ambiguous usage is not silently apportioned just to complete a total. This is not an argument against dashboards. Use a dashboard when visual exploration is the job. Use OpenTelemetry when fleet observability is the job. Use a small accounting primitive when you need a subprocess contract and want measurement to remain separate from attribution policy. Keeping an unknown visible is not a failure to measure. It is how the next layer avoids false confidence. Start with the 60-second path in agent-cost . If you also need a workflow to own task attribution, see spec-lane .