feat/kwh-price
safefcf7919 · incrementalPR #344reviewed 2026-07-23 21:00 UTC0H · 0M · 1L · 2I- Purpose
- Port the $-layer pricing into energy findings so D1 (punta_management) and D2b (overnight_load_factor) carry estimated $ impact, enabling ranking alongside FP findings.
- Goal
- Price D1 with capacityCostMxnPerKw (falling back to $380/kW default when rate is zero/unresolved) and D2b with genBCost/baseCostMxnPerKwh (impact=null when unresolved). Both detectors share a single ContractRate[] input from fetchContractRatesForOrg.
- Sub-goals
- D1 punta_management: price avoidable punta demand × capacityCostMxnPerKw × 12; >0 guard avoids storing $0 rate
- D2b overnight_load_factor: price overnight gap × avgBaseKw × 12 × baseCostMxnPerKwh; >0 guard, fields absent when unpriced
- Shared ContractRate type and fetchContractRatesForOrg query replace per-detector ContractCapacityRate
- Test coverage: 81 detector+consumption tests across the full branch
- What
- Single test file: +1 test in detectPuntaManagement (verifies the cap=0 >0 guard falls through to $380 default), +2 assertions in an existing detectPerPeriodLoadFactor test (verifies baseCostMxnPerKwh and avoidableBaseKwhPerYear are absent when baseCost is unresolved). No production code changes in this commit.
- Why
- Loop C (iteration 2) flagged a gap: the D1 guard (>0 on capacityCostMxnPerKw) was added last commit but had no dedicated test. The D2b asymmetric-guard test existed but only checked impactBasis, not the other two conditionally-emitted fields.
- Areas
- domains/utility/src/finding+718−173domains/utility/src/consumption-series+580−0docs/energia+291−0
- Blast
- 14 files, +2981/−374 across utility domain + energia docs. Test-only on this commit. No infra, no API surface, no migrations.
Findings · 3
conventions1
Inline comment body partially overlaps the it() description
domains/utility/src/finding/__tests__/finding.detectors.test.ts:482
The 3-line comment above the fixture restates the test name with one addendum ('not a suppressed $0 finding'). The comment qualifies as WHY (pinning non-obvious guard behavior), so it meets the comment policy. Reducing it to one line covering only the non-obvious part would tighten the test.
tests1
No test for double-zero ContractRate row in D1 (cap=0 AND base=0)
domains/utility/src/finding/__tests__/finding.detectors.test.ts
The new D1 test uses {capacityCostMxnPerKw: 0, baseCostMxnPerKwh: 1}. A row with both fields zero — plausible if the DB returns a rate row before pricing is seeded — also falls through to the $380 default via the cap>0 guard. Not a regression risk (the guard is the same), but a combined-zero fixture would rule out any future condition on baseCostMxnPerKwh being added to D1's loop.
improvement1
New D1 test and existing fallback test assert identical expected values — consider test.each if a third variant appears
domains/utility/src/finding/__tests__/finding.detectors.test.ts:481
The new test (rates=[{cap:0,base:1}]) and the existing test at ~line 469 (rates=[]) both verify capacityRateMxnPerKw=380, rateResolved=false, estimatedImpact≈91200 — because both result in an empty rateByContract map. The distinction (zero-valued entry vs no entry at all) is the point of the new test and is valid. If a third guard variant appears, a parameterized test.each would eliminate the duplication.