Audit, zweite Runde: der Knopf schrieb ein erfundenes Ende (2026.9.3.14)
_letztes_lebenszeichen() fiel auf "jetzt" zurueck, sobald verlauf_lesen() eine leere Liste lieferte - und die bedeutet sowohl "nichts aufgezeichnet" als auch "nicht lesbar". Am 03.09. trat der zweite Fall ein: der Knopf traf einen Neustart, der recorder war noch nicht bereit, die Abfrage lief 2:47 und kam leer zurueck. Die Fahrt bekam 18:43 statt 17:59 als Ende, 74 Minuten statt 30. Drei Quellen in fester Reihenfolge statt einer: Aufzeichnung, dann hass.states (last_reported/last_updated - weiss dasselbe ohne Datenbank), dann "jetzt", und das nur noch mit Warnung. Vier Regressionstests, drei davon gegen den alten Stand nachweislich rot. Dazu aus der Bestandspruefung: odometer_km liegt in drei alten Tankvorgaengen als Zeichenkette vor, ablage.distanz_seit_letzter_tankung() rechnete damit float - str. Laeuft jetzt durch als_kilometerstand(). Der Eingang war schon zu; das ist Altbestand. Und acht Dienste hatten keinen Klarnamen in translations/de.json - jetzt 28 Dienste, 28 Klarnamen. Co-Authored-By: Claude Opus 5 <[email protected]>
This commit is contained in:
@@ -9207,3 +9207,75 @@ klemmen den gelesenen Wert jetzt auf 1–60.
|
||||
Versionssprung ohne `npm run ota` lässt das Bündel zurück — und seit Befund 3
|
||||
sagt die App dann wenigstens die Wahrheit darüber. Die Reihenfolge ist: erhöhen,
|
||||
**dann** Bündel bauen, dann ausliefern.
|
||||
|
||||
### Zweite Runde des Audits: der Knopf hat ein erfundenes Ende geschrieben (2026.9.3.14)
|
||||
|
||||
**Der schwerste Befund des Tages, und er ist an echten Daten aufgefallen.** Der
|
||||
Eigentümer hat „Fahrt beenden" gedrückt; die Fahrt bekam als Ende **18:43:31**
|
||||
statt des letzten Lebenszeichens **17:59:33** — 74 Minuten statt 30. Die
|
||||
Zeitstempel im Protokoll erklären es:
|
||||
|
||||
| | |
|
||||
|---|---|
|
||||
| 18:43:29–31 | Home Assistant startet neu (Auslieferung von `.11`) |
|
||||
| 18:43:31.826 | `_letztes_lebenszeichen()` nimmt `jetzt` |
|
||||
| 18:46:18 | erst jetzt die Meldung — der Verlaufsabruf lief **2:47** und kam **leer** zurück |
|
||||
|
||||
Der Knopf traf genau in den Neustart. Der recorder war noch nicht bereit,
|
||||
`verlauf_lesen()` fing den Fehler ab (so gebaut, „der Verlauf ist Beiwerk, nie
|
||||
der Kern") und lieferte eine leere Liste — **und die ist von „nichts
|
||||
aufgezeichnet" nicht zu unterscheiden**. Der Rückfall auf `jetzt` machte daraus
|
||||
ein erfundenes Fahrtende. Nachgemessen: der recorder hatte die Punkte sehr wohl,
|
||||
fünf Zeilen im Fahrtfenster, letzte um 17:59:33, Abfrage in 0,0 s.
|
||||
|
||||
`_letztes_lebenszeichen()` hat jetzt **drei Quellen in fester Reihenfolge**:
|
||||
|
||||
1. die Aufzeichnung (genauester Wert),
|
||||
2. **`hass.states`** — `last_reported`/`last_updated` der Entität. Weiss dasselbe
|
||||
ohne Datenbank und ist sofort da; genau die Quelle, die im Fehlerfall gefehlt
|
||||
hat. Eine Meldung VOR dem Fahrtbeginn zählt nicht, sie kann nicht das Ende
|
||||
sein,
|
||||
3. `jetzt` — aber nicht mehr stillschweigend, sondern mit Warnung.
|
||||
|
||||
**Vier Regressionstests** (`tests/fahrterkennung/test_fahrtende_knopf.py`, jetzt
|
||||
13); drei davon gegen den alten Rückfall nachweislich rot, alle vier gegen den
|
||||
neuen grün.
|
||||
|
||||
**Die betroffene Fahrt bleibt, wie sie ist:** `t-98425e4f140e`, 03.09. 17:29 bis
|
||||
18:43, `ende_vorlaeufig: True`. Sie gehört dem Eigentümer; korrigiert wird sie
|
||||
über „Werte bearbeiten" (richtiges Ende: 17:59:33) oder gelöscht.
|
||||
|
||||
### Datenbestand: zwei Funde, einer davon eine latente Ausnahme
|
||||
|
||||
Geprüft wurden 17 Fahrten, 16 Tankvorgänge, 16 Batterietage auf Nullstrecken,
|
||||
negative Werte, doppelte IDs, Überlappungen, naive Zeitstempel, Sortierung und
|
||||
Wertebereiche.
|
||||
|
||||
* **Sauber:** keine 0-km-Fahrt, keine negative Strecke, keine doppelte ID, keine
|
||||
Überlappung, beide Dateien aufsteigend, alle Batteriewerte innerhalb 10–15,5 V.
|
||||
* **`odometer_km` als Zeichenkette** in drei Tankvorgängen (`"50250"`,
|
||||
`source: beleg`, 2024/2025). Der Eingang ist längst zu — jede Schreibstelle
|
||||
normalisiert über `verlauf.als_kilometerstand()`, und der Belegweg rührt
|
||||
`odometer_km` grundsätzlich nicht an (§7.7 Regel 2). Im Bestand liegen sie
|
||||
aber weiter, und `ablage.distanz_seit_letzter_tankung()` rechnete
|
||||
`float - str` — ein TypeError, sobald so ein Datensatz der jüngste wäre.
|
||||
Läuft jetzt ebenfalls durch `als_kilometerstand()` (funktionslokal importiert:
|
||||
`verlauf.py` zieht das recorder-Modul mit, und `ablage.py` ist sonst reine
|
||||
Datei-E/A). `verbrauchskorrektur._datum_passt()` war mit seinem `float(...)`
|
||||
schon abgesichert.
|
||||
* Vier naive Zeitstempel bei Tankvorgängen sind Altbestand und seit Abschnitt Y
|
||||
behandelt: die Dublettenprüfung normalisiert auch gespeicherte Werte.
|
||||
|
||||
### Und die Lücke, die seit Wochen offenstand, ist zu
|
||||
|
||||
Acht Dienste hatten keinen Klarnamen in `translations/de.json` und erschienen in
|
||||
den Entwicklerwerkzeugen als roher Schlüssel: `fahrt_jetzt_beenden`,
|
||||
`zugang_setzen`, `dongle_lesen`, `csv_importieren`, `batterieverlauf_loeschen`
|
||||
und die drei `reifen_archiv*`. Jetzt **28 Dienste, 28 Klarnamen**, in beide
|
||||
Richtungen geprüft.
|
||||
|
||||
**Ein Merkposten für mich selbst, aus einem Nebenschauplatz:** eine Warteschleife
|
||||
mit `docker logs` **ohne** `--tail` lief in diesem Container zweieinhalb Stunden
|
||||
im Kreis — das Protokoll hat 1,3 Millionen Zeilen, ein Durchlauf dauert länger
|
||||
als die Pause zwischen zwei Runden. `docker logs --tail 60` beantwortet „ist der
|
||||
Start durch" in Millisekunden.
|
||||
|
||||
Reference in New Issue
Block a user