bar chart
Rows matched for one completed tool call
Illustrative logic, not a measurement: assumes each call emits one decision event and one result event.
Counts the decision and the result.
Counts only the event that carries the outcome.
Flow
The four spots I changed
Listed in the order data flows, not the order I would promise to deploy them.
1Normalizer
Writes the flag as the word true or false, as text.
2View
Takes only rows the normalizer named. Reads the agent's own flag first.
3Queries
Ask for result rows or decision rows by name. Lowercase the flag. Return percents.
4Charts
Only round and show the percent. No more multiplying.
The tool charts on my coding-agent dashboard counted the wrong rows, and the success rate looked for a word that never showed up.
My agents each report tool use in their own way. A normalizer turns all of it into one shared set of names before it hits the database. A permission check and a tool result both get the same name. The original name is kept in its own column. My queries never looked at it.
So the "calls per tool" count took every row with the shared name. One call could show up twice: once for the check, once for the result. I think the counts were too high, but I never measured it. Success rates were off too, since a check has no success value and counted as a fail.
Then the success flag. The normalizer wrote it as a true-or-false value. The database keeps these as text, so it landed as a digit. My query looked for the word true, and a digit never matches. The view also read that broken copy before the agent's own copy. No error showed up. Just a wrong number that looked fine.
Each tool query now says which kind of row it wants:
WHERE source_kind = 'result' AND lower(ok) = 'true'
The normalizer now writes the word true or false as text. The view reads the agent's own flag first. And every check lowercases the value, so "True" and "true" count the same.
I also gave the percent sign one home. Rates used to come back as fractions and get multiplied by a hundred in a few places. Now the query returns the percent and the charts just show it.
There are no tests for any of this yet. Any flag that isn't spelled true counts as false, so an agent that says yes gets marked as a fail. An old row that only has the digit would still read as a fail too, but I haven't checked whether any agent ever wrote one like that.
The view used to let in raw names as a backup. Now it only takes rows the normalizer named. That's tidier, but an event the normalizer misses now just disappears.