@pierre-gilles,
Zunächst einmal vielen Dank für diese Antwort, die den Vorteil hat, vollständig und ehrlich zu deinen Einschränkungen zu sein.
Um dir vollständig antworten zu können und nicht nur « Ich bin mir meines Codes sicher », habe ich mir die Zeit genommen, eine gründliche Überprüfung des PR (Pull Request) am Nachmittag durchzuführen. Ich habe die Anforderung mit einer klaren Anweisung gerahmt: Überprüfen, dass jede Änderung notwendig, solide, gut durchdacht, ohne Rückschritte und vor allem, dass die Tests nicht verfälscht werden — einschließlich des absichtlichen Brechens des Codes, um sicherzustellen, dass die Tests Rückschritte tatsächlich erkennen (echte Mutationstests). Genau das ist der legitime Zweifel, den du bezüglich KI und Tests aufwirfst, und ich wollte eine überprüfbare Antwort darauf geben, keine bloße Behauptung.
Ich präsentiere dir unten das Ergebnis dieser Überprüfung, das bei dir faktisch und reproduzierbar ist.
1. Zu deinem zentralen Einwand: « Der PR verändert die Logik der Berechnung grundlegend »
Ich behalte meine vorherige Antwort bei, diesmal jedoch mit einer zeilenweisen Überprüfung. Die beiden Dateien, in denen die Geschäftslogik lebt, sind:
server/services/energy-monitoring/lib/energy-monitoring.calculateConsumptionFromIndex.js
server/services/energy-monitoring/lib/energy-monitoring.calculateCostFrom.js
Ich habe jede geänderte Zeile gelesen. Was streng genommen unverändert bleibt:
convertEnergyUnit(...) — Einheitenumrechnungen
contracts[contract](...) — alle Kostenformeln (Basis, HP/HC, Tempo)
- Berechnung des Index-Deltas und Verwaltung des Zähler-Rücksetzens
- Filterung der Preise nach Datum + EDF Tempo +
subtract(30, 'minutes')
saveMultipleHistoricalStates / saveHistoricalState
Die einzigen Ergänzungen in calculateConsumptionFromIndex.js sind in einem if (selectorSet.size > 0) eingekapselt (Filterung durch Whitelist): deaktiviert bei fehlender Whitelist, daher wird das Legacy-Verhalten für die Live-Inkrementberechnung, die alle 30 Minuten läuft, strikt beibehalten.
In calculateCostFrom.js sind die Ergänzungen:
- Parsing der Start-/Enddaten (neuer Eintrag für den Bereichsmodus)
- Filterung nach Selectoren (eingekapselt in
if (selectorSet.size > 0))
destroyStatesBetween, wenn endAt angegeben ist, andernfalls destroyStatesFrom (Legacy-Verhalten)
- Ein
try/catch pro Kostenfunktion (Robustheit: eine Funktion, die abstürzt, blockiert die anderen nicht)
Die Kostenformel selbst — diejenige, die du monatelang auf Basis/HP/HC/Tempo stabilisiert hast — wurde nicht um ein einziges Zeichen verändert. Das ist in wenigen Minuten mit git diff master HEAD -- server/services/energy-monitoring/lib/energy-monitoring.calculateCostFrom.js und einem Strg+F auf contracts[contract], convertEnergyUnit, energyPricesForDate überprüfbar.
2. Zu deinem Einwand bezüglich der Tests: « KI kann Rückschritte einführen und die Tests anpassen, um sie zu validieren »
Das ist eine völlig legitime Sorge, und genau deshalb habe ich echte Mutationstests durchgeführt: Ich habe temporär drei kritische Punkte des Codes gebrochen und die gesamte Suite erneut gestartet. Das Ziel ist es, zu überprüfen, dass die Tests den Rückschritt erkennen, nicht, dass sie bestehen mit dem Rückschritt.
Hier sind die drei getesteten Mutationen und das Ergebnis:
| Angewandte Mutation |
Tests, die brechen |
| Validierung der Daten aus dem Controller entfernt |
reject invalid start date format + reject invalid end date format (2 Tests) |
Whitelist-Filter des Live-Motors calculateConsumptionFromIndex entfernt |
skip consumption features not in whitelist selectors (1 Test) |
finally-Block zur Wiederherstellung des Cursors ENERGY_INDEX_LAST_PROCESSED entfernt |
restore last processed value on selector-based recalculation + restore last processed value on window error (2 Tests) |
Gesamt: 5 Tests brechen genau bei den richtigen Behauptungen. Die Tests sind kein KI-Greenwashing, sie erkennen tatsächlich Rückschritte bei kritischen Schutzmaßnahmen. Reproduzierbar bei dir: Es reicht, diese drei Blöcke zu kommentieren und npm run test-service --service=energy-monitoring erneut zu starten. Die Dateien wurden nach der Überprüfung wiederhergestellt, ich habe mit git status bestätigt, dass keine restlichen Änderungen vorhanden sind.
Ich habe auch die Nicht-Rückschrittigkeit der bestehenden Tests überprüft:
- Die geänderten Legacy-Tests (in
calculateConsumptionFromIndex.test.js) haben ausschließlich eine Ergänzung von undefined als zweitem Parameter (um sich an die neue Signatur anzupassen). Keine Behauptung wurde geschwächt oder entfernt.
calculateCostFromYesterday.test.js ist tatsächlich gestärkt: Früher überprüfte er nur calledOnce, jetzt überprüft er die exakte Signatur der übergebenen Argumente.
- Controller-Tests: Das Muster
try/catch + expect.fail wird durch next(error) ersetzt — das ist das korrekte Express-Muster.
3. Zu deinem Vorschlag « PR nur für Frontend mit Presets 1/3/6/12 Monate »
Ich verstehe die Logik: Vermeide die schwere Validierungsphase im Geschäftsbereich, indem du den Backend nicht anfasst. Aber dieser Vorschlag deckt den tatsächlichen Bedarf der Benutzer nicht ab, und es ist wichtig, dass ich das erkläre:
Anwendungsfall Nr. 1 — Historische Neuberechnung für bestehende Geräte, die nachträglich überwacht werden
Das ist die Versprechen der Energienachverfolgung in Gladys. Viele Benutzer (auch ich) haben seit mehreren Jahren Tasmota- oder Zigbee2mqtt-Steckdosen in ihrer Gladys-Instanz, manchmal mit 4 Jahren gespeicherten Indexdaten. Wenn man die Energieüberwachungsintegration für diese Geräte hinzufügt, muss man die Verbrauch- und Kostenberechnung über den gesamten verfügbaren Verlauf neu berechnen können, nicht nur über die letzten 12 Monate.
Heute ist der einzige Weg die globale Neuberechnung von Anfang an, die:
- für alle Geräte gleichzeitig neu berechnet (und nicht nur für das, das gerade hinzugefügt wurde)
- bei größeren Installationen nicht abgeschlossen wird (ein Fall, den ich erlebe und den andere melden)
- die HTTP-Anfrage auf der Frontend-Seite während der gesamten Berechnungsdauer blockiert (wahrscheinlich der stille Rückschritt, der dazu führt, dass man « es funktioniert nicht » für große Installationen sagt)
Ein Preset « Letzte 12 Monate » löst keines dieser drei Probleme. Eine Auswahl nach Funktion + Datumsbereich löst alle drei.
Anwendungsfall Nr. 2 — Der rückwirkende Tarif
Ein Benutzer, der feststellt, dass er vor 18 Monaten seinen Tarif geändert hat und nur diesen Zeitraum neu berechnen möchte, ist mit einem 12-Monats-Preset blockiert.
Anwendungsfall Nr. 3 — Die Granularität
Wenn man 30 überwachte Geräte hat und nur eine Datenkorrektur für ein einzelnes Gerät über eine Woche durchführen möchte, ist eine globale Neuberechnung über 1/3/6/12 Monate unverhältnismäßig und riskant für die anderen Geräte.
4. Zu den Robustheitsverbesserungen, die durch den PR gebracht werden
Ich möchte auch zwei Punkte erwähnen, in denen der PR den Code sicherer macht als master:
wrapperDetached (neue Job-Primitive) entkoppelt die HTTP-Anfrage von der langen Berechnung. Früher blockierte die Neuberechnung von Anfang an die HTTP-Anfrage bis zum Ende der Berechnung, was bei großen Installationen zu Timeouts führte. Das ist wahrscheinlich die stille Ursache für die « Es funktioniert nicht »-Meldungen, die regelmäßig gemeldet werden.
- Der
try/finally-Block um die Wiederherstellung des Cursors ENERGY_INDEX_LAST_PROCESSED schützt die Instanz vor einem korrupten Zustand, wenn ein Fenster während der Neuberechnung abstürzt. In master, wenn ein Fenster abstürzt, bleibt der Cursor zerstört und die Live-Inkrementberechnung startet bei der nächsten Iteration von vorne.
5. Ehrliche Punkte, die ich vor dem Push korrigieren werde
Um dir nicht zu erzählen, dass alles perfekt ist, hat die Überprüfung auch drei kleinere Cleanups (nicht blockierend, ohne Risiko eines Rückschritts) aufgedeckt:
- Ein Fragment der Abwärtskompatibilität in
calculateCostFrom, das keine Bedeutung mehr hat (alle internen Aufrufer verwenden bereits die neue Signatur).
- Eine etwa 70%ige Duplikation zwischen
FromBeginning und Range, die ich faktorisieren kann.
- 2-3 « zufällig bestandene » Tests in
calculateCostFrom.test.js, die nicht wirklich das testen, was sie behaupten (zu härten oder zu entfernen — die echten Geschäftstests bleiben in den ursprünglichen, unmodifizierten Tests).
Ich verpflichte mich, diese drei Punkte vor dem Aufteilen zu korrigieren.
6. Mein konkreter Vorschlag
Angesichts dessen schlage ich dir immer noch das Aufteilen in Unter-PRs vor, jedoch mit der Berücksichtigung deiner Sorge um « unverzichtbare Validierungsphase »:
- PR1 :
destroyStatesBetween + wrapperDetached + deren Tests. ~150-200 Zeilen. Keine Geschäftslogik, nur Hilfsfunktionen. Kann schnell gemerged werden, geringes Risiko.
- PR2 : Neuberechnung durch Feature-Auswahl von Anfang an (ohne Datumsbereich). Der Whitelist-Filter ist in
if (selectorSet.size > 0) gekapselt, daher bleibt das Legacy-Verhalten streng erhalten.
- PR3 : Neuberechnung durch Datumsbereich +
shouldRestoreLastProcessed. Hier liegt die „echte“ geschäftliche Überprüfung.
- PR4 : UI-Anpassungen.
Bei PR3 — diejenige, die dir die meisten Validierungen abverlangt — bin ich bereit:
- Dir reproduzierbare Testdaten aus meiner Produktion (anonymisierte SQL-Ausgabe) bereitzustellen, die du bei dir nachspielen kannst
- Dokumentation im Code zu
shouldRestoreLastProcessed hinzuzufügen
- Dir eine Video-Demonstration des Verhaltens vor/nachher zu geben, falls das hilft
Falls du diese Aufteilung validierst, beginne ich diese Woche mit PR1. Falls du lieber möchtest, dass ich in der Warteschleife bleibe, lass es mich mit einem Zeithorizont — auch wenn er weit ist — wissen, und ich werde die PR wieder gelassen schließen.
Danke für deine Zeit, und entschuldige, falls die Antwort dicht ist — ich habe lieber Fakten geliefert als Behauptungen.