# I Could Measure Claude and Codex Usage. I Still Couldn't Honestly Assign It to a Task.

> Source: <https://dev.to/shikiyusuke/i-could-measure-claude-and-codex-usage-i-still-couldnt-honestly-assign-it-to-a-task-2ghj>
> Published: 2026-08-22 21:20:35+00:00

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?

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:

``` php
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.

There is an attractive shortcut when building task-level cost reports:

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-a> \
  --session-id <session-b> \
  --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.

`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:

``` bash
$ 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.

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:

`unpriced`

or `lower_bound`

.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](https://github.com/shiki-yusuke/agent-cost). If you also need a workflow to own task attribution, see

`spec-lane`
