The problem
Teams put agents into production and then find they cannot answer basic questions about them. Which agent spawned which, and why. Whether the skill that runs twice a day is worth what it costs. What last month actually consisted of. Claude Code emits a great deal of information about all of this, and almost none of it is retained anywhere a person can read it. The first sign of a problem is usually the invoice.
The constraint that made it interesting
The competition is free. Claude Code ships its own OpenTelemetry export; point it at a Grafana instance and you have cost, usage, and adoption dashboards for the price of an afternoon — with a cost figure more authoritative than anything you could reconstruct from the outside. Any part of Trenchcoat that merely re-displayed those numbers would be work a customer could avoid paying for.
So it could not be a better dashboard over the same facts. It had to answer the questions the emission surface does not express: which agent spawned which and why, what a skill activation cost end to end, whether one version of an agent did better than the last. That is structure rather than data, and it decided the architecture.
It also meant deliberately not building an operations monitor. Watching agents live is a different product with a different failure mode, and the questions actually worth answering — what did this cost, which agents earn their keep, what happened in here — are all asked afterwards. They reward a complete record over a fast one.
The approach
Two independent capture paths, deliberately. A Python plugin of Claude Code hooks captures what only a hook can see — the subagent spawn tree, the semantics of each edge in it, skills modelled as scopes with their own statistics. A second path receives Claude Code’s own telemetry over standard OTLP, which reaches the CLI’s internals a hook cannot. That path exports continuously, which matters for a reason that has nothing to do with watching it live: the hook path flushes at the end of a session, so an unclean exit takes the whole session with it. Two paths means a crash costs you part of the record instead of all of it.
One command sets up both. It writes the plugin’s API key and, as a side effect,
the five OTEL_* variables Claude
Code’s exporter needs, derived from that same key — so the part that would
otherwise be an afternoon with the documentation is a thing the user never configures.
It also refuses to overwrite an existing non-Trenchcoat OTLP endpoint. Quietly
hijacking somebody’s collector is not a feature.
Storage is Postgres on Supabase and the dashboard is Next.js. Capture is governed per attribute rather than per feed: a key carries a capture level, every attribute resolves to one, and anything unclassified is withheld rather than stored. That last rule is not fastidiousness. Over two days of ordinary use the observed attribute surface moved from 308 event-and-key pairs to 319, and from 144 distinct keys to 149. A governance model built on enumerating the keys you have already seen loses to the vendor shipping a release.
What the data says about agent spend
Agent cost is not where a rate card puts it. Price a corpus of sessions twice — once counting cache-creation and cache-read tokens, once ignoring them, which is what tokens × published rate quietly does — and the two totals separate by 57%. Across the sessions we priced, cache-aware came to $3,026.95 against cache-blind’s $1,302.87, and the whole gap was carried by 2.95 billion cache tokens concentrated in about a quarter of the sessions.
What makes that dangerous is how well it hides. The median gap between the two methods, per session, is 0.0%. Open any individual session and the two agree, so a spot check confirms whatever you already believed. The divergence lives only in the aggregate — daily spend, cost by model, cost per team member — which are the only views anyone opens to make a budget decision.
So Trenchcoat prices cache tokens everywhere, through a single pricing function every path calls and a guard query that fails the moment a new path stops calling it. One number, one derivation, one place to change when the rates move.
One thing that was harder than expected
Two capture paths sound like redundancy. They are not. They are two instruments pointed at the same event from different positions, and the real work is knowing when they agree.
Some facts line up exactly. Joining the paths on the tool-use identifier gives 6,578 paired tool calls, and on every one of them the hook path’s error flag and the telemetry path’s success flag are perfect inverses — 6,578 agreements, no exceptions. Two independently built pipelines, reaching the same answer that many times in a row, is the thing that makes a number worth putting in front of someone.
Others look like disagreements and are not. Both paths report how large a tool result was, and the two numbers differ almost everywhere — because they measure at different boundaries. One counts the whole serialized response object; the other counts only the result payload. For thin responses they track almost perfectly with a constant offset: file reads correlate at 0.9999, a median 42 bytes apart. For tools whose response carries extra structure, like edits and writes, they diverge completely, and correctly. Both are right, neither is the other’s check, and subtracting one from the other would produce a number that means nothing at all.
Which is why the artifact underneath the dashboard is a ledger rather than a schema: 192 facts, each traced to where it comes from, what has to be true for it to arrive, which column holds it, and who reads it. Instrumenting a system is not the same as being able to see it. Every number has a provenance, or it does not get shown.