# yaris-obd Bug Tracker Standalone bug/issue tracker for the yaris-obd project, kept **separate from `PLAN.md`**. Use this file to record, track status, and hand off bugs to another agent/session. `PLAN.md` still summarizes bug-fix progress in its phase sections (and Phase 26 is the dedicated road-test bug-fix phase), but the authoritative, issue-by-issue record lives **here**. ## How to use this tracker - **File format**: one `## BUG-XX` section per issue. Keep the numbering sequential. - **Status field**: `OPEN` → `IN PROGRESS` → `FIXED` (commit ref) → `CLOSED` (deployed + verified) or `WONTFIX`/`DUP` as appropriate. - **Relate to phases**: each entry's `Phase` field links a bug to the relevant `PLAN.md` phase so an agent can find the surrounding context and acceptance criteria. - **When fixed**: keep the original symptom/impact recorded (don't delete history), set status to `FIXED`, add the commit SHA in the `Fix` field, then move to `CLOSED` after deploy/verify. Also drop a one-line mention in the corresponding `PLAN.md` phase. - **For future bugs**: add new `## BUG-XX` entries here rather than only burying them in chat history. Keep them SMALL and one-issue-per-entry so they are easy to pick up and verify. --- ## Open bugs ### BUG-01 — WiFi/hotspot unreachable from phone while cell data is on (and wrong documented gateway) - **Status**: `OPEN` - **Severity**: Medium (usability of the in-car hotspot from a phone) - **Area**: Networking / Pi hotspot (`yaris-uplink`, NetworkManager shared AP, docs) - **Phase**: Phase 26 - **Symptom**: While a phone is connected to the Pi's no-internet hotspot and cell data is on, the phone cannot load `yaris.local`, `10.42.0.1`, **or** the Pi's DHCP address at all. With cell data disabled, `http:///` loads, but `yaris.local` and `10.42.0.1` still do not. The working address seen was `10.112.<...>`. - **Root cause (confirmed)**: Phone-OS "smart network switching" — with a no-internet WiFi network associated and cellular available, the OS keeps cellular as the whole-device default route, so no traffic (even a plain page navigation or `fetch()`) reaches the Pi hotspot. Disabling cell data forces the hotspot WiFi to become the default network. This is the same OS-level ceiling documented in Phase 11/22. Separately, the actual NetworkManager shared-AP subnet is `10.112.0.x`, **not** the documented `10.42.0.1`, and `yaris.local` mDNS isn't resolved from the phone on the Pi AP. - **Fix scope / actions**: - Make the documented/reachable address correct (surface the Pi's real DHCP-assigned address in the UI / status page rather than hardcoding `10.42.0.1`). - Extend the `yaris.local` unicast (shared-dnsmasq) fallback to the AP subnet so it resolves from a phone on the Pi hotspot. - Clearly document the cell-data limitation and the workaround (disable cell data / "smart network switching"). - **Acceptance**: phone on the Pi hotspot can load the web UI via a documented, correct address (optionally `yaris.local`), and the cell-data caveat is documented. --- ### BUG-02 — Micro-USB power port on the Pi is physically damaged - **Status**: `OPEN` - **Severity**: Hardware (reliability/power) - **Area**: Hardware / power - **Phase**: Phase 26 - **Symptom**: The Pi's micro-USB 5V power connector is worn/damaged and no longer makes a reliable connection. Power is likely to drop under vibration in the car. - **Root cause**: Physical wear on the connector. - **Fix scope / actions**: No code. Owner hard-wires 5V and GND directly to the Pi's GPIO header (pins 2/4 for 5V, 6/9/14/20/25/30/34/39 for GND) with a suitable PSU, bypassing the micro-USB port. Confirm `vcgencmd get_throttled` shows no current undervoltage after rewiring (the Phase 19/20 alerts monitor this). - **Acceptance**: Stable power with no undervoltage/throttling under load. --- ### BUG-03 — DTC/MIL codes are never persisted; lost on OBD disconnect or reboot - **Status**: `CLOSED` (commit `e2f2314`; deployed + verified 2026-08-11) - **Fix**: Persist MIL + codes to SQLite on each poll (`save_dtc_state`), `/api/dtc` falls back to the persisted snapshot when OBD is offline, `get_overview_stats` counts the real `dtc` table, and a schema migration rebuilds the unused `NAME/STATUS/DTC_ID` table. Cleared codes are tracked via `IS_SET` + `FIRST_SEEN_AT`/`LAST_SEEN_AT`. Tests: `tests/test_dtc_persistence.py`. - **Severity**: Medium (lost diagnostic data; also a stats bug) - **Area**: `modules/app_obd.py`, `modules/app_sql.py`, `get_overview_stats` - **Phase**: Phase 26 - **Symptom**: Stored DTC trouble codes and MIL state live only in the in-memory `dtc_codes` global in `app_obd.py`. They vanish as soon as OBD disconnects or the Pi reboots. Separately, `get_overview_stats` queries a table named `dtc_codes` that does not exist (the created table is `dtc`), so the post-road-test state may be wrong/broken. - **Root cause**: The SQLite `dtc` table is created in schema but is never written to or read from by any code path. No DTC persistence exists at all. - **Fix scope / actions**: - Persist MIL + DTC codes to the `dtc` SQLite table on each successful poll. - Read back from SQLite when OBD is disconnected so last-known codes stay visible. - Fix `get_overview_stats` to query the real `dtc` table (and use current MIL/DTC state). - Add a "cleared/since" concept so a code that was cleared is distinguishable from one still set. - **Acceptance**: Codes set during a drive survive a power cycle / OBD disconnect and appear on the DTC page and overview stats. --- ### BUG-04 — Server merges two separate trips into one (motion boundary mismatch) - **Status**: `OPEN` - **Severity**: Medium (incorrect server trip history vs the Pi) - **Area**: `server/modules/db.py` `compute_trips_for_device` - **Phase**: Phase 26 - **Symptom**: Two distinct drives (parked in between) appear as a single merged trip on the remote portal, while the Pi correctly reports them as separate trips. - **Root cause**: The Pi splits trips by **motion** (OBD wheel speed + sustained GPS displacement, with a 5-min motion gap). The server splits only by a **pure time gap** (>5 min) and filters by duration/50 m, so parked idle tracking rows bridging two drives get combined into one trip. - **Fix scope / actions**: Mirror the Pi's motion-aware trip-boundary logic on the server so the two systems agree. Note this may re-split/recompute existing server trips — get owner sign-off before recomputing, and re-run `scripts/recompute_trips.py` after the fix. - **Acceptance**: A real drive with a parked gap produces the same number of trips on the server --- ### BUG-05 — Server route replay reports `speed_source: "gps"` even when OBD speed is present - **Status**: `CLOSED` (commit `e2f2314`; deployed + verified 2026-08-11) - **Fix**: `get_trip_route` now mirrors the Pi's logic: `"obd"` when speed > 0, otherwise `"gps"`. Server docker container rebuilt and health-checked. --- ### BUG-06 — Server timestamps are shifted (Postgres stores naive local time as UTC) - **Status**: `OPEN` - **Severity**: High-ish (all server trip times / boundaries off by the EDT offset) - **Area**: Pi upload (`modules/app_upload.py` / SQL), `server/sql`, `server/modules/db.py`, portal display - **Phase**: Phase 26 - **Symptom**: Server shows trip times (and effectively trip boundaries/comparisons against the Pi) shifted by the Montreal-timezone-to-UTC offset (~4-5 hours depending on DST). - **Root cause**: `tracking_points.recorded_at` is `TIMESTAMPTZ`, but the Pi sends **naive local (Montreal) time** (`datetime.now()`) with no offset. Postgres (UTC container timezone) interprets that naive string as UTC. - **Fix scope / actions** (delicate — touches sending, storage, and display): - Pi should send timezone-aware timestamps in local Eastern time (or explicit UTC). - Server should store/read as `America/Toronto` (or consistent UTC) and display in Eastern. - Optional one-time migration to correct already-stored rows (depends on offset at insert time — needs a decision: re-map by DST window or accept a best-effort correction). Rebuilding the server `trips` table may be needed. **Get owner sign-off on migrating existing data.** - **Acceptance**: New data displays at the correct local time on the portal; existing shifted rows are corrected (or explicitly accepted as-is if the owner opts out of migration). --- ### BUG-07 — Trip replay on the portal sometimes does nothing - **Status**: `OPEN` (needs reproduction/diagnostics) - **Severity**: Low/Medium (intermittent UI) - **Area**: `static/js/history.js` (shared Pi+server), server `/api/v1/portal/trip/` - **Phase**: Phase 26 - **Symptom**: Clicking a trip to replay on the `/history` page intermittently does nothing — no route renders, no error shown. - **Root cause**: Not yet isolated. Plausible contributors: an API/DB race (`recompute_trips` running concurrently with a route read), DB lock, or clicking before the map is ready. Note `history.js` uses shared parsing that expects space-separated Pi timestamps and `"T"`-separated server ones — verify both. - **Fix scope / actions**: Add targeted diagnostics (log/reproduce) before fixing. Check map readiness, error handling on the trip-fetch, and the shared timestamp parsing. Add an explicit "failed to load route" state instead of silent inaction. - **Acceptance**: Replay is reliable; any failure surfaces a visible error rather than doing nothing. --- ### BUG-08 — No trip deletion on the local Pi (only on the remote portal) - **Status**: `OPEN` - **Severity**: Medium (feature parity: delete trips locally) - **Area**: Pi `templates/components.html`, `templates/base.html`, `templates/history.html`, `static/js/history.js`, `app.py` - **Phase**: Phase 26 - **Symptom**: The Pi `/history` page has no way to delete a trip. The remote portal does. - **Root cause**: Several Pi pieces are missing compared to the server: 1. Pi `components.html` `data_table` macro lacks the `deletable` arg that emits `data-deletable="1"` (the server's macro has it). 2. Pi `base.html` does not define `window.confirmDelete` (the server's does). 3. Pi `history.js` hardcodes the **server** delete endpoint `/api/v1/portal/trip/` — which does not exist on the Pi. 4. There is no `DELETE /api/trip/` route on the Pi at all. - **Fix scope / actions**: Add all four pieces on the Pi — the `deletable` macro arg, the `window.confirmDelete` helper, a `DELETE /api/trip/` route that removes only the derived trip record (keeping the raw `tracking` data), and make the delete endpoint configurable in `history.js` (e.g. via a data attribute like the tile URL) so the same JS works on Pi and server. - **Acceptance**: The Pi `/history` page can delete a trip with confirmation; raw GPS data is preserved; the server portal is unaffected. --- ## Resolved bugs ### BUG-03 — DTC/MIL codes are never persisted; lost on OBD disconnect or reboot - **Status**: `CLOSED` 2026-08-11 - **Commit**: `e2f2314` - **Verification**: Deployed to Pi, service restarted clean, `/api/dtc` returns persisted snapshot when OBD offline, `/api/stats` shows `active_dtc: 0`, `save_dtc_state`/`get_persisted_dtc_state` confirmed present in running code. ### BUG-05 — Server route replay speed_source always "gps" - **Status**: `CLOSED` 2026-08-11 - **Commit**: `e2f2314` - **Verification**: Server git reset to origin, Docker rebuild, `/health` OK on LAN and Cloudflare Tunnel. `get_trip_route` now uses `"obd" if speed > 0 else "gps"` matching Pi logic.