Skip to content

Release v3.1.0#332

Draft
TheAngryRaven wants to merge 31 commits into
mainfrom
BETA
Draft

Release v3.1.0#332
TheAngryRaven wants to merge 31 commits into
mainfrom
BETA

Conversation

@TheAngryRaven

@TheAngryRaven TheAngryRaven commented Jul 16, 2026

Copy link
Copy Markdown
Owner

v3.1.0 — 2026-07-17

Added

  • Silent auto-update on an idle home page. When a new build is detected and the user isn't in the middle of anything (home page, no session loaded), the app now applies the update itself: a brief "Updating…" progress toast, an automatic reboot onto the new version, and a confirmation toast on the fresh load — no more mystery refresh that eventually "flashes" into the new build. Mid-session (or on any other page) the existing "Update ready" toast is shown instead, so a loaded session is never yanked away. A per-commit guard prevents reload loops if an update fails to stick. (plan 0010)
  • Simulator system-pages glossary. The simulator guide now ends with a glossary of the firmware's menu and system pages — boot splash, GPS status, main menu, race mode, session review, transfer (BLE/USB), camera pairing, SD format, warnings/faults, and the charging screen — so users can see the full feature set of the firmware, including the pages that need real hardware and can't be reached in the simulator. (plan 0010)
  • Simulator "what the device is doing" explainer. A short description below the playback controls walking through the firmware's actual behavior: boot on button press or RPM, nearest-track lookup with all courses loaded into memory, automatic race-mode entry on RPM, and the waypoint-drop course-detection pass that picks the matching course and registers an accurate first lap. (plan 0010)
  • Firmware simulator page (/simulator). The real DovesDataLogger firmware — the vendored WebAssembly build — running in the browser on a simulated 128×64 OLED, replaying the bundled Orlando Kart Center sample session: watch it boot, acquire GPS, auto-enter race mode, detect the track and count laps, exactly as the physical device does (the same wasm core reproduces this session's 13 hardware lap times to the millisecond). Three on-screen buttons (plus ← Enter →) drive the real firmware menus, even mid-playback. Includes play/pause, 1–10× speed, a scrubbable timeline (rewinds re-boot the firmware and fast-replay — it's a real device, not a video), an integer-scale zoom picker, and a true-size toggle showing the display at its physical 1.3″ size. Fully offline-capable. (plan 0010, Phase A)
  • Vendored BirdsEye firmware simulator (public/sim/, groundwork). The real DovesDataLogger firmware compiled to WebAssembly (207 KB), pinned by construction: birdseye-sim.mjs (API contract v1 — see BirdsEye/sim/API.md in the firmware repo), the Emscripten core module + wasm, version.json build provenance (firmware sha, DovesLapTimer sha, display-lib versions), and a standalone sim/test.html harness that boots the firmware on a canvas and replays real .dovex files. Renders byte-identically to the firmware repo's golden fixtures and reproduces a hardware session's 13 lap times to the exact millisecond. The /simulator page consuming it lands in a follow-up (sim plan Phases 5-7).
  • Shareable session links. Cloud-synced logs can now be shared publicly behind an opaque link (/s/…) that never reveals the file name — anyone with the link (no account needed) opens the full session in the read-only viewer, with precise lap and sector timing even on your personal custom courses (the course geometry travels with the share; custom courses are also auto-submitted to the community track database for review). Share or unshare any session from the link icon in the file browser or the Cloud Logs panel; a new profile setting makes future uploads public by default (private remains the default), and your public sessions appear on your driver profile page. Shared copies count toward cloud storage. Opening a shared session selects its fastest lap automatically.
  • Read-only views gained overlays + weather. The Overlays menu now works in read-only sessions (leaderboards and shared links), so you can compare those laps against your own local sessions, and historical weather is shown when the session carries a date. Vehicle/setup editing stays hidden — including the pro-mode Vehicle tab, which read-only views previously still showed.
  • Logger firmware updates in the native app. The DovesLogger/Fledgling flow's connected screen gains a Firmware update button: it checks the published OTA manifest, confirms the build (asking you to pick the sense/nonsense variant explicitly if the device didn't report one), downloads and CRC-verifies the image, and uploads it to the logger with progress. The device then flashes and restarts — the app says so and returns you to the scan screen to reconnect. On app versions whose native shell can't flash yet, the button gracefully reports "not available in this app version" instead of erroring. Web firmware updates are unchanged.

Changed

  • Friendlier logger-download errors. All logger download flows (Fledgling web + native, MyChron, Alfano) now translate failures into plain-language messages with the right recovery button — Try again, Rescan, or Reconnect — instead of showing raw backend errors. A denied Android Bluetooth permission gets a dedicated "Bluetooth permission needed" message, a cancelled MyChron Wi-Fi join asks you to pick the network again, and Alfano on unsupported platforms shows an informational "not available on this device" notice. The raw technical detail stays available behind a collapsible line, and download failures now only claim the file was saved when it actually was.
  • The web Fledgling download dialog and shared download panels are now fully translated (they had hard-coded English).

Full changelog: v3.0.0...v3.1.0

claude and others added 9 commits July 3, 2026 02:23
The native logger backend rejects with prefix-coded strings (device
unreachable:/device hung:/protocol error:/unsupported:/Wi-Fi join was
declined/no logger connected) but every download flow rendered the raw
string verbatim. Add a pure classifier (lib/loggers/errors.ts) that maps
prefixes — plus web NotAllowedError/SecurityError — to typed categories
with per-stage recovery actions, and a shared ErrorPanel that shows a
translated headline with the matching Retry/Rescan/Reconnect button,
tucking the raw message behind a collapsed "technical details" line.

Android BLE permission denials (reported under device unreachable:) get
their own "Bluetooth permission needed" guidance; a declined Wi-Fi join
re-drives the OS picker; Alfano's desktop `unsupported:` rejection renders
as an informational not-available panel instead of an error. Download-stage
failures retry the same file while the link is alive and only claim the
file was saved when the auto-save actually succeeded.

Also migrates the last hard-coded English out of the web Fledgling flow
and the shared download panels into the logger namespace, with hand-seeded
translations for all six languages (no ANTHROPIC_API_KEY here for the
seeder — same stop-gap as the profiles/datalogger strings).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HHEmsdQeeMQ4pjF8BKDV8P
The native shell had no firmware path at all — OTA only worked on the web
build over Web Bluetooth. Add the native flow end-to-end while reusing the
entire transport-agnostic dfu/ layer (manifest fetch, variant pick, version
evaluate, CRC-verified download):

- lib/ble/dfu/firmwareImage.ts: extract the download+CRC-verify step
  (acquireFirmwareImage) out of useFirmwareUpdate so web and native share
  it; the web hook now calls it with no behavior change.
- doveslogger/ipc.ts: loggerUpdateFirmware(image, onProgress) invoking
  logger_update_firmware with a Channel for upload progress.
- doveslogger/firmwareInfo.ts: pure extractor deriving version/model/
  variant from the connect handshake's LoggerDeviceInfo.fields (candidate
  key probing — exact names still aligning with the LapWing side) plus the
  optional cap.fw_update capability flag.
- useNativeFirmwareUpdate + NativeFirmwarePanel: a Firmware button on the
  connected file-list screen drives check -> confirm (with an explicit
  variant pick when the device didn't report one) -> download -> upload ->
  "device is restarting" -> back to the scan screen. Preview builds bypass
  the version compare like the web flow.

Availability is runtime-detected, not flag-gated: an unsupported:/unknown-
command rejection renders a "not available in this app version" panel and
latches for the session, so the UI degrades gracefully until the backend
command ships. A link drop after the full image was received is treated as
the documented flash-and-reboot success, not an error. Settings/tracks/
battery stay web-only (supportsDeviceDetails is untouched).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HHEmsdQeeMQ4pjF8BKDV8P
Record the design in docs/plans/0008 (including the open items to align
with the LapWing backend: firmware command byte transport, device-info
field keys, unknown-command wording, MyChron SSID prefix, Alfano format),
write the DovesLogger-native and Alfano sections docs/android.md was
missing, point docs/ble.md at the shared dfu layer's native path, refresh
the CLAUDE.md architecture map, and start the [3.1.0] unreleased changelog
heading with the error-UX and native-firmware entries.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HHEmsdQeeMQ4pjF8BKDV8P
…y-u2t8bm

plan 0008: logger error UX + native Fledgling firmware update
Cloud-synced logs can now be shared behind an opaque /s/{token} URL that
never reveals the filename: the anon-readable shared_sessions table has no
filename column at all (the owner-side token<->filename mapping rides the
log's private sync_records index row as data.share), and the raw blob is
copied into a public shared-sessions bucket at {userId}/{token}. Anyone with
the link opens the full session in the plan-0005 read-only viewer — the
frozen course geometry travels with the share, so laps and sectors time
precisely even on personal custom courses (which also auto-queue to the
community submissions pipeline, never blocking the link).

- profiles.share_sessions_default: new uploads auto-publish when on
  (future uploads only); per-session share modal overrides both ways, with
  an optedOut marker so an unshared file never resurrects
- share/unshare UI: Link2 icon on synced file-browser rows (new FileRow
  mount) and in the Cloud Logs panel; default toggle in the Account panel
- viewer: cloud-gated /s/:token route self-loads through Index (reload-safe,
  works cold for anonymous visitors); Exit returns by source
- driver profile page gains a public "Shared sessions" section
- quota: shared copies count toward the pooled byte budget (new
  enforce_share_quota trigger; usage/trim functions redefined) and
  trim_expired_logs/deleteCloudFile/account deletion all retire shares
- fixed: uploadBlob clobbered the index row's data on re-upload, which
  would have wiped share state (now read-merges; regression-tested)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QKZefCzbyk4ZZPzckLbJvk
…xvjbo

plan 0009: shareable public session links for cloud-synced logs
…hide Vehicle tab

Post-merge feedback on the shared-session viewer:
- opening a share now auto-selects the fastest lap (was whole-session view)
- the Overlays menu is enabled in read-only (shares and leaderboards) so
  viewers can compare read-only laps against their own local sessions
- weather panels (map + pro-mode InfoBox) work in read-only again; the
  per-session weather cache and station-metadata write-back are skipped so
  the synthetic "shared"/"leaderboard" names never key a cache entry
- the pro-mode InfoBox Vehicle tab is hidden in read-only (missed in the
  original pass — no vehicle/setup saving on read-only views)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QKZefCzbyk4ZZPzckLbJvk
@supabase

supabase Bot commented Jul 16, 2026

Copy link
Copy Markdown

Updates to Preview Branch (BETA) ↗︎

Deployments Status Updated
Database Sat, 18 Jul 2026 02:23:51 UTC
Services Sat, 18 Jul 2026 02:23:51 UTC
APIs Sat, 18 Jul 2026 02:23:51 UTC

Tasks are run on every commit but only new migration files are pushed.
Close and reopen this PR if you want to apply changes from existing seed or migration files.

Tasks Status Updated
Configurations Sat, 18 Jul 2026 02:23:51 UTC
Migrations Sat, 18 Jul 2026 02:23:52 UTC
Seeding Sat, 18 Jul 2026 02:23:52 UTC
Edge Functions Sat, 18 Jul 2026 02:23:57 UTC

View logs for this Workflow Run ↗︎.
Learn more about Supabase for Git ↗︎.

@cloudflare-workers-and-pages

cloudflare-workers-and-pages Bot commented Jul 16, 2026

Copy link
Copy Markdown

Deploying with  Cloudflare Workers  Cloudflare Workers

The latest updates on your project. Learn more about integrating Git with Workers.

Status Name Latest Commit Preview URL Updated (UTC)
✅ Deployment successful!
View logs
lapwing 7606c2f Commit Preview URL

Branch Preview URL
Jul 18 2026, 02:24 AM

@github-actions

github-actions Bot commented Jul 16, 2026

Copy link
Copy Markdown

Coverage Summary

Lines: 57.51% (6570/11424) · Statements: 56.61% · Functions: 55.23% · Branches: 53.51%

Per-file coverage
File Lines Functions Branches
src/components/map/positionArrowMarker.ts 0% 0% 0%
src/components/video-overlays/dataSourceResolver.ts 85.07% 71.42% 80.82%
src/components/video-overlays/overlayUtils.ts 100% 100% 100%
src/components/video-overlays/registry.ts 100% 100% 100%
src/components/video-overlays/sectorUtils.ts 94.73% 100% 84.37%
src/components/video-overlays/themes.ts 100% 100% 100%
src/components/video-overlays/types.ts 100% 100% 100%
src/hooks/use-mobile.tsx 0% 0% 100%
src/hooks/use-toast.ts 0% 0% 0%
src/hooks/useAuth.ts 100% 100% 100%
src/hooks/useDataLoader.ts 14.11% 20% 17.02%
src/hooks/useDocumentHead.ts 0% 0% 0%
src/hooks/useEngineManager.ts 0% 0% 0%
src/hooks/useFileManager.ts 0% 0% 0%
src/hooks/useFirmwareUpdate.ts 0% 0% 0%
src/hooks/useKartManager.ts 100% 100% 100%
src/hooks/useLapManagement.ts 0% 0% 0%
src/hooks/useLapOverlays.ts 0% 0% 0%
src/hooks/useLapSnapshots.ts 0% 0% 0%
src/hooks/useNativeFirmwareUpdate.ts 0% 0% 0%
src/hooks/useNoteManager.ts 0% 0% 0%
src/hooks/useOnlineStatus.ts 0% 0% 0%
src/hooks/usePlayback.ts 0% 0% 0%
src/hooks/useReferenceLap.ts 0% 0% 0%
src/hooks/useSessionData.ts 0% 0% 0%
src/hooks/useSessionMetadata.ts 0% 0% 0%
src/hooks/useSettings.ts 0% 0% 0%
src/hooks/useSetupManager.ts 0% 0% 100%
src/hooks/useSimPlayback.ts 0% 0% 0%
src/hooks/useStripePrices.ts 0% 0% 0%
src/hooks/useSubscription.ts 0% 0% 0%
src/hooks/useTemplateFields.ts 0% 0% 0%
src/hooks/useTemplateManager.ts 0% 0% 0%
src/hooks/useTrackEditorForm.ts 0% 0% 0%
src/hooks/useVehicleManager.ts 0% 0% 100%
src/hooks/useVideoSync.ts 0% 0% 0%
src/hooks/useWakeLock.ts 0% 0% 0%
src/hooks/useWaybackImagery.ts 0% 0% 0%
src/lib/test/idb.ts 100% 100% 100%
src/lib/aimParser.ts 95.85% 100% 81.46%
src/lib/alfanoParser.ts 83.22% 100% 61.9%
src/lib/appActivity.ts 0% 0% 100%
src/lib/billing.ts 97.36% 100% 98.21%
src/lib/billingClient.ts 0% 0% 0%
src/lib/ble/test/mockBle.ts 96% 90% 50%
src/lib/ble/battery.ts 93.33% 100% 87.5%
src/lib/ble/connection.ts 0% 0% 0%
src/lib/ble/dfu/dfuPackage.ts 96.66% 100% 81.25%
src/lib/ble/dfu/dfuTypes.ts 100% 100% 100%
src/lib/ble/dfu/firmwareImage.ts 100% 100% 100%
src/lib/ble/dfu/firmwareManifest.ts 96.92% 92.85% 93.18%
src/lib/ble/dfu/index.ts 100% 100% 100%
src/lib/ble/dfu/version.ts 96.77% 100% 83.33%
src/lib/ble/fileTransfer.ts 90.69% 95% 72.91%
src/lib/ble/firmwareCrc.ts 100% 100% 100%
src/lib/ble/firmwareUpload.ts 90.4% 87.5% 84.31%
src/lib/ble/format.ts 100% 100% 100%
src/lib/ble/index.ts 100% 100% 100%
src/lib/ble/internal.ts 100% 100% 50%
src/lib/ble/settings.ts 93.6% 100% 85.29%
src/lib/ble/trackSync.ts 89.69% 90.9% 70.96%
src/lib/ble/types.ts 100% 100% 100%
src/lib/bleDatalogger.ts 100% 100% 100%
src/lib/brakingZones.ts 97.14% 100% 86.11%
src/lib/browserCompat.ts 0% 0% 0%
src/lib/buildInfo.ts 100% 100% 100%
src/lib/canvas2d.ts 100% 100% 91.66%
src/lib/channels.ts 100% 100% 84.61%
src/lib/chartAxis.ts 98% 100% 82.92%
src/lib/chartColors.ts 100% 100% 100%
src/lib/chartUtils.ts 100% 100% 97.05%
src/lib/courseDetection.ts 97.16% 100% 80.23%
src/lib/courseSectors.ts 100% 100% 94.87%
src/lib/datalogParser.ts 83.56% 80% 81.81%
src/lib/db/index.ts 0% 0% 0%
src/lib/db/submissionMaterialize.ts 100% 100% 97.5%
src/lib/db/supabaseAdapter.ts 0% 0% 0%
src/lib/db/types.ts 100% 100% 100%
src/lib/dbUtils.ts 76.23% 80% 17.2%
src/lib/debugConsole.ts 57.74% 61.11% 47.72%
src/lib/deviceSettingsSchema.ts 93.33% 100% 96.42%
src/lib/deviceTrackSync.ts 100% 100% 100%
src/lib/doveParser.ts 89.6% 72.72% 77.27%
src/lib/dovexParser.ts 90.62% 100% 80%
src/lib/driverProfileGroups.ts 100% 100% 83.33%
src/lib/emailValidation.ts 100% 100% 100%
src/lib/engineStorage.ts 100% 75% 100%
src/lib/engineUtils.ts 100% 100% 91.66%
src/lib/fieldResolver.ts 100% 100% 83.33%
src/lib/fileBrowserTree.ts 98.87% 97.5% 89.36%
src/lib/fileLoadingState.ts 100% 100% 100%
src/lib/fileStorage.ts 82.79% 78.12% 72.22%
src/lib/fnv1a.ts 100% 100% 100%
src/lib/garageEvents.ts 100% 100% 100%
src/lib/gforceCalculation.ts 100% 100% 97.22%
src/lib/ggDiagram.ts 100% 100% 94.73%
src/lib/gps/customGps.ts 100% 100% 82.05%
src/lib/gps/dovepWriter.ts 100% 100% 92%
src/lib/gps/gpsFix.ts 100% 100% 100%
src/lib/gps/index.ts 100% 100% 100%
src/lib/gps/observationSample.ts 100% 100% 100%
src/lib/gps/realtimeTimer.ts 90.07% 100% 76.04%
src/lib/gps/sessionGate.ts 100% 100% 100%
src/lib/graphPrefsStorage.ts 100% 100% 100%
src/lib/i18n/config.ts 100% 100% 100%
src/lib/i18n/format.ts 100% 100% 85.71%
src/lib/i18n/pluginLocales.ts 0% 0% 100%
src/lib/i18n/seedUtils.ts 100% 100% 95.23%
src/lib/imageCrop.ts 12.19% 10% 0%
src/lib/iracingParser.ts 91.93% 80% 76.47%
src/lib/kartStorage.ts 100% 75% 100%
src/lib/lapAlignment.ts 100% 80% 76.92%
src/lib/lapCalculation.ts 95.85% 100% 89.68%
src/lib/lapDelta.ts 99.2% 100% 85.07%
src/lib/lapOverlays.ts 100% 100% 86%
src/lib/lapSnapshot.ts 100% 100% 88.46%
src/lib/lapSnapshotStorage.ts 100% 83.33% 100%
src/lib/leaderboardBrowse.ts 100% 92.3% 80.76%
src/lib/leaderboardHandoff.ts 100% 100% 100%
src/lib/leaderboardSession.ts 97.67% 83.33% 67.85%
src/lib/leaderboardTypes.ts 100% 100% 100%
src/lib/logFileType.ts 100% 100% 100%
src/lib/loggers/alfano/alfanoConnection.ts 100% 100% 100%
src/lib/loggers/alfano/ipc.ts 100% 100% 100%
src/lib/loggers/doveslogger/dovesloggerConnection.ts 100% 100% 100%
src/lib/loggers/doveslogger/firmwareInfo.ts 100% 100% 100%
src/lib/loggers/doveslogger/ipc.ts 100% 100% 100%
src/lib/loggers/errors.ts 100% 100% 100%
src/lib/loggers/fledglingConnection.ts 100% 100% 100%
src/lib/loggers/index.ts 100% 100% 100%
src/lib/loggers/mychron/ipc.ts 100% 100% 100%
src/lib/loggers/mychron/mychronConnection.ts 100% 100% 100%
src/lib/loggers/native/ipc.ts 100% 100% 100%
src/lib/loggers/progress.ts 94.73% 100% 96.15%
src/lib/loggers/types.ts 100% 100% 100%
src/lib/mapMarker.ts 100% 100% 100%
src/lib/motecParser.ts 89.61% 81.48% 55.71%
src/lib/navBack.ts 100% 100% 71.42%
src/lib/nmeaParser.ts 85.62% 92.85% 71.22%
src/lib/noteStorage.ts 100% 80% 100%
src/lib/overlayCanvasRenderer.ts 0% 0% 0%
src/lib/parserUtils.ts 100% 100% 98.52%
src/lib/passwordStrength.ts 100% 100% 100%
src/lib/pendingCheckout.ts 58.82% 25% 100%
src/lib/platform.ts 100% 87.5% 91.3%
src/lib/profanity.ts 100% 100% 75%
src/lib/referenceUtils.ts 98.29% 100% 86.66%
src/lib/sampleData.ts 100% 100% 100%
src/lib/satelliteImagery.ts 100% 100% 90%
src/lib/setupHistory.ts 94.17% 100% 72.51%
src/lib/setupRevision.ts 100% 100% 94.11%
src/lib/setupRevisionStorage.ts 84.48% 78.26% 36.84%
src/lib/setupStatus.ts 100% 100% 100%
src/lib/setupStorage.ts 81.35% 58.62% 100%
src/lib/shareSession.ts 100% 100% 100%
src/lib/sim/simClient.ts 0% 0% 0%
src/lib/sim/simPlayback.ts 100% 100% 97.22%
src/lib/speedBounds.ts 94.28% 66.66% 87.17%
src/lib/speedEvents.ts 86.56% 100% 76%
src/lib/speedHeatmap.ts 100% 100% 100%
src/lib/submittedTracksStorage.ts 0% 0% 0%
src/lib/templateEdit.ts 97.82% 88.88% 86.95%
src/lib/templateStorage.ts 93.54% 69.23% 100%
src/lib/trackStorage.ts 16.4% 28.3% 18.18%
src/lib/trackSubmission.ts 100% 100% 92.64%
src/lib/trackUtils.ts 100% 100% 97.05%
src/lib/ubxParser.ts 99% 100% 89.58%
src/lib/units.ts 100% 100% 100%
src/lib/updateFlow.ts 100% 100% 100%
src/lib/utils.ts 100% 100% 100%
src/lib/vboParser.ts 90.66% 100% 71.59%
src/lib/vehicleHistory.ts 97.29% 100% 80%
src/lib/vehicleStorage.ts 100% 75% 100%
src/lib/versionCheck.ts 19.44% 12.5% 44.73%
src/lib/videoExport.ts 0% 0% 0%
src/lib/videoExportTarget.ts 100% 100% 84.61%
src/lib/videoFileStorage.ts 100% 76% 61.11%
src/lib/videoPlaylist.ts 95.71% 100% 90%
src/lib/videoStorage.ts 100% 76.92% 77.77%
src/lib/videoTimeline.ts 100% 100% 100%
src/lib/wakeLock.ts 100% 80% 100%
src/lib/weatherCacheStorage.ts 100% 100% 100%
src/lib/weatherService.ts 23.8% 35.29% 25.89%
src/lib/xrk/xrkClient.ts 3.57% 0% 0%
src/lib/xrk/xrkConfig.ts 100% 100% 100%
src/lib/xrk/xrkImporter.ts 70% 75% 80%
src/lib/xrk/xrkMapping.ts 98.7% 100% 82.81%
src/lib/xrk/xrkResample.ts 100% 100% 90.9%
src/lib/xrk/xrkTypes.ts 100% 100% 100%
src/lib/xrk/xrkWorker.ts 0% 0% 0%
src/plugins/cloud-sync/accountDeletion.ts 100% 100% 100%
src/plugins/cloud-sync/accountExport.ts 0% 0% 0%
src/plugins/cloud-sync/accountImport.ts 13.51% 25% 26.08%
src/plugins/cloud-sync/activeUser.ts 100% 100% 100%
src/plugins/cloud-sync/autoSync.ts 0% 0% 0%
src/plugins/cloud-sync/cloudClient.ts 55% 25% 87.5%
src/plugins/cloud-sync/CloudLogsPanel.tsx 0% 0% 0%
src/plugins/cloud-sync/DataPrivacyPanel.tsx 0% 0% 0%
src/plugins/cloud-sync/exportManifest.ts 100% 100% 100%
src/plugins/cloud-sync/FileDeleteToggle.tsx 0% 0% 0%
src/plugins/cloud-sync/fileSync.ts 91.66% 85.71% 100%
src/plugins/cloud-sync/FileSyncToggle.tsx 0% 0% 0%
src/plugins/cloud-sync/index.ts 0% 0% 0%
src/plugins/cloud-sync/LapSnapshotsPanel.tsx 0% 0% 0%
src/plugins/cloud-sync/leaderboardClient.ts 8.92% 10.52% 1.56%
src/plugins/cloud-sync/leaderboardSubmission.ts 100% 100% 94.11%
src/plugins/cloud-sync/LeaderboardSubmitPanel.tsx 0% 0% 0%
src/plugins/cloud-sync/localUsage.ts 100% 100% 50%
src/plugins/cloud-sync/merge.ts 100% 100% 100%
src/plugins/cloud-sync/pendingSync.ts 100% 100% 100%
src/plugins/cloud-sync/profile.ts 61.53% 50% 63.33%
src/plugins/cloud-sync/publicProfile.ts 6.25% 28.57% 0%
src/plugins/cloud-sync/publicShare.ts 100% 100% 100%
src/plugins/cloud-sync/publicVehicleSync.ts 92.85% 100% 78.57%
src/plugins/cloud-sync/sessionShare.ts 92.15% 76.92% 91.66%
src/plugins/cloud-sync/setupRevisionTombstones.ts 100% 100% 83.33%
src/plugins/cloud-sync/ShareFileButton.tsx 0% 0% 0%
src/plugins/cloud-sync/ShareSessionDialog.tsx 0% 0% 0%
src/plugins/cloud-sync/shareState.ts 100% 100% 100%
src/plugins/cloud-sync/shareToken.ts 100% 100% 100%
src/plugins/cloud-sync/snapshotSync.ts 97.22% 87.5% 75%
src/plugins/cloud-sync/snapshotTombstones.ts 100% 100% 100%
src/plugins/cloud-sync/StoragePanel.tsx 0% 0% 0%
src/plugins/cloud-sync/storageTypes.ts 100% 100% 100%
src/plugins/cloud-sync/storeAccessors.ts 100% 100% 90%
src/plugins/cloud-sync/syncEngine.ts 99.05% 91.3% 80%
src/plugins/cloud-sync/syncStores.ts 100% 100% 100%
src/plugins/cloud-sync/trackAutoSubmit.ts 100% 100% 78.57%
src/plugins/fileSources.ts 0% 0% 100%
src/plugins/index.ts 0% 0% 0%
src/plugins/mounts.ts 71.42% 75% 100%
src/plugins/panels.ts 75% 83.33% 100%
src/plugins/PluginMount.tsx 0% 0% 0%
src/plugins/PluginPanelHost.tsx 0% 0% 0%
src/plugins/registry.ts 77.27% 50% 100%
src/plugins/storage.ts 90.32% 70.83% 66.66%
src/plugins/tools/i18n.ts 0% 0% 100%
src/plugins/tools/index.ts 0% 0% 100%
src/plugins/tools/laptimer/lapTimerSession.ts 89.06% 80% 84.37%
src/plugins/tools/laptimer/LapTimerTool.tsx 0% 0% 0%
src/plugins/tools/laptimer/useLapTimer.ts 0% 0% 0%
src/plugins/tools/seat-position/model.ts 100% 100% 60%
src/plugins/tools/seat-position/SeatDiagram.tsx 0% 0% 0%
src/plugins/tools/seat-position/SeatPositionTool.tsx 0% 0% 0%
src/plugins/tools/toolList.ts 0% 0% 100%
src/plugins/tools/ToolsLandingTile.tsx 0% 0% 0%
src/plugins/tools/ToolsPanel.tsx 0% 0% 0%
src/plugins/types.ts 100% 100% 100%
src/types/racing.ts 0% 0% 0%

TheAngryRaven and others added 17 commits July 16, 2026 00:26
…xvjbo

plan 0009: read-only viewer polish — fastest lap, overlays, weather, hide Vehicle tab
Sim plan Phase 4 vendoring: the real DovesDataLogger firmware compiled
to WebAssembly, committed here so the viewer only ever runs a
deliberately-pinned build (no CDN, no CORS, same-origin fetch).

- public/sim/birdseye-sim.mjs      stable public wrapper (API contract v1)
- public/sim/birdseye-sim-core.mjs Emscripten factory (internal)
- public/sim/birdseye-sim-core.wasm the firmware, 207 KB
- public/sim/version.json          provenance: firmware sha ef62d0f,
                                   DovesLapTimer sha, display-lib versions
- public/sim/test.html             standalone harness (boot + dovex replay)
- public/_headers                  /sim/* always revalidates (not hashed)

Built from DovesDataLogger BETA (PR #96) with emsdk 3.1.61; node smoke
test green; renders byte-identically to the firmware repo's native
golden fixtures. The /simulator page consuming this lands in follow-up
work (sim plan Phases 5-7).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013BCdGxqExj9ojEQgZj4f6G
…ork-ssd2ft

Vendor BirdsEye firmware simulator wasm build (public/sim/)
…ase A)

The real DovesDataLogger firmware (vendored wasm, public/sim/) booted in
the browser, replaying the bundled OKC sample session — the same file
the firmware repo's CI oracle proves reproduces the hardware's 13 lap
times to the millisecond.

- lib/sim/simClient: typed loader for /sim/birdseye-sim.mjs (API
  contract v1; async reset() = true fresh boot via re-instantiation).
- lib/sim/simPlayback (PURE, Vitest-covered): sample→injectPvt mapping
  on canonical channel ids, tick planning (every row injected at its own
  timestamp, stepMillis between — one injection per step batch, per the
  contract), no-fix boot pre-roll, scrub planning (backward = reset +
  headless fast-replay).
- hooks/useSimPlayback: rAF loop, play/pause/1-10x/seek, frame-hash-
  gated canvas blits, ~10 Hz state publishing; paused keeps the firmware
  clock breathing so menus stay interactive.
- components/sim/SimDevicePanel: 128x64 canvas at integer scales
  (4/6/8x, pixelated) or TRUE SIZE (55x27.5 mm via CSS mm, default ON
  for first-time visitors with a why-so-small tooltip); bezel; three
  buttons (pointer + arrow/Enter keys) through the real firmware
  debounce.
- pages/Simulator (public, lazy, offline-first): demo session via
  ensureSampleFile's IDB cache + the standard datalogParser (no second
  dovex parser), transport bar, live firmware-state chips, provenance
  footer from getVersion().
- i18n: new 'simulator' namespace across all 7 languages; SW precache
  glob gains mjs so the sim works offline.

Verified end-to-end in Chromium against the production build: boot page
renders on canvas, Select navigates the real menu, playback runs,
scrub-to-end fast-replays the full 15-min session to exactly 13 laps,
rewind (reset + replay) works.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013BCdGxqExj9ojEQgZj4f6G
…ork-ssd2ft

plan 0010: /simulator — the firmware running in the browser (sim panel + playback engine)
Map above the device panel, one clock for everything:

- components/sim/SimMap.tsx: lean Leaflet map — session path polyline,
  the detected course's start/finish line (loadTracks + autoDetectCourse,
  the same offline track DB the whole app uses), dark/satellite tile
  toggle, and the shared positionArrowMarker cursor (created once, moved
  per tick).
- lib/sim/simPlayback: positionIndexAt (pure, binary search, tested) —
  the map cursor index is computed from the SAME playback cursor the
  firmware consumes, so the marker, the lap counter on the simulated
  display, and the scrubber cannot disagree at any speed (the spec's
  Phase-6 done-criterion, by construction).
- Session picker: bundled sample by name + 'Upload .dovex' — user logs
  are parsed in-memory with the standard datalogParser (no second
  parser, nothing saved to the file manager) and swap the running sim
  to a fresh boot, paused.
- useSimPlayback: a session swap now starts paused at the fresh boot.
- i18n: picker/map keys across all 7 languages.

Verified end-to-end in Chromium against the production build: map draws
path + start/finish, no cursor during pre-roll, scrub-to-end lands on 13
laps with the marker present, and the marker moves with live playback.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013BCdGxqExj9ojEQgZj4f6G
- SimMap: z-0 + isolate on the map wrapper. Leaflet's internal panes
  carry z-indexes in the hundreds; without a contained stacking context
  they floated over the sticky SiteHeader (z-30) once the page scrolled.
- Remove 'Upload .dovex' from the simulator (owner call: not taking on
  the support surface of arbitrary logs here). The page shows the
  bundled sample session only; the adoptSession seam keeps a future
  add-back small. Upload i18n keys dropped from all 7 locales;
  CHANGELOG + plan 0010 updated to match.

Verified in Chromium: with the page scrolled, elementFromPoint inside
the header band resolves to the header (not the map), and no Upload
button renders.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013BCdGxqExj9ojEQgZj4f6G
…ork-ssd2ft

plan 0010: simulator map + synced playback cursor + session picker (Phase B)
…fix)

- Re-vendor public/sim/ from DovesDataLogger 97b2248 (PR #97): the
  firmware-side fix for the dead center button in race mode (the SIM
  invert-display carve-out is gone — Select now does the hardware's
  speed-aware pace/best page jump) plus the new getBootFrames() API.
- Boot intro: useSimPlayback queues the boot keyframes captured by
  init() (splash -> boot page, virtual timestamps) and plays them
  through the canvas sink at their real pace before live stepping
  starts. The frame sink now carries raw 1024-byte framebuffers so boot
  frames and live frames share one blit path.
- data-sim-page attribute on the state chips (invisible) so e2e drives
  can assert the firmware's current page.

Verified in Chromium against the production build: the boot animation
visibly plays on load, and pressing Select mid-session switches the
firmware page (tach 6 -> best-lap 9; pace 8 when moving fast) instead
of doing nothing.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013BCdGxqExj9ojEQgZj4f6G
…ork-ssd2ft

plan 0010: real boot sequence on load + center-button fix (re-vendored sim)
Landing: the 'Build your own logger' tile now opens a dialog instead of
deep-linking straight to GitHub — the Fledgling photo (same asset as the
download picker), a short pitch, the GitHub build-guide link, and a
prominent 'See how it works!' CTA that launches /simulator.

Simulator: a collapsible guide at the top of the page (open by default)
replaces guesswork — booting (skip with any button, or auto-advance ~3 s
after GPS lock), outer-buttons/center-button controls, replaying a
session (auto race mode -> track search -> course detection), ending it
(auto or via END RACE), reviewing the temporarily-saved session via the
Review menu, a reference table for all nine race-mode pages, and the
Bluetooth/USB not-simulated note.

All strings in the landing/simulator namespaces across the 7 languages.
Verified in Chromium: modal opens with image + links, CTA navigates to
/simulator, guide sections and the page reference render.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013BCdGxqExj9ojEQgZj4f6G
…ork-ssd2ft

plan 0010: "Build your own logger" dialog + simulator how-to guide
…ssary

Update UX: a new build detected while the user sits idle on the home
page now applies itself — "Updating…" progress toast, automatic reboot
onto the new version, confirmation toast on the fresh load — instead of
the silent eventually-flashing refresh. The persistent "Update ready"
toast remains for every other case (session loaded, any non-home route),
so active work is never yanked. Policy is the pure, tested
decideUpdateAction(); a sessionStorage per-commit guard prevents reload
loops when an apply fails to stick. Index.tsx publishes session-loaded
state via the new appActivity host flag.

Simulator guide: added a "Menu & system pages" glossary (boot splash,
GPS status, main menu, race, review, transfer, camera, SD format,
warnings, charging) across all 7 locales, with a note on which pages
need real hardware.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013BCdGxqExj9ojEQgZj4f6G
…ork-ssd2ft

Auto-update on idle home page + simulator system-pages glossary (plan 0010)
…nsport

A short description card under the playback controls walking through the
firmware's actual behavior: boot on button press or RPM, nearest-track
lookup with all courses loaded into memory, automatic race-mode entry on
RPM, and the waypoint-drop course-detection pass (distance match +
multiple line crossings) that selects the course and registers an
accurate first lap. All 7 locales.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013BCdGxqExj9ojEQgZj4f6G
…ork-ssd2ft

Simulator "what the device is doing" explainer below the transport (plan 0010)
Set package.json version 3.0.0 -> 3.1.0 to match the topmost CHANGELOG
heading and dated [3.1.0] to 2026-07-17. Coach plugin was already on the
production npm package (@perchwerks/eye-in-the-sky 0.5.0) on BETA, so no
flip was needed.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VTBXGG2rNGSSqeozPyfjxy
@TheAngryRaven TheAngryRaven changed the title Beta Release v3.1.0 Jul 17, 2026
Fixes the four confirmed, non-blocking findings from the BETA->main
release-gate review:

- PERF-01/COR-01: coalesce simulator scrub seeks (useSimPlayback) via a
  pendingSeekRef so a backward drag no longer re-boots the wasm + replays
  once per slider tick and no longer strands the thumb at a stale target;
  only the released position is rendered.
- TEST-01: add publicShare.test.ts (12 tests) for the new anon shared-session
  read module — both profile-embed shapes, null/missing profile, null dates,
  not-found and error paths.
- DOC-01: add README Features bullets for the /simulator page and shareable
  session links.

Also adds the review report under docs/reviews/.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VTBXGG2rNGSSqeozPyfjxy

Copy link
Copy Markdown
Owner Author

Beta → Main Release Review — 2026-07-17

🚦 Verdict: GO-WITH-FIXES → all confirmed findings fixed in #343 (now effectively GO)

No Critical and no release-blocking High. Four confirmed findings (2 Medium, 2 Low), all non-blocking; every one has been addressed on the release-prep branch. Blockers: 0.

PR: #332 (BETAmain) · Release: v3.1.0 · Since: v2.9.2
Run: multi-agent (7 finders, single adversarial verify per finding) · Diff: 131 files, 25 commits
Excluded: node_modules, dist, generated supabase client, lockfile line-noise, vendored public/sim/* wasm blobs

Release contract checklist

Gate Status
Coach on production npm (@perchwerks/eye-in-the-sky, both package.json + vite.config.ts) ✅ (already production on BETA; no flip needed)
package.json version == topmost CHANGELOG heading ✅ (bumped 3.0.0 → 3.1.0 in #343)
CHANGELOG heading dated (not - unreleased) ✅ (dated 2026-07-17 in #343)
CHANGELOG complete vs commits in this release
No beta/preview-only config leaked into prod path
CI green (lint / typecheck / test / build) ✅ (2209 tests)

Summary

Severity Count
Critical 0
High 0
Medium 2
Low 2

Two headline features drive this release — a public firmware simulator (/simulator) and shareable public session links (/s/…) — plus native firmware OTA and friendlier logger-download errors. The release contract is satisfied: coach is on the production npm package, version/changelog are set and dated (#343), and CI is green. The only confirmed issues were quality gaps on the new surfaces (a simulator backward-scrub perf/correctness pair on a non-core novelty page, one untested new anon-read module, and stale README). None block the release, and all four have been fixed on the release-prep branch. A notable High was refuted: the shared_sessions RLS is a deliberate public-read design (see "What was not covered").

Findings (sorted: Critical → Low, then by dimension)

[Medium] PERF-01 — Backward scrub re-boots the wasm sim and synchronously replays the whole session from start

  • Dimension: performance · Blocks release: no
  • Location: src/hooks/useSimPlayback.ts:238-263
  • Evidence: planScrub returns {reset:true, replayFromMs:sessionStartMs} for any backward target, so seek() does await sim.reset() (re-instantiates the ~208 KB wasm) then a synchronous injectPvt+stepMillis loop over every sample from session start to target. The scrubber is wired via Radix onValueChange (fires continuously during a drag), so a near-end backward drag replays nearly the whole multi-minute session on the main thread.
  • Impact: UI-thread jank/stutter (potentially seconds) on the public /simulator demo when dragging the timeline backward.
  • Recommendation: Coalesce drag seeks to the latest target so only the settled value drives a reset+replay.
  • Effort: M · Confidence: High
  • Status: ✅ Fixed in Release prep v3.1.0 #343seek() now coalesces via pendingSeekRef, collapsing an N-tick backward drag to at most two replays and rendering only the released position.

[Medium] TEST-01 — New anon-read module publicShare.ts shipped with zero tests

  • Dimension: testing · Blocks release: no
  • Location: src/plugins/cloud-sync/publicShare.ts:35-40
  • Evidence: New 89-line module powering the /s/:token viewer and the driver-profile "Shared sessions" list. It has testable pure logic — embeddedDisplayName() normalizes the Supabase profile embed across array/object shapes, and the fetch mappers null-guard date parsing — but no test referenced it (its write-side sibling sessionShare.ts has 16). Golden Rule 3.
  • Impact: A silent regression (embed shape flips to array → driver name renders null; bad date) would ship undetected on a public, offline-first release.
  • Recommendation: Add publicShare.test.ts covering both embed shapes, the null/absent-profile case, and the row→object mappings with present/null dates.
  • Effort: S · Confidence: High
  • Status: ✅ Fixed in Release prep v3.1.0 #343 — added publicShare.test.ts (12 tests) covering both embed shapes, null profile, null dates, not-found, and error paths.

[Low] COR-01 — Backward scrub drops the released slider value, snapping the thumb to a stale intermediate position

  • Dimension: correctness · Blocks release: no
  • Location: src/hooks/useSimPlayback.ts:232-260
  • Evidence: seek() bailed immediately while busyRef was set and never queued the dropped request, so during a backward drag the first value locked the guard and every later value — including the final release value — was silently discarded; the controlled slider then snapped the thumb to the first intermediate target.
  • Impact: Scrubbing backward on /simulator lands the cursor/thumb at a stale position (self-corrects on next interaction). Minor, novelty surface.
  • Recommendation: Remember the latest requested target while busy and re-run to it once the in-flight seek clears.
  • Effort: S · Confidence: Medium
  • Status: ✅ Fixed in Release prep v3.1.0 #343 — same pendingSeekRef coalescing fix as PERF-01.

[Low] DOC-01 — README not updated for the new /simulator page and shareable session links

  • Dimension: docs · Blocks release: no
  • Location: README.md:24-49
  • Evidence: Both features are documented in CHANGELOG, CLAUDE.md and docs/backend.md, but README.md is not in the 131-file diff — its Features list never mentions the simulator or session sharing. Golden Rule 5.
  • Impact: The OSS front door omits two headline v3.1.0 features (a public page needing no account, and public link sharing).
  • Recommendation: Add Features bullets for the firmware simulator and shareable session links.
  • Effort: S · Confidence: High
  • Status: ✅ Fixed in Release prep v3.1.0 #343 — added README Features bullets for both.

Must-fix before merge

None. No confirmed finding blocks the release, and all four have been fixed on the release-prep branch (#343 → BETA). Merge order still matters: land #343 into BETA before merging this PR (BETA → main).

What was not covered / refuted

  • SEC-01 (High) — refuted, not a finding. A finder flagged the shared_sessions RLS SELECT policy (using(true)) as "fully enumerable by anon." The verifier refuted it on the merits: the same PR intentionally ships fetchSharedSessionsByUser(userId) — an anon .eq("user_id", …) query consumed by the public DriverProfile "Shared sessions" list — so public per-user enumeration is a deliberate, load-bearing feature mirroring the app's existing leaderboard_entries public-read pattern. The protected secret per the migration's own comments is the filename, and there is no filename column. Enumerable columns (track/course/date) plus the blob in the intentionally-public bucket are content the user explicitly published (and already surfaces on /driver/:username). No private data leaks. Recommend a human sanity-check of supabase/migrations/20260715000000_shared_sessions.sql given it's a new public surface, but it is not a release blocker.
  • Two more refuted/pre-existing: CFG-01 (a landing "Build your own logger" tile rewired to a modal — the CHANGELOG covers it) and PERF-02 (the ~234 KB sim wasm added to the SW precache — intended, offline-first). Both verified as introduced-but-intended, not defects.
  • Vendored public/sim/* wasm/mjs binaries were reviewed for wiring/intent only, not byte contents. Single adversarial verify per finding (default depth); no completeness-critic pass.

Full report also saved to docs/reviews/beta-release-review-2026-07-17.md. Multi-agent release-gate review via the beta-release-review skill.


Generated by Claude Code

claude and others added 4 commits July 18, 2026 02:09
Rebuilt the sim from the firmware repo's v3.0.0 release tag (ecc88c3)
with emsdk 3.1.61 — same recipe as the firmware sim-build CI wasm job —
and vendored the dist into public/sim/. Node smoke test (boot, state,
determinism, reset) passed on the artifact before vendoring; verified
in Chromium that /simulator boots it and reports the new provenance.
DovesLapTimer moves to its current BETA revision (a6add21).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013BCdGxqExj9ojEQgZj4f6G
…ADME.md

The artifact list, exact build commands (emsdk 3.1.61, emcmake
configure/build, node smoke test), the configure-time version.json
stamping gotcha, the five-file vendor step, and the CI-artifact
alternative — previously only encoded in the firmware repo's sim-build
workflow. Not precached (md is outside the PWA glob).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013BCdGxqExj9ojEQgZj4f6G
…ork-ssd2ft

Vendor firmware v3.0.0 wasm simulator build (plan 0010)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants