@pierre-gilles,
First of all, thank you for this response, which has the merit of being complete and honest about your constraints.
To be able to answer you fully, and not just « I’m sure of my code », I took the time to conduct a rigorous audit of the PR this afternoon. I framed the request with an explicit instruction: to verify that each change is necessary, solid, well thought out, without regression, and above all that the tests are not distorted — including by deliberately breaking the code to verify that the tests detect regressions (real mutation testing). This is precisely the legitimate doubt you raise about AI and tests, and I wanted to provide a verifiable answer, not just a statement.
I am providing you below with the result of this audit, factual and reproducible on your side.
1. On your central objection: « the PR deeply modifies the calculation logic »
I maintain my previous answer, but this time with line-by-line verification. The two files where the business logic lives are:
server/services/energy-monitoring/lib/energy-monitoring.calculateConsumptionFromIndex.js
server/services/energy-monitoring/lib/energy-monitoring.calculateCostFrom.js
I have read every line modified. What is strictly unchanged:
convertEnergyUnit(...) — unit conversions
contracts[contract](...) — all cost formulas (base, HP/HC, Tempo)
- Index delta calculation and counter reset management
- Price filtering by date + EDF Tempo +
subtract(30, 'minutes')
saveMultipleHistoricalStates / saveHistoricalState
The only additions in calculateConsumptionFromIndex.js are encapsulated in an if (selectorSet.size > 0) (whitelist filtering): disabled in the absence of a whitelist, so legacy behavior is strictly preserved for the live incremental calculation that runs every 30 minutes.
In calculateCostFrom.js, the additions are:
- Parsing of start/end dates (new entry for range mode)
- Filtering by selectors (encapsulated in
if (selectorSet.size > 0))
destroyStatesBetween if endAt provided, otherwise destroyStatesFrom (legacy behavior)
- A
try/catch per cost feature (robustness: a feature that crashes does not block the others)
The cost formula itself — the one that took you months to make reliable on base/HP/HC/Tempo — has not moved a character. This is verifiable in a few minutes with git diff master HEAD -- server/services/energy-monitoring/lib/energy-monitoring.calculateCostFrom.js and a Ctrl+F on contracts[contract], convertEnergyUnit, energyPricesForDate.
2. On your objection about the tests: « AI can introduce regressions and adapt the tests to validate them »
This is a perfectly legitimate concern, and that’s precisely why I did real mutation testing: I temporarily broke 3 critical points in the code and reran the entire suite. The goal is to verify that the tests detect the regression, not that they pass with the regression.
Here are the 3 mutations tested and the result:
| Applied Mutation |
Tests that Break |
| Date validation removed from the controller |
reject invalid start date format + reject invalid end date format (2 tests) |
Whitelist filter of the live engine calculateConsumptionFromIndex removed |
skip consumption features not in whitelist selectors (1 test) |
finally block for restoring the cursor ENERGY_INDEX_LAST_PROCESSED removed |
restore last processed value on selector-based recalculation + restore last processed value on window error (2 tests) |
Total: 5 tests break precisely on the right assertions. The tests are not AI green-washing, they really detect regressions on critical protections. Reproducible on your side: just comment out these 3 blocks and rerun npm run test-service --service=energy-monitoring. Files restored after verification, I confirmed with git status that there are no residual modifications.
I also verified the non-regression of existing tests:
- The modified legacy tests (on
calculateConsumptionFromIndex.test.js) have only an addition of undefined as the second parameter (to align with the new signature). No assertion has been weakened or removed.
calculateCostFromYesterday.test.js is actually strengthened: before it only checked calledOnce, now it checks the exact signature of the passed arguments.
- Controller Tests: the pattern
try/catch + expect.fail is replaced by next(error) — this is the correct Express pattern.
3. On your proposal « front-only PR with presets 1/3/6/12 months »
I understand the logic: to avoid the heavy validation phase on the business by not touching the backend. But this proposal does not cover the real needs of users, and it’s important that I explain this:
Use Case #1 — Historical recalculation on existing devices added to tracking a posteriori
This is the promise of energy tracking in Gladys. Many users (including me) have Tasmota or Zigbee2mqtt outlets for several years in their Gladys instance, sometimes with 4 years of index data stored. When we add the energy tracking integration to these devices, we must be able to recalculate the consumption and cost over all available history, and not just the last 12 months.
Today, the only way is the global from-beginning, which:
- recalculates for all devices at the same time (and not just the one just added)
- does not complete on substantial installations (case I am experiencing and others report)
- blocks the HTTP request on the front end for the entire duration of the calculation (probably the silent regression that makes us say « it doesn’t work » for large installs)
A preset « last 12 months » does not solve any of these three problems. A selection by feature + date range solves all three.
Use Case #2 — Retroactive tariff
A user who realizes that they changed their tariff 18 months ago and wants to recalculate only that range is blocked with a 12-month preset.
Use Case #3 — Granularity
When you have 30 devices tracked and you just want to correct data on a single device for a week, launching a global recalculation on 1/3/6/12 months is disproportionate and risky for the other devices.
4. On the robustness improvements brought by the PR
I also want to mention two points where the PR makes the code safer than master:
wrapperDetached (new job primitive) decouples the HTTP request from the long calculation. Before, the from-beginning blocked the HTTP request until the end of the calculation, which caused timeouts on large installs. This is probably the silent cause of the « it doesn’t work » that we regularly report.
- The
try/finally around the restoration of the cursor ENERGY_INDEX_LAST_PROCESSED protects the instance from a corrupted state if a window fails during recalculation. On master, if a window crashes, the cursor remains destroyed and the live incremental calculation starts from zero at the next iteration.
5. Honest points that I will correct before push
To not tell you that everything is perfect, the audit also raised 3 minor cleanups (non-blocking, no risk of regression):
- A backward-compat fragment in
calculateCostFrom that is no longer useful (all internal callers already pass the new signature).
- A ~70% duplication between
FromBeginning and Range that I can factorize.
- 2-3 tests « passing by chance » in
calculateCostFrom.test.js that do not really test what they claim (to be hardened or removed — the real business tests remain in the original unmodified tests).
I commit to correcting these 3 points before the split.
6. My concrete proposal
Given the above, I still propose the split into sub-PRs, but by accommodating your concern about the « uncompressible validation phase »:
- PR1:
destroyStatesBetween + wrapperDetached + their tests. ~150-200 lines. No business logic, just utilities. Can be merged quickly, low-risk.
- PR2: Recalculation by feature selection from the start (without date range). The whitelist filter is encapsulated in
if (selectorSet.size > 0), so the legacy behavior is strictly preserved.
- PR3: Recalculation by date range +
shouldRestoreLastProcessed. This is where the « real » business review is focused.
- PR4: UI adjustments.
On PR3 — the one that requires the most validation from you — I’m ready to:
- Provide reproducible test datasets from my production (anonymized SQL output) that you can replay at your end
- Add code documentation on
shouldRestoreLastProcessed
- Give you a video demonstration of the before/after behavior if it helps
If you validate this breakdown, I’ll start with PR1 this week. If you prefer I stay on hold, let me know with a timeline — even a broad one — and I’ll close the PR again calmly.
Thanks for your time, and sorry if the response is dense — I preferred to give you facts rather than assertions.