Skip to main content
Version: 0.5.0

The investment case

The Investment Case dashboard is the page for the question "what did the AI spend buy us". It answers it without a return-on-investment figure, because one cannot be built honestly from the data โ€” and it explains, on the page, what it would take to believe one.

Every dollar is a number you typed, multiplied by a number we measuredโ€‹

The tool does not know what you pay for a seat, what an hour of a developer costs, or how many seats you bought. Those are text boxes at the top of the dashboard:

inputwhat it is
paid seatshow many licences you pay for
$ per seat, monthlythe licence cost per seat
$ per developer hourthe loaded cost of a developer hour โ€” salary plus employer taxes, benefits, equipment, overhead
working hours per monthused only by the hypothetical ROI

Change one and everything below recomputes. What the tool measures is who used the agent, what was delivered with assistance, and what was consumed. Every figure on the page is one of the first list multiplied or divided by one of the second, and the reader can see which.

Loaded, not base. Salary divided by hours gives a base rate. The loaded rate adds everything else the company pays to have that person โ€” typically 1.3ร— to 1.8ร— base. The break-even figures divide by this rate, and a base rate makes break-even look harder than it is, because a saved hour returns the whole cost of that hour. For a consultancy, use the billable rate.

What the page showsโ€‹

Seats. Paid seats against people with at least one verified assisted commit in range; utilisation; cost per active user; and spend on seats nobody has used โ€” the one number on the page that is a saving you can actually make.

Cost per unit of output. Licence spend over the selected range divided by assisted commits, assisted lines, and closed issues with an assisted commit linked. The spend and the counts sit on the same row as the per-unit figures, so the arithmetic is visible: spend, count, per-unit.

Multiplying a per-unit cost back by its count returns the spend โ€” always, exactly, because that is how the per-unit cost was defined. The page does not show that product as a "total cost", because it would be the same number twice under a different label.

Break-even โ€” what the tool would have to save to pay for itself. This row carries the argument. Hours per seat per month; hours per active user per month, which is higher whenever seats sit idle because the people who use the tool have to earn back the ones who do not; minutes per assisted commit; minutes per active user per working day. The page states what the investment would have to return and leaves the plausibility to the reader, who supplied both inputs.

Why there is no ROI figureโ€‹

ROI = (benefit โˆ’ cost) รท cost. The cost side is complete. The benefit side is not: the tool measures lines, commits, issues and cycle time, never hours returned, and turning any of those into dollars needs an assumption that is not in the data โ€” hours per line, or "the output difference is time saved". That assumption decides whether ROI is 40% or 400%, and a single ROI figure would be that assumption wearing a chart.

So the page shows two things instead, each with its assumption in the open:

A sensitivity grid. Hours saved per developer per month down the side โ€” half an hour to eight โ€” with the value returned, the licence spend, and the ROI for each. Nothing in it is measured except the count of active users. The reader picks the row they believe, and the row where ROI crosses zero is the break-even figure above.

One hypothesis, titled as one. If the conditioned output difference from Measuring productivity were entirely time returned, what would ROI be. Beside it, the hours that hypothesis implies per user per month โ€” so the reader sees at once what believing it requires. On demo data a +38% difference over 160 working hours implies 61 hours returned and an ROI in the thousands, which is the point of showing the implied hours next to it. It is coloured amber, not green: a hypothesis is not good news.

Token prices are fetched per model, not typedโ€‹

Token spend at list price sits in its own row, labelled as meaningful only under per-token billing. Under a subscription the marginal token costs nothing and every dollar in that row is notional โ€” see Cost is in tokens, never currency.

One price per million tokens is wrong the moment two models are in use: Opus is several times Sonnet. So prices are not typed. A script, fetch-model-prices.py in the DevLake deployment, reads the providers' public pricing pages into a table, whodunit_model_prices, and every session is priced at its own model's rate โ€” input, output, cache read and cache write each at that model's price.

python3 fetch-model-prices.py --database lake

It fails closed. A pricing page that changes shape parses to nothing, and nothing must never become zero. If a provider's page parses thinner than a dated snapshot baked into the script, the snapshot is written instead, marked baked <date>. The price-list table on the dashboard shows every rate in use with its source โ€” fetched or baked <date> โ€” and the date, so a stale price is never mistaken for a current one. OpenAI's page is rendered by JavaScript and parses thin from static HTML, so it is expected to show as baked until that changes.

A session whose model has no price row is listed as unpriced with the reason โ€” the agent reports no tokens, or there is no price for that model โ€” and excluded from the total. Unpriced is unmeasured, not free.

What the page does not claimโ€‹

No time saved, no hours returned, no return-on-investment figure. Token spend is list price, not an invoice. Agents that report no tokens appear as unmeasured, not as free. The measured differences beside the break-even figures are between self-selected cohorts and are not effects. The closing text on the dashboard says all of this, so it travels with the screenshot.