Audit: Livepfad und Rueckblick messen wieder dasselbe (2026.9.1.17)
BEFUND 1 (behoben): dieselbe Fahrt, zwei Strecken screening.py las den Verlauf ueber verlauf_lesen(), das den Stand zu Beginn des Fensters mitliefert. historienimport.py schnitt sein Fenster streng heraus und verlor genau diesen Punkt. An der echten Fahrt nachgemessen: 6,896 km gegen 6,816 km fuer denselben Zeitraum. Neu verlauf.zaehlerstrecke(punkte, start, ende) - der Anker ist der letzte Datensatz am oder vor dem Beginn, dieselbe Ueberlegung wie bei wert_bei(). Beide Wege schneiden jetzt durch dieselbe Funktion. Nachgewiesen: Rueckblick 6,896, Livepfad 6,896. Dritter Fall dieser Art nach UNPLAUSIBLE_KMH und MINDESTDAUER_S - und ich hatte ihn am selben Tag selbst eingebaut. BEFUND 2 (behoben): STANDARDWERTE gingen unnoetig ueber die Leitung. zuordnung.py schickte sie mit jeder Katalogantwort; gelesen hat sie seit dem Entfernen des Zuruecksetzen-Knopfes niemand mehr. Die Konstante bleibt, sie traegt intern die Vorgabewerte und leitet SCHLUESSEL ab. BEFUND 3 und 4 bleiben offen und gehoeren dem Eigentuemer: die vier Tage alte Reichweite auf der Uebersicht (RANGE_SENSOR besser leer lassen) und die Nullfahrt-Regel, die nur der Rueckblick kennt - im Livepfad hiesse sie, einen bereits gespeicherten Datensatz automatisch zu loeschen. SAUBER: 24 Backend-Dateien py_compile, Panel als Modul geparst, tsc --noEmit und vite build sauber, 165/165 Tests, 27 Katalogeintraege gegen 27 Dataclass-Felder ohne Abweichung, jedes Listenfeld mit so vielen Beispielen wie Positionen, keine verwaisten Verweise, in der Konsole nur das bekannte ServiceWorker-Rauschen. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -1,7 +1,8 @@
|
||||
# AGENTS.md — Project state, review findings, open items, and working rules
|
||||
|
||||
**Last updated: 2026-09-01** (Setup: gruene Bestaetigung und Erwartungswert je Listenposition,
|
||||
`2026.9.1.16`, Abschnitt BG. Davor: Rueckblick ohne Nullfahrten, Streckenwahl gemeinsam in `verlauf.py`,
|
||||
**Last updated: 2026-09-01** (Audit ueber Panel, App und Backend: vier Befunde, zwei behoben,
|
||||
`2026.9.1.17`, Abschnitt BH. Davor: Setup mit gruener Bestaetigung und Erwartungswert je
|
||||
Listenposition, Abschnitt BG. Davor: Rueckblick ohne Nullfahrten, Streckenwahl gemeinsam in `verlauf.py`,
|
||||
`2026.9.1.14`, Abschnitt BF. Davor: Fahrtstrecke auf 100 m: CAN als Anker, GNSS als Nachkommastelle,
|
||||
`2026.9.1.10`, Abschnitt BE. Davor: Nachlauf-Abzug nur mit zugeordnetem `TRIP_SENSOR`, `2026.9.1.9`;
|
||||
davor „502" beim Neustart entschärft, OTA-Bündel auf `2026.9.1.8`
|
||||
@@ -6935,6 +6936,64 @@ ganze Zeit richtig.
|
||||
oder Stylesheet - nicht davor. Wer waehrend der Pruefung noch nachbessert, erhoeht danach noch
|
||||
einmal.
|
||||
|
||||
## BH. Audit vom 01.09.2026 — vier Befunde, zwei behoben (2026.9.1.17)
|
||||
|
||||
Nach einem Tag mit siebzehn Auslieferungen ein Rundumblick über beide Oberflächen und das Backend.
|
||||
|
||||
**Was sauber war:** 24 Backend-Dateien `py_compile`, Panel als **Modul** geparst (Abschnitt AQ),
|
||||
`tsc --noEmit` und `vite build` sauber, 165/165 Tests grün, 27 Katalogeinträge gegen 27
|
||||
Dataclass-Felder ohne Abweichung in beide Richtungen, keine doppelten Schlüssel, jedes Feld mit
|
||||
Beispielnamen, jedes Listenfeld mit so vielen Beispielen wie Positionen. In der Konsole nur das
|
||||
bekannte ServiceWorker-Rauschen. Keine verwaisten Verweise auf das entfernte
|
||||
`„Auf Standard zurücksetzen"`.
|
||||
|
||||
### Befund 1 (behoben): Livepfad und Rückblick maßen dieselbe Fahrt unterschiedlich
|
||||
|
||||
Die schwerste Sorte Fehler in diesem Projekt — und ich hatte sie am selben Tag selbst eingebaut.
|
||||
|
||||
`screening.py` las den Verlauf über `verlauf_lesen()`, das den Stand **zu Beginn** des Fensters
|
||||
mitliefert. `historienimport.py` schnitt sein Fenster streng heraus (`f_start <= p[0] <= f_ende`)
|
||||
und verlor genau diesen Punkt. An der echten Fahrt nachgemessen: **6,896 km gegen 6,816 km** für
|
||||
denselben Zeitraum, je nachdem welcher Weg sie bewertete.
|
||||
|
||||
Neu `verlauf.zaehlerstrecke(punkte, start, ende)`: der Anker ist der letzte Datensatz **am oder
|
||||
vor** dem Beginn, dieselbe Überlegung wie bei `wert_bei()`. Beide Wege schneiden jetzt durch
|
||||
dieselbe Funktion. Nachgewiesen: Rückblick 6,896 — Livepfad 6,896.
|
||||
|
||||
Dritter Fall dieser Art nach `UNPLAUSIBLE_KMH` und `MINDESTDAUER_S`. **Regel bestätigt sich:**
|
||||
sobald zwei Wege dieselbe Größe berechnen, gehört die Rechnung in `verlauf.py` — nicht als
|
||||
Aufräumarbeit hinterher, sondern beim ersten Mal.
|
||||
|
||||
### Befund 2 (behoben, kleiner): `STANDARDWERTE` gingen unnötig über die Leitung
|
||||
|
||||
`zuordnung.py` schickte sie mit jeder Katalogantwort ans Frontend. Gelesen hat sie dort seit dem
|
||||
Entfernen des Zurücksetzen-Knopfes (`2026.9.1.11`) niemand mehr. Die Konstante selbst bleibt — sie
|
||||
trägt intern die Vorgabewerte und leitet `SCHLUESSEL` ab; nur die Veröffentlichung ist weg.
|
||||
|
||||
### Befund 3 (offen, gehört dem Eigentümer): die Reichweite ist vier Tage alt
|
||||
|
||||
Auf der Übersicht steht **87 km** als größte Zahl des Bildschirms — daneben 61 % Tank. Der Wert
|
||||
stammt vom 29.08. und aus einem **anderen Fahrzeug**; die beiden zuletzt genutzten Autos liefern
|
||||
`can.vehicle.remaining.range` nicht über CAN (an flespis Zeitstempeln nachgewiesen, HAs Recorder
|
||||
kann tote von konstanten Sensoren nicht unterscheiden).
|
||||
|
||||
Kein Codefehler. Solange kein Fahrzeug den Wert liefert, wäre die Rolle `RANGE_SENSOR` besser leer:
|
||||
dann steht dort „Reichweite unbekannt" statt einer falschen Zahl. Im Endzustand liefert das
|
||||
Fahrzeug sie wieder — Entscheidung des Eigentümers.
|
||||
|
||||
### Befund 4 (offen, braucht eine Entscheidung): die Nullfahrt-Regel gilt nur im Rückblick
|
||||
|
||||
`ist_gefahren()` wird nur von `historienimport.py` aufgerufen. Der Livepfad kann weiterhin eine
|
||||
Fahrt anlegen, die sich später zu 0,0 km ausrechnet — genau das, was der Eigentümer aus dem Bestand
|
||||
haben wollte. Im aktuellen Bestand gibt es keine solche Fahrt, und unter der neuen Gerätekonfiguration
|
||||
(`Start Speed = 3 km/h`) sollte sie kaum entstehen. Aber es ist wieder eine Regel, die nur ein Weg
|
||||
kennt.
|
||||
|
||||
**Bewusst nicht einseitig gebaut:** der Livepfad kennt die Strecke erst beim Screening, also
|
||||
nachträglich. Die Fahrt dann zu verwerfen hieße, einen bereits gespeicherten Datensatz automatisch
|
||||
zu löschen. Das ist eine Entscheidung über die Daten des Eigentümers, keine über den Code — dieselbe
|
||||
Linie wie in Abschnitt W.
|
||||
|
||||
## Working conventions (observed — keep them)
|
||||
|
||||
- German is the project language: identifiers, comments, commits, UI texts. Exceptions:
|
||||
|
||||
@@ -1 +1 @@
|
||||
{"version":"2026.9.1.16","sha256":"500a3e3bf6b2c9663bea77d47adc770aa973797f17724297af4a2438096d7221","bytes":265720,"gebaut":"2026-09-01T17:03:56Z"}
|
||||
{"version":"2026.9.1.17","sha256":"96fa9737a475fe862c2dd309490f687a3e3e4088a8f356acd3f5c718cc919306","bytes":265731,"gebaut":"2026-09-01T17:08:45Z"}
|
||||
Binary file not shown.
@@ -49,8 +49,8 @@ from .verlauf import (
|
||||
durchschnitt_kmh,
|
||||
fahrtsignal,
|
||||
ist_gefahren,
|
||||
strecke_aus_zaehler,
|
||||
strecke_waehlen,
|
||||
zaehlerstrecke,
|
||||
hoechstwert_im_fenster,
|
||||
route_aus_verlauf,
|
||||
UNPLAUSIBLE_KMH,
|
||||
@@ -198,9 +198,7 @@ async def _fahrten_importieren(k: Koordinator, verlaeufe: dict) -> dict:
|
||||
# Sie entscheidet hier zusätzlich über Sein oder Nichtsein: eine Fahrt
|
||||
# von 400 Metern steht im CAN-Wert als Null und wäre sonst verworfen
|
||||
# worden, obwohl gefahren wurde.
|
||||
fein = strecke_aus_zaehler(
|
||||
[p for p in gnss_verlauf if f_start <= p[0] <= f_ende]
|
||||
)
|
||||
fein = zaehlerstrecke(gnss_verlauf, f_start, f_ende)
|
||||
distanz, gnss_verwendet = strecke_waehlen(distanz, fein)
|
||||
|
||||
if not ist_gefahren(distanz):
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"domain": "audi_dashboard",
|
||||
"name": "Audi Dashboard",
|
||||
"version": "2026.9.1.16",
|
||||
"version": "2026.9.1.17",
|
||||
"documentation": "https://gitea.nothaft.cloud/paul/audi-app/src/branch/main/README.md",
|
||||
"issue_tracker": "https://gitea.nothaft.cloud/paul/audi-app/issues",
|
||||
"codeowners": ["@paul"],
|
||||
|
||||
@@ -35,8 +35,8 @@ from .verlauf import (
|
||||
UNPLAUSIBLE_KMH,
|
||||
durchschnitt_kmh,
|
||||
hoechstwert_im_fenster,
|
||||
strecke_aus_zaehler,
|
||||
strecke_waehlen,
|
||||
zaehlerstrecke,
|
||||
naechster_wert,
|
||||
route_aus_verlauf,
|
||||
verbrauch_aus_literstaenden,
|
||||
@@ -257,8 +257,10 @@ async def _gnss_verfeinern(k: Koordinator, gnss_sensor: str, fahrt: dict) -> Non
|
||||
if start is None or ende is None or grob is None:
|
||||
return
|
||||
|
||||
punkte = await verlauf_lesen(k.hass, gnss_sensor, start, ende)
|
||||
fein = strecke_aus_zaehler(punkte)
|
||||
# Etwas vor dem Fahrtbeginn mitlesen, damit zaehlerstrecke() ihren Anker
|
||||
# findet - den letzten Datensatz am oder vor dem Beginn.
|
||||
punkte = await verlauf_lesen(k.hass, gnss_sensor, start - SUCHFENSTER, ende)
|
||||
fein = zaehlerstrecke(punkte, start, ende)
|
||||
gewaehlt, genommen = strecke_waehlen(grob, fein)
|
||||
if not genommen:
|
||||
if fein is not None:
|
||||
|
||||
@@ -283,6 +283,32 @@ def route_aus_verlauf(
|
||||
GNSS_TOLERANZ_KM = 1.0
|
||||
|
||||
|
||||
def zaehlerstrecke(
|
||||
punkte: list[Verlaufspunkt],
|
||||
start: datetime.datetime,
|
||||
ende: datetime.datetime,
|
||||
) -> float | None:
|
||||
"""Die im Fenster [start, ende] gefahrene Strecke aus einem laufenden
|
||||
Kilometerzaehler.
|
||||
|
||||
Der Anker ist der letzte Datensatz AM ODER VOR `start`, nicht der erste
|
||||
danach: der Zaehlerstand zu Fahrtbeginn ist der zuletzt gemeldete - dieselbe
|
||||
Ueberlegung wie bei wert_bei(). Ohne ihn fehlt die Strecke zwischen dem
|
||||
Beginn und dem ersten Datensatz danach.
|
||||
|
||||
WARUM DIESE FUNKTION EXISTIERT: Livepfad und Rueckblick haben dieselbe
|
||||
Fahrt unterschiedlich gemessen. Das Screening las den Verlauf ueber
|
||||
verlauf_lesen(), das den Stand ZU BEGINN des Fensters mitliefert; der
|
||||
Rueckblick schnitt sein Fenster streng heraus und verlor genau diesen
|
||||
Punkt. Am 01.09.2026 an der echten Fahrt nachgemessen: 6,896 km gegen
|
||||
6,816 km fuer denselben Zeitraum. Das ist die Doppelbewertung, die dieses
|
||||
Projekt bei UNPLAUSIBLE_KMH und MINDESTDAUER_S schon zweimal ausgeraeumt
|
||||
hat - jetzt schneiden beide Wege durch dieselbe Funktion."""
|
||||
davor = [p for p in punkte if p[0] <= start]
|
||||
im_fenster = [p for p in punkte if start < p[0] <= ende]
|
||||
return strecke_aus_zaehler((davor[-1:] if davor else []) + im_fenster)
|
||||
|
||||
|
||||
def strecke_waehlen(
|
||||
grob: float | None, fein: float | None
|
||||
) -> tuple[float | None, bool]:
|
||||
|
||||
@@ -70,7 +70,10 @@ class Zuordnung:
|
||||
die Oberfläche bereits angewendete Overrides und eingebaute
|
||||
Standardwerte einheitlich."""
|
||||
werte = {feld["key"]: getattr(self.werte, feld["key"], None) for feld in FELDER}
|
||||
return {"katalog": FELDER, "werte": werte, "standardwerte": STANDARDWERTE}
|
||||
# "standardwerte" ist mit dem Zuruecksetzen-Knopf (2026.9.1.11)
|
||||
# entfallen - niemand liest sie mehr, also gehen sie auch nicht mehr
|
||||
# ueber die Leitung.
|
||||
return {"katalog": FELDER, "werte": werte}
|
||||
|
||||
|
||||
__all__ = ["Zuordnung", "einstellungen"]
|
||||
|
||||
Reference in New Issue
Block a user