One file per feature, in the v2 shape that worked: goal, context, design, implementation, acceptance criteria, tests, risks, out of scope -- written before implementing so the risks are on paper rather than walked into. Three carry more weight than their size suggests. V3-01 ships the port's first real migration, and since the destructive fallback is gone, getting addColumn and a v1-database test right matters more than the feature. V3-08 forces a routing-engine decision with ongoing cost, so it sits behind a RoutingService interface mirroring what LocationSource did for GPS. V3-13 is not code at all -- it answers the three questions open since v2, and V3-15 may close unbuilt as a result, which is a legitimate outcome. Several tickets record constraints that are easy to lose: no activity picker in front of Start, because the founding premise is press-and-go with gloves; the Route-to-Trip foreign key must not cascade, or deleting an old plan deletes the ride; V3-14 will deliberately break the parity harness by adding GPX <type>, and that expectation should be updated rather than the check dropped; and V3-16 must not regress the explicit text colours that exist because of the black-on-black bug. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
53 lines
2.2 KiB
Markdown
53 lines
2.2 KiB
Markdown
# V3-06 — Live stats in the notification
|
|
|
|
**Phase** Live map · **Depends on** nothing · **Size** S · **Status** Not started
|
|
|
|
## Goal
|
|
Distance and duration readable from the notification shade without unlocking.
|
|
|
|
## Context
|
|
The ongoing notification currently says "Rippr is recording / Tracking your ride" — static
|
|
text. During a pocketed ride that is a wasted surface.
|
|
|
|
**Constraint that shapes this whole ticket:** the notification is owned by `geolocator`'s
|
|
`ForegroundNotificationConfig`, which takes fixed strings at stream-subscription time and
|
|
offers no update path and no actions. That is the parity gap recorded in
|
|
[../port/PARITY-AUDIT.md](../port/PARITY-AUDIT.md).
|
|
|
|
## Design
|
|
Two honest options:
|
|
|
|
**A. Restart the position stream with new text.** Cheap to write, but it tears down and
|
|
re-establishes location updates every time — unacceptable during recording.
|
|
|
|
**B. Take the notification back with `flutter_local_notifications`,** and let geolocator
|
|
raise a silent minimal one. More moving parts, but it also **restores Pause/Resume actions**
|
|
— closing the one capability lost in the port.
|
|
|
|
**Recommend B**, precisely because it buys back the parity gap as well.
|
|
|
|
## Implementation
|
|
1. Add `flutter_local_notifications`
|
|
2. Own a notification on the same channel, updated on each writer flush (~2 s), not per fix
|
|
3. Pause/Resume actions routed back into `RecordingEngine`
|
|
4. Android 13+ notification permission is already requested
|
|
|
|
## Acceptance criteria
|
|
- [ ] Distance and elapsed update while riding, without unlocking
|
|
- [ ] Pause and Resume work from the shade
|
|
- [ ] Location updates are **not** interrupted when the notification changes
|
|
- [ ] The notification cannot be swiped away mid-ride
|
|
|
|
## Tests
|
|
- Unit: notification text formatting from a trip
|
|
- Integration on a device: start, confirm the text advances, pause from the shade, confirm
|
|
the engine actually paused (check the database, do not trust the UI)
|
|
|
|
## Risks
|
|
- Two notification sources fighting is the obvious failure. Verify only one is visible.
|
|
- An update per fix would be a battery and jank problem; drive it from the flush.
|
|
|
|
## Out of scope
|
|
iOS. There is no equivalent live notification surface; a Live Activity is a much larger
|
|
piece of work and belongs in its own ticket.
|