509de6ef97
15 Befunde aus den 18 neuen Commits, nach Schaden sortiert und mit
Datei-Zeile-Belegen. Die drei schwersten liegen im neuen Setup-Menue:
Speichern bei noch nicht geladenem Katalog ueberschreibt entitaeten.json
mit {} und loescht damit alle Zuordnungen, Zuruecksetzen wirkt bei 15 von
17 Feldern nicht, und alle vier Listenpositionen bekommen denselben Sensor,
wodurch "Sicher abgestellt" gesichert meldet, obwohl drei Tueren nie
geprueft wurden.
Jeder Befund ist als belegt oder plausibel gekennzeichnet; die belegten
sind am Code nachvollzogen, die Zuruecksetz-Regel zusaetzlich isoliert
nachgestellt.
313 lines
20 KiB
Markdown
313 lines
20 KiB
Markdown
# AGENTS.md — Project state, review findings, open items, and working rules
|
||
|
||
**Last updated: 2026-08-11** (after building the app and the native iOS hull — phases 1, 2, 4–11 of `UMSETZUNGSPLAN.md`). This file is the entry point for every new agent
|
||
session: what this repo is, what is finished, what is missing, and how to work here. Detail lives in
|
||
the linked documents — this file points, it does not duplicate.
|
||
|
||
> **Maintenance rule (binding):** whenever you change this repo in a way that affects anything
|
||
> recorded here — completing an open item, making or reversing a decision, adding/removing a
|
||
> component, discovering a new gap — **update this file in the same session** (tick the checkbox,
|
||
> adjust the status table, bump the "Last updated" date). This file must always be written in
|
||
> **English**, even though the rest of the project is German.
|
||
|
||
**How this file is loaded:** Claude Code does not read `AGENTS.md` natively; the root `CLAUDE.md`
|
||
imports it via `@AGENTS.md` (official recommended pattern). Other agents (Codex, Cursor, Copilot)
|
||
read `AGENTS.md` directly. Edit content here, not in `CLAUDE.md`.
|
||
|
||
---
|
||
|
||
## Working rules
|
||
|
||
### Karpathy guidelines (adopted as project standard)
|
||
|
||
From [`multica-ai/andrej-karpathy-skills`](https://github.com/multica-ai/andrej-karpathy-skills),
|
||
behavioral rules derived from Andrej Karpathy's observations on LLM coding failures. They bias
|
||
toward caution over speed — for trivial tasks, use judgment.
|
||
|
||
1. **Think before coding.** Don't assume. Don't hide confusion. Surface tradeoffs. State
|
||
assumptions explicitly; if multiple interpretations exist, present them — don't pick silently.
|
||
If a simpler approach exists, say so. Push back when warranted. If something is unclear, stop
|
||
and ask.
|
||
2. **Simplicity first.** Minimum code that solves the problem. Nothing speculative: no features
|
||
beyond what was asked, no abstractions for single-use code, no unrequested configurability, no
|
||
error handling for impossible scenarios. If 200 lines could be 50, rewrite.
|
||
3. **Surgical changes.** Touch only what you must. Don't "improve" adjacent code, comments, or
|
||
formatting; don't refactor what isn't broken; match existing style. Remove only orphans **your**
|
||
change created; mention (don't delete) pre-existing dead code. Every changed line should trace
|
||
directly to the request.
|
||
4. **Goal-driven execution.** Define success criteria, loop until verified. "Fix the bug" → write a
|
||
test that reproduces it, then make it pass. For multi-step tasks, state a brief plan with a
|
||
verify step per item.
|
||
|
||
### Claude Code practices (current guidance, 2026)
|
||
|
||
- Keep this file **concise and specific** — instructions concrete enough to verify ("run X before
|
||
Y"), markdown headers/bullets, no dense prose. Context files reduce adherence as they grow;
|
||
target roughly ≤200 lines of actual instruction.
|
||
- Don't record here what the code or git history already shows (directory dumps, dependency
|
||
lists) — record pitfalls, rationale, decisions, and conventions that differ from defaults.
|
||
- Conflicting instructions get picked arbitrarily — when updating, **remove** superseded rules
|
||
rather than stacking corrections.
|
||
- Verify loading with `/context` (file must appear under **Memory files**); edit via `/memory`.
|
||
|
||
---
|
||
|
||
## What this project is
|
||
|
||
Private replacement for the Audi *connect plug & play* app (discontinued end of 2026), for exactly
|
||
one vehicle (**Audi RS 4 Avant competition**), single user, self-hosted on Home Assistant, access
|
||
via Tailscale only. Read-only toward the vehicle — no remote control. Covers: vehicle status, trip
|
||
log, fuel log with Shell receipt parsing, service forecasting, insurance/tax, tire management.
|
||
|
||
**License rule (hard):** Audi Type fonts, four-rings SVG, and model-badge SVGs are cleared **only
|
||
for this one private, unpublished installation**. Therefore `design-system/` is brand-free (it gets
|
||
uploaded to Claude Design), while the HA panel and the future sideload-only app may use the real
|
||
assets. Never mix these the other way around.
|
||
|
||
**Reading order for a new session:**
|
||
1. This file (overview + open items)
|
||
2. `UMSETZUNGSPLAN.md` — the step-by-step execution plan for all open items (13 phases with
|
||
commands and acceptance criteria); when working on an open item, follow the plan's phase
|
||
3. `SPECIFICATION.md` — authoritative for the HA panel, incl. §7 "Known Gaps"
|
||
4. `COMPANION_APP_ARCHITECTURE.md` — authoritative for DataMetric360 (decided, barely built)
|
||
5. `AUDIT_2026-08-10.md` — accessibility/platform audit of the panel (partly done, rest below)
|
||
6. `bauauftrag.md` — **historical only**; code has diverged (see SPECIFICATION.md §7)
|
||
|
||
## The three projects in this repo
|
||
|
||
| Area | What | Status |
|
||
|---|---|---|
|
||
| `homeassistant/` | HA panel (`panel_custom`): pyscript backend + vanilla-JS frontend | ✅ **finished, in use** — to be replaced by the app |
|
||
| `testumgebung/` | Script that rebuilds a throwaway Home Assistant with the real backend | ✅ new, reproducible |
|
||
| `design-system/` | React component library `@audi-dash/ui`, brand-free, feeds Claude Design | ✅ done as a kit (20 components, 1,690 lines) |
|
||
| `companion-app/` | **DataMetric360** — successor app (web, PWA, native iOS/Android via Capacitor, HA iframe); will **replace** the panel | ✅ **all 21 screens built and tested**; runs natively on iOS with real data |
|
||
| `design/` | Export of the Claude Design draft for DataMetric360 | 🚧 interim and now behind the code — screens were derived from the old panel instead (owner's decision) |
|
||
|
||
Root files: `dashboard-muster*.html` = original static prototype (superseded, reference only),
|
||
`bauauftrag.md`/`.html` = original build brief (historical), `DESIGN_BRIEF_DATAMETRIC360.md` = the
|
||
prompt for the Claude Design project.
|
||
|
||
**`homeassistant/` in one paragraph:** 10 pyscript scripts + 4 modules (`pyscript/modules/`); trip
|
||
detection via the iPhone WLAN sensor with pause tolerance, two-stage trip completion via HA history
|
||
screening (odometer often updates only on the next trip), fill-up detection on fuel-level rise,
|
||
Shell PDF parser (`data/shell_beleg_parser.py`, subprocess, the only tested part of the repo), tire
|
||
km counter, backup, self-update, image management. Frontend: one file `www/audi-dashboard-app.js`
|
||
(3,130 lines, custom element, no framework/bundler), 5 tabs + ~19 detail routes, cache-busting via
|
||
`audi-dashboard-version.json` + loader stub. Entity IDs configured centrally in
|
||
`pyscript/modules/einstellungen.py` (the one file edited before install). Deploy: `update.ps1`
|
||
(robocopy to Samba share) or — still inactive — self-update from git.
|
||
|
||
**`companion-app/` — what exists:** the full app. Data layer (`src/api/`: REST, WebSocket with
|
||
reconnect backoff, persistent offline write queue, credential storage), domain logic
|
||
(`src/daten/`: profile adapter, statistics, service forecast, data context), all 21 screens
|
||
(`src/screens/`), Audi assets (`src/assets/audi/`), PWA manifest and icons. Verified by 90 unit
|
||
and render tests plus 9 checks against a live Home Assistant. `npm run dev` in the repo root
|
||
starts it; `testumgebung/aufsetzen.sh` provides the server side.
|
||
|
||
**DataMetric360 architecture (short — details in `COMPANION_APP_ARCHITECTURE.md`):**
|
||
- Future data source: **Teltonika FMM003** on the CAN bus, fully replacing the iPhone WLAN sensor
|
||
and the VAG integration.
|
||
- **Current data path (since 2026-08-11): Codec JSON → MQTT/TLS → Mosquitto add-on → HA.** Traccar
|
||
is **dropped** (the decision log deliberately remains in the architecture doc, marked ÜBERHOLT).
|
||
- Frontend access: HA REST + WebSocket with a long-lived access token (no `hass` object).
|
||
- External access: Cloudflare Tunnel + reverse proxy with a path allowlist; HA itself stays
|
||
unreachable. Domain **`datametric360.app`** registered (all-inkl, 2026-08-11).
|
||
- Distribution: **sideload only** (license reason) — hence real Audi assets are allowed there.
|
||
- Trip detection moves to FMM003 ignition; the pause-tolerance feature
|
||
(`fahrten_pausenzeit_min`) is a deliberate product decision and must be preserved.
|
||
|
||
---
|
||
|
||
## Review findings (2026-08-11)
|
||
|
||
### HA panel — known gaps (most also in SPECIFICATION.md §7)
|
||
|
||
- **GPS is dead schema:** `start_lat/lon`, addresses, `route`, `avg_speed_kmh` never populated.
|
||
The trip-detail map draws a **fabricated** line via `fakeTrack()`
|
||
(`audi-dashboard-app.js:414`) — not a real track.
|
||
- **No GPS fallback in trip completion:** trips without an odometer match stay `status="offen"`
|
||
forever (`modules/fahrtabschluss_logik.py:16-20`).
|
||
- **RAM-only state:** running trip (`_fahrt_start_ts`) and fuel low-water-mark
|
||
(`_tiefststand_pct`) do not survive an HA restart. Deliberately deferred hardening.
|
||
- **Inactive features:** `BATTERIE_SENSOR = ""` (entire battery-history feature is a no-op) and
|
||
`UPDATE_REPO_URL = ""` (self-update inactive), both in `pyscript/modules/einstellungen.py`.
|
||
`www/bilder/` has no vehicle photos (all slots show placeholders); `steuer.faellig` unset.
|
||
- **Robustness:** `profil_lesen()` in `modules/profil.py` does not handle a missing/corrupt
|
||
`fahrzeugprofil.json` — all callers throw.
|
||
- **Tests:** only the receipt parser is tested (`data/tests/test_shell_beleg_parser.py`, 10 real
|
||
receipts) — and its test PDFs are gitignored, so a fresh clone can't run it. Backend and
|
||
frontend: no tests, no CI.
|
||
- **Leaflet via CDN:** trip map needs public internet in addition to the Tailscale tunnel.
|
||
|
||
### Documentation drift ✅ FIXED 2026-08-11 (kept as a record of what was wrong)
|
||
|
||
- `homeassistant/README.md:99`, `INSTALL.md:204`, and the header comment
|
||
`audi-dashboard-app.js:13-15` claim the statistics view shows sample numbers — **false**;
|
||
`vStat()` computes real values from `TRIPS`/`FILLS`.
|
||
- `INSTALL.md` step 4 names variables that no longer exist (`DOORS_SENSOR`, `WINDOWS_SENSOR`,
|
||
`LOCK_ENTITY`, `BATTERY_VOLTAGE_SENSOR`) — actual names: `TUER_SENSOREN`/`FENSTER_SENSOREN`/
|
||
`TUERSCHLOSS_SENSOREN`/`BATTERIE_SENSOR`.
|
||
- `homeassistant/README.md:110` mentions the removed 97% full-tank rule (removal documented in
|
||
`belegverarbeitung.py:18-20`); the README file list omits 5 pyscript files.
|
||
- Obsolete comment `belegverarbeitung.py:41` ("TODO: Datei ablegen" — file has long existed).
|
||
|
||
### Audit leftovers — two of three now fixed in the new app
|
||
|
||
- ✅ Swipe-to-delete without a gesture-free fallback — **fixed in the app**: every list row also
|
||
carries an always-visible "…" menu (`companion-app/src/screens/Zeilenmenue.tsx`). Still open in
|
||
the old panel, which is being replaced anyway.
|
||
- ✅ Leaflet from a CDN — **fixed in the app**: bundled from node_modules as a lazy chunk.
|
||
- 🟡 Popup close-by-tap-outside in the old panel: still unchecked (the app uses the library's
|
||
Popup, which is keyboard-reachable).
|
||
- Audit's own note: these three may be better done in DataMetric360 than retrofitted — the panel
|
||
gets replaced anyway.
|
||
|
||
### companion-app / design-system ✅ RESOLVED 2026-08-11
|
||
|
||
- **Not wired together** — fixed: an npm workspace in the repo root links `@audi-dash/ui` into the
|
||
app as a real dependency.
|
||
- Both packages: no `node_modules`, no `dist` — smoke tests need `npm install` first
|
||
(design-system additionally `npm run build`; `scripts/smoke.mjs` imports from `../dist/`).
|
||
- No unit tests, no Storybook (substitute: SSR smoke over 21 cases in design-system).
|
||
- `design/` export (2026-08-11) has two known, already-commissioned fixes not yet in the export:
|
||
shows **RS 6 instead of RS 4**, and **16 sub-pages** the HA panel already has are missing
|
||
(list in `design/README.md`).
|
||
|
||
---
|
||
|
||
### Found while building (2026-08-11) — all fixed
|
||
|
||
Three defects that only surfaced by running against a real Home Assistant, not by reading code:
|
||
|
||
1. **The data layer declared field names the backend never sends.** `Fahrzeugstatus` had
|
||
`tank_prozent`/`sicher_abgestellt`/`sicherheit`; the backend writes
|
||
`tankprozent`/`gesichert`/`sicherheitscheck`. Every screen would have read `undefined` without
|
||
anything failing. `technik`/`ausstattung` were typed as objects but are arrays.
|
||
2. **The profile adapter handed out live references into the raw profile.** Editing a form would
|
||
have silently mutated the baseline and broken the promise never to overwrite the
|
||
backend-maintained tire odometer. Sections are copied now.
|
||
3. **`design-system` still carried the pre-audit `--fg3: #657081`** (3.0:1 on `--tile`, fails WCAG
|
||
AA) that the panel had already fixed to `#8a94a3`. The new app would have inherited a
|
||
already-solved contrast defect.
|
||
|
||
Also: two TypeScript parameter properties in the data layer broke Node's strip-only mode, which is
|
||
what the smoke scripts run on — rewritten as plain fields.
|
||
|
||
Three more that only screenshots revealed — nothing failed, the pixels were simply wrong:
|
||
|
||
4. **The vehicle block on the home screen was invisible.** `overflow: hidden` sets a flex item's
|
||
automatic minimum size to 0, so once the page was taller than the screen, flex-shrink squashed
|
||
the block to zero height and took model name, badge and plate with it. Fixed with
|
||
`.dm-inhalt > * { flex: none }`.
|
||
5. **The model name appeared twice** — once as the badge image, once as text beside it.
|
||
6. **Filenames overflowed the gallery thumbnails.**
|
||
|
||
And one in the backend, found by watching the log against a current Home Assistant: the odometer
|
||
screening called `urlopen` directly, which HA aborts as a blocking call since 2026.8. Every trip
|
||
stayed without a distance, visible only as a warning. Now runs through `task.executor`.
|
||
|
||
**CORS is a real constraint for this app.** `cors_allowed_origins` did not take effect on HA
|
||
2026.8 (preflight 403 even same-origin). Two consequences, both handled: the web build is served
|
||
from Home Assistant itself (`/local/dm360/`, same origin — the planned deployment anyway), and the
|
||
native hull enables `CapacitorHttp`, which routes fetch through native HTTP where CORS does not
|
||
apply. Verify this again at commissioning if the app ever moves to a separate hostname.
|
||
|
||
**A self-written QR encoder was discarded.** It disagreed with a reference implementation on 1239
|
||
of 3249 modules — the code would have been unreadable. `homeassistant/www/dm360-qr.html` now uses a
|
||
vendored MIT library served from Home Assistant itself, which satisfies the actual requirement
|
||
(no network call, token never leaves the local network) and round-trips correctly.
|
||
|
||
## Pending: this branch has diverged from `main` (noted 2026-08-13)
|
||
|
||
A full review of `main`'s 18 new commits is in `REVIEW_main_2026-08-13.md` — 15 findings, the
|
||
three most serious in the new setup menu (saving with an unloaded catalogue wipes the whole
|
||
mapping; "reset" has no effect on 15 of 17 fields; all four list positions get the same sensor,
|
||
which makes "securely parked" report safe while three doors were never checked).
|
||
|
||
`main` has moved 18 commits ahead of `umsetzung-datametric360` (FMM003 switch, sensor-mapping setup
|
||
menu, iOS/large-screen overlay from Claude Design). **Deliberate decision: do not merge yet** — the
|
||
owner keeps working on `main` first.
|
||
|
||
Three files conflict (`AGENTS.md`, `homeassistant/INSTALL.md`,
|
||
`homeassistant/pyscript/fahrterkennung.py`); four more are touched by both sides but merge cleanly.
|
||
|
||
**One hazard that a clean merge will not catch.** This branch changed `profil_lesen()` in
|
||
`homeassistant/pyscript/modules/profil.py` to return `None` when the profile file is missing or
|
||
corrupt, and guarded all seven call sites that existed here. `profil.py` is untouched on `main`, so
|
||
it merges silently — but `main`'s call sites (`fahrterkennung.py`, `tankerkennung.py`,
|
||
`reifenzaehler.py`, `modules/frontend_veroeffentlichung.py`) do **not** guard against `None` and
|
||
would raise `AttributeError` on a missing profile instead of logging a clear error.
|
||
|
||
When merging: take `main`'s FMM003 version of `fahrterkennung.py`, then re-apply the `None` guard to
|
||
every remaining `profil.profil_lesen()` call site.
|
||
|
||
## Open items
|
||
|
||
Execution order, exact steps, and acceptance criteria for every item below live in
|
||
`UMSETZUNGSPLAN.md` (phases 1–13). Additional decisions of 2026-08-11: **no Electron** (Capacitor
|
||
wraps the web app for iPhone; a PWA home-screen install is the accepted intermediate step) and
|
||
**no separate backend** (the app talks to the HA REST/WebSocket API directly).
|
||
|
||
### A) Build DataMetric360 (the big block)
|
||
|
||
- [x] Fix the Claude Design draft (RS 4, not RS 6) and extend it by the 16 missing sub-pages;
|
||
then re-export to `design/`
|
||
- [x] `companion-app`: set up Vite + React + Capacitor scaffold; wire `@audi-dash/ui` as a real
|
||
dependency (possibly add a workspace/monorepo root)
|
||
- [x] Implement the screens from the design draft on top of the existing `DataMetricApi` layer
|
||
- [x] Add Audi assets (fonts/rings/badges) at implementation time from `homeassistant/www/` —
|
||
**never** into `design-system/`
|
||
- [x] Secure storage for the LLAT (iOS Keychain / Android Keystore via Capacitor plugin; the
|
||
`ablageSetzen()` hook already exists)
|
||
- [x] Onboarding: manual token paste (required); QR scan only if it stays simple (QR generated
|
||
locally under HA `/local/`, architecture §3)
|
||
- [x] Authenticated smoke tests of the data layer (reads, service calls, queue round-trip)
|
||
against `audi_ha_test` with a real token
|
||
- [x] Offline UX per design brief (offline marker, visible pending queue)
|
||
|
||
### B) Infrastructure / commissioning (partly waits for FMM003 hardware)
|
||
|
||
- [ ] Switch `datametric360.app` nameservers at all-inkl to Cloudflare ("full setup") —
|
||
prerequisite for the tunnel; domain carries nothing else, so this is consequence-free
|
||
- [ ] Decide hostname split (app on apex + API on `api.` subdomain, or vice versa)
|
||
- [ ] Choose reverse proxy (Nginx Proxy Manager vs. Traefik) — **can be done before hardware**
|
||
against the existing HA API
|
||
- [x] Define the reverse-proxy path allowlist (depends on final entity/service names)
|
||
- [ ] Install/wire the FMM003; record firmware version (Codec JSON is firmware-dependent)
|
||
- [ ] Generate TLS certificates for Mosquitto + device (small private CA); FMM003 refuses MQTT
|
||
without them
|
||
- [ ] Decide broker reachability for the vehicle: port-forward 8883 vs. VPS broker with
|
||
Mosquitto bridge over Tailscale
|
||
- [ ] Capture the first real Codec JSON message (`mosquitto_sub`/MQTT Explorer) and build the
|
||
field mapping from it — **do not guess beforehand** (explicit decision)
|
||
- [ ] Move trip detection to FMM003 ignition (reuse the `fahrterkennung.py` pattern, keep pause
|
||
tolerance); then remove `sensor.iphone_wifi_connection` from `einstellungen.py`
|
||
|
||
### C) Maintain the existing HA panel (low priority — being replaced)
|
||
|
||
- [x] Fix documentation drift (statistics claim, INSTALL variable names, README gaps, obsolete
|
||
TODO comment) — text-only changes
|
||
- [x] Harden `profil_lesen()` against missing/corrupt `fahrzeugprofil.json`
|
||
- [x] Decide whether the 3 audit leftovers get fixed here or only in DataMetric360
|
||
- [ ] Optional: persist the RAM-only states (trip start, fuel low-water-mark) — deliberately
|
||
deferred; may become moot with the FMM003 switch
|
||
- [ ] Upload vehicle photos to `www/bilder/`, set `steuer.faellig` (operational data, not code)
|
||
|
||
### D) Once the panel is superseded
|
||
|
||
- [ ] **Archive** `audi-dashboard-app.js`, don't delete (settled decision, architecture §1)
|
||
|
||
---
|
||
|
||
## Working conventions (observed — keep them)
|
||
|
||
- German is the project language: identifiers, comments, commits, UI texts. Exceptions:
|
||
`design-system/` uses English props/JSDoc (Claude Design audience) — and **this file**, which is
|
||
English by rule.
|
||
- Decisions are logged, including rejected ones (see the Traccar section of the architecture
|
||
doc); superseded sections stay in place marked "ÜBERHOLT" rather than being deleted.
|
||
- Numbers in `de-DE` format; font weights 300/400 only, never ≥600 (only those cuts exist).
|
||
- Real vehicle/movement data stays local (`data/` contents and receipt PDFs are gitignored).
|
||
- Security principle: HA is never publicly exposed; only narrowly scoped surfaces (MQTT broker,
|
||
proxy allowlist) may be exposed, each by explicit decision.
|