Skip to content

[Board] Boards are tombstoned, never deleted - #435

Closed
KacperKozak wants to merge 1 commit into
devfrom
board-tombstones
Closed

[Board] Boards are tombstoned, never deleted#435
KacperKozak wants to merge 1 commit into
devfrom
board-tombstones

Conversation

@KacperKozak

@KacperKozak KacperKozak commented Aug 30, 2026

Copy link
Copy Markdown
Collaborator

Deleting a Board now stamps deleted_at instead of removing its row, so the rides it produced keep a Board identity that still resolves. Board-owned configuration is still hard-deleted, and Ride History is untouched, exactly as before.

Note

Risk: Medium — changes deletion semantics and the Board read paths on both platforms.
Complexity: Medium — small surface, but the list/lookup split and the no-resurrect rule have to hold identically in Room and GRDB.
DB: Migration — additive nullable boards.deleted_at (Room 36 → 37, GRDB v37_board_deleted_at); existing rows upgrade as live.

Tasks

Description

Ride History outlives the Board that produced it. The app has always kept telemetry after a Board delete, but the boards row vanishing left those rides pointing at an id that resolved to nothing, so history could only fall back to the device_name snapshotted on each row — a frozen label, not an identity. A tombstone keeps the row resolvable.

The rule is a deliberate split: getBoards() filters deleted_at IS NULL, so a tombstone is gone from every rider-facing list, while getBoard(id) resolves tombstones on purpose, because history still has to name a deleted Board. Anything that acts on a Board rather than describing one — building a session config for a connect — checks the stamp and refuses. Deletion is terminal: an ordinary upsert from the bridge carries an existing tombstone forward rather than clearing it, and deleting an already-tombstoned Board is a no-op, so the stamp never moves.

The decision, its rejected alternatives and its consequences are in docs/adr/0027-boards-are-tombstoned-never-deleted.md.

This slice was specified in #279 and implemented on the unmerged sync branch behind #276, which left dev still hard-deleting. It is extracted here without the sync half — no sync_seq, no delete-action log, no server contract; those stay with #276 and will need reconciling when it merges, since it carries its own deleted_at at a different migration number.

Making Board identity resolvable is the prerequisite for the identity half of #274 — putting board_id on the telemetry tables so history stops relying on the denormalized device_name. That remains a separate slice, and device_name is deliberately untouched here: telemetry keys on the BLE id, not the Board id, and boards hard-deleted by earlier versions are already gone, so that label is still the only one some existing rides have.

Closes #428


View with [code]smith Autofix with [code]smith
Need help on this PR? Tag @codesmith-bot with what you need. Autofix is disabled.

@KacperKozak KacperKozak added area:board Board profiles, board table, and board settings area:history Ride history, sessions, buckets, graphs area:native Touches native side (modules/vesc-ble, Swift/Kotlin) area:db Touches database / persistent storage complexity:medium Needs care, moderate integration surface. Use sonnet. labels Aug 30, 2026
KacperKozak added a commit that referenced this pull request Aug 31, 2026
* Tombstone deleted Boards, never remove the row

Deleting a Board now stamps boards.deleted_at instead of removing the row, so
Ride History keeps a resolvable Board identity (ADR 0027). Board-owned
configuration is still hard-deleted; telemetry and Tune Profiles are not.

getBoards() filters tombstones, getBoard(id) deliberately resolves them, and
connect paths refuse a tombstoned Board. An ordinary upsert carries an existing
tombstone forward, so deletion is terminal.

Ported from #276 (#279) and the closed #435 (#428), renumbered onto schema 40 -> 41.

* Key telemetry on the Board id, not the BLE identifier

Every telemetry table — frames, minute buckets, markers, diagnostic events and
metric exclusion ranges — now keys on board_id and drops both device_id (the
mutable BLE identifier) and device_name (the Board name denormalized at capture
time). Ride History resolves Board names by lookup, so a rename relabels history
(ADR 0028, closes #274).

Migration 41 -> 42 rebuilds all five tables, resolving each BLE identifier to a
Board exactly once through a shared scratch map so no two tables can pick
different claimants of a duplicated identifier. Rows that resolve to no Board
mint a tombstoned Board named from their historical device_name, so orphaned
history keeps a label and stays joinable.

The boardId -> bleId translation used by markers, events and ranges is deleted.
RideHistoryRepository (added on dev after #276 branched) is rekeyed the same way.

Ported from #276 (#280) with the follow-up dedup and marker/event/range rekey.

* Align the iOS schema stamp and the docs with board-id keying

The iOS backup stamp still claimed schema 41, so a current backup left the
board-id migration unstamped and replayed it on restore — caught by
TelemetryMigrationTests. Also rekeys the native API and history docs, adds both
ADRs to the docs index, and records the Board Tombstone rules in CONTEXT.md.

* Record Board-id keying as a whole-schema rule, not a telemetry one

Every durable Board-owned table now keys on board_id; boards.ble_id is the only
BLE identifier left and is a Board Link attribute, not a join key.

* Fix nested GRDB read crash in Ride History page

boardNamesById() opens its own pool read; calling it inside getPage's read
tripped GRDBPrecondition and killed the reader queue. Hoist it out, like
every other call site already does.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area:board Board profiles, board table, and board settings area:db Touches database / persistent storage area:history Ride history, sessions, buckets, graphs area:native Touches native side (modules/vesc-ble, Swift/Kotlin) complexity:medium Needs care, moderate integration surface. Use sonnet.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Board] Verify Board tombstoning on dev

1 participant