You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
telemetry_frames and telemetry_minute_buckets key on board_id and stop carrying device_id (the BLE identifier) and device_name (the Board name copied onto every row at capture time). Board names on Ride History are resolved by looking the Board up by id.
The reasoning, including why the denormalized name existed and why it stops earning its keep once Boards are tombstoned, is in docs/adr/0028-telemetry-is-keyed-on-board-id.md. This closes #274.
The Board id is already known at capture — the session config carries it next to the BLE identifier — it simply is not written down.
Migration, Room 30 -> 31 and the GRDB peer:
add board_id, backfilled by matching boards.ble_id to device_id
mint one tombstoned Board per device_id that matches nothing, named from that row's historical device_name, so telemetry from Boards deleted before [Sync] 1 - Tombstone deleted Boards #279 or re-linked to a different peripheral keeps a label, stays joinable, and can still be backed up
move the minute bucket primary key from (bucket_start_ms, device_id) to (bucket_start_ms, board_id), which is what the server already uses
drop device_id and device_name from both tables
telemetry_markers, diagnostic_events and metric_exclusion_ranges are not touched. They keep both columns, because that is what crosses the wire for them and they are low-cardinality display rows rather than a per-sample cost.
Three read paths stop reading the name off the sample row and resolve it by lookup instead. This is permitted by docs/adr/0005-ride-history-read-paths-stay-precomputed.md, whose Scope section now says explicitly that the no-reconstruction rule is about replaying Telemetry Samples, not about bounded configuration lookups.
Renaming a Board now retroactively relabels its Ride History. That is intended — it is the same Board — but it is a visible behaviour change.
Likely files
modules/vescape-core/android/src/main/java/expo/modules/vescapecore/telemetry/TelemetryEntities.kt - the two entities, their indices and the bucket primary key
modules/vescape-core/android/src/main/java/expo/modules/vescapecore/telemetry/TelemetryDao.kt - upsertBuckets, getBucket, insertBatch and the frame range reads
modules/vescape-core/android/src/main/java/expo/modules/vescapecore/telemetry/TelemetryDatabase.kt - Room version, the migration and the orphan-Board mint
modules/vescape-core/ios/telemetry/TelemetryDatabase.swift - the GRDB peer migration
modules/vescape-core/ios/telemetry/TelemetryDao.swift - the GRDB peer writes and reads
modules/vescape-core/android/src/main/java/expo/modules/vescapecore/connection/SessionConfigBuilder.kt - where the Board id and the BLE id are both already in hand
src/modules/history/lib/sessions.ts - HistorySession, the main read shape carrying the name
src/screens/main/history/HistoryTelemetryPanel.tsx, src/modules/history/components/HistorySessionSheet.tsx, src/modules/history/lib/historyMapMarkerInfo.ts - the three display consumers that switch to a lookup
Implementation hints
buildSessionConfig already produces appBoardId alongside deviceId = bleId and deviceName = boardName. The Board id reaches the recording path in the session config; it is not missing, just unused by the telemetry writes.
src/app/settings/eventLog.tsx also renders a device name, but from diagnostic_events, which keeps its columns. Do not change it.
The minute bucket primary key change means a table rebuild on both platforms, not an ALTER. The updated_at and sync_seq columns added earlier on the branch have to survive that rebuild — check them explicitly in the migration test rather than assuming the copy carried them.
Orphan minting is the only migration in this sequence that creates rows the Rider never made. Give it its own test: a frame whose device_id matches no Board must end up pointing at a Board that exists, is tombstoned, and carries the historical name.
Acceptance criteria
Frames and minute buckets carry board_id and no longer carry device_id or device_name
New frames and buckets are stamped with the Board id at capture time
Existing rows backfill by matching boards.ble_id, on both platforms
Rows matching no Board get a minted tombstoned Board named from their historical device_name
A minted Board never appears in the Rider's Board list
Minute bucket primary key is (bucket_start_ms, board_id), and the rebuild preserves updated_at and sync_seq
Markers, diagnostic events and metric exclusion ranges are unchanged
Ride History, the session sheet and map marker callouts show the Board name, including for a tombstoned Board
Renaming a Board changes the name shown on its existing history
Migration tests cover backfill, orphan minting and the bucket rebuild on both platforms
Parent
What to build
telemetry_framesandtelemetry_minute_bucketskey onboard_idand stop carryingdevice_id(the BLE identifier) anddevice_name(the Board name copied onto every row at capture time). Board names on Ride History are resolved by looking the Board up by id.The reasoning, including why the denormalized name existed and why it stops earning its keep once Boards are tombstoned, is in
docs/adr/0028-telemetry-is-keyed-on-board-id.md. This closes #274.The Board id is already known at capture — the session config carries it next to the BLE identifier — it simply is not written down.
Migration, Room 30 -> 31 and the GRDB peer:
board_id, backfilled by matchingboards.ble_idtodevice_iddevice_idthat matches nothing, named from that row's historicaldevice_name, so telemetry from Boards deleted before [Sync] 1 - Tombstone deleted Boards #279 or re-linked to a different peripheral keeps a label, stays joinable, and can still be backed up(bucket_start_ms, device_id)to(bucket_start_ms, board_id), which is what the server already usesdevice_idanddevice_namefrom both tablestelemetry_markers,diagnostic_eventsandmetric_exclusion_rangesare not touched. They keep both columns, because that is what crosses the wire for them and they are low-cardinality display rows rather than a per-sample cost.Three read paths stop reading the name off the sample row and resolve it by lookup instead. This is permitted by
docs/adr/0005-ride-history-read-paths-stay-precomputed.md, whose Scope section now says explicitly that the no-reconstruction rule is about replaying Telemetry Samples, not about bounded configuration lookups.Renaming a Board now retroactively relabels its Ride History. That is intended — it is the same Board — but it is a visible behaviour change.
Likely files
modules/vescape-core/android/src/main/java/expo/modules/vescapecore/telemetry/TelemetryEntities.kt- the two entities, their indices and the bucket primary keymodules/vescape-core/android/src/main/java/expo/modules/vescapecore/telemetry/TelemetryDao.kt-upsertBuckets,getBucket,insertBatchand the frame range readsmodules/vescape-core/android/src/main/java/expo/modules/vescapecore/telemetry/TelemetryDatabase.kt- Room version, the migration and the orphan-Board mintmodules/vescape-core/ios/telemetry/TelemetryDatabase.swift- the GRDB peer migrationmodules/vescape-core/ios/telemetry/TelemetryDao.swift- the GRDB peer writes and readsmodules/vescape-core/android/src/main/java/expo/modules/vescapecore/connection/SessionConfigBuilder.kt- where the Board id and the BLE id are both already in handsrc/modules/history/lib/sessions.ts-HistorySession, the main read shape carrying the namesrc/screens/main/history/HistoryTelemetryPanel.tsx,src/modules/history/components/HistorySessionSheet.tsx,src/modules/history/lib/historyMapMarkerInfo.ts- the three display consumers that switch to a lookupImplementation hints
buildSessionConfigalready producesappBoardIdalongsidedeviceId = bleIdanddeviceName = boardName. The Board id reaches the recording path in the session config; it is not missing, just unused by the telemetry writes.src/app/settings/eventLog.tsxalso renders a device name, but fromdiagnostic_events, which keeps its columns. Do not change it.The minute bucket primary key change means a table rebuild on both platforms, not an
ALTER. Theupdated_atandsync_seqcolumns added earlier on the branch have to survive that rebuild — check them explicitly in the migration test rather than assuming the copy carried them.Orphan minting is the only migration in this sequence that creates rows the Rider never made. Give it its own test: a frame whose
device_idmatches no Board must end up pointing at a Board that exists, is tombstoned, and carries the historical name.Acceptance criteria
board_idand no longer carrydevice_idordevice_nameboards.ble_id, on both platformsdevice_name(bucket_start_ms, board_id), and the rebuild preservesupdated_atandsync_seqBlocked by
deleted_atto existRelated