Reconcile calculation audit and changelog with degradation work #21

Merged
claude-bot merged 1 commit from docs/reconcile-degradation into main 2026-07-17 02:00:20 +00:00
Contributor

Follow-up to #20. Docs-only.

What went wrong

I cut the #20 branch from 1d2a274 without fetching first. Main had already advanced to b31aa8c (Add battery degradation and rate-escalation-adjusted payback, Jun 5). Git merged cleanly and the merged tree is green — I re-ran the full suite on merged main: 46 frontend tests, 22 backend tests, build all pass. But there were no textual conflicts precisely because the collision was semantic: the docs I merged described the pre-degradation code.

CI passing on #20 did not catch this, because it ran against my branch head, which never contained b31aa8c.

What was wrong on main

Paybacks documented as plain division. The audit stated setupPaybackVsAnytimeYears = totalInstalledCost / vsAnytime and similar. Every payback now goes through calculateAdjustedPaybackYears (payback.js:11), which compounds (1 + escalationPct/100) * (1 - degradationPct/100) per year and interpolates the break-even year, capped at 50. Documented the real algorithm, including the short-circuit to plain division when both rates are zero.

Two claims that were outright false, both asserting battery degradation is not modeled — in the projection caveats and in What Is Not Included In The Money Model. Battery and solar degradation are both modeled now. Replacement, maintenance, and cycle limits genuinely remain unmodeled; cycle count under dispatch stays tracked in #11.

Projection escalation formula. Now degrades battery and solar savings independently while leaving rate-plan-switch savings untouched — a real modeling decision that was undocumented.

Drifted line references: runAllPlansSimulation (113→120), calcParametricBill (247→250), calculateQuoteComparisons (338→349), buildProjectionData (55→73), the scenario gates, buildCalculationDataRevision, the output object, and the warnings array.

Two identifiers that never existedsetupPaybackVsAnytimeYears and batteryInverterPaybackYears — replaced with the real paybackVsAnytimeYears and paybackYears. These predate b31aa8c; they were wrong when originally written.

Changelog

Recorded the degradation and adjusted-payback work under Unreleased, plus the lockfiles and gitignore from #20.

Lesson

Fetch before branching. My earlier "formulas verified accurate against the code" claim was true against 1d2a274 and not against main.

Follow-up to #20. Docs-only. ## What went wrong I cut the #20 branch from `1d2a274` without fetching first. Main had already advanced to `b31aa8c` (*Add battery degradation and rate-escalation-adjusted payback*, Jun 5). Git merged cleanly and the merged tree is green — I re-ran the full suite on merged main: 46 frontend tests, 22 backend tests, build all pass. But there were no *textual* conflicts precisely because the collision was semantic: the docs I merged described the pre-degradation code. CI passing on #20 did not catch this, because it ran against my branch head, which never contained `b31aa8c`. ## What was wrong on main **Paybacks documented as plain division.** The audit stated `setupPaybackVsAnytimeYears = totalInstalledCost / vsAnytime` and similar. Every payback now goes through `calculateAdjustedPaybackYears` (`payback.js:11`), which compounds `(1 + escalationPct/100) * (1 - degradationPct/100)` per year and interpolates the break-even year, capped at 50. Documented the real algorithm, including the short-circuit to plain division when both rates are zero. **Two claims that were outright false**, both asserting battery degradation is not modeled — in the projection caveats and in *What Is Not Included In The Money Model*. Battery and solar degradation are both modeled now. Replacement, maintenance, and cycle limits genuinely remain unmodeled; cycle count under dispatch stays tracked in #11. **Projection escalation formula.** Now degrades battery and solar savings independently while leaving rate-plan-switch savings untouched — a real modeling decision that was undocumented. **Drifted line references**: `runAllPlansSimulation` (113→120), `calcParametricBill` (247→250), `calculateQuoteComparisons` (338→349), `buildProjectionData` (55→73), the scenario gates, `buildCalculationDataRevision`, the output object, and the warnings array. **Two identifiers that never existed** — `setupPaybackVsAnytimeYears` and `batteryInverterPaybackYears` — replaced with the real `paybackVsAnytimeYears` and `paybackYears`. These predate `b31aa8c`; they were wrong when originally written. ## Changelog Recorded the degradation and adjusted-payback work under `Unreleased`, plus the lockfiles and gitignore from #20. ## Lesson Fetch before branching. My earlier "formulas verified accurate against the code" claim was true against `1d2a274` and not against main.
Reconcile calculation audit and changelog with degradation work
All checks were successful
CI / Frontend tests, backend tests, and build (pull_request) Successful in 57s
23392f9989
My branch for #20 was cut from 1d2a274 while main had already advanced to
b31aa8c. The merge was textually clean and tests pass, but the docs merged in
described the pre-degradation code, so several claims landed on main wrong.

calculation-logic-audit.md:

- Paybacks were documented as plain cost/savings division. All paybacks now go
  through calculateAdjustedPaybackYears, which compounds rate escalation and
  degradation per year and interpolates the break-even year, capped at 50.
  Documented the real algorithm and its zero-escalation short circuit.
- Corrected two claims that were outright false after b31aa8c: that battery
  degradation is not modeled, in both the projection caveats and the money
  model exclusions. Replacement, maintenance, and cycle limits remain
  genuinely unmodeled; cycle count under dispatch is tracked in #11.
- Fixed the projection escalation formula, which now degrades battery and solar
  savings independently while leaving rate-plan-switch savings untouched.
- Corrected line references that drifted: runAllPlansSimulation, calcParametricBill,
  calculateQuoteComparisons, buildProjectionData, the scenario gates,
  buildCalculationDataRevision, the output object, and the warnings array.
- Removed setupPaybackVsAnytimeYears and batteryInverterPaybackYears, which
  never existed in the code, in favour of the real paybackVsAnytimeYears and
  paybackYears.

CHANGELOG.md: recorded the degradation and adjusted-payback work under
Unreleased, along with the lockfiles and gitignore.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
claude-bot deleted branch docs/reconcile-degradation 2026-07-17 02:00:20 +00:00
Sign in to join this conversation.
No description provided.