Cost Center
Re-evaluate the qdash `/cost` page against what the backend now serves, and re-draw it on the Organization / Inventory listing pattern
Why
/cost was built on a single feed (/api/costs) before the listing pattern existed. It reports totals four ways, then two bar lists, a chart, four tables and a guardrail panel, and answers none of the reader's questions directly. It has three readers:
- the engineering manager asking who and why
- FinOps asking where spend is heading against budget
- the CFO asking for department attribution
Since then the backend has started serving token kinds, account ownership and billing mode, per-repo and per-session cost, and spend targets. The page shows almost none of it.
Objective
A Cost Center that leads with its three questions:
- Spend: how much, and who
- Pace: against the ceiling
- Rate: what we pay per unit, and the model lever
One ledger then breaks the spend down eight ways, followed by receipts for the costliest sessions and every ceiling read against the same numbers. It is designed on the design site, confirmed by Mark, then ported to the Fable app. Done = plan.md §Done means.
Scope
- Design site:
/pages/cost-centermock + registry entry, on a served snapshot (app/data/cost-center-snapshot.tsviacapture-snapshot.py; derivations inapp/data/cost-center-mock.ts). Seat-pricing design (plan Phase 2). - Fable app:
pages/cost.vuecomposables/fleet/costs.tslib/cost-rollups.tscomponents/fleet/Cost*(four new, four retired)lib/projected/fleet.ts(seat prices staged)
- Out:
- qcontrol
src/and the seed (read-only; asks filed instead) - qdash
ui/:3030(deprecated) - editing ceilings on this page (Policies and Campaigns own them)
- qcontrol
Key Changes
Stage 1 (2026-10-01). Initial prompt; evaluation of the nine cost-bearing feeds, eight lenses, value ranking and backend asks.
The mock is at /pages/cost-center. It has a Spend · Pace · Rate band, a "Spend by" ledger with Team · User · Repo · Model · Provider · Harness · Account · Token kind tabs, the Team quick filter, the column picker, costliest-session receipts, a Ceilings table and design notes. It was verified on :3000 across lenses, periods and the Team filter. The design dev server needed a touch to pick up edits.
Snapshot findings:
- cache is 62 % of spend
- Opus is 6 % of tokens and 38 % of spend
- the campaign window bug serves
current: 0 - the guardrail trips on 22 of 29 days
- 90 % of spend is on sign-in (plan) accounts
- Gemini is unpriced
Kick-off (2026-10-01). Mark's answers (evaluation §9):
- Rate card kept.
- All eight lenses kept for a review round.
- Plan seats get a seat-price setting.
- Ceilings: all shown read-only, possibly broken out to their own pages later.
- The c66 toggle retired (c66 closed into c105).
- The Fable port approved, gated on the design being confirmed and revisions complete.
plan.md Phases 0–6 written.
Phase 1, mock round 2 (2026-10-01, awaiting Mark's review):
- Plan wording. Plan-seat dollars now read as usage at list price:
- The Spend card's description switches to "Usage at list price" when sign-in accounts carry ≥ 50 %.
- Its provenance splits metered (billed) from plan seats (at list): $186 vs $1,740 over 30 d.
- In the Account lens, the plan rows' Spend cells say "at list", and the Billing pills read "Plan · at list" / "Metered · billed".
- Ceilings. The block is now the design-local
CostCeilings(app/components/cost/Ceilings.vue): title, table and provenance self-contained, rows in andopenout, so it can move to its own page. - Doors. Every row (ledger, receipts, ceilings) carries the Fable route it opens, built with the app's real query keys:
?team?user?workspace?model?provider?adapter?account&provider?run?policy- Clicking a row shows the route in the mock strip; hovering the label shows it too.
- The Repo lens opens the costliest of a repo's checkouts and says so ("a repo-level page doesn't exist yet").
- Fixes. The served campaign
-0printed as "$-0".
Verified on :3000: the Account and Repo doors, the ceiling door (/policies/detail?policy=budget), and the plan wording. The dev server needed a second touch to serve the data-module edit.
Review 1 (Mark, 2026-10-01): the filter trigger bar (Team quick filter) is removed for now. The ledger's toolbar keeps a standalone UxColumnPicker, and ?team= is gone from the mock. The data module still supports team scope (buildCostCenter(period, team)) for when filtering returns.
Outcome
Path change (c107, 2026-10-02). The Fable app is now ../qcontrol/crates/qdash/ui on branch feat/new-pages. Its dev server is :3030 (launch config qdash-app) and the seed backend is :7882 (qdash-seed). The qcontrol-c99-fable worktree, its c99/claude-code-fable-5.1 working branch and :3032 are gone. Read every apps/claude-code-fable-5.1/… path in this cycle as crates/qdash/ui/…, the same files moved. The c99 rule that kept src/ read-only applied to the parallel apps and ended with the cutover. Commit to feat/new-pages, never main. See c107.