Lap function #46

Closed
opened 2026-08-31 17:14:54 +02:00 by robert · 1 comment
robert commented 2026-08-31 17:14:54 +02:00 (Migrated from git.butzei.de)

Goal

Mark segments during a ride.

Acceptance criteria

  • A button gesture records a lap without disturbing the ride state
  • Lap distance, time and average speed captured
  • Brief lap summary shown on the watch, then back to the ride view
  • Laps sent to the phone and recorded in the GPX
  • Lap list viewable on the phone after the ride

Files

  • watchapp/src/c/state.c
  • companion/.../ride/
## Goal Mark segments during a ride. ## Acceptance criteria - [ ] A button gesture records a lap without disturbing the ride state - [ ] Lap distance, time and average speed captured - [ ] Brief lap summary shown on the watch, then back to the ride view - [ ] Laps sent to the phone and recorded in the GPX - [ ] Lap list viewable on the phone after the ride ## Files - `watchapp/src/c/state.c` - `companion/.../ride/`
Owner

Closed by PR #115 (area/lap-function), built as a genuinely cross-platform split: Vela did the watch side, then handed the phone side to a Kestrel sub-agent on the same branch — a good pattern, kept for future cross-cutting issues.

All five acceptance criteria addressed:

  • A button gesture records a lap without disturbing the ride state: double-click Select (window_multi_click_subscribe, 300ms window) — chosen after auditing every existing click binding in carousel.c (#60) and finding double-click the one gesture type unclaimed. ride_state_lap_mark() has exactly one observable side effect (queuing RIDE_CMD_LAP on #11's existing dropout-survival queue) and touches none of RideStateSnapshot's four persisted fields or checkpoint_dirty — verified both by a real emulator double-click test and by 7 new host C tests (test_state.c, all passing, including no-op cases for idle/paused/stopped/stop-pending and the queue-full drop-newest contract).
  • Lap distance, time and average speed captured: LapSplitCalculator (phone-side, host-tested) slices the same cumulative moving-distance/time figures DISTANCE_M/ELAPSED_S already report ride-wide — a genuinely new average-speed formula since none existed anywhere in this codebase to reuse (checked and stated plainly, not silently claimed as a reuse).
  • Brief lap summary on the watch: a 4-second timed overlay (QUAD template, the four FIELD_LAP_* ids #7 already reserved), dismissible early by any button press, skipped (not queued) if a confirmation prompt happens to be showing.
  • Laps sent to the phone and recorded in the GPX: wire keys LAP_NUMBER/LAP_DISTANCE_M/LAP_TIME_S/LAP_AVG_SPEED_MMS already existed from #7's original codegen (verified directly in proto.h/Proto.kt/message_keys.json/PROTOCOL.md §2.3 before merging — nothing needed regenerating). GPX representation is a point-level <pp:lap> extension (own reserved-per-RFC-2606 .invalid namespace), not a new <trkseg> — keeps segment boundaries meaning only pause/resume. Honest, tested deviation from D65's literal "closest point" wording: GpxWriter's incremental-flush design means a marker can only attach to the next point written, not retroactively to one already on disk — documented in GpxRecorder's KDoc with a dedicated test.
  • Lap list viewable on the phone after the ride: new laps Room table (RideHistoryDatabase 1→2, real MIGRATION_1_2, schema JSON hand-diffed against the SQL), expandable per-ride in RideHistoryScreen.

DECISIONS.md D65 records the four real design choices (gesture, split-computation split between watch/phone, summary-as-overlay-not-page, GPX-marker-not-segment) plus the known #4 (real PebbleKit transport) scope gap — nothing calls RideService.recordLap() or a real inbound CMD=LAP handler yet, stated openly rather than faked.

Real verification, not hand-review: ./gradlew :companion:core:test :companion:pebble:testDebugUnitTest :companion:ride:assembleDebug :companion:assembleDebug --rerun-tasks — full genuine rebuild, all green (LapSplitCalculatorTest 8/8, LapHistoryTest 5/5, GpxWriterTest 20/20, GpxRecorderTest 11/11, LapWireEncodingTest 12/12). pebble build clean on gabbro/emery/basalt after a full pebble clean. Host C test_state.c hand-compiled (no cmake in this sandbox) and run directly: 55/55 passing including all 7 new lap tests, -Wall -Wextra -Werror clean. Plus a real emulator double-click verification during design (see D65).

40 issues closed.

Closed by PR #115 (`area/lap-function`), built as a genuinely cross-platform split: Vela did the watch side, then handed the phone side to a Kestrel sub-agent on the same branch — a good pattern, kept for future cross-cutting issues. All five acceptance criteria addressed: - **A button gesture records a lap without disturbing the ride state**: double-click Select (`window_multi_click_subscribe`, 300ms window) — chosen after auditing every existing click binding in `carousel.c` (#60) and finding double-click the one gesture type unclaimed. `ride_state_lap_mark()` has exactly one observable side effect (queuing `RIDE_CMD_LAP` on #11's existing dropout-survival queue) and touches none of `RideStateSnapshot`'s four persisted fields or `checkpoint_dirty` — verified both by a real emulator double-click test and by 7 new host C tests (`test_state.c`, all passing, including no-op cases for idle/paused/stopped/stop-pending and the queue-full drop-newest contract). - **Lap distance, time and average speed captured**: `LapSplitCalculator` (phone-side, host-tested) slices the same cumulative moving-distance/time figures `DISTANCE_M`/`ELAPSED_S` already report ride-wide — a genuinely new average-speed formula since none existed anywhere in this codebase to reuse (checked and stated plainly, not silently claimed as a reuse). - **Brief lap summary on the watch**: a 4-second timed overlay (QUAD template, the four `FIELD_LAP_*` ids #7 already reserved), dismissible early by any button press, skipped (not queued) if a confirmation prompt happens to be showing. - **Laps sent to the phone and recorded in the GPX**: wire keys `LAP_NUMBER`/`LAP_DISTANCE_M`/`LAP_TIME_S`/`LAP_AVG_SPEED_MMS` **already existed from #7's original codegen** (verified directly in `proto.h`/`Proto.kt`/`message_keys.json`/`PROTOCOL.md` §2.3 before merging — nothing needed regenerating). GPX representation is a point-level `<pp:lap>` extension (own reserved-per-RFC-2606 `.invalid` namespace), not a new `<trkseg>` — keeps segment boundaries meaning only pause/resume. Honest, tested deviation from D65's literal "closest point" wording: `GpxWriter`'s incremental-flush design means a marker can only attach to the *next* point written, not retroactively to one already on disk — documented in `GpxRecorder`'s KDoc with a dedicated test. - **Lap list viewable on the phone after the ride**: new `laps` Room table (`RideHistoryDatabase` 1→2, real `MIGRATION_1_2`, schema JSON hand-diffed against the SQL), expandable per-ride in `RideHistoryScreen`. DECISIONS.md D65 records the four real design choices (gesture, split-computation split between watch/phone, summary-as-overlay-not-page, GPX-marker-not-segment) plus the known #4 (real PebbleKit transport) scope gap — nothing calls `RideService.recordLap()` or a real inbound `CMD=LAP` handler yet, stated openly rather than faked. **Real verification, not hand-review**: `./gradlew :companion:core:test :companion:pebble:testDebugUnitTest :companion:ride:assembleDebug :companion:assembleDebug --rerun-tasks` — full genuine rebuild, all green (LapSplitCalculatorTest 8/8, LapHistoryTest 5/5, GpxWriterTest 20/20, GpxRecorderTest 11/11, LapWireEncodingTest 12/12). `pebble build` clean on gabbro/emery/basalt after a full `pebble clean`. Host C `test_state.c` hand-compiled (no cmake in this sandbox) and run directly: 55/55 passing including all 7 new lap tests, `-Wall -Wextra -Werror` clean. Plus a real emulator double-click verification during design (see D65). 40 issues closed.
Sign in to join this conversation.
No milestone
No project
No assignees
2 participants
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Reference
robert/PedalPebble#46
No description provided.