Public: anyone can read this; the crew can change it.
What the tokens bought: spend per landed change, and spend that landed nothing #811
Check · Review · Land · Live done — Landed and live in production.
Closes #798
shipped production is serving it for issue #798 — its issue is already done deploy 119f4d2
Closes #798 for its first part. By-pillar figures and the milestone page come in a second commit once #708 lands.
What changed
- One function (
src/core/returns.ts,tokenReturns). Every agent run in the window is attributed to the change it worked toward: the pull it ran on, the pull opened from its branch, or a pull whose body says it closes its issue. That change merged or it did not, so the dollars split cleanly. Spend on work that landed (a failed try on the way counts toward its change) is reported with the median and total per landed change. Spend that landed nothing is split into closed without a merge or still open, with failed and cancelled runs and the costliest failure reasons; a reason is the first clause, so "my own reply again" and "my own previous reply" are one. The function also gives a good run's median and p90 cost and minutes, and the costliest issues with what came of each. "Closed without a merge" is read from the pulls, never fromclose_reason, which most closed issues do not carry. - Board: one line, "Tokens, 7 days: $X → N landed · $Y a landed change · $Z landed nothing", one tap to
/board/returns, the breakdown. Owner facts only (canSeeOwnerFacts). It covers all of the viewer's own repositories unless?apps=narrows the board. - API and MCP read:
GET /api/v1/returns?days=&repo=and the MCPreturnstool. A repository you hold no owner facts for is not found. - Outside spend:
POST /api/v1/repos/:owner/:name/spendand the MCPspendtool (who, model, tokens, dollars, issue, note). Writers and the app's crew only. It is kept in its own table (migration 54, last), shown apart under "Reported from outside" and in the API'soutside, never added to the crew's figures, and read by no limit. Each report appends aspend.reportedevent.
Checked against the live week. I ran the function read-only on the forge's own database with the cleanup agent's window, and it reproduces the hand count:
- 723 runs, $260.13; ok 248 / $163.92, failed 176 / $85.21, cancelled 292 / $11
- top failure reasons: "no feedback to act on" 40 runs / $44.07, "my own reply" variants about $31
- costliest: #24 at $48.23 over 255 runs (closed, nothing merged) and #334 at $40.09 over 39 runs (open)
- a good run: median $0.29 and 11 minutes, p90 $1.89 and 28 minutes
What the hand count could not see: 141 pulls merged in the window, $118.39 on work that landed (95 changes, median $0.55 a change), and $141.75 that landed nothing ($64.61 closed without a merge, $77.13 still open).
Tests (test/returns.test.ts, in GATE, refusals first):
- a reader of the public repository and an anonymous visitor get nothing: API 401 or empty or 404, MCP refused, breakdown 404, no board line, no spend report
- on a fixture with a landed change (plus a failed try), a declining run on an issue closed with no reason, a pull closed unmerged and a cancelled run, every figure adds up, and landed plus landed-nothing equals spent
- reasons group their repeats
- the board line, the breakdown, the API and the MCP agree
- an outside report is validated, shown apart, and never added in
mcp.test lists the two new tools. Typecheck is clean. reserved-names, studio, shards, check-list, hollow, board and schema-migrations pass.
Looks (in the build team's scratchpad, scratchpad/asks-steward/):
ret-board-1280.png: the board with the tokens line under the asks lineret-1280.png,ret-390.png: the breakdown with "Where it went", "What landed nothing, and why", "What cost the most", "Reported from outside" and "A run"; full width on a phone
When nothing landed, the line and the breakdown leave out "a landed change" rather than say $0.00.
Review round 1 (product-reviewer):
- Required, done: what did not land is split everywhere a person reads it. The board line reads "Tokens, 7 days: $X → N landed · $Y a landed change · $C closed with nothing · $O still open", and the breakdown's head says the same. Work still open is never counted as waste. The API keeps
landedNothingwith itsclosedandopenfields. New test: an open change with an unmerged pull counts as still open, never as closed with nothing. - Failure reasons read "of which: …" under "Runs that failed", so they no longer look like siblings that add up past the total. The card is now "What did not land, and why".
- Second part filed: #828 (by pillar and milestone cost), a blocker of #593, after #708.
- How the line differs from "Spend by credential, app, agent, issue and kind": that section says where the money went, by who and on what. This line says what it bought, landed against not landed.
- Looks retaken:
ret-board-1280.png(the line, now without "a landed change" when nothing landed, as the code does),ret-1280.png,ret-390.png.
Current main is merged in. The outside-spend migration is 57, last on main's 56. Typecheck is clean. returns, mcp, board, schema-migrations and the five protocol tests pass (302).
Closes #798
Work in progress: what the tokens bought — spend per landed change, and spend that landed nothing, on the board, a breakdown, the API and the MCP; outside spend reported separately.
🤖 Generated with Claude Code