carlos / platform Public

docs: unit-economics spec — CE API bill pull + billing-readiness section

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Paul Campbell 9a5db5b68e29b811d92f3532c82b49e968dfd5df parent 091de05
1 file changed, +66 −16
  • docs/superpowers/specs/2026-08-11-unit-economics-design.md +66 −16
diff --git a/docs/superpowers/specs/2026-08-11-unit-economics-design.md b/docs/superpowers/specs/2026-08-11-unit-economics-design.md
index b1415dcbf..9a03c9797 100644
--- a/docs/superpowers/specs/2026-08-11-unit-economics-design.md
+++ b/docs/superpowers/specs/2026-08-11-unit-economics-design.md
@@ -29,7 +29,8 @@ Questions it must answer:
| Decision | Choice |
| --- | --- |
| Purpose | Modelling instrument (decision-grade) |
-| Cost truth | Tweakable rate card × metered usage, reconciled monthly against the real AWS bill |
+| Cost truth | Tweakable rate card × metered usage, reconciled against the real AWS bill pulled daily from the Cost Explorer API |
+| Billing later | Not built now, but pre-empted: stable meter vocabulary + materialized monthly per-account usage statements are billing's future input |
| Meters v1 | CPU + memory, edge requests + egress bytes, storage bytes, instance-hours / census |
| Knobs | Live client-side recompute + named scenarios persisted to bucket config |
| Scope | Generic, per-deployment, operator-gated |
@@ -135,18 +136,56 @@ A scenario = rate card + allocation policy + pricing mode + prices/margin
usage JSON). "Save scenario" persists via an operator-gated,
CSRF-protected console endpoint.
-### Bill reconcile
-
-- `economics/bills/<YYYY-MM>.json`: real monthly AWS bill, total plus
- by-service, `source: manual` in v1.
-- Entered through a small console form **and** a `carlos economics bill`
- operator verb (the no-box-commands rule holds either way).
-- Dashboard renders model-vs-actual delta per service, so rate-card drift
- is visible the month it happens.
-- Automated Cost Explorer ingestion is a filed follow-up, not v1: it needs
- new IAM and tagging discipline, and both AWS accounts hold non-CARLOS
- resources (keymail's on flagship, Tito's legacy fleet on titogo), so an
- unfiltered bill overstates CARLOS costs.
+### Bill reconcile — pulled from the Cost Explorer API
+
+- The console's daily tick calls `ce:GetCostAndUsage` (monthly
+ granularity, grouped by service, current + previous month) and upserts
+ `economics/bills/<YYYY-MM>.json`: total plus by-service lines,
+ `source: costexplorer`, with a `pulledAt` stamp. Cost Explorer data
+ lags ~24h and the API costs $0.01/call — a daily pull is both fresh
+ enough and negligible.
+- **IAM prerequisite:** the box role that runs the console gains
+ `ce:GetCostAndUsage` (tofu change in `infra/modules/region-host` /
+ pinfra for flagship, `infra/tito` for tito).
+- **Attribution is a knob, not a pull-time filter.** Both AWS accounts
+ hold non-CARLOS resources (keymail's on flagship, Tito's legacy fleet
+ on titogo), so the pull stores the whole account's by-service lines
+ untouched; the reconcile view lets the operator mark which service
+ lines count as CARLOS (persisted with the scenario). An optional
+ cost-allocation-tag filter can be configured once tagging discipline
+ exists; it is not required for v1.
+- **Manual fallback:** deployments without CE access (no IAM, non-AWS)
+ enter the bill through a small console form or the
+ `carlos economics bill` operator verb; `source: manual` overrides a
+ pulled month.
+- Dashboard renders model-vs-actual delta per service, so rate-card
+ drift is visible the month it happens.
+
+## Billing readiness (pre-empting phase 2)
+
+Billing is **not** built now, but it is the obvious phase after these
+numbers are analysed, so v1 deliberately lays its inputs:
+
+- **Stable meter vocabulary.** Every meter has one canonical identifier
+ used everywhere — usage objects, scenario prices, dashboard columns:
+ `cpu_core_hours`, `mem_gb_hours`, `egress_gb`, `requests_1k`,
+ `storage_gb_month`, `app_months`, `instance_hours`. A future billing
+ engine speaks this vocabulary unchanged; renaming a meter later would
+ orphan history, so these names are chosen once, here.
+- **Materialized monthly usage statements.** The console's daily tick
+ doesn't only feed the render cache — it upserts
+ `usage/accounts/<acct>/<YYYY-MM>.json`: the account's month-to-date
+ totals per meter, plus coverage. These durable per-account statements
+ are exactly what an invoicing phase consumes (statement × price sheet
+ = invoice); they also make the accounts page cheap to render.
+- **Scenario → price sheet promotion.** The rate-sheet pricing mode's
+ shape *is* a future published price sheet. Scenarios are versioned
+ manifests, so "the pricing" at billing time is one promoted scenario,
+ not a new schema.
+- **Ledger upgrade path (recorded, not built).** When invoice-grade
+ guarantees are needed, collection points append usage events to an
+ `internal/ledger`-backed usage ledger and statements become
+ materializations of it. Nothing in v1's shapes blocks that upgrade.
## Console surface
@@ -204,6 +243,11 @@ as a follow-up gap rather than built now.
falls back to built-in defaults with a visible warning banner.
- Object-store credential failures during the storage sweep mark that
store "unswept" for the day rather than aborting the pass.
+- Cost Explorer pull failures (missing IAM, API error) leave the last
+ pulled bill in place; the reconcile view shows its `pulledAt` age and
+ a "bill data stale / unavailable" banner, and manual entry remains
+ available. A deployment that has never pulled shows the reconcile
+ panel in manual-entry mode, not an error.
## Testing
@@ -222,13 +266,19 @@ as a follow-up gap rather than built now.
- All additive. Boxes need the new edge binary (ship → promote →
converge) before egress data accrues; console deploys with the usual
restart rule.
+- The Cost Explorer IAM grant (pinfra for flagship, `infra/tito` for
+ tito) is an operator-queue tofu apply; until it lands, the reconcile
+ panel runs in manual-entry mode.
- The dashboard is useful on day one from existing CPU/mem history;
edge, storage and census series fill in as they accrue.
## Out of scope (v1)
-- Payments, invoicing, or any customer-facing pricing display.
-- Invoice-grade usage ledger (revisit when payments arrive).
-- Automated Cost Explorer ingestion (filed follow-up).
+- Payments, invoicing, or any customer-facing pricing display — but see
+ "Billing readiness": v1's statements and meter vocabulary are built as
+ billing's inputs.
+- Invoice-grade usage ledger (upgrade path recorded above).
+- Cost-allocation tagging discipline (the reconcile knob covers
+ attribution until then).
- Tunnel/non-edge traffic metering.
- Read-side `carlos economics` CLI verb (filed follow-up).