The proof

Building a calculator with Groundwork.

One real build across 20 sessions, measured call by call. Every number here comes from OpenCode’s own token accounting and links to the raw row it came from, including the ones that don’t flatter it.

DeepSeek V4.1 Flash (max thinking) in OpenCode 2.0.18 · measured 2026-09-26

In short

What it all means.

  1. 01Each step costs the same, however big the project gets.Every step of the build started with about 9k tokens of context, from the first piece of work to the last.see the numbers ↓
  2. 02Picking up where you left off is cheap.A new session knew where the project stood after reading 6.8× fewer tokens than one without Groundwork, and got it right.see the numbers ↓
  3. 03The honest catch.On an app this small, one plain chat would likely be cheaper. Groundwork pays off as a project grows past what one chat can hold.see the numbers ↓

Don’t take our word for it: every number links to its raw data, and every session is readable in full →

What was built

A working calculator, built and checked piece by piece.

Why it matters

This is a finished, deployed app, not a demo. The numbers below are what it actually took.

$0.586to build it: 6 cards in 2 phases
1.33Mfresh tokens, 41% of them reasoning
58tests passing, each written before its code
20sessions: 2 main chats, 18 role sessions

Deploying to GitHub Pages ($0.020) and exporting the transcripts ($0.061) happened in the same chats and are counted separately.measurements.md

Every card starts the same size

Each step started from the same small context.

Why it matters

Card 60 should cost about what card 6 did. A growing project doesn’t make each step slower or more expensive.

Each role runs in a fresh session that reads its role file, the card and only the code the card touches. All 18 role sessions started between 8.8k and 9.7k tokens of context, from card 1.1 to card 2.2. The one long chat (the runner) is plotted on the same scale for comparison. every session
The runner: one long chat, as each of your messages came inEach role session, at its start
0100k200k300k280kphase 1 main chat, message by message →1.11.21.31.42.12.28.8–9.7k, every one
Show as a table
Context at the start of each runner message and each role session
WhereContext at start (tokens)
runner, message 1: /gw9,157
runner, message 2: building a calculator15,490
runner, message 3: /gw22,094
runner, message 4: init, then plan55,407
runner, message 5: commit; what’s next?103,158
runner, message 6: /gw-approve122,721
runner, message 7: cards left?130,195
runner, message 8: finish phase 1130,994
runner, message 9: /gw-approve171,618
runner, message 10: /gw-retro178,217
runner, message 11: transcript export189,991
runner, message 12: transcript export248,158
runner, message 13: transcript export252,996
runner, end of the chat280,280
card 1.1 tester9,257
card 1.1 implementer9,264
card 1.1 reviewer8,851
card 1.2 tester9,222
card 1.2 implementer9,201
card 1.2 reviewer8,789
card 1.3 tester9,211
card 1.3 implementer9,232
card 1.3 reviewer8,826
card 1.4 tester9,248
card 1.4 implementer9,317
card 1.4 reviewer8,851
card 2.1 tester9,330
card 2.1 implementer9,652
card 2.1 reviewer9,001
card 2.2 tester9,469
card 2.2 implementer9,497
card 2.2 reviewer8,982

Cost per card

Cost followed the work in the card, not the size of the project.

Why it matters

You can estimate a project’s cost by counting its cards. Screens and interface work cost more than logic.

Tester, implementer and reviewer together. The two UI cards cost more because the tester built a browser check from scratch, not because the project had grown. cost per card
  • 1.1 Number entry$0.059
  • 1.2 Operations and equals$0.045
  • 1.3 Clear and error state$0.040
  • 1.4 Result formatting$0.060
  • 2.1 Page and keypad$0.128
  • 2.2 Wire it up$0.084
Show as a table
Cost and fresh tokens per card
CardCostFresh tokensLargest role context
1.1 Number entry$0.0593140,27274,174
1.2 Operations and equals$0.0449121,03338,892
1.3 Clear and error state$0.0395108,35641,030
1.4 Result formatting$0.0599151,85056,751
2.1 Page and keypad$0.1279287,821134,817
2.2 Wire it up$0.0843224,16885,044

By role

  • Tester · 6 sessions$0.170
  • Reviewer · 6 sessions$0.149
  • Implementer · 6 sessions$0.097
  • Runner · 2 sessions$0.170

The runner row is the main chats’ building turns only. by role

Picking up where you left off

A new session knows where things stand.

Why it matters

Closing the laptop, switching tools or coming back next week doesn’t mean re-explaining the project, or paying to re-read it.

A fresh session in the finished calculator, asked where the project stands and what’s next. Then the same question with Groundwork’s files removed. One run each, so read it as the shape to expect, not a benchmark.
groundwork removed
tokens read, all calls
956,066
model calls
27
tool calls
50
cost
$0.0229

Rebuilt it from git history, and read folders outside the project.

with groundwork: /gw
tokens read, all calls
140,682
model calls
9
tool calls
11
cost
$0.0086

Right: phase 2 built, waiting for the phase review.

resuming

The honest part

On a calculator, a plain chat is cheaper.

Why it matters

For a quick script, one chat may be all you need. For anything that takes days, the flat cost per card wins.

Three roles, tests first and a review per card is a fixed cost, and on an app this small it’s most of the bill. What Groundwork adds there is proof, not savings. The cost story starts when the project outgrows one chat: every call re-reads the whole conversation, so a long chat’s total grows with the square of the work. Move the sliders to see where that crosses over.
One long chat (model)Fresh role sessions per card (measured average)
050M100M150M200M250M051015202530crossover ≈ card 9

cards built → · tokens re-read, total ↑

One chat stops being cheaper
around card 9
At card 30, one chat re-reads
3.4× as much
One chat’s context per call by then
759kpast a 200k window
This is a model, not a measurement. The blue line is the calculator’s measured average for the three role sessions of a card (2.3M re-read per card), and stays straight because each card starts fresh. The orange line is re-read ≈ m·N·S + m·h·N²/2 with S = 9k (OpenCode’s measured start) and your m and h; the plain-chat run was never measured, so its defaults are assumptions. Neither line includes the runner, which Groundwork now restarts each phase.

The full derivation: What it costs, and when it pays off.

What didn’t stay flat

The main chat grew. So we changed how it works.

Why it matters

The one part that grew was found by measuring, and fixed. Starting a new chat for each phase keeps it small.

The measurements found the one part that behaved like a long chat, because it is one: the runner, the main session that hands cards to the roles.
  1. Measured

    280k

    tokens of context per call, by the end of phase 1’s main chat. The two main chats account for 61% of all tokens re-read (21.6M of 35.2M), and about half of that history was the runner’s own retained reasoning. The roles’ reports back were about a thousand tokens each. summary

  2. Changed

    • Short hand-offs: the card’s path, the role file and checked facts. No retelling what was agreed, no statuses to set.
    • No reruns between roles: the reviewer reruns everything once.
    • A fresh session each phase, suggested when a phase closes.

    the change

  3. Why it works

    9.2k

    was where phase 2’s new main chat started, instead of about 190k had phase 1’s chat continued. Over its 61 calls, that’s roughly 11M tokens not re-read. Phase 2 did this by accident; now Groundwork suggests it at every phase close. summary

The lessons ledger

It caught problems in Groundwork itself.

Why it matters

The ledger catches problems in the process, not just in the code, and Groundwork got better because of it.

At the end of phase 1, the calculator’s retro wrote two lessons. Neither was the model’s mistake: both were gaps in how Groundwork told the roles to work, and both are now fixed.
.groundwork/LESSONS.md

L-001 · Runner prompts hand over role files, not status rules

origin The runner’s prompt told the implementer to set the card to done; the implementer role says review. Both cards had to be reset.

fixed The runner no longer passes statuses at all. The role file says.

.groundwork/LESSONS.md

L-002 · Calls are recorded on the card by whoever makes them

origin Implementers reported their judgment calls but left the call: lines to the runner, who backfilled them.

fixed Tester and implementer write their own History and call: lines; a report isn’t enough.

the calculator’s LESSONS.mdthe fix

The reviewer

It says what it couldn’t check.

Why it matters

You know exactly what wasn’t verified, so you know what to check yourself.

No card was sent back in this run. What the reviewers did do was rerun everything and say plainly where their proof stopped.

“Live browser check — attempted, not possible … verified in Node only, not verified live in a browser”

card 1.1 reviewer, listed as a caveat for youL2997

“Minor, non-blocking … a multi-character key like ‘1abc’ would be treated as a digit … Noted in my report, not sent back.”

card 1.1 reviewer, reading the diffL3001

Overhead

Groundwork adds 681 tokens to a session start.

Why it matters

Installing it costs about the length of a short paragraph at the start of each session.

First model call of fresh sessions in copies of the finished calculator. The rest is OpenCode’s own instructions and this machine’s global rules.
OpenCode alone, empty repo8,563
The calculator, Groundwork removed8,564
The calculator, with Groundwork9,245
Groundwork’s tester subagent9,069

npx groundwork-ai doctor shows this for your own project.startup

Measure your own

Don’t take our numbers. Take yours.

Why it matters

You can check all of this on your own project instead of trusting ours.

OpenCode exports any session with its per-call token counts. The script that produced every figure on this page runs on yours.
your project
# export each session, then summarize them
opencode session export <session-id> > main.json
node checks/session-costs.mjs main.json tester-1.1.json ...

Receipts

Every session, as it ran.

Why it matters

Everything on this page can be checked against the actual conversations, word for word.

Two main chats and 18 role sessions, exported from OpenCode and left unedited. Start with ten key moments, or open any session in full.

Read the receipts →