Files
Mikkeli Matlock acfbeb85a2 docs(03-01): complete overlay foundation plan
Tasks completed: 2/2
- Task 1: Create OverlayService and add retry dependency
- Task 2: Add timestamp field to OverlayMessage model

SUMMARY: .planning/phases/03-overlay-refactor/03-01-SUMMARY.md

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-11 19:16:21 +09:00

10 KiB

phase, plan, subsystem, tags, requires, provides, affects, tech-stack, key-files, decisions, metrics
phase plan subsystem tags requires provides affects tech-stack key-files decisions metrics
03-overlay-refactor 01 overlay-infrastructure
service-layer
ipc
staleness-detection
retry-logic
02-02
Provider pattern infrastructure for overlay state management
OverlayService wrapper isolating all flutter_overlay_window platform calls
Timestamp tracking in OverlayMessage for staleness detection
Staleness threshold constants (3s dim, 10s dash) for UI feedback
GPS grace period constant (30s) for background GPS lifecycle
03-02
OverlayProvider will use OverlayService instead of direct FlutterOverlayWindow calls
03-02
Overlay screen will consume staleness constants for visual feedback
03-02
SpeedometerScreen will use GPS_GRACE_PERIOD for background GPS management
added patterns
retry
^3.1.2
Service wrapper pattern for platform channel isolation
Exponential backoff retry (3 attempts, 200ms delay factor)
Fire-and-forget with warn-level logging for non-critical failures
Timestamp-based staleness detection via millisecondsSinceEpoch serialization
created modified
lib/services/overlay_service.dart
lib/models/overlay_message.dart
lib/config/overlay_constants.dart
lib/config/timing_constants.dart
pubspec.yaml
id choice rationale status
OVL-01 OverlayService wraps all FlutterOverlayWindow calls Isolate platform-specific code, centralize retry logic and error handling implemented
id choice rationale status
OVL-02 showOverlay verifies overlay opened via isActive() call Platform channel may succeed but overlay fail to appear - explicit verification required implemented
id choice rationale status
OVL-03 shareData is fire-and-forget with warn-level logging User decision - send every GPS tick, staleness detection handles drops, no retry needed implemented
id choice rationale status
OVL-04 Staleness thresholds are 3s dim, 10s dash (NOT 2s/10s) User specified 3 seconds for dim threshold in plan implemented
id choice rationale status
OVL-05 Timestamp auto-populated by DateTime.now() in factories Timestamp is metadata, not user data - automatic population prevents caller errors implemented
id choice rationale status
OVL-06 Remove const from OverlayMessage constructors DateTime.now() is runtime value, cannot be const - necessary for timestamp field implemented
duration completed
4.0 minutes 2026-02-11

Phase 03 Plan 01: Overlay Foundation Infrastructure Summary

One-liner: OverlayService wrapper with retry logic, timestamp-enhanced OverlayMessage, and staleness constants (3s dim, 10s dash, 30s GPS grace period).

Objective Achieved

Created the foundation building blocks for overlay refactor by:

  1. Isolating all flutter_overlay_window platform calls behind OverlayService with retry logic
  2. Adding timestamp tracking to OverlayMessage for staleness detection
  3. Defining staleness thresholds (3s dim, 10s dash) and GPS grace period (30s) as named constants

These components provide the service layer, enhanced model, and constants that Plan 02 will integrate into OverlayProvider, overlay screen, and SpeedometerScreen.

Tasks Completed

Task Description Commit Files Modified
1 Create OverlayService and add retry dependency 505219c overlay_service.dart, overlay_constants.dart, timing_constants.dart, pubspec.yaml
2 Add timestamp field to OverlayMessage model 9e3decc overlay_message.dart

Implementation Details

OverlayService Architecture

Platform Channel Wrapper:

  • showOverlay({width, height}) - Retry logic with post-verification via isActive()
  • closeOverlay() - Retry logic for robust cleanup
  • shareData(Map) - Fire-and-forget with warn-level logging (no retry)
  • isActive() - Error-safe overlay status check
  • overlayListener - Direct stream delegation
  • checkPermission() - Permission status helper

Retry Configuration:

  • 3 attempts maximum
  • 200ms delay factor (exponential backoff)
  • Removed unnecessary retryIf checks (analyzer warnings fixed)

Error Handling Strategy:

  • showOverlay/closeOverlay: Logger.error after exhausted retries, return false
  • shareData: Logger.warn on failure, no retry (staleness detection handles drops)
  • isActive: Logger.error + return false on exception

OverlayMessage Enhancements

Timestamp Field:

  • Auto-populated via DateTime.now() in all factory constructors
  • No manual timestamp passing required from callers
  • Serialized as millisecondsSinceEpoch (int) in toMap()
  • Deserialized with fallback to DateTime.now() in fromMap() for backward compatibility

Breaking Change Mitigation:

  • Removed const modifiers (DateTime.now() is runtime value)
  • Added timestamp to copyWith() for completeness
  • All existing factory APIs unchanged (timestamp is additive metadata)

Constants Configuration

Staleness Thresholds (overlay_constants.dart):

  • STALENESS_DIM_THRESHOLD: 3 seconds (dim speed display)
  • STALENESS_DASH_THRESHOLD: 10 seconds (show dashes instead of speed)
  • STALENESS_DIM_OPACITY: 0.5 (dimmed state opacity)
  • STALENESS_CHECK_INTERVAL: 1 second (check frequency)

GPS Grace Period (timing_constants.dart):

  • GPS_GRACE_PERIOD: 30 seconds (continue GPS after overlay close when backgrounded)

Verification Results

flutter pub get:

  • retry package (^3.1.2) installed successfully
  • All dependencies resolved

flutter analyze:

  • 26 info-level warnings (SCREAMING_SNAKE constants - expected per project convention)
  • 0 errors or warnings in new code
  • Fixed 2 unnecessary type check warnings in OverlayService retry logic

Code Quality:

  • OverlayService: 93 lines with comprehensive error handling
  • OverlayMessage timestamp integration: 15 lines added across all methods
  • All must-have requirements satisfied

Deviations from Plan

None - plan executed exactly as written.

Decisions Made

OVL-01: Service Wrapper Pattern

  • All flutter_overlay_window calls isolated in OverlayService
  • Benefits: Centralized retry logic, single error handling point, easier testing
  • Impact: Plan 02 will migrate OverlayProvider to use OverlayService

OVL-02: Post-Verification in showOverlay

  • Added isActive() verification after FlutterOverlayWindow.showOverlay() completes
  • Addresses: Platform channel may report success but overlay fails to appear
  • Prevents: False positive overlay status in OverlayProvider

OVL-03: Fire-and-Forget shareData

  • No retry logic for data sharing (user decision: forward every tick)
  • Staleness detection handles dropped messages via timestamp tracking
  • Warn-level logging prevents error log noise from expected occasional failures

OVL-04: Staleness Timing

  • 3 seconds for dim (NOT 2 seconds as in some research notes)
  • 10 seconds for dashes (consistent across all references)
  • User explicitly specified 3s in plan MUST_HAVES

OVL-05: Automatic Timestamp Population

  • DateTime.now() called in factory constructors, not passed by callers
  • Prevents caller errors (forgetting timestamp, using wrong time source)
  • Ensures consistency (all timestamps from same clock source)

OVL-06: Remove const Modifiers

  • Required to allow DateTime.now() runtime values
  • Trade-off: Lose compile-time const optimization for timestamp functionality
  • Impact: Negligible (OverlayMessage instances are short-lived IPC data)

Technical Debt

None identified.

All code follows project conventions:

  • SCREAMING_SNAKE for behavioral constants
  • Logger with caller tags for all logging
  • Error handling with graceful degradation
  • Type-safe OverlayMessage serialization

Integration Readiness

Ready for Plan 02:

  • OverlayService provides all needed platform operations
  • OverlayMessage timestamp field ready for staleness calculation
  • Constants defined and accessible for UI feedback logic

Migration Path for Plan 02:

  1. Replace FlutterOverlayWindow.showOverlay in OverlayProvider with OverlayService.showOverlay
  2. Replace FlutterOverlayWindow.closeOverlay with OverlayService.closeOverlay
  3. Replace FlutterOverlayWindow.shareData with OverlayService.shareData
  4. Add staleness detection timer in overlay screen using STALENESS_CHECK_INTERVAL
  5. Implement dim/dash logic using STALENESS_DIM_THRESHOLD and STALENESS_DASH_THRESHOLD
  6. Add GPS grace period logic in SpeedometerScreen using GPS_GRACE_PERIOD

Artifacts Generated

Service Layer:

  • lib/services/overlay_service.dart (93 lines)

Enhanced Model:

  • lib/models/overlay_message.dart (+18 lines, -3 lines)

Configuration:

  • lib/config/overlay_constants.dart (+12 lines)
  • lib/config/timing_constants.dart (+3 lines)

Dependencies:

  • pubspec.yaml (+3 lines - retry package)

Next Steps

Plan 02 will:

  1. Integrate OverlayService into OverlayProvider
  2. Add staleness detection timer and UI feedback in overlay screen
  3. Implement GPS grace period logic in SpeedometerScreen
  4. Replace all direct FlutterOverlayWindow calls with OverlayService methods

Estimated complexity: Medium (refactor existing OverlayProvider and overlay screen)

Blockers: None - all foundation infrastructure complete

Commits

  1. 505219c - feat(03-01): create OverlayService and add retry dependency

    • Added retry package (^3.1.2)
    • Created OverlayService wrapper with showOverlay, closeOverlay, shareData, isActive, overlayListener, checkPermission
    • Implemented 3-attempt retry with 200ms exponential backoff
    • Added staleness constants: 3s dim, 10s dash, 0.5 opacity, 1s check interval
    • Added GPS_GRACE_PERIOD constant (30 seconds)
  2. 9e3decc - feat(03-01): add timestamp field to OverlayMessage model

    • Added timestamp field with auto-population via DateTime.now()
    • Updated all factory constructors (updateDisplay, longPressClose, overlayClosed)
    • Serialized timestamp as millisecondsSinceEpoch in toMap()
    • Deserialized with fallback to DateTime.now() in fromMap()
    • Added timestamp parameter to copyWith()
    • Removed const modifiers for runtime timestamp values