feat/smarter-mrr
needs attentionviewing older commita4fbc2b · incrementalpre-PRreviewed 2026-08-08 00:42 UTC7H · 11M · 5L- Purpose
- Measure Smarter (Energía Real) MRR accurately with an event-sourced pipeline instead of hardcoded Terraform locals.
- Goal
- Replace Phase 1 hardcoded $20/report rate and static FX with a Supabase-sourced rate + live FX snapshot, emitting revenue.smarter_mrr_snapshot events to PostHog.
- Sub-goals
- SG-1: custom-smarter plan as rate SOT in Supabase (unbound, never charged)
- SG-2: live USD→MXN FX via open.er-api.com with frankfurter.app fallback
- SG-3: shell-verified calculated bill count query (findCalculatedBillCountsByOrg)
- SG-4: manual monthly refresh script (billing:smarter-mrr-refresh)
- SG-5: daily mark in mrr-sync Lambda (section 10)
- SG-6: event-sourced PostHog insights (smarter_mrr + smarter_mrr_calc_log)
- What
- Adds packages/billing/src/fx.ts (FX fetcher), smarter-mrr.ts (pure computation), database seed-smarter-rate.ts, manual refresh script, mrr-sync handler section 10, bill.queries.ts aggregate query, and PostHog Terraform insights/dashboard updates.
- Why
- Phase 1 hardcoded rate and FX in Terraform; a rate change required a TF apply. Event-sourced approach stores rate in Supabase (SOT), snapshots live FX at calculation time, and emits PostHog events so MRR is attributable to a specific rate+FX moment.
- Areas
- packages/billing+119−0packages/database+99−0scripts/billing+136−0services/billing+83−1domains/utility+42−0infra/posthog+101−0packages/analytics+36−0
- Blast
- 7 areas, +617/-1 lines. No DB migrations, no public API changes, no RLS changes. Lambda handler gains a new try/catch section that never fails the sync. Measurement-only.
Findings · 17
correctness4
COUNT(DISTINCT contract) semantics — assumes one bill per contract per month
domains/utility/src/bill/bill.queries.ts:2906
count(DISTINCT utilityContractId) counts unique contracts, not bills. Docstring says dedupe is delete+replace at (contract, period) so this should be invariant — but if that breaks, under-count is silent.
yearMonth derived from UTC — mismatch with Mexico-local CFE billing periods
scripts/billing/smarter-mrr-refresh.ts:27
currentYearMonth() uses getUTCFullYear/Month. Bills store Mexico-local month strings. At UTC midnight on month boundary these diverge. Same bug in mrr-sync.handler.ts.
Double drizzle instance on same connection
scripts/billing/smarter-mrr-refresh.ts:42
drizzle(connection) called twice — once as `db` (unused) and once inline. Use one ORM instance.
fetchUsdMxnRate has no timeout — can block Lambda indefinitely
packages/billing/src/fx.ts:13
Neither fetch call uses AbortSignal.timeout(). Hung external call consumes full Lambda invocation timeout.
security3
No timeout + no rate sanity check on external FX fetch
packages/billing/src/fx.ts:6
Add AbortSignal.timeout(5000) and sanity range (5 < rate < 100 for USD/MXN).
org_name emitted verbatim to PostHog — PII / confidential data leakage
scripts/billing/smarter-mrr-refresh.ts:90
Customer org name in PostHog event. Prefer emitting org_id only; resolve display name via group properties.
Service-role all-orgs query without active/billable filter
services/billing/src/handlers/mrr-sync.handler.ts:445
findCalculatedBillCountsByOrg aggregates across every org. Add active org filter.
conventions2
Section 10 silently swallows errors — FCIS violation
services/billing/src/handlers/mrr-sync.handler.ts:438
Bare try/catch only logs to console.error. FCIS requires discriminated-union errors, not swallowed exceptions.
fetchUsdMxnRate throws plain Error instead of Result<T, E>
packages/billing/src/fx.ts:1
ADR-016 FCIS requires discriminated-union errors. Return Result<UsdMxnRate, FxError>.
tests5
No tests for computeSmarterMrr pure function
packages/billing/src/smarter-mrr.ts:1
Pure function with boundary cases — zero tests added.
No tests for readSmarterUsdRate metadata parsing
packages/billing/src/smarter-mrr.ts:1
Parses unknown JSONB blob. All paths (null, wrong keys, happy path) untested.
No tests for fetchUsdMxnRate — injectable fetch unused
packages/billing/src/fx.ts:1
fetchImpl injectable for testing but no test exercises it. Primary, fallback, and both-fail paths all untested.
No integration test for findCalculatedBillCountsByOrg
domains/utility/src/bill/bill.queries.ts:1
COUNT(DISTINCT contract) invariant needs a DB integration test.
Section 10 has no test coverage for skip and FX-fail paths
services/billing/src/handlers/mrr-sync.handler.ts:438
No rate plan → skip; FX throws → swallowed. Both untested.
improvement3
No timeout on FX HTTP calls
packages/billing/src/fx.ts:1
Use AbortSignal.timeout(5000) on both fetch calls.
yearMonth validation missing at query boundary
domains/utility/src/bill/bill.queries.ts:1
Add regex guard at entry so malformed values throw instead of silently returning 0 rows.
Fallback FX error discards upstream cause
packages/billing/src/fx.ts:1
Include response body (first 200 chars) in error for faster production debugging.