215 lines
12 KiB
Markdown
215 lines
12 KiB
Markdown
# 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://<dhcp-ip>/` 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**: `CLOSED` (commit `96fb897`; deployed + verified 2026-08-11)
|
||
- **Fix**: Replaced pure time-gap splitting with Pi's motion-aware logic: OBD speed > 1 km/h
|
||
primary, GPS displacement >= 8 m (3 consecutive) fallback, 5-min inactivity gap. Server
|
||
trips cleared and recomputed: 17 trips on both Pi and 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/<id>`
|
||
- **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**: `CLOSED` (commit `c5c478b`; deployed + verified 2026-08-11)
|
||
- **Fix**: Added `deletable` param to `data_table` macro, `window.confirmDelete` helper in
|
||
`base.html`, `DELETE /api/trip/<id>` route + `app_sql.delete_trip()`, and configurable
|
||
`data-delete-url` in `history.js` (shared Pi+server). Trip 1967 deleted, 18→17 trips,
|
||
2028 tracking rows preserved.
|
||
|
||
---
|
||
|
||
### BUG-09 — Server trip scrubber geocoding is slow on each step
|
||
- **Status**: `OPEN`
|
||
- **Severity**: Medium (UX)
|
||
- **Area**: `static/js/history.js` `showReplayPoint()` / `scheduleGeocode()`
|
||
- **Phase**: Phase 26
|
||
- **Symptom**: Scrubbing through the trip timeline on the server portal is sluggish — the location
|
||
name takes a long time to appear because each scrub step fires a fresh `/api/geocode` HTTP
|
||
request (debounced 600ms). On the Pi this is fast (offline reverse_geocoder), but the server
|
||
calls Nominatim which adds latency.
|
||
- **Root cause**: Every `showReplayPoint()` call triggers `scheduleGeocode()` → debounced
|
||
`fetch(/api/geocode...)`. No pre-caching or batch loading.
|
||
- **Fix scope / actions**: Pre-fetch geocode results for ALL trip points when the route is first
|
||
loaded. Store in a local JS map keyed by rounded coordinate. Show cached label instantly during
|
||
scrubbing. Only fall back to live fetch for uncached points. Optionally batch-geocode at the
|
||
server API level too (accept multiple lat/lon pairs).
|
||
- **Acceptance**: Scrubbing through trip replay shows location names near-instantly.
|
||
|
||
### BUG-10 — GPS-only false trips generated when car is not connected
|
||
- **Status**: `OPEN`
|
||
- **Severity**: Medium (data quality)
|
||
- **Area**: `modules/app_trip.py` `build_trip_summaries()`, `modules/app_sql.py` `refresh_trips()`
|
||
- **Phase**: Phase 26
|
||
- **Symptom**: The Pi generates trips from GPS data alone (GPS jitter/drift) even when the car
|
||
isn't connected. Since the Pi bench-tests with no OBD, GPS displacement fallback creates
|
||
phantom "trips" from stationary GPS noise.
|
||
- **Root cause**: The GPS motion fallback (3+ consecutive 8m displacements) can be satisfied by
|
||
GPS jitter over time, especially indoors or in weak-signal areas. The system doesn't gate trip
|
||
detection on whether OBD was ever present.
|
||
- **Fix scope / actions**:
|
||
- **Option A (strict)**: Only start trips when OBD wheel speed has been detected at least once
|
||
since the app started (a `car_was_connected` flag). No OBD ever = no trips, even with GPS.
|
||
- **Option B (lenient)**: Increase GPS motion requirements when OBD has never been seen (e.g.
|
||
require 10+ consecutive displacements of 20m+ instead of 3×8m).
|
||
- **Recommend Option A** — it matches the real-world use case: the Pi is powered by the car's
|
||
ignition, so OBD should always be reachable when the car is running. No OBD = bench test,
|
||
don't generate trips.
|
||
- **Acceptance**: Bench-testing the Pi (no car connected) produces zero trips. Real driving with
|
||
OBD connected still produces correct trips.
|
||
|
||
---
|
||
|
||
## 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.
|