docs(03): complete overlay refactor phase
Phase 3 verified: 9/9 must-haves, 6/6 OVRL requirements satisfied. OverlayService wrapper, staleness detection, error surfacing all verified. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
+13
-13
@@ -30,12 +30,12 @@ Requirements for the restructure milestone. Each maps to roadmap phases.
|
||||
|
||||
### Overlay reliability
|
||||
|
||||
- [ ] **OVRL-01**: Overlay receives continuous debounced data updates (not one-shot push + heartbeat)
|
||||
- [ ] **OVRL-02**: Overlay errors surfaced to user via snackbar/toast (no silent catch blocks)
|
||||
- [ ] **OVRL-03**: Overlay creation verified before setting `isOverlayActive` flag
|
||||
- [ ] **OVRL-04**: Messages include timestamp for staleness detection in overlay
|
||||
- [ ] **OVRL-05**: Overlay displays "--" when data is stale (no updates for >2 seconds)
|
||||
- [ ] **OVRL-06**: `OverlayService` encapsulates all `FlutterOverlayWindow` platform calls
|
||||
- [x] **OVRL-01**: Overlay receives continuous debounced data updates (not one-shot push + heartbeat)
|
||||
- [x] **OVRL-02**: Overlay errors surfaced to user via snackbar/toast (no silent catch blocks)
|
||||
- [x] **OVRL-03**: Overlay creation verified before setting `isOverlayActive` flag
|
||||
- [x] **OVRL-04**: Messages include timestamp for staleness detection in overlay
|
||||
- [x] **OVRL-05**: Overlay displays "--" when data is stale (dims at 3s, dashes at 10s per user decision)
|
||||
- [x] **OVRL-06**: `OverlayService` encapsulates all `FlutterOverlayWindow` platform calls
|
||||
|
||||
### GPS performance
|
||||
|
||||
@@ -113,12 +113,12 @@ Deferred to future release. Tracked but not in current roadmap.
|
||||
| STATE-05 | Phase 2 | Complete |
|
||||
| STATE-06 | Phase 2 | Complete |
|
||||
| STATE-07 | Phase 2 | Complete |
|
||||
| OVRL-01 | Phase 3 | Pending |
|
||||
| OVRL-02 | Phase 3 | Pending |
|
||||
| OVRL-03 | Phase 3 | Pending |
|
||||
| OVRL-04 | Phase 3 | Pending |
|
||||
| OVRL-05 | Phase 3 | Pending |
|
||||
| OVRL-06 | Phase 3 | Pending |
|
||||
| OVRL-01 | Phase 3 | Complete |
|
||||
| OVRL-02 | Phase 3 | Complete |
|
||||
| OVRL-03 | Phase 3 | Complete |
|
||||
| OVRL-04 | Phase 3 | Complete |
|
||||
| OVRL-05 | Phase 3 | Complete |
|
||||
| OVRL-06 | Phase 3 | Complete |
|
||||
| GPS-01 | Phase 4 | Pending |
|
||||
| GPS-02 | Phase 4 | Pending |
|
||||
| GPS-03 | Phase 4 | Pending |
|
||||
@@ -140,4 +140,4 @@ Deferred to future release. Tracked but not in current roadmap.
|
||||
|
||||
---
|
||||
*Requirements defined: 2026-02-09*
|
||||
*Last updated: 2026-02-10 (Phase 2 requirements marked Complete)*
|
||||
*Last updated: 2026-02-11 (Phase 3 requirements marked Complete)*
|
||||
|
||||
@@ -13,8 +13,8 @@ This roadmap transforms a 1,133-line monolithic Flutter app into a maintainable
|
||||
Decimal phases appear between their surrounding integers in numeric order.
|
||||
|
||||
- [x] **Phase 1: Foundation** - Establish directory structure and extract models (COMPLETE 2026-02-09)
|
||||
- [ ] **Phase 2: Provider Migration** - Replace setState with proper state management
|
||||
- [ ] **Phase 3: Overlay Refactor** - Fix overlay communication reliability
|
||||
- [x] **Phase 2: Provider Migration** - Replace setState with proper state management (COMPLETE 2026-02-10)
|
||||
- [x] **Phase 3: Overlay Refactor** - Fix overlay communication reliability (COMPLETE 2026-02-11)
|
||||
- [ ] **Phase 4: GPS & Power Optimization** - Implement adaptive precision and lifecycle management
|
||||
|
||||
## Phase Details
|
||||
@@ -72,7 +72,7 @@ Plans:
|
||||
|
||||
**Success Criteria** (what must be TRUE):
|
||||
1. Overlay receives continuous GPS updates without going stale
|
||||
2. Overlay displays "--" when data is more than 2 seconds old
|
||||
2. Overlay dims display after 3 seconds of no data, shows "--" after 10 seconds
|
||||
3. Overlay creation failures surface to user via snackbar instead of silent failure
|
||||
4. OverlayService encapsulates all flutter_overlay_window platform calls
|
||||
5. Heartbeat timer only runs when overlay active and app backgrounded
|
||||
@@ -81,8 +81,8 @@ Plans:
|
||||
**Plans**: 2 plans
|
||||
|
||||
Plans:
|
||||
- [ ] 03-01-PLAN.md — OverlayService wrapper, OverlayMessage timestamp, staleness/grace period constants
|
||||
- [ ] 03-02-PLAN.md — OverlayProvider refactor, overlay staleness UI, error snackbar surfacing
|
||||
- [x] 03-01-PLAN.md — OverlayService wrapper, OverlayMessage timestamp, staleness/grace period constants (4.0 min)
|
||||
- [x] 03-02-PLAN.md — OverlayProvider refactor, overlay staleness UI, error snackbar surfacing (4.25 min)
|
||||
|
||||
### Phase 4: GPS & Power Optimization
|
||||
|
||||
@@ -114,7 +114,7 @@ Phases execute in numeric order: 1 → 2 → 3 → 4
|
||||
|-------|----------------|--------|-----------|
|
||||
| 1. Foundation | 2/2 | **COMPLETE** | 2026-02-09 |
|
||||
| 2. Provider Migration | 2/2 | **COMPLETE** | 2026-02-10 |
|
||||
| 3. Overlay Refactor | 0/2 | Not started | - |
|
||||
| 3. Overlay Refactor | 2/2 | **COMPLETE** | 2026-02-11 |
|
||||
| 4. GPS & Power Optimization | 0/TBD | Not started | - |
|
||||
|
||||
## Notes
|
||||
@@ -130,4 +130,4 @@ Phases execute in numeric order: 1 → 2 → 3 → 4
|
||||
|
||||
---
|
||||
*Roadmap created: 2026-02-09*
|
||||
*Last updated: 2026-02-10 (Phase 2 complete - 11.5 min total, 2/2 plans)*
|
||||
*Last updated: 2026-02-11 (Phase 3 complete - 8.25 min total, 2/2 plans)*
|
||||
|
||||
@@ -0,0 +1,121 @@
|
||||
---
|
||||
phase: 03-overlay-refactor
|
||||
verified: 2026-02-11T10:29:03Z
|
||||
status: passed
|
||||
score: 9/9 must-haves verified
|
||||
re_verification: false
|
||||
---
|
||||
|
||||
# Phase 3: Overlay Refactor Verification Report
|
||||
|
||||
**Phase Goal:** Fix overlay data staleness and communication reliability issues
|
||||
**Verified:** 2026-02-11T10:29:03Z
|
||||
**Status:** PASSED
|
||||
**Re-verification:** No - initial verification
|
||||
|
||||
## Goal Achievement
|
||||
|
||||
### Observable Truths
|
||||
|
||||
| # | Truth | Status | Evidence |
|
||||
|---|-------|--------|----------|
|
||||
| 1 | Overlay receives continuous GPS updates without going stale | VERIFIED | OverlayProvider subscribes to GpsDataManager.dataStream and forwards every GPS tick via _pushDataToOverlay (lines 55-59) |
|
||||
| 2 | Overlay displays dashes when data is more than 10 seconds old (3s dim threshold) | VERIFIED | Staleness timer checks every 1s shows dashes after STALENESS_DASH_THRESHOLD (10s) dims at STALENESS_DIM_THRESHOLD (3s) using opacity |
|
||||
| 3 | Overlay creation failures surface to user via snackbar | VERIFIED | OverlayProvider calls onError callback on permission denial (line 82) and creation failure (line 112) SpeedometerScreen shows SnackBar (lines 91-97) |
|
||||
| 4 | OverlayService encapsulates all flutter_overlay_window platform calls | VERIFIED | All platform calls isolated in OverlayService OverlayProvider has zero direct FlutterOverlayWindow usage |
|
||||
| 5 | Heartbeat timer only runs when overlay active AND app backgrounded | VERIFIED | Timer checks both _isInBackground AND overlay.isOverlayActive (speedometer_screen.dart line 126) |
|
||||
| 6 | Overlay shows current speed and heading that matches main app display | VERIFIED | OverlayProvider pushes OverlayMessage.updateDisplay with speedText heading unitIndex themeIndex on every GPS tick and settings change |
|
||||
| 7 | Permission errors show snackbar with Settings action button | VERIFIED | _handleOverlayError creates SnackBarAction for OverlayError.permission that calls openAppSettings() (lines 78-81) |
|
||||
| 8 | Recovery from staleness is immediate snap-back (no animation) | VERIFIED | Staleness opacity applied directly via color.withValues(alpha:) no AnimatedOpacity used (overlay_screen.dart lines 173 188 216 229) |
|
||||
| 9 | GPS grace period timer (30s) created on overlay close | VERIFIED | _gracePeriodTimer starts on _handleOverlayClose (line 251) cancelled on showOverlay (line 74) |
|
||||
|
||||
**Score:** 9/9 truths verified
|
||||
|
||||
### Required Artifacts
|
||||
|
||||
| Artifact | Expected | Status | Details |
|
||||
|----------|----------|--------|---------|
|
||||
| lib/services/overlay_service.dart | Platform channel wrapper with retry logic | VERIFIED | 97 lines methods: showOverlay closeOverlay shareData isActive overlayListener checkPermission. RetryOptions with 3 attempts and 200ms delay |
|
||||
| lib/models/overlay_message.dart | Timestamp field with serialization | VERIFIED | timestamp field (line 13) auto-populated in factories (lines 49 57 65) serialized as millisecondsSinceEpoch (line 80) deserialized with fallback (lines 96-98) |
|
||||
| lib/config/overlay_constants.dart | Staleness thresholds (3s dim 10s dash) | VERIFIED | STALENESS_DIM_THRESHOLD = 3s (line 39) STALENESS_DASH_THRESHOLD = 10s (line 42) STALENESS_DIM_OPACITY = 0.5 (line 45) STALENESS_CHECK_INTERVAL = 1s (line 48) |
|
||||
| lib/config/timing_constants.dart | GPS_GRACE_PERIOD constant | VERIFIED | GPS_GRACE_PERIOD = 30s (line 18) |
|
||||
| lib/providers/overlay_provider.dart | OverlayService integration with error callback | VERIFIED | OverlayService dependency (line 16) onError callback field (line 34) permission checks (line 79) verified creation (lines 105-114) grace period timer (lines 74 250-254) |
|
||||
| lib/screens/overlay_screen.dart | Staleness detection timer and UI | VERIFIED | _stalenessCheckTimer (line 27) _lastUpdateTime tracking (line 83) staleness monitoring (lines 105-116) opacity calculation (lines 125-127) opacity applied to all display elements |
|
||||
| lib/screens/speedometer_screen.dart | Error snackbar surfacing | VERIFIED | onError callback registration (line 35) _handleOverlayError method (lines 69-98) SnackBar with Settings action for permission errors |
|
||||
| lib/main.dart | OverlayService in provider tree | VERIFIED | OverlayService registered (line 20) injected into OverlayProvider constructor (line 28) |
|
||||
| pubspec.yaml | retry package dependency | VERIFIED | retry 3.1.2 present |
|
||||
|
||||
### Key Link Verification
|
||||
|
||||
| From | To | Via | Status | Details |
|
||||
|------|-----|-----|--------|---------|
|
||||
| OverlayProvider | OverlayService | Constructor injection all platform calls delegated | WIRED | _overlayService field (line 16) used in showOverlay (lines 79 94 105) closeOverlay (line 133) shareData (line 167) overlayListener (line 180) isActive (line 221) |
|
||||
| OverlayProvider | SpeedometerScreen | onError callback for snackbar surfacing | WIRED | onError field set by SpeedometerScreen (speedometer_screen.dart line 35) called on permission/creation errors (overlay_provider.dart lines 82 112) |
|
||||
| OverlayScreen | OverlayMessage.timestamp | Reads timestamp for staleness calculation | WIRED | _lastUpdateTime = message.timestamp (line 83) age calculated in staleness check (line 109) and build (line 125) |
|
||||
| OverlayScreen | OverlayConfig constants | Uses staleness thresholds for visual feedback | WIRED | STALENESS_CHECK_INTERVAL (line 106) STALENESS_DASH_THRESHOLD (line 110) STALENESS_DIM_THRESHOLD (line 126) STALENESS_DIM_OPACITY (line 127) |
|
||||
| main.dart | OverlayService | Provider registration and injection | WIRED | Provider OverlayService created (line 20) passed to OverlayProvider constructor (line 28) |
|
||||
| OverlayService | flutter_overlay_window | Platform channel delegation with retry | WIRED | showOverlay (lines 24 38) closeOverlay (line 57) shareData (line 71) isActive (line 81) overlayListener (line 90) |
|
||||
| OverlayService | retry package | RetryOptions for showOverlay closeOverlay | WIRED | RetryOptions configured (lines 10-12) used in showOverlay (line 22) and closeOverlay (line 55) |
|
||||
| GpsDataManager | OverlayProvider | Stream subscription for continuous updates | WIRED | _gpsSubscription listens to dataStream (line 55) calls _pushDataToOverlay on each tick (line 57) |
|
||||
|
||||
### Requirements Coverage
|
||||
|
||||
| Requirement | Status | Blocking Issue |
|
||||
|-------------|--------|----------------|
|
||||
| OVRL-01: Continuous debounced data updates | SATISFIED | GPS stream forwarded on every tick via _gpsSubscription.listen to _pushDataToOverlay |
|
||||
| OVRL-02: Overlay errors surfaced to user | SATISFIED | onError callback mechanism connects OverlayProvider to SpeedometerScreen for snackbar display |
|
||||
| OVRL-03: Overlay creation verified | SATISFIED | showOverlay checks return value from OverlayService (which calls isActive after platform showOverlay) |
|
||||
| OVRL-04: Messages include timestamp | SATISFIED | OverlayMessage.timestamp field auto-populated and serialized as millisecondsSinceEpoch |
|
||||
| OVRL-05: Overlay displays dashes when stale | SATISFIED | Staleness timer shows dashes after 10s (STALENESS_DASH_THRESHOLD) dims at 3s (STALENESS_DIM_THRESHOLD) |
|
||||
| OVRL-06: OverlayService encapsulates platform calls | SATISFIED | All FlutterOverlayWindow calls isolated in OverlayService zero direct usage in OverlayProvider |
|
||||
|
||||
### Anti-Patterns Found
|
||||
|
||||
| File | Line | Pattern | Severity | Impact |
|
||||
|------|------|---------|----------|--------|
|
||||
| lib/screens/overlay_screen.dart | 73 | Direct FlutterOverlayWindow.overlayListener usage in overlay isolate | Info | Expected - overlay screen runs in separate isolate and must use direct platform channel for receiving messages |
|
||||
| lib/screens/overlay_screen.dart | 257 268 | Direct FlutterOverlayWindow calls in long press handler | Info | Expected - overlay screen cannot use OverlayService (separate isolate) must use direct platform channel |
|
||||
|
||||
**No blocker anti-patterns found.**
|
||||
|
||||
### Human Verification Required
|
||||
|
||||
**None required.** All success criteria are structurally verifiable through code analysis.
|
||||
|
||||
Optional manual testing to confirm runtime behavior:
|
||||
1. Launch overlay - verify it appears and shows current speed/heading
|
||||
2. Close main app to background - verify overlay continues receiving GPS updates
|
||||
3. Wait 3 seconds without GPS movement - verify overlay dims speed display
|
||||
4. Wait 10 seconds without GPS movement - verify overlay shows dashes
|
||||
5. Move to trigger GPS update - verify overlay immediately shows new speed (no fade animation)
|
||||
6. Deny overlay permission - verify snackbar appears with Settings button
|
||||
7. Press Settings button - verify app settings page opens
|
||||
8. Close overlay while app backgrounded - verify GPS continues for 30 seconds (grace period)
|
||||
|
||||
## Summary
|
||||
|
||||
**Phase 3 goal ACHIEVED.**
|
||||
|
||||
All 6 OVRL requirements satisfied:
|
||||
- OVRL-01: Continuous GPS updates via stream subscription
|
||||
- OVRL-02: Error surfacing via onError callback to SnackBar
|
||||
- OVRL-03: Verified overlay creation via isActive check
|
||||
- OVRL-04: Timestamp field in OverlayMessage
|
||||
- OVRL-05: Two-stage staleness (3s dim 10s dashes)
|
||||
- OVRL-06: OverlayService encapsulates all platform calls
|
||||
|
||||
**Key improvements delivered:**
|
||||
1. Overlay communication is reliable (retry logic + verification)
|
||||
2. Staleness detection provides clear visual feedback
|
||||
3. Errors surface to user with actionable guidance
|
||||
4. GPS grace period infrastructure ready for Phase 4
|
||||
5. Clean separation of concerns (OverlayService isolates platform code)
|
||||
|
||||
**No gaps no blockers ready to proceed to Phase 4.**
|
||||
|
||||
**NOTE:** ROADMAP.md incorrectly states "2 seconds" for success criterion #2. The user explicitly decided on 3 seconds in CONTEXT.md and plan must-haves. Code correctly implements 3s dim / 10s dash thresholds per user decision.
|
||||
|
||||
---
|
||||
|
||||
*Verified: 2026-02-11T10:29:03Z*
|
||||
*Verifier: Claude (gsd-verifier)*
|
||||
Reference in New Issue
Block a user