← all branches

feat/kwh-price

needs attentionviewing older commit
b968509 · fullPR #344reviewed 2026-07-23 15:48 UTC0H · 4M · 3L · 2I
The branch
Purpose
Incremental $-layer for the energy findings system: demand detectors now compute estimatedImpact in MXN using real CFE tariff rates.
Goal
Price D2b overnight_load_factor and finalize D1 punta_management with real genBCost/capacityCost from tariff_rates — so findings can be prioritized by dollar magnitude alongside billing errors.
Sub-goals
  • New consumption-series module: pure FCIS, calendar-allocated dedup kWh series
  • D1 punta_management: real capacityCostMxnPerKw from tariff_rates
  • D2b overnight_load_factor: estimatedImpact = (util-median) x avgBaseKw x 12 x genBCost
  • Unified ContractRate type, single fetchContractRatesForOrg query for both detectors
The changes (whole branch)
What
Added consumption-series FCIS module (type, decisions, errors, queries, tests) and extended finding domain with real-rate $-layer for D1+D2b demand detectors via new fetchContractRatesForOrg query. Detector versions bumped to @2.
Why
Detectors previously ranked findings by severity only; with real rates they can be sorted by estimated annual MXN impact — a key prioritization signal for the Batu for Enterprise product.
Areas
domains/utility/src/consumption-series+8620domains/utility/src/finding+1153371docs/energia+2910
Blast
3 areas, 14 files, +2306/-371 lines. No schema changes, no API surface changes, no new Lambda or CDK resources. Pure domain logic + one new SQL query.
stacked-on-pr-343 no-migration domain-only
typecheck· not run in PR checkstests· no CI checks visiblecoderabbit· no .coderabbit.yamlseo· no apps/web changes

Findings · 9

correctness4

medium

LATERAL filter gates genBCost on capacityCost presence — stale rates if fields diverge

domains/utility/src/finding/finding.queries.ts:813

In PostgreSQL NULL <> '' evaluates to NULL, so rate rows lacking capacityCost are excluded. D2b silently reads genBCost from a potentially older row if CFE publishes a partial update.

medium

D1 rateByContract lacks > 0 guard — asymmetric with D2b

domains/utility/src/finding/finding.decisions.ts:1069

D2b filters baseCostMxnPerKwh > 0 before inserting into its map; D1 does not guard capacityCostMxnPerKw. A zero value suppresses the documented DEFAULT_PUNTA_CAPACITY_RATE_MXN_PER_KW fallback.

low

4x `as number` casts suppress TypeScript narrowing

domains/utility/src/finding/finding.decisions.ts:1079

Casts at lines 1079, 1170, 1174, 1239 are semantically safe but fragile under refactoring. Use type-predicate filters instead.

info

D2b avoidableBaseKwhPerYear positivity guaranteed implicitly, not explicitly

domains/utility/src/finding/finding.decisions.ts:1214

Positivity follows from ratio > D2B_WARN_RATIO, but is not asserted. Low risk; named avoidable implies positive.

conventions1

low

ContractRate type defined in finding.decisions.ts, imported by finding.queries.ts

domains/utility/src/finding/finding.decisions.ts:1046

Per canonical form, domain types belong in finding.type.ts. Both decisions and queries import from the same source to avoid lateral coupling.

tests4

medium

Missing test: D2b estimatedImpact null when baseCostMxnPerKwh = 0 but D1 resolves

domains/utility/src/finding/__tests__/finding.detectors.test.ts

The > 0 guard asymmetry between D1 and D2b is not exercised by any test. A ContractRate with positive capacityCost but zero baseCost should produce null for D2b while D1 still computes a figure.

medium

No integration test for fetchContractRatesForOrg LATERAL join semantics

domains/utility/src/finding/finding.queries.ts:789

The LATERAL filter, pricing_zone_id IS NOT NULL gate, and baseCostMxnPerKwh fallback are not covered at the DB level. The shell integration test does not seed tariff_rates rows.

low

Missing test for perPeriod absent on mixed-coverage bimonthly month

domains/utility/src/consumption-series/__tests__/consumption-series.decisions.test.ts

The daysWithPerPeriod === daysCovered guard's negative branch is untested. A bimonthly bill with mixed per-period coverage should emit perPeriod: undefined.

info

decideUpsertFinding null→number estimatedImpact transition untested

domains/utility/src/finding/__tests__/finding.detectors.test.ts

The null→number path in decideUpsertFinding is not covered. Logic is correct but the transition case is implicit.

History · 3 commits

  1. fcf7919safeincremental0H · 0M · 1L2026-07-23 21:00
  2. 87725ffsafeincremental0H · 1M · 2L2026-07-23 16:02
  3. b968509needs attentionfull0H · 4M · 3L2026-07-23 15:48current