Skip to content

Debug export: strap & data state + analytics-funnel diagnostics#20

Merged
ryanbr merged 1 commit into
mainfrom
debug-export-strap-data-funnels
Jul 7, 2026
Merged

Debug export: strap & data state + analytics-funnel diagnostics#20
ryanbr merged 1 commit into
mainfrom
debug-export-strap-data-funnels

Conversation

@ryanbr

@ryanbr ryanbr commented Jul 7, 2026

Copy link
Copy Markdown
Owner

Enriches the shareable strap-log / bundle header, which was connectivity-only (device, OS, battery-opt, permissions), for the data-quality bug classes that actually recur (no steps / 0% REM / skin-temp absent / missing nights).

Added

Strap & data state (offline-safe — persisted prefs + the my-whoop daily spine):

  • Strap model, last-known firmware (now persisted on connect so it survives disconnect + the background export), last-sync (relative), timezone/offset, days of history, most recent sleep + recovery day.

Analytics funnels (latest night, best-effort, self-reporting):

  • Recomputes the REM + skin-temp funnels for the most recent sleep session and prints each .summary, so a "0% REM" / "skin-temp absent" report arrives with the funnel already broken down.
  • Prints the sample counts it read, and says plainly when it cannot compute (no recent session / no raw samples under the canonical id) — so it never fabricates a misleading verdict.

Plumbing

  • New AndroidDiagnostics.dynamicLines(context) (suspend, on Dispatchers.Default) appended to both the interactive and scheduled export headers.
  • LogExport share/write paths become suspend; DebugExportWorker already runs in a coroutine; the 4 UI share buttons now launch on their screen scope.
  • NoopPrefs.lastFirmware persisted at the firmware-decode point.

Scope / safety

  • No analytics-pipeline changes — all additions live in the diagnostics/export layer (kept out of the perf-sensitive analyze path deliberately).
  • Every section is guarded (best-effort) so a header build never throws into the export.
  • Kotlin + unit-test sources compile clean locally.

Not included (deliberate)

  • Battery %/live-only fields in the scheduled export (no live BLE there); iOS parity — both as follow-ups if wanted.

The shareable strap-log/bundle header was connectivity-only (device, OS, battery-opt,
permissions). Enrich it for the data-quality bug classes that actually recur.

Strap & data state (offline-safe: persisted prefs + the "my-whoop" daily spine):
- Strap model, last-known firmware (now persisted on connect so it survives disconnect
  and the background export), last-sync (relative), timezone/offset, days of history,
  and the most recent sleep + recovery day.

Analytics funnels (latest night, best-effort, self-reporting):
- Recompute the REM + skin-temp funnels for the most recent sleep session and print each
  ".summary", so a "0% REM" / "skin-temp absent" report arrives with the funnel already
  broken down. Prints the sample counts it read and says plainly when it cannot compute
  (no session / no raw samples under the canonical id) so it never fabricates a verdict.

Plumbing:
- New AndroidDiagnostics.dynamicLines(context) (suspend, on Dispatchers.Default) appended
  to BOTH the interactive and scheduled export headers.
- LogExport share/write paths become suspend; the DebugExportWorker already runs in a
  coroutine, and the 4 UI share buttons now launch on their screen scope.
- NoopPrefs.lastFirmware persisted at the firmware-decode point in WhoopBleClient.

No analytics-pipeline changes; all additions live in the diagnostics/export layer.
@ryanbr ryanbr merged commit 71cfc64 into main Jul 7, 2026
4 checks passed
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.

1 participant