docs: sign off Phase 25 road test, add BUGS.md, renumber Android to Phase 27

This commit is contained in:
Mathieu Moran
2026-08-10 23:42:39 -04:00
parent d216aac97e
commit 3e626830d5
3 changed files with 328 additions and 29 deletions
+190
View File
@@ -0,0 +1,190 @@
# 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**: `OPEN`
- **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
as on the Pi.
---
### BUG-05 — Server route replay reports `speed_source: "gps"` even when OBD speed is present
- **Status**: `OPEN`
- **Severity**: Low (display/metadata)
- **Area**: `server/modules/db.py` `get_trip_route`
- **Phase**: Phase 26
- **Symptom**: Server route replay always labels points with `speed_source: "gps"`, even though the
points carry real OBD `speed`. The Pi's `route_points` correctly uses `"obd"` when speed > 0.
- **Root cause**: `get_trip_route` hardcodes `speed_source: "gps"` rather than deriving it from the
per-point speed value.
- **Fix scope / actions**: Mirror the Pi's logic (`"obd"` when speed > 0, otherwise `"gps"`) in the
server route response.
- **Acceptance**: Server route points report `obd` speed source where wheel speed was recorded.
---
### 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**: `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/<id>` — which does not exist on the Pi.
4. There is no `DELETE /api/trip/<id>` 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/<id>` 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
(No entries yet — resolved-and-fixed bugs land here, oldest→newest. Keep the bug's original
symptom text intact and add the fix/closure details.)
+128 -22
View File
@@ -1400,7 +1400,7 @@ including two UX polish passes and multiple bug-fix rounds.
**Roadmap ordering note (2026-08-10):** the late phases are intentionally sequenced as **Roadmap ordering note (2026-08-10):** the late phases are intentionally sequenced as
Phase 25.5a–25.5d (configuration/sync/status work) -> Phase 25 (real-car acceptance gate) -> Phase 25.5a–25.5d (configuration/sync/status work) -> Phase 25 (real-car acceptance gate) ->
Phase 26 (Android, always last). Phase 26 (road-test bug fixes) -> Phase 27 (Android, always last).
### Phase 25.5a: Device configuration sync backend — ✅ COMPLETE (connection-driven server sync) ### Phase 25.5a: Device configuration sync backend — ✅ COMPLETE (connection-driven server sync)
**Server API and Pi sync thread implemented.** Server stores device config; Pi fetches when internet available and applies changes without restart. **Server API and Pi sync thread implemented.** Server stores device config; Pi fetches when internet available and applies changes without restart.
@@ -1714,7 +1714,14 @@ architecture.
`/` HTML places `Overview` before `Live GPS`. `/` HTML places `Overview` before `Live GPS`.
--- ---
### Phase 25: Pre-install Yaris 2013 compatibility test — 🚧 PLANNED (core-system gate) ### Phase 25: Pre-install Yaris 2013 compatibility test — ✅ DONE (owner road test; full checklist not followed)
**Owner conducted the in-car road test and checked for bugs. Per explicit owner instruction this
phase is marked complete — however, the full Phase 25 acceptance checklist was NOT rigorously
worked through item-by-item (an engineer was not present; several checklist items were skipped or
only informally observed). Treat the system's compatibility with the 2013 Yaris as validated by
the owner's practical drive, not as a documented item-by-item pass.** Bugs surfaced by the road
test are cataloged in `BUGS.md` and tracked for fixing in Phase 26. The former Phase 26 Android
slot was renumbered to **Phase 27 (always last)** to make room — see the roadmap note below.
**Added 2026-08-09; intentionally immediately before the Android app.** Bench tests, synthetic **Added 2026-08-09; intentionally immediately before the Android app.** Bench tests, synthetic
routes, attached-hardware tests, and stationary Pi runs cannot prove the complete system works routes, attached-hardware tests, and stationary Pi runs cannot prove the complete system works
when temporarily connected to and powered by the actual 2013 Toyota Yaris. This phase is the when temporarily connected to and powered by the actual 2013 Toyota Yaris. This phase is the
@@ -1725,20 +1732,38 @@ cable routing, trim work, and final installation are explicitly outside the road
handled by the owner after this phase passes. handled by the owner after this phase passes.
Prepare a temporary, removable test setup before driving: Prepare a temporary, removable test setup before driving:
- Connect the Pi, SIM868 HAT/antennas, USB OBD adapter, dashcam, and representative intended power - ~~Connect the Pi, SIM868 HAT/antennas, USB OBD adapter, dashcam, and representative intended power
source without permanently mounting or routing anything. Secure every temporary component and source without permanently mounting or routing anything. Secure every temporary component and
cable so it cannot interfere with pedals, steering, airbags, visibility, occupants, or loose-item cable so it cannot interfere with pedals, steering, airbags, visibility, occupants, or loose-item
safety during the controlled drive; remove the setup after testing. safety during the controlled drive; remove the setup after testing.~~ — ✅ **DONE** (owner set up
- Resolve the Phase 19/20 undervoltage/throttling warning before the test; confirm the temporary test rig).
- ~~Resolve the Phase 19/20 undervoltage/throttling warning before the test; confirm
`vcgencmd get_throttled` shows no current power fault at idle and under load with the temporary `vcgencmd get_throttled` shows no current power fault at idle and under load with the temporary
representative power setup. This validates electrical compatibility, not final cable routing. representative power setup. This validates electrical compatibility, not final cable routing.~~ —
- Back up the Pi SQLite database and record baseline row/trip/video/upload counts on the Pi and ⚠️ **OPEN — NOT DONE.** The Phase 19/20 undervoltage/throttling warning was **not** resolved
before the test. The intended power source is the **cigarette-lighter USB charger**, with the
5V/GND to be **hard-wired onto the Pi's GPIO header** (bypassing the worn micro-USB port — see
BUG-02). Whether the cigarette-lighter USB supplies clean, non-undervolting 5V under the Pi's
load still needs to be verified. **Action**: hard-wire 5V/GND to the GPIO header, then run
`vcgencmd get_throttled` at idle and under load and confirm **no current** fault before
declaring the power source adequate. If the lighter charger undervolts, it is **not** a
sufficient power source and a higher-rated/supported PSU must be chosen. (Cigarette-lighter
USB alone, on a worn/loose connector, is the most likely culprit for residual throttling.)
- ~~Back up the Pi SQLite database and record baseline row/trip/video/upload counts on the Pi and
remote server. Use a uniquely timestamped test window so generated records can be reconciled remote server. Use a uniquely timestamped test window so generated records can be reconciled
without deleting unrelated history. without deleting unrelated history.~~ — ⚠️ **SKIPPED** (owner: "didn't do it but don't very
- Verify the service, watchdog, storage mounts, hotspot, `http://yaris.local/`, alert engine, GPS care"). Acceptable to skip the pre-test backup/baseline, but be aware there is now **no
antenna view, dashcam recording mode, free space, and remote server health before moving. pre-test record** to reconcile test drive rows against, and no pre-drive SQLite backup exists.
- Use a second person for UI/log observations while the vehicle is moving, or record diagnostics - ~~Verify the service, watchdog, storage mounts, hotspot, `http://yaris.local/`, alert engine, GPS
for review afterward. The driver must not operate or troubleshoot the UI while driving. antenna view, dashcam recording mode, free space, and remote server health before moving.~~ —
🟡 **PARTIAL.** Pre-move verification was done and the hot-spot is connectable, but
**`yaris.local` does not resolve** on the phone → tracked as **BUG-01**. Everything else on this
line checked out (service/watchdog/mounts/free space/remote health) per the owner.
- ~~Use a second person for UI/log observations while the vehicle is moving, or record diagnostics
for review afterward. The driver must not operate or troubleshoot the UI while driving.~~ —
⛔ **SCRATCHED** (owner: "will do it solo"). **Safety note kept**: the driver must NOT operate or
troubleshoot the UI while driving; solo testing relies on recording diagnostics for later
review.
Run a controlled route containing ignition-off/on, stationary idle, low-speed streets, normal road Run a controlled route containing ignition-off/on, stationary idle, low-speed streets, normal road
speed, several complete stops, turns, a short GPS-obstructed segment if safely available, and a speed, several complete stops, turns, a short GPS-obstructed segment if safely available, and a
@@ -1747,22 +1772,34 @@ clean trip end:
adapter firmware, and verify real speed/RPM/coolant/fuel/throttle/MIL/DTC values. Compare wheel adapter firmware, and verify real speed/RPM/coolant/fuel/throttle/MIL/DTC values. Compare wheel
speed against the instrument cluster at several safe speeds. Record unsupported PIDs (especially speed against the instrument cluster at several safe speeds. Record unsupported PIDs (especially
odometer PID A6) as capabilities, not failures, and verify reconnect after ignition cycling and a odometer PID A6) as capabilities, not failures, and verify reconnect after ignition cycling and a
deliberate USB disconnect/reconnect. deliberate USB disconnect/reconnect. — ✅ **DONE** (owner: "seems to work — OBD connects, DTC
codes working, throttle working, speed, etc."). Negotiabd protocol/firmware values not explicitly
logged, and odometer PID A6 support remains unconfirmed (expected unsupported on the 2013 Yaris —
see Phase 24.6).
- **GPS and motion logging:** confirm fixes while stationary and moving, route coordinates on the - **GPS and motion logging:** confirm fixes while stationary and moving, route coordinates on the
offline map, OBD-speed-first motion detection, GPS displacement fallback when OBD is deliberately offline map, OBD-speed-first motion detection, GPS displacement fallback when OBD is deliberately
unavailable, one-minute idle heartbeat behavior, and no false trip from stationary GPS drift. unavailable, one-minute idle heartbeat behavior, and no false trip from stationary GPS drift. —
✅ **DONE** (owner: "seems to work for the trip"). **Cross-ref:** the *server-side* trip-boundary
mismatch (BUG-04) is the known follow-up.
- **Trip boundary/replay:** after the inactivity timeout, verify exactly one trip (or the expected - **Trip boundary/replay:** after the inactivity timeout, verify exactly one trip (or the expected
number for deliberate ignition gaps), correct start/end time and coordinates, plausible number for deliberate ignition gaps), correct start/end time and coordinates, plausible
distance/average/max speed, km/mi and km/h/mph values, route polyline, start/end markers, timeline distance/average/max speed, km/mi and km/h/mph values, route polyline, start/end markers, timeline
scrubber, play/pause marker position, and stopped portions of the route. scrubber, play/pause marker position, and stopped portions of the route. —
🟡 **PARTIAL / bug reported.** Local replay observed OK, but the server-side trip-splitting and
replay issues are tracked in **BUG-04** (server merges two trips) and **BUG-05** (server replay
`speed_source`), with **BUG-06** (timezone shift affecting times) also relevant.
- **Local web UI:** test the final Phase 23 interface on the actual phone over the Pi/phone network at - **Local web UI:** test the final Phase 23 interface on the actual phone over the Pi/phone network at
`http://yaris.local/`; verify the explicit **In-car / Local Pi** identity, dashboard telemetry, `http://yaris.local/`; verify the explicit **In-car / Local Pi** identity, dashboard telemetry,
DTC page, trip history, dashcam library, settings, alerts, responsive layout, reconnect behavior, DTC page, trip history, dashcam library, settings, alerts, responsive layout, reconnect behavior,
and no new browser console/API errors. and no new browser console/API errors. —
🟡 **PARTIAL / bug reported.** Reaching the UI over the Pi hotspot is blocked by `yaris.local`
not resolving on the phone → **BUG-01**. Other local UI pages work once reachable.
- **Dashcam workflow:** create a known short real clip, switch the camera safely into USB storage - **Dashcam workflow:** create a known short real clip, switch the camera safely into USB storage
mode, detect/list it without confusing the OBD adapter, import only that selected file, verify mode, detect/list it without confusing the OBD adapter, import only that selected file, verify
thumbnail/playback/Range seeking, preserve the source until checks finish, and confirm the camera thumbnail/playback/Range seeking, preserve the source until checks finish, and confirm the camera
returns to recording afterward. Test deletion only with the disposable clip. returns to recording afterward. Test deletion only with the disposable clip. —
⏳ **DEFERRED** (owner: "will test later stages"). Not yet exercised as part of this test; remains
a follow-up for the Phase 26 bug-fix/verification pass.
- **Alerts:** safely exercise conditions that can be simulated without damaging hardware (upload - **Alerts:** safely exercise conditions that can be simulated without damaging hardware (upload
backlog, temporary GPS loss while OBD reports motion, and disposable threshold overrides). backlog, temporary GPS loss while OBD reports motion, and disposable threshold overrides).
Confirm local bell transition/recovery/cooldown behavior and offline alert-state reconciliation Confirm local bell transition/recovery/cooldown behavior and offline alert-state reconciliation
@@ -1771,12 +1808,31 @@ clean trip end:
transport can reliably deliver ntfy notifications while WiFi/Ethernet is unavailable, including transport can reliably deliver ntfy notifications while WiFi/Ethernet is unavailable, including
authentication, timeout, retry, and recovery. If that cannot be made reliable on the actual authentication, timeout, retry, and recovery. If that cannot be made reliable on the actual
modem/network, remove the ntfy feature and its settings rather than retain a transport that fails modem/network, remove the ntfy feature and its settings rather than retain a transport that fails
in the only situation where it is useful. in the only situation where it is useful. —
🟡 **PARTIAL / done in part.** Owner reports alerts "seem to work for engine and DTC codes +
power". The SIM868 **ntfy path remains unverified** (no real SIM on the modem yet, so the
"determine whether the SIM868 transport can reliably deliver ntfy" sub-item is still open).
- **Power/reliability:** perform multiple normal ignition/power cycles, confirm automatic boot, - **Power/reliability:** perform multiple normal ignition/power cycles, confirm automatic boot,
service/watchdog startup, SIM868 power-on, OBD/GPS recovery, no SQLite corruption, and no current service/watchdog startup, SIM868 power-on, OBD/GPS recovery, no SQLite corruption, and no current
undervoltage/throttling. The deferred Phase 14 clean-shutdown limitation must be explicitly undervoltage/throttling. The deferred Phase 14 clean-shutdown limitation must be explicitly
accepted or resolved with representative delayed-power/ignition-sense hardware before declaring accepted or resolved with representative delayed-power/ignition-sense hardware before declaring
the design ready for permanent installation. the design ready for permanent installation. —
🟡 **PARTIAL — Pi logs checked 2026-08-10 (at home, car not connected).** Verified from the live
Pi:
- `yaris-obd.service` **active**; uptime 2:48h with **no crash/restart** in the current boot
window (systemd auto-restart + the 10s hardware watchdog did not need to fire since startup).
- `vcgencmd get_throttled` → **`0x50000` = NO current undervoltage right now** (only the
historical "occurred" bits 0x40000/0x10000 remain set). Confirms the Pi is not *currently*
undervolting on whatever power is connected at home, but does not prove the in-car
cigarette-lighter source yet (re-check under load in the car after hard-wiring — see the open
power item above and BUG-02).
- OBD reconnect loop logging as designed ("handshake succeeded / ECU did not respond" = car not
on/not connected here). Uploads syncing fine ("sent N tracking rows").
- No SQLite corruption or tracebacks in the log tail.
**Still open**: a real multi-ignition-cycle verification *in the car* (boot recovery, watchdog
restart under power loss, OBD/GPS recovery, no corruption) with the final hard-wired power
source has not been demonstrated. The Phase 14 clean-shutdown limitation remains **accepted**
(delayed-power / ignition-sense hardware not yet present).
Verify the complete remote path after connectivity returns: Verify the complete remote path after connectivity returns:
- Confirm every test-window tracking row uploads exactly once and the selected dashcam clip resumes - Confirm every test-window tracking row uploads exactly once and the selected dashcam clip resumes
@@ -1786,9 +1842,12 @@ Verify the complete remote path after connectivity returns:
vehicle-scoped trip summary/replay, route and timeline agreement with the local Pi, archived clip vehicle-scoped trip summary/replay, route and timeline agreement with the local Pi, archived clip
thumbnail/playback/Range seeking/download, synchronized alert history/current state without thumbnail/playback/Range seeking/download, synchronized alert history/current state without
duplicate transitions after an offline interval, and absence of local-only OBD/cellular/Pi duplicate transitions after an offline interval, and absence of local-only OBD/cellular/Pi
controls. controls. —
🟡 **PARTIAL.** Owner can connect to the remote server; the portal is reachable. The main
blocker is the **Pi hotspot** reachability issue (**BUG-01**), which affects exercising the
portal from the in-car phone. Server-side trip/replay/time issues (BUG-04/05/06) also remain.
- The Android companion is intentionally outside this core acceptance gate and remains the final, - The Android companion is intentionally outside this core acceptance gate and remains the final,
low-priority Phase 26. Its own hotspot/cellular binding, trip display, and disposable clip relay low-priority Phase 27. Its own hotspot/cellular binding, trip display, and disposable clip relay
require separate real-phone/in-car acceptance when that phase is implemented. require separate real-phone/in-car acceptance when that phase is implemented.
Acceptance requires a written result for every item, captured negotiated protocol/PID support, Acceptance requires a written result for every item, captured negotiated protocol/PID support,
@@ -1800,7 +1859,54 @@ phase roadmap.
--- ---
### Phase 26: Native Android companion app — 🚧 PLANNED (low priority, always last) ### Phase 26: Road-test bug fixes — 🚧 PLANNED
**Renumbered/added 2026-08-10 after the Phase 25 owner road test.** This phase (the former Phase 26
Android slot, which is now **Phase 27**) is a targeted bug-fix pass for the defects surfaced by the
in-car road test. Each item references its tracking entry in `BUGS.md`; distinct fixes should be
committed individually with a `fix(bug-N): ...` prefix and each closed out in `BUGS.md` as it
lands. The current known issues from the road test (also recorded in `BUGS.md`):
1. **BUG-01 (WiFi/hotspot reachability).** While cell data is on, a phone connected to the Pi's
no-internet hotspot cannot load `yaris.local`, `10.42.0.1`, or the Pi's DHCP address at all —
the phone OS keeps cellular as the default route (Phase 11/22 "smart network switching"
behavior). It only works once the user disables cell data, then `http://<dhcp-ip>/` loads but
`yaris.local` and `10.42.0.1` still do not. Also: the live hotspot subnet is `10.112.0.x`, not
the documented `10.42.0.1`. Fix scope: make the documented/reachable address correct, surface
the real DHCP address in the Pi UI, document the cell-data behavior, and see if `yaris.local`
unicast fallback can be extended to the AP subnet.
2. **BUG-02 (micro-USB power port damaged).** Hardware-only (worn power connector). Action is
documentation + owner hard-wiring 5V/GND to the GPIO header pins; no code.
3. **BUG-03 (DTCs not persisted).** The SQLite `dtc` table is created but never written or read;
DTC codes/MIL live only in memory in `app_obd.py` and vanish on OBD disconnect/reboot.
`get_overview_stats` also queries a non-existent `dtc_codes` table. Fix: persist on each poll,
read back when disconnected, fix the stats query, add a "cleared/since" concept.
4. **BUG-04 (server merges two trips into one).** The Pi splits trips by *motion* (OBD wheel
speed + sustained GPS displacement, inactivity gap); the server splits only by *time gap* and
filters by duration. Parked idle tracking rows bridge two drives into one server-side trip.
Fix: mirror the Pi's motion-boundary logic server-side.
5. **BUG-05 (server replay speed_source).** Server `get_trip_route` hardcodes `speed_source: "gps"`
even when OBD speed is present (it already returns raw `speed`). Fix: mirror the Pi's
`obd if speed>0 else gps` logic.
6. **BUG-06 (server timezone is UTC-shifted).** `tracking_points.recorded_at` is `TIMESTAMPTZ`
but the Pi sends naive local (Montreal) time with no offset; Postgres (UTC container) treats
it as UTC, so server times/trip boundaries are off by the EDT offset. Fix: Pi sends
timezone-aware timestamps; server displays/stores in `America/Toronto`; optional one-time
migration to correct already-stored rows.
7. **BUG-07 (trip replay sometimes does nothing).** Intermittent `/history` replay failure —
needs reproduction/diagnostics (likely an API/DB race or map-not-ready timing). Add targeted
diagnostics before fixing.
8. **BUG-08 (no trip deletion on the local Pi).** The Pi's `components.html` `data_table` macro
lacks the `deletable` arg, `base.html` lacks `window.confirmDelete`, `history.js` hardcodes the
*server* delete endpoint, and there is no `DELETE /api/trip/<id>` route on the Pi. Fix: add all
four pieces and make the delete endpoint configurable in `history.js`.
**Acceptance for Phase 26:** every `BUGS.md` entry in scope is fixed, regression-tested, deployed,
marked resolved, and the corresponding fix recorded here.
---
### Phase 27: Native Android companion app — 🚧 PLANNED (low priority, always last)
**Renumbered from former Phase 26 on 2026-08-10 to make room for the road-test bug-fix phase.**
Revives the phone-relay goal (Phase 11/12) properly this time, using a real native app instead of Revives the phone-relay goal (Phase 11/12) properly this time, using a real native app instead of
a web page - the only way to actually hold a WiFi connection to the Pi's hotspot and a cellular a web page - the only way to actually hold a WiFi connection to the Pi's hotspot and a cellular
connection to the internet open simultaneously (`ConnectivityManager.bindProcessToNetwork()`, connection to the internet open simultaneously (`ConnectivityManager.bindProcessToNetwork()`,
+10 -7
View File
@@ -197,7 +197,9 @@ rm db/sql/quoije_Yaris2013.db
The build is now at the endgame stage: the Pi app, remote server, and device-management/sync work The build is now at the endgame stage: the Pi app, remote server, and device-management/sync work
are implemented and verified on the real Pi/unraid setup. The remaining roadmap phases are the are implemented and verified on the real Pi/unraid setup. The remaining roadmap phases are the
temporary real-vehicle acceptance test and the low-priority Android companion. road-test bug-fix pass (Phase 26) and the low-priority Android companion (Phase 27). The owner has
already completed the Phase 25 real-vehicle test; the bugs it surfaced are tracked in
[BUGS.md](./BUGS.md).
- **Phases 1–23.5**: core Pi system — GPS, OBD, dashcam, trip history, web UI redesign, WiFi upgrade, cellular fallback, alerts, settings, upload - **Phases 1–23.5**: core Pi system — GPS, OBD, dashcam, trip history, web UI redesign, WiFi upgrade, cellular fallback, alerts, settings, upload
- **Phase 24**: remote web portal at `https://yaris.ceaf.dev/` — login-protected, trip history with CARTO live map, dashcam archive, delete, overview stats - **Phase 24**: remote web portal at `https://yaris.ceaf.dev/` — login-protected, trip history with CARTO live map, dashcam archive, delete, overview stats
@@ -213,8 +215,8 @@ temporary real-vehicle acceptance test and the low-priority Android companion.
main remote overview/history/dashcam browsing views still follow one configured `DEVICE_NAME` main remote overview/history/dashcam browsing views still follow one configured `DEVICE_NAME`
until a later portal-wide device selector/filter is added until a later portal-wide device selector/filter is added
**Next**: Phase 25 — temporary real-vehicle test in the 2013 Yaris (see [PLAN.md](./PLAN.md) for **Next**: Phase 26 — road-test bug fixes from the Phase 25 in-car test (see [BUGS.md](./BUGS.md)
the full acceptance checklist). Phase 26 (Android companion app) follows after that. for the tracked issues). Phase 27 (Android companion app) follows after that.
Phase 14 is partially complete: the Pi's hardware watchdog is deployed and reboot-verified. Phase 14 is partially complete: the Pi's hardware watchdog is deployed and reboot-verified.
Read-only-root storage hardening is deferred until persistent storage is moved off the SD root Read-only-root storage hardening is deferred until persistent storage is moved off the SD root
@@ -222,7 +224,7 @@ partition, and clean shutdown still needs delayed-power or ignition-sense hardwa
The browser-based phone relay was removed after real-world testing confirmed that mobile browsers The browser-based phone relay was removed after real-world testing confirmed that mobile browsers
cannot reliably use the Pi's WiFi and cellular data simultaneously. A native Android companion is cannot reliably use the Pi's WiFi and cellular data simultaneously. A native Android companion is
planned later instead (Phase 26). planned later instead (Phase 27).
The local web UI includes a Settings page for safe runtime configuration: dashcam uploads, The local web UI includes a Settings page for safe runtime configuration: dashcam uploads,
uploaded-video retention, cellular APN fallback, optional ntfy delivery, alert thresholds, local uploaded-video retention, cellular APN fallback, optional ntfy delivery, alert thresholds, local
@@ -235,9 +237,10 @@ The Pi can also join a configured phone hotspot for outbound internet when no SI
including trip synchronization and WiFi dashcam uploads, with automatic recovery to home WiFi or including trip synchronization and WiFi dashcam uploads, with automatic recovery to home WiFi or
the Pi-hosted local hotspot. the Pi-hosted local hotspot.
A temporary, removable real-vehicle compatibility test in the 2013 Yaris (Phase 25) precedes the The Phase 25 real-vehicle compatibility test has been completed by the owner (road test). The
final low-priority Android phase (Phase 26). Permanent installation is owner-handled and is not a bugs it surfaced are tracked in [BUGS.md](./BUGS.md) and scheduled for fixing in Phase 26. The
roadmap phase. Full phase history, verification notes, and current scope live in final low-priority Android companion follows in Phase 27. Permanent installation is owner-handled
and is not a roadmap phase. Full phase history, verification notes, and current scope live in
[PLAN.md](./PLAN.md). [PLAN.md](./PLAN.md).
## Diagnostics & recovery ## Diagnostics & recovery