EngineerPrep / Engineering Research · Cost Analysis

The Zero-Dollar Orchestrator: Why Your AI Cost Dashboard Can Lie

How 54% of our AI bill hid under a $0.00 row

2026-09-05 · 7 min read

Our AI bill was $193.50. The dashboard ranked every capability by cost, most expensive first, and put the biggest spender at the bottom with a zero next to it.

COST BY CAPABILITY · 16 ACTIVE DAYS

Synthesize Speech (expressive)      1,085 runs      $105.24
Author Lesson                          92 runs       $11.94
Synthesize Episode Audio               80 runs        $0.00

That last row is the most expensive thing in the system. It is responsible for $105.24 — 54% of the entire bill — and the accounting is not wrong.

Every system that calls a model eventually gets a bill it does not recognise. Ours arrived at $193.50 across 2,380 recorded executions — not a large number, but large enough that we wanted to know where it went before it became one. We had instrumented every capability with per-execution cost tracking precisely so that this question would have an answer.

It had an answer. The answer was wrong in a way that took an afternoon to see, and the mistake is structural rather than clever — which is why it is worth writing down. A flat cost dashboard can make exactly the same mistake, whatever is generating the rows.

The two days that were 71% of the bill

The first cut was by day, most expensive first, because "which day cost more" is the question you actually have. Two days out of sixteen carried nearly three quarters of the spend:

DayCostShareRunsBiggest driver
2026-07-26$88.1445.6%1,119Synthesize Speech (expressive)
2026-07-25$48.8525.2%632Synthesize Speech (expressive)
…14 more days$56.5129.2%629mixed

That part was honest and useful. Two days of bulk audio generation, and the rest of the window is background noise. So we went one level down, to capabilities, expecting the same clarity.

The ranking that lied

Here is the by-capability table as the dashboard first rendered it. Read it the way anyone reads a ranked list — top is worst, bottom is fine:

CapabilityCostRunsPer runAvg duration
Synthesize Speech (expressive)$105.241,085$0.09704.8s
Author Lesson$11.9492$0.1298270.6s
Synthesize Episode Audio$0.0080$0.000069.1s

A capability that ran eighty times, took sixty-nine seconds each time, and cost nothing. Free work. The natural reading is that it is some cheap coordination step not worth thinking about, and the natural next move is to go optimise the speech synthesis at the top.

Both readings are wrong. Synthesize Episode Audio is the thing that spent the $105.24. It calls no model itself — it decides how to break an episode into utterances and then calls Synthesize Speech once per utterance, about fourteen times per episode. Every dollar it is responsible for was billed to its children.

It is two ordinary Spring beans, each with an annotation on the method:

@Service
class EpisodeAudioService {

    private final SpeechService speech;          // a different bean

    @Capability(name = "Synthesize Episode Audio")
    public Episode synthesize(Script script) {
        for (Utterance u : script.utterances()) {
            speech.speak(u);                     // ~14 calls, each one priced
        }
        return assemble();
    }
}

@Service
class SpeechService {

    @Capability(name = "Synthesize Speech")
    public Audio speak(Utterance u) {
        // the actual model call
    }
}

synthesize() never calls a model. Its own cost is genuinely $0.00.

How we found that is the part worth repeating. The table above already held both halves — 1,085 speech calls carrying $105.24, and 80 orchestrations carrying nothing — and no amount of staring at it produces the connection, because the connection is not in it. What produced it was opening a single speech execution and seeing its parent id point back at Synthesize Episode Audio. One child, once, and the whole ranking reads differently.

That is the lesson: a parent row will fool you. Look inside a child. A parent reports what it spent, which for anything that delegates is nothing at all. Only the child knows who asked for it.

Per-capability cost accounting is exactly correct and almost useless, because in an agentic system the thing that spends and the thing that decides to spend are never the same row.

What the parent/child tree showed

The fix is not a better query over the same flat table. It is recording the parent/child relationship between executions, and then reporting two numbers per node instead of one: what this execution spent itself, and what it and everything beneath it spent.

Here is a single episode, expanded:

EXECUTION                                    OWN      SUBTREE    DURATION

▾ Synthesize Episode Audio                $0.0000     $1.3155       69.1s
  ├─ Synthesize Speech (expressive)       $0.0970     $0.0970        4.8s
  ├─ Synthesize Speech (expressive)       $0.0970     $0.0970        5.1s
  ├─ …11 more utterances                  $1.0670     $1.0670           —
  └─ Synthesize Speech (expressive)       $0.0545     $0.0545        2.7s

Open the cost explorer to expand and collapse that tree yourself — collapsed, it is the view that started this investigation.

Eighty of those, 1,085 children in total, and the $105.24 that the flat table had filed under a capability nobody was looking at. The orchestrator's own cost is still $0.00 — that remains true and it is worth keeping, because it tells you its direct AI-provider cost is nil and the model calls are the entire provider bill. Orchestration is not free in any other sense: it still spends JVM time, database round trips, queue capacity and tracing volume. What the zero means is narrow, and stating it narrowly is what makes it hard to argue with. It just cannot be the only number on the row.

Once the tree existed, the operational question changed shape. "Can we make speech synthesis cheaper" is a vendor negotiation. "Do we need fourteen separate calls per episode, and are we regenerating episodes we already have" is an engineering decision, and it is only visible from the parent.

Two more ways the same table lies

Once we started checking arithmetic rather than trusting it, two further defects turned up in the same view. Both are the sort that survive code review because the code is doing exactly what it says.

Cost per attempt is not cost per outcome

Author Lesson ran 92 times and failed 11 of them — better than one in nine. At $11.94 total, the dashboard reported $0.1298 per run. But you do not pay for outcomes, you pay for attempts, and you only get 81 lessons. The real figure is $0.1474 per lesson that exists, 14% higher.

That gap is the price of unreliability, and reporting cost per attempt hides it precisely where it matters: a capability that fails often looks cheap, and an engineer comparing two implementations picks the flakier one.

The degenerate case is worse. A capability that failed every run divided its cost by zero successes and reported $0.0000 — so the most wasteful thing in the system sorted to the very bottom as the cheapest. Cost per success is undefined there, not zero, and the view has to say "never succeeded" rather than print a number.

Rounding before sorting

The daily table sorted on values already rounded to cents. Two days whose real costs differed by less than a penny ranked arbitrarily, on a page whose own heading promises "most expensive first". Sort on the raw totals, display the rounded ones, and give equal values a deterministic tie-break so the order does not change between page loads.

Three rules, if you are building this

1. Record the parent, not just the execution. One nullable parent_execution_id is the difference between a cost table and a cost explanation. It has to be captured at call time — you cannot reconstruct the tree afterwards from timestamps, and every system we have looked at that tried has been wrong about concurrent runs.

2. Show own cost and subtree cost together. Never one without the other. Subtree cost alone double-counts as you walk down the tree; own cost alone reports orchestrators as free. Highlight the difference between them — that difference is the finding, and where they are equal you are looking at a leaf that genuinely did the spending.

3. Denominate in successful outcomes. Cost per run flatters unreliable capabilities. Cost per success is what the business is actually buying, and the gap between the two is a number worth putting on a dashboard on its own.

On these numbers

Every figure here is an estimate computed from per-token and per-character rates configured in our own system, not a reconciliation against a vendor invoice. The text-to-speech rate in particular is an approximation of a pricing tier, and all the audio figures scale linearly off it. We publish them because the shape is what matters — 54% of a bill hiding under a zero is the same finding at any rate — but if you are reproducing this, reconcile against a real invoice before you trust the absolute values. We are doing the same.


Where to take this

Want to diagnose failures like this yourself? EngineerPrep's production failure labs give you the execution tree, logs, state, tool calls and cost evidence from realistic agent incidents. Your job is to identify the failure pattern and decide whether the proposed remediation is actually safe.

Building this into a Spring Boot system? Capstead records the parent/child execution tree, own cost, subtree cost, latency and capability metadata automatically. It is open source and Apache-2.0.

The subtree view above — a whole execution tree in one call, with cost rolled up at every level — is what shipped in Capstead 0.8.0. Worth being straight about one thing: parent and child have been linked since 0.3.1, so the data was always there and we had simply never added it up.