Where: src/lib/scoring-formula.ts, computeScoresWithFormula()
(lines ~94-115).
What's wrong:
const powerComponent = (solar.power_output_kw / solar.max_power_kw) * 100 * w.power_weight;
There is no guard against solar.max_power_kw === 0. Compare with the
default scoring path in src/lib/scoring.ts:
const powerRatio = maxPower > 0 ? powerOutput / maxPower : POWER_RATIO_FALLBACK;
which explicitly falls back rather than dividing by zero. If
max_power_kw is 0 and power_output_kw is also 0 (a plausible reading
for an idle/misconfigured panel), 0 / 0 is NaN, which then propagates
through Math.max(0, Math.min(100, NaN)) → NaN, and
Math.round(NaN) → NaN. credit_quality ends up NaN instead of a
number.
Impact: this function is reachable via GET /v1/scoring/formulas/:id/preview/:projectId
(src/routes/scoring-formulas.ts), which feeds it live simulated
solar/satellite readings. A NaN credit_quality would flow into the
response's scores.withFormula/delta fields un-caught (no isNaN check
anywhere on this path), unlike computeScores, whose output is always a
finite number by construction.
Suggested fix: add the same zero-guard used in scoring.ts:
solar.max_power_kw > 0 ? solar.power_output_kw / solar.max_power_kw : 0.
There is no src/__tests__/scoring-formula.test.ts at all (confirmed: zero
references to scoring-formula/ScoringFormula anywhere under
src/__tests__) — add coverage for this edge case once fixed.
Where:
src/lib/scoring-formula.ts,computeScoresWithFormula()(lines ~94-115).
What's wrong:
There is no guard against
solar.max_power_kw === 0. Compare with thedefault scoring path in
src/lib/scoring.ts:which explicitly falls back rather than dividing by zero. If
max_power_kwis0andpower_output_kwis also0(a plausible readingfor an idle/misconfigured panel),
0 / 0isNaN, which then propagatesthrough
Math.max(0, Math.min(100, NaN))→NaN, andMath.round(NaN)→NaN.credit_qualityends upNaNinstead of anumber.
Impact: this function is reachable via
GET /v1/scoring/formulas/:id/preview/:projectId(
src/routes/scoring-formulas.ts), which feeds it live simulatedsolar/satellite readings. A
NaNcredit_quality would flow into theresponse's
scores.withFormula/deltafields un-caught (noisNaNcheckanywhere on this path), unlike
computeScores, whose output is always afinite number by construction.
Suggested fix: add the same zero-guard used in
scoring.ts:solar.max_power_kw > 0 ? solar.power_output_kw / solar.max_power_kw : 0.There is no
src/__tests__/scoring-formula.test.tsat all (confirmed: zeroreferences to
scoring-formula/ScoringFormulaanywhere undersrc/__tests__) — add coverage for this edge case once fixed.