Context
DEC-G3 committed to Vercel-style pricing: published unit prices, included allowances, measured consumption, a projected-spend panel and tenant-configurable spend caps. All of that rests on a measurement that is frequent enough to be actionable and cheap enough to run for every tenant forever. DEC-G1 settled it at every 30 minutes with deltas. Computing consumption on demand would put an aggregate query on a dashboard path; daily snapshots would let a tenant blow through a quota for 23 hours before anyone noticed. The measurement has contractual weight: the ToS names these snapshots as the evidentiary basis for billing. That is why they are append-only and why a correction is a new row, never an edit.Scope (normative)
core.usage_snapshots, append-only, monthly partitioned, every 30 minutes per tenant.core.billable_metrics: parametric — marketable contacts, tracked events, messages by channel, storage.- Delta computation between t1 and t2 so consumption never needs a scan.
- The marketable-contact count, using the predicate defined in FS-CORE-0004.
- Threshold alerts at 80 / 90 / 100% through the platform’s own notification system.
- Enforcement hooks: grace, hard stop, or overage per metric, configurable per tenant from Ops.
Non-scope (normative)
- The rating engine — it consumes these snapshots; it is its own epic (ADR-016).
- Invoicing and charging — Fintoc, Paddle, LibreDTE.
- The consumption dashboard UI —
frontend/console.
Behaviour (normative)
- Snapshots run every 30 minutes for every tenant, on cron fan-out. Not tiered — the cost is low and a tenant discovering an overage a day late is a support problem for us.
- Each row stores the absolute value and the delta since the previous snapshot. Deltas make billing a sum instead of a scan.
- The table is append-only. A correction is a new row with a
correction_ofreference; editing a snapshot would destroy the evidentiary value the ToS relies on. - Marketable contacts use the predicate from FS-CORE-0004 — one definition, shared, so metering and messaging can never disagree about who is billable.
- Alerts at 80, 90 and 100% are sent through the platform’s own notification cascade. Building a second delivery path for our own alerts would put them outside the rules everything else obeys.
- Enforcement per metric is configurable per tenant from Softcrum Ops (DEC-G2). Defaults: contacts grace + notice, messages hard at 110%, events soft with overage.
- A spend cap is the tenant’s self-service face of enforcement: reaching it applies the same stop the operator would.
- A missed snapshot window is recorded as missing, never interpolated. An interpolated number in a billing basis is a number we cannot defend.
- Partitioned monthly; retention follows the plan, with the legal minimum applying independently.
Data (normative)
API (normative)
Events (normative)
core.usage.threshold_crossed on the outbox at 80, 90 and 100%, which is what drives the alert
campaign through messaging.
Acceptance criteria (normative)
- Snapshots land every 30 minutes for every tenant, verified over a 24-hour synthetic run.
- The sum of deltas over a cycle equals the difference of absolutes at its endpoints.
- The marketable-contact count matches the messaging module’s own view for the same tenant and instant.
- Crossing 80% emits once and does not re-emit while it stays above.
- A missed window is recorded as missing and is visibly absent, not interpolated.
- A spend cap applies the configured enforcement within one snapshot window.
- Corrections append with
correction_ofand leave the original readable. - Negative: no code path updates or deletes a snapshot row.
Execution
Asynchronous pipeline. Cron fan-out inbackend/scheduler, computation in backend/workers,
read endpoints in backend/api.