Use case · Mapping tokens to outcomes
Your agents burn tokens. Some of it ships. The rest, you can't see.
Coding agents cost real money. Claude Code, Codex, Cursor, and Gemini all charge by the token, and none of them tell you what the money bought. Cardinal ties every session to a branch, a pull request, and an initiative, then classifies the spend by what actually happened to the work. Any row you cite, you can click into for the evidence.
The join, in one line
tokens
62.4M
in the plugin's OTel stream
session
01JD…K4QF
Claude · $14.20
branch
feat/outcomes-observability
parsed on ingest
PR
#421
reconciled from GitHub
outcome
merged
predicate + reason code
01 · The outcome accounting
Four buckets. Every dollar lands in exactly one.
The top of the dashboard is a scorecard. Total spend splits four ways: MERGED, IN-FLIGHT, LOST, and AD-HOC. Below that, a by-type strip breaks the same total down into Feature, Bugfix, Refactor, Infra, and Research. The spend-over-time curve flags days beyond +2σ so unusual spikes are easy to find, and the activity heatmap surfaces off-hours bursts and single hot cells. Every number traces back to the session rows further down through the same primitives. Nothing is smoothed or estimated.
- · MERGED: PR merged from the branch this session touched
- · IN-FLIGHT: PR open, work still moving
- · LOST: PR closed unmerged, stale, or superseded
- · AD-HOC: research or investigation with no PR expected
Overview · four buckets, spend curve, activity heatmap
Click to enlarge · esc to close
02 · The session ledger
Every session is a row. Every row carries its branch.
A small plugin in each engineer's agent streams OpenTelemetry to Cardinal after every tool call. Each row carries the session id, tokens, tool count, duration, and cost. A local git hook adds the branch and head SHA the work was done on, so we know which piece of work each session belongs to. Sessions land with the effort tier they ran at (HIGH or XHIGH), the model they used, and the skill they invoked. Sessions that sprawled across unrelated work get a DRIFT flag with the dollars it cost.
- · Runs in Claude Code, Codex CLI, Cursor, and Gemini CLI
- · git_state event fires post-tool, so nothing blocks the user
- · Cost priced from the emitter's own usage records, not estimates
- · Effort, model, skill, and drift signal are all attributes on the row
Sessions · state, model, skill, effort per row
Click to enlarge · esc to close
03 · Branches become initiatives
One branch, one initiative. Related PRs collapse.
Cardinal parses the branch name (feat/, fix/, refactor/, infra/, research/, spike/) and uses it to name and type the initiative. Every session on that branch aggregates into it, and sibling PRs from linked branches collapse into the same initiative. So the backend PR and the UI PR for one feature stop showing up as two unrelated line items. You see the engineers who touched it, the PRs it produced, and the spec it moved forward.
- · Convention: <type>/<kebab-name>, parsed on ingest
- · Delivery column shows shipped / in-progress / stuck per initiative
- · Spec column shows scope conformance where a spec is attached
- · Engineers, tokens, cost, session count all attributed per initiative
Initiatives · curated groups, sibling PRs collapsed
Click to enlarge · esc to close
04 · PRs carry the delivery signal
Every session joined to its pull request. Or none at all.
A PR reconciler polls your connected GitHub integration for lifecycle state on every branch Cardinal is watching, then joins by branch and head SHA. Every session that ran on a git branch lands in the PR grid: one row per PR, with cost, sessions, model, spec conformance, and outcome. Sessions with no branch at all show up in a separate footnote. They can't map to a PR, and we don't count them as failures.
- · GitHub App or PAT. One integration per org
- · Reconciler runs every 30 min; webhook lands sub-minute (opt-in)
- · Attribution confidence surfaced per row, never silently rolled up
- · Stuck / abandoned / on-scope pills come from PR state and spec joins
Pull requests · sessions joined to PR state
Click to enlarge · esc to close
05 · The same numbers, cut a different way
One ledger, several axes. The dollars in each view reconcile.
The same session-level facts drive every cut. Cost by model tier, by repo, by engineer. Nothing is re-aggregated in the UI. If the per-repo bars sum to the top-line, it's because the pipeline emitted them that way.

By model tier
How much of the token mass lands on Haiku vs Sonnet vs Opus. Click a row to compare $/Mtok against teammates running the same kind of work on cheaper tiers. Useful when a workflow could run on a cheaper model but isn't.

By repo
Which codebase the agent is actually investing in. Useful when one repo is quietly consuming most of the token budget, or when a repo that isn't a priority is soaking up sessions.

By engineer
Per-person throughput, spend, and contribution shape. Compare two engineers side by side to see who's producing durable delivery per dollar, and which initiatives they're actually landing.
06 · How the join works
Five primitives. Every rollup traces back to them.
No "productivity index." No opaque scoring. Each row on every screen is reproducible from a fixed set of versions (rule, predicate, normalizer, and PR-attribution). A scorecard you cite in a review can be regenerated a quarter later from the same trace.
- 01
PluginEmits OTLP per tool call plus a cardinal.git_state event with { branch, head_sha, remote_url }. - 02
SessionizerWatermarks each session on the OTel logs stream, then closes it on inactivity or an explicit session.end. - 03
Initiative resolverParses the branch prefix, applies aliases (feature→feat, bugfix→fix), and attributes the session to a typed initiative. - 04
PR reconcilerPolls your connected GitHub integration and joins branch + head_sha against pull_request state. - 05
PredicateEmits { verdict, strength, reason_code } per session × workflow. Reprocessable, versioned, idempotent.
Point agents to Cardinal. Get the ledger the next day.
The plugin installs in one command and needs no code changes. The first outcomes show up as soon as sessions close. PR attribution starts once the GitHub integration is connected.