docs: Phase 26 rewritten — all 12 bugs documented consistently. Phase 27/28 cleaned up.

This commit is contained in:
Mathieu Moran
2026-08-11 18:29:03 -04:00
parent 9ebfea8be3
commit 19c538389b
+74 -46
View File
@@ -1860,58 +1860,83 @@ phase roadmap.
---
### Phase 26: Road-test bug fixes — ✅ DONE (12 closed)
**Completed 2026-08-11.** All 12 bugs from the Phase 25 road test are fixed, deployed,
and verified by owner.
### Phase 26: Road-test bug fixes — ✅ DONE
**Completed 2026-08-11.** Targeted bug-fix pass for 12 defects surfaced by the Phase 25
in-car road test. Every item tracked in [BUGS.md](./BUGS.md), fixed, deployed, and verified
by owner. BUGS.md Open section is now empty — all 12 bugs in Resolved.
**Closed (verified in prod):**
- BUG-03: DTC/MIL persistence (`e2f2314`)
- BUG-04: Server trip merge — motion-aware logic (`96fb897`)
- BUG-05: Server speed_source (`e2f2314`)
- BUG-08: Pi trip deletion (`c5c478b`)
- BUG-09: Geocode pre-caching for fast scrubber (pre-existing)
- BUG-10: GPS-only false trips gated on OBD presence (pre-existing). Side fix: `refresh_trips()` preserves existing trips when `was_car_ever_connected()` is False (was deleting all trips on restart).
- BUG-11: Dashboard GPS "Stale fix" vs Status "Live fix" inconsistency. `renderGpsStatus()` now reads lat/lon from API. Status page uses same 30s staleness threshold (`9733036`).
- BUG-12: Dashboard OBD "—" while Status shows correct OBD state. Added `/api/obd/status` endpoint. Dashboard polls it on load + 15s. Socket events still update in real-time. Side fix: `Number()` wrap on API string lat/lon prevents crash (`91281b1`). GPS lat/lon now display correctly from both API and Socket paths (`a4a92cc`).
**Implemented and verified 2026-08-11:**
**BUG-07 supplementary fix — history.js rewrite (`static/js/history.js`):**
The trip history page had lingering responsiveness issues (freezing on rapid trip-switching,
no loading feedback, no visual active-trip indicator). A full clean rewrite of the JS was done
rather than incremental patching:
- **Map loading spinner**: Leaflet control in top-right corner, shown during trip fetch + render.
- **Active row highlight**: `.table-active` class on the selected trip row.
- **Debounce**: 300ms (was 100ms) to prevent accidental double-taps from stacking loads.
- **Unified state**: Single `st` object instead of 10+ scattered module-level variables.
- **Unified AbortController**: `st.ctrl` cancels both fetch and downstream rendering in one shot.
- **Geocode preload**: kicks off immediately after fetch, before map rendering, so cache is warm
by the time the scrubber moves.
- **Signal guards**: `sig.aborted` checked after every async step (route build, fitBounds,
timeline init) so rapid switching can't leave stale layers on the map.
- **Play/pause timeline preserved** — kept as-is per user request.
- **Delete/checkbox/batch-delete flow preserved** — no logic changes, just cleaner names.
- **Same JS file works on both Pi and server**: tile URL/attrib/zoom defaults read from
`data-*` attributes on `#history-map`, with Pi-local fallbacks when absent.
- **BUG-01 — WiFi/hotspot unreachable from phone while cell data is on**: Dual-radio setup —
wlan0 is permanent AP ("yaris 911" at 10.42.0.1, password "yaris911!"), wlan1 handles
internet uplink only. `yaris_uplink.py` never touches wlan0. Phone-hotspot auto-connects on
wlan1 within ~60s of appearing, falls back to home-wifi when it disappears. Settings page has
in-car access card with hotspot URL, copy button, and cell-data warning. Tested on iOS and
Android: `yaris.local` resolves, AP stays up with cell data active, phone-hotspot uplink
verified end-to-end.
- **BUG-02 — Pi micro-USB power port physically damaged**: Hard-wired 5V/GND directly to GPIO
header (red to Pin 2, black to Pin 6), bypassing the worn micro-USB connector. `vcgencmd
get_throttled` now returns `0x0` — no undervoltage, no throttling. Pi runs at full 1.2 GHz
instead of downclocked ~600 MHz. GPS fix acquired (45.544°N, 73.537°W, 7 satellites).
System noticeably more responsive.
- **BUG-03 — DTC/MIL codes never persisted** (`e2f2314`): `save_dtc_state()` persists MIL +
codes each OBD poll to SQLite. `/api/dtc` falls back to `get_persisted_dtc_state()` when
OBD is offline. Cleared codes tracked via IS_SET timestamps.
- **BUG-04 — Server merges two trips into one** (`96fb897`): Replaced server-side time-gap
splitting with Pi's motion-aware logic — OBD > 1 km/h primary, GPS >= 8m × 3 consecutive
fallback, 5-min inactivity gap. Server trips cleared and recomputed; 17 trips matched on
both Pi and server.
- **BUG-05 — Server route replay speed_source always "gps"** (`e2f2314`): Fixed to
`"obd" if speed > 0 else "gps"` matching Pi logic. Docker rebuild verified.
- **BUG-06 — Server timestamps shifted (naive local time stored as UTC)** (`7ce2a1e` +
migration): New `_to_tz_aware()` helper in `app_upload.py` converts naive SQLite timestamps
to TZ-aware ISO before upload (`2026-08-10 19:27:30` → `2026-08-10T19:27:30-04:00`). One-time
SQL migration on Unraid shifted 3,593 tracking rows +4h EDT, cleared deleted-trip markers,
recomputed 43 trips from corrected data. Server trip times now match real-world clock
(Aug 10 trips at 7:27 PM / 7:58 PM — was 3:27 PM / 3:58 PM).
- **BUG-07 — Trip replay on portal sometimes does nothing (intermittent freeze)** (`fa93384`):
Full rewrite of `static/js/history.js` — unified AbortController cancels fetch + all
downstream rendering; 300ms debounce prevents double-tap stacking; loading spinner on map +
`.table-active` row highlight give clear feedback; geocode preload starts before map
rendering so cache is warm; `signal.aborted` guards after every async step (route build,
fitBounds, timeline init). Play/pause timeline preserved. Same JS file works on both Pi
and server with tile config via `data-*` attributes. Verified on both platforms.
- **BUG-08 — No trip deletion on local Pi** (`c5c478b`): `deletable` macro, `window.confirmDelete`,
`DELETE /api/trip/<id>` endpoint, configurable `data-delete-url` in shared `history.js`.
Raw tracking preserved. Server-side: `deleted_trip_markers` table prevents deleted trips
from reappearing on recompute, batch-delete with checkboxes via `POST /api/v1/portal/trips/delete`.
Trip ID column added on server (`data-show-id="1"`), checkbox column server-only via
`data-batch-delete-url`. Both now share same 6-column layout.
- **BUG-09 — Server trip scrubber geocoding is slow**: `preloadGeocodes()` batch-fetches all
unique coords (concurrency 2) on trip load with AbortController. `geocodePoint()` checks
in-memory cache first. Verified on both Pi and server Docker container.
- **BUG-10 — GPS-only false trips when car not connected**: `_car_ever_connected` flag in
`app_obd.py` — set to `True` on first successful ECU connection. `build_trip_summaries()`
returns empty list when car was never OBD-connected. `refresh_trips()` preserves existing
trips when no OBD history (was deleting all trips on restart). Pi bench-test shows 0 trips.
- **BUG-11 — Dashboard GPS "Stale fix" while Status page shows "Live fix"** (`9733036`):
`renderGpsStatus()` now reads `status.latitude`/`status.longitude` from the `/api/gps/status`
API response (not just socket events). Status page now uses the same 30s staleness threshold
(`fix_age_seconds > 30` → "Stale fix" badge) instead of calling any fix "Live". Both pages
agree on fix status. Deployed to Pi.
- **BUG-12 — Dashboard OBD shows "—" while Status page shows correct OBD state** (`91281b1`,
`a4a92cc`): Added `/api/obd/status` endpoint returning adapter presence, port, and car
connection state. Dashboard calls `refreshObdStatus()` on page load + every 15s. Socket
events still update in real-time. Side fix: `renderGpsStatus()` crashed on `status.latitude
.toFixed is not a function` — API returns lat/lon as strings, fixed with `Number()` wrapper
(`a4a92cc`). Socket `gps_location` event now passes lat/lon to `renderGpsStatus` so both
API and Socket paths display coordinates correctly.
**Deployed and verified:**
- BUG-01: Dual-radio — wlan0="yaris 911" at 10.42.0.1 (password "yaris911!"),
wlan1=uplink. Tested on iOS + Android, AP stays up. Phone-hotspot auto-connects
on wlan1, falls back to home-wifi. Settings page shows in-car access card.
- BUG-06: `_to_tz_aware()` in `app_upload.py` converts naive SQLite timestamps
to TZ-aware before upload. One-time SQL migration on Unraid corrected all
existing tracking_points (+4h EDT) and recomputed 43 trips.
- BUG-07: history.js rewrite — rapid trip-switching no longer freezes, loading
spinner + active row highlight. Verified on Pi and server.
**Still open:** None.
BUG-02 is hardware (Pi micro-USB port damage) — owner's responsibility.
**Acceptance:** every `BUGS.md` entry in scope is fixed, regression-tested, deployed, and marked resolved.
**Result**: All 12 bugs closed. Pi runs at full 1.2 GHz with clean power. GPS has fix. Dual-radio
AP stays up regardless of uplink state. Server trip times are correct. Dashboard and Status page
agree on OBD and GPS state. BUGS.md Open section is empty. readme.md project status updated.
---
### Phase 27: Feature additions — 🚧 UP NEXT (pick one)
**Phase 26 is complete.** Select a feature from [FEATURES.md](./FEATURES.md) to implement.
Current candidates:
### Phase 27: Feature additions — 🚧 PLANNED (pick one)
**New phase. Phase 26 is complete.** Select a feature from [FEATURES.md](./FEATURES.md) to
implement one at a time, marking each `IN PROGRESS` → `SHIPPED` as they land. Current
candidates (also listed in FEATURES.md):
1. **FEAT-01**: Parallel/concurrent chunked dashcam uploads
2. **FEAT-02**: QR / claim-code device pairing flow
@@ -1921,6 +1946,9 @@ Current candidates:
6. **FEAT-08**: Predictable IP on phone hotspot (DHCP reservation / static IP)
7. **FEAT-09**: mDNS (yaris.local) on phone hotspot
Also shipped in Phase 26: **FEAT-06** (trip ID column on server history) and **FEAT-07**
(phone-hotspot auto-connect documentation — already implemented in Phase 22).
### Phase 28: 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. Renumbered to Phase 28 on 2026-08-11 to make room for Phase 27 feature additions.**
Revives the phone-relay goal (Phase 11/12) properly this time, using a real native app instead of