diff --git a/AGENTS.md b/AGENTS.md
index a8f35a8..0472845 100644
--- a/AGENTS.md
+++ b/AGENTS.md
@@ -3267,6 +3267,181 @@ measured 736×170px at a 1400px viewport (Y-scale exactly `1`), axis label `getC
`tsc --noEmit` clean. Manifest bumped to `2026.8.27.5`, `npm run ota` rerun, `audi_ha_test` restart confirmed
clean via log.
+**Fourth follow-up (2026.8.27.6): the real gap behind the `.5` "0V on the real instance" report - a second,
+unfiltered publish path, not (only) stale/not-yet-updated data.** Owner report: "Mein Audi/Zustand Box"
+still shows `Batteriespannung 0,0V`, while the Batterie-Messwerte list has no such entry. Traced to a second
+code path that was never touched by the `SPANNUNG_MIN_V` floor at all: `veroeffentlichung.py`'s
+`fahrzeugstatus()` publishes `batteriespannung` as the **raw, unfiltered current `BATTERIE_SENSOR` reading**
+(`_zu_zahl(zustand_oder_none(...))`), completely independent of `batterie.py`'s history-recording path that
+the floor actually guards. The panel's "Zustand" tile (`audi-dashboard-app.js` ~line 1611) and
+companion-app's `MeinAudi.tsx`/`Batterie.tsx` "Aktuell gemeldet" line all read this same published field
+directly - so a live sensor glitch (confirmed live: the test container's `BATTERIE_SENSOR` was genuinely
+reporting raw state `"0"`) always showed through unfiltered there, in both codebases, regardless of whether
+the instance had ever been updated. Fixed by applying the identical floor to this publish too: `spannung =
+None` when the raw reading is below `SPANNUNG_MIN_V`, so "Zustand"/`Aktuell gemeldet` now show "unbekannt"/
+"—" for the same implausible readings the history path already silently drops - one backend fix covers both
+frontends, since both read the same published field. Needed a **function-local** import of `SPANNUNG_MIN_V`
+from `batterie.py` (not a module-level one): `batterie.py` already imports `zustand_oder_none` from
+`veroeffentlichung.py`, so a top-level import the other direction risks a circular-import failure depending
+on load order (batterie.py reaches its own `from .veroeffentlichung import zustand_oder_none` line *before*
+its `SPANNUNG_MIN_V = 10.0` assignment, so an eager reverse import can hit a partially-initialized module).
+A local import inside `fahrzeugstatus()` sidesteps this entirely - both modules are always fully loaded by
+the time that function actually runs. Verified live: a real Docker Desktop outage interrupted this fix
+mid-session (confirmed via `docker ps` failing, unrelated to the code) - resumed once Docker was back.
+`py_compile` clean, `audi_ha_test` restart confirmed clean via log **with no circular-import traceback**
+(the actual proof this pattern needed, `py_compile` alone can't catch an import-order bug), and live in the
+browser: raw sensor confirmed still reporting `"0"` via a direct state query, "Zustand" tile now reads
+"Batteriespannung unbekannt" instead of "0,0 V". No companion-app source changes this round (backend-only
+fix), so no OTA rebuild needed. Manifest bumped to `2026.8.27.6`.
+
+## V. Per-trip Verbrauch (l/100 km): dead schema closed with a liter-based approximation, real-sensor
+question left open (2026.8.27.7)
+
+**The trip detail's "Verbrauch" row (`fahrtVerbrauch()`, `audi-dashboard-app.js`) always showed "liegt nicht
+vor" - traced (owner asked "why is it so? what sensor are you waiting for?") to `t.verbrauch_l_100km` being
+dead schema exactly like `start_lat`/`route` were before section U: it existed **only** in the frontend, no
+backend code path (live or import) had ever written it, and no Setup-mappable sensor role existed for it
+either. The "vom Fahrzeug gemeldet" caption was aspirational leftover, not a description of anything actually
+wired up.**
+
+**Owner's decision, to close this properly rather than just deleting the dead row:** two options exist -
+(a) the FMM003 might expose a genuine short-trip l/100km value directly over OBD (the owner is checking), or
+(b) compute an approximation from `TANK_LITER_SENSOR`'s liter-delta over the trip ÷ distance. Built (b) now
+as the working default, with (a) tracked as an explicit follow-up: **if a real short-trip consumption sensor
+turns up, swap to it directly instead of this approximation** - noted here rather than only in a chat
+message, per this file's own "state changes, note it" maintenance rule.
+
+**Implementation**, mirroring the existing `route_aus_verlauf()`/position-screening pattern from section U
+for the exact same reason (live vs. import parity, no duplicated math): new
+`verbrauch_aus_literstaenden(start_liter, end_liter, distance_km)` in `verlauf.py` - `None` unless both liter
+readings and a positive distance are available *and* the liter level actually fell (a level that rose or held
+steady means a mid-trip refuel or noise, not a meaningful ratio). `verbrauch_l_100km` added to the trip schema
+(`leere_fahrt()`, was entirely absent before). Live path: new `_verbrauch_screenen()` in `screening.py`,
+wired into `durchfuehren()` exactly like `_position_screenen()` - runs whenever `TANK_LITER_SENSOR` is mapped,
+re-attempted on every screening pass until it succeeds (liter data can lag just like odometer/GPS data).
+Import path: `_fahrten_importieren()` in `historienimport.py` computes it inline from the already-fetched
+`verlaeufe["tank_liter"]` history (was fetched for tank-refill import already, just never used for this).
+Both frontends: panel's caption changed from the false "vom Fahrzeug gemeldet" to "Näherung aus
+Tankfüllstand"; companion-app's `FahrtDetail.tsx` had **no Verbrauch row at all before this** (a pre-existing
+parity gap, not something this change broke) - added one in the same position as the panel's (after
+"Durchschnitt"), same honest caption, `verbrauch_l_100km` added to the `Fahrt` type (was typed `unknown`
+nowhere - simply absent).
+
+**A second, real bug found and fixed while live-testing this: `_fahrt_screenen()` (the existing odometer
+screening, unchanged by this feature) never synced its result back onto the in-memory `fahrt` dict, only into
+storage.** `_vollstaendig()` only ever wrote `distance_km`/`km_quelle`/`status` into a local `aenderungen`
+dict, which `_fahrt_screenen()` then persisted via `fahrt_aktualisieren()` - but never applied to the `fahrt`
+object itself, unlike `_position_screenen()`, which already does `fahrt.update(aenderungen)` for exactly this
+reason. Consequence: when a trip's distance was computed for the first time and the *same* `durchfuehren()`
+pass then reached the new `_verbrauch_screenen()` step, the `ohne_verbrauch` filter still saw the stale
+`distance_km: None` from the `fahrten` list fetched at the top of the function - `verbrauch_l_100km` silently
+stayed unfilled for one whole screening cycle, only catching up on the *next* trigger. Fixed by adding the
+same `fahrt.update(aenderungen)` to `_fahrt_screenen()`, matching `_position_screenen()`'s already-correct
+pattern - found by reproducing it directly (a trip's `distance_km` and `verbrauch_l_100km` both stayed empty
+after the first screening pass, filled in only after a second explicit trigger) rather than by reading the
+code and guessing.
+
+Verified: `py_compile` clean on all five touched backend files (incl. this `screening.py` fix), `node --check`
+clean on the panel JS, companion-app `tsc --noEmit` clean and full suite green at 146/146. Live end-to-end in
+`audi_ha_test` (`TANK_LITER_SENSOR` was already mapped to the container's real
+`sensor.testcar_b9_fmm003_testintegratoin_can_fuel_volume`, liters - no Setup change needed): ran a synthetic
+20.0→19.0 l trip over a simulated 10 km, confirmed `verbrauch_l_100km: 10.0` computed correctly and rendered
+on the panel's trip detail ("10,0 l/100 km · Näherung aus Tankfüllstand", screenshot taken). companion-app's
+`FahrtDetail` row was **not** live-rendered this round (no running dev server connected to a backend this
+session) - verified only via `tsc --noEmit` and the full test suite, same as several other companion-app-only
+changes this session.
+
+**Separately, while pulling data for this: found the container's `ZUENDUNG_SENSOR` mapping had changed since
+last checked** - now `binary_sensor...engine_ignition_or_acc_status` (stable, no chattering observed) instead
+of the `instant_movement_status` entity used earlier this session, which **was** flapping on/off dozens of
+times per minute (test-integration noise, unrelated to any real vehicle behavior). The owner had reconfigured
+the Setup mapping themselves in the meantime. Confirms this container's synthetic test entities remain
+unrelated to the owner's real-vehicle trip-merging report from the same day (section P/the "no clean
+ignition-off signal" conversation) - it needs the real instance's own history, not this container's.
+
+Manifest bumped to `2026.8.27.7`, `npm run ota` rerun (companion-app source changed).
+
+## W. Correction: `audi_ha_test` is connected to the real vehicle, not synthetic data - and the real
+trip-merging/GPS-jump report, root-caused with real data (2026.8.27.8)
+
+**Correction to sections U and V above: the owner confirmed `audi_ha_test` (this session's "test container")
+receives live telemetry from the real car/real FMM003 - the same sensors are wired into both this container
+and the real HA instance in parallel.** Both prior sections state the opposite ("synthetic test-integration
+noise", "unrelated to the owner's real-vehicle... report") - that was wrong, and this project's own
+convention is to correct rather than silently rewrite history, so it stays here rather than editing U/V after
+the fact. The erratic on/off chatter observed on `instant_movement_status` earlier the same day is real
+telemetry from a real accelerometer/movement-based signal, not fabricated test noise - see below for what it
+actually means. **The owner also confirmed creating synthetic test trips in this container (as done
+throughout sections R-V) remains fine** - it's a secondary instance fed by the same sensors, not the sole
+copy of their trip history, so the established "simulate via `hass.callApi` POST states, verify, clean up
+afterward" testing convention stands unchanged going forward.
+
+**With that corrected, the real trip-merging/GPS-jump report (originally raised in section P's conversation)
+was root-caused directly from this container's real recorder history - no separate pull from a "production"
+instance was needed after all, since this *is* that instance's real data.** Matched the owner's report
+exactly once UTC/local (`CEST = UTC+2`) is accounted for:
+
+- `t-da363d681915` (`09:55:31`–`12:05:01` UTC = **11:55–14:05 local**) - the wrongly-merged trip.
+- `t-8be105166025` (`12:28:05`–`12:29:12` UTC = **14:28–14:29 local**), `distance_km: 22.0` over 67 seconds
+ (~1180 km/h) - the "GPS jumps 22 km" report.
+
+**Root cause of the merge, confirmed from the raw history (not the "unavailable-instead-of-off" hypothesis
+originally guessed in section P's conversation):** `instant_movement_status` (mapped as `ZUENDUNG_SENSOR` at
+the time) reported sustained, unbroken `on` for 45–64-minute stretches with zero transitions - including
+straight through the ~30-minute real stop the owner described around `10:22` UTC, with no `off` at all during
+it. Where it did flip `off`, it almost always flipped back `on` within milliseconds to a couple of seconds,
+so the 15-minute pause-close logic (`fahrterkennung.py`) never got an uncanceled window to actually end the
+trip - every near-instant `on` cancels the pending close (`k.warte_ende_ab_abbrechen()`, correct behavior in
+isolation) before the wait can complete. No `unavailable` states appeared anywhere in the relevant window, so
+the section P dropout-hypothesis genuinely doesn't apply to this specific incident. The entity itself is the
+problem: a real-time movement/accelerometer heuristic, not a debounced ACC/ignition state - it can both
+chatter during genuine driving *and* fail to register a genuine stop. **The owner had already fixed this
+independently by remapping `ZUENDUNG_SENSOR` to `engine_ignition_or_acc_status`** (confirmed stable, no
+chatter, when checked) - the correct fix; nothing further needed there from this end.
+
+**The GPS-jump trip (`t-8be105166025`) is a second, related but distinct defect: the live trip-completion
+path had no plausibility guard on computed average speed at all.** `historienimport.py` already had one
+(`UNPLAUSIBLE_KMH = 300`, added 2026-08-25/section P) for exactly this failure mode - a real 22 km drive
+whose *actual* duration was much longer than 67 seconds, but whose recorded trip window got compressed by the
+chattering ignition entity creating a spuriously short boundary, while `naechster_wert()`'s odometer lookup
+still (correctly, on its own terms) found the real km delta nearest those wrong timestamps. `screening.py`
+(the live path) never got the equivalent guard - a gap already flagged as open in section P but not built
+until now. Fixed by moving the check into a shared home: `UNPLAUSIBLE_KMH` and a new `durchschnitt_kmh()`
+helper now live in `verlauf.py` (both `screening.py` and `historienimport.py` import them, no more duplicated
+constant), and `screening.py`'s `_vollstaendig()` - the single choke point both `_fahrt_screenen()` and
+`_lueckenlos_verketten()` already funnel through - now rejects (not just the distance, both `odo_start` *and*
+`odo_end`) whenever the implied average speed exceeds the limit, logs a warning, and leaves the trip `offen`
+to be retried on the next screening pass, exactly matching how `historienimport.py` already handles this
+class of error.
+
+**The two already-broken real trip records were deliberately left untouched** - they're the owner's real
+driving history, not disposable test data (see the correction above), so fixing/deleting them is the owner's
+call via the app's own edit/delete controls, not something to do unilaterally from this session.
+
+**Owner then confirmed synthetic trips in this container remain fine** (same sensors feed both this
+container and the real instance in parallel, this one is not the sole copy of trip history) **- and drew a
+hard line that stays for the rest of this project: `audi_ha_test`/`:18123` is the only instance ever to be
+touched; the real instance is never to be worked on, full stop.** Attempted a live end-to-end repro of the
+exact bug (POSTed a fake +22 km jump over ~8 s, same recipe used successfully for the position/verbrauch
+features earlier this session) - and hit a new, worth-remembering limitation: **the real FMM003/flespi
+integration re-asserts its own live odometer reading shortly after a manual `POST /api/states` write**, so
+the simulated +22 km value never actually landed in the recorder at all (confirmed via a direct history
+query - only the real, unchanged ~21324 km value ever appears). This container's earlier "first screening
+pass runs before the recorder commits" races (sections R/S) may have partly been this same effect rather than
+pure commit latency - a real competing writer, not just replay timing. **Consequence for future sessions:**
+simulating a *specific* sensor value in this container is unreliable for entities the real FMM003 actively
+reports (ignition, odometer, GPS, tank) - it can work if the value is posted right before the dependent
+event fires (as it did for the position/verbrauch tests), but don't assume a posted value will still be there
+moments later if the real device happens to report in the meantime.
+
+Fell back to the same no-live-state verification as the first pass: `py_compile` clean on all three touched
+backend files, `audi_ha_test` restart confirmed clean via log (single `audi_dashboard` loader warning, no
+duplicate-domain regression), and the plausibility math verified by calling `durchschnitt_kmh()` directly
+against the real bug's own numbers (no HA state involved): `durchschnitt_kmh(22.0, 67)` → `1182.1` km/h,
+correctly exceeding `UNPLAUSIBLE_KMH=300` and therefore now rejected; a real plausible drive
+(`durchschnitt_kmh(22.0, 1200)` → `66.0` km/h) correctly passes through unaffected. No companion-app source
+changes this round (backend-only). Manifest bumped to `2026.8.27.8`.
+
---
## Working conventions (observed — keep them)
diff --git a/companion-app/src/api/types.ts b/companion-app/src/api/types.ts
index 15dd683..c521b90 100644
--- a/companion-app/src/api/types.ts
+++ b/companion-app/src/api/types.ts
@@ -56,6 +56,10 @@ export interface Fahrt {
start_address?: string | null;
end_address?: string | null;
route?: [number, number][] | null;
+ /** Näherung aus der Literstand-Differenz (TANK_LITER_SENSOR) ÷ Distanz, kein
+ * vom Fahrzeug selbst für diese eine Fahrt gemeldeter Wert - siehe
+ * verbrauch_aus_literstaenden() im Backend. */
+ verbrauch_l_100km?: number | null;
pausen?: unknown[];
}
diff --git a/companion-app/src/screens/FahrtDetail.tsx b/companion-app/src/screens/FahrtDetail.tsx
index 99e7b89..38d28a9 100644
--- a/companion-app/src/screens/FahrtDetail.tsx
+++ b/companion-app/src/screens/FahrtDetail.tsx
@@ -90,6 +90,11 @@ export function FahrtDetail({ id }: { id: string | undefined }) {
: "—"
}
/>
+