الصفحات · Health Debug
Session Card — Module X3: Sensors & Biometrics
Planning artifact. A future build session opens this card, reads the referenced plan docs and the latest
session-handofffor the target cell, builds exactly one cell, then writes its own handoff. No production code lives here.
Purpose
Module X3 gives Health Debug its device-sensor bridge and the body-metrics feature that sits on top of it. The ToGO backend gains a biometrics ingest path plus a body-metrics module (its own ToGO plugin) that stores raw health samples verbatim and computes a baseline-from-earliest-reading progress model server-side. The native clients read the device health stores on-device only — Apple via HealthKit, Android via Health Connect — serialize new samples to JSON, and push them to ToGO; they also write Health Debug's own confirmed logs back into the device store for ecosystem parity. The web client renders read-only body-metrics display cards. Consistent with the two Core Opinions, no client ever interprets a biometric (no thresholds, no trend copy, no "your weight is concerning"): samples are transported and rendered verbatim, all analysis is backend behavioral-pattern analysis only, and any surfaced conclusion always defers to "Consult your doctor." This is a cross-cutting module, so it runs alongside the Apple/Android engine sessions (doc 13 §4), and — like every module — its backend session must land before any client session.
Engine / feature parameters relevant to this module
Use these exact values. Do not invent numbers. Items are tagged [spec-fixed], [legacy precedent — PENDING ratification], or [PENDING DESIGN].
- Body-metrics scope (backend, superset): Weight, Body Fat %, Body Water %, Heart Rate, Skin Temp, Sleep. [spec-fixed]
- Progress model: baseline is the earliest logged reading; progress is computed from that baseline forward, server-side (a legacy precedent reused; clients never compute it). [legacy precedent — PENDING ratification]
- Apple HealthKit read scope → push: Weight (
bodyMass, direct), Body Fat % (bodyFatPercentage, direct), Body Water % (no first-party HealthKit type exists — source undefined), Heart Rate (heartRate, direct), Skin Temp (appleSleepingWristTemperatureand/orbodyTemperature— mapping undefined). Apple's §5 read scope has no Sleep read. [spec-fixed scope; Water% + Skin Temp mapping = PENDING DESIGN] - Apple HealthKit write-back: exactly two things — (1) burned active calories (backend-provided figures), (2) completed hydration (each server-accepted 250 ml water log written as a dietary water sample). Only server-confirmed entries; samples tagged with the backend entry ID for idempotency. [spec-fixed]
- Android Health Connect read scope → push: Weight, Body Fat %, Heart Rate, Sleep (sessions/wake time). Android has no Body Water % and no Skin Temp. Permission groups requested at point of use. [spec-fixed]
- Android Health Connect write-back: hydration (accepted 250 ml unit → Hydration record) and nutrition/food (categorical model → quantitative Health Connect record mapping is unresolved). Only backend-accepted logs are mirrored; a cooldown- or cap-rejected hydration tap is never written. [spec-fixed; nutrition mapping = PENDING DESIGN]
- Cross-scope reconciliation: Apple lacks Sleep read; Android lacks Water % + Skin Temp. The unified backend body-metrics scope must tolerate per-platform partial coverage without treating an absent type as an error. [PENDING DESIGN — open question]
- Hydration write-back units carried from M1: 250 ml logging unit, 5000 ml/day cap, 30-second cooldown are enforced server-side by the Hydration engine; X3 clients only mirror the accepted result, never re-decide it. [spec-fixed]
- Legacy weigh-in window 07:00–11:00 existed in the legacy
notify.gofor weigh-in nudges. It is a legacy precedent only, not a greenfield spec constant — do not bake it into the body-metrics module without ratification. [legacy precedent — PENDING ratification] - Schema rule:
health_samplesanddaily_summaries— every column NOT NULL with an explicit default; all migrations strictly expand-contract; sqlc + Atlas; raw samples stored verbatim (raw-sensor push model). [spec-fixed]
Session table
Surfaces in scope for X3: backend → web → apple → android (per the surface order; mcp, chrome, desktop are out of scope for this module — biometrics do not surface in a browser popup, the brief names no MCP body-metrics tools, and desktop reuses the web cards). Backend precedes all clients.
| Session ID | Surface | Scope (this one session builds) | Entry deps | Done criteria (doc 13 §5 template) | Plan docs to read |
|---|---|---|---|---|---|
S-<date>-X3-backend-NN | backend | Biometrics ingest endpoint(s) + the body-metrics module as its own ToGO plugin: accept batched biometric samples (Weight, Body Fat %, Body Water %, Heart Rate, Skin Temp, Sleep) and store them verbatim in health_samples; roll per-day state into daily_summaries; compute the baseline-from-earliest-reading progress model server-side; expose a read endpoint returning that computed state. No interpretation/thresholds. | M0.1–M0.4 (kernel/config, TOGO auth, plugin framework, expand-contract migration harness) | Backend-engine template: module registered on the kernel as its own plugin; health_samples + daily_summaries migrations are expand-contract with every column NOT NULL + explicit default; ingest is idempotent (same batch/key ⇒ one row, replayed result) and stores raw samples verbatim; read endpoint returns server-computed baseline/progress state (no client math); unit tests encode the baseline-from-earliest + progress logic literally and pass; integration test against real Postgres passes (idempotent-replay, cross-user isolation 404-not-403); no threshold/interpretation logic anywhere (analysis defers to backend AI + "Consult your doctor"); no protocol constant leaks outside the module's core/ package. | 13 §3/§4/§5; 04 (§1 layout, §3 sqlc+Atlas, §4 NOT-NULL rule, §5 expand-contract, §8 plugin purity, §9 testing); 03; 01 (Core Opinions); 12 (legacy health_samples, weigh-in precedent) |
S-<date>-X3-web-NN | web | Body-metrics display cards: render the server-computed body-metrics state (latest reading + baseline + progress per type) fetched from the X3 read endpoint. Read-only; no logging, no interpretation. | S-<date>-X3-backend-NN; M0.5 (web shell + typed API client + auth), M0.6 (theme), M0.7 (i18n/RTL) | Client-engine template: cards render the X3 endpoint state and never compute baseline/progress locally; thin-client/SSOT audit passes (grep finds no biometric thresholds, no baseline math, no protocol constants in client code); both dark-glow and light-flat themes render per the strict color spec; Arabic RTL mirrors correctly (incl. any progress bars); all strings ICU, none concatenated. | 13 §3/§5; 05 (§ data-fetch/mutation rules, endpoint consumption sketch); 01 (i18n/RTL, Core Opinions); 04 §7 (endpoint conventions) |
S-<date>-X3-apple-NN | apple | HealthKit bridge (HDSensors): observer + anchored queries per type, background delivery; read Weight/Body Fat %/Heart Rate/Skin Temp on-device, serialize to JSON, push to the X3 ingest endpoint; write-back burned active calories + server-confirmed completed hydration (250 ml → dietary water sample) with idempotency-tagged sample metadata. Water % + Skin Temp mapping flagged, not blocking direct-mapped types. | S-<date>-X3-backend-NN; Apple suite scaffold + iOS core-app sessions (workspace, HDAPIClient, TOGO auth); M1 hydration backend (for completed-hydration write-back) | Client-engine template + HealthKit: reads bridged as JSON and pushed to the X3 endpoint; write-back writes only backend-accepted entries (calories + hydration), idempotency-tagged so retries do not duplicate samples; denied-permission paths are non-fatal (silently-empty types, no guessed error states); thin-client/SSOT audit passes (no on-device interpretation, thresholds, or protocol constants — the 250/5000/30 never embedded as rule values); both themes + Arabic RTL for any body-metrics UI; ICU strings. | 13 §3/§4/§5; 07 (§3 HDSensors layout, §3.1 sensor-vs-logic line, §5 HealthKit read/permission/write-back, §Open Questions); 01 (Core Opinions, RTL); 04 §7 |
S-<date>-X3-android-NN | android | Health Connect bridge (core/healthconnect): read Weight/Body Fat %/Heart Rate/Sleep on-device with point-of-use permission groups; push raw records to the X3 ingest endpoint via WorkManager + changes-token differential (deltas, not full history); write-back backend-accepted hydration (250 ml → Hydration record) and nutrition (mapping flagged) for ecosystem parity. | S-<date>-X3-backend-NN; Android suite scaffold + core-app sessions (core/network, TOGO auth token handling); M1 hydration backend (hydration write-back); nutrition write-back also needs M4 trigger-families backend (parity, flagged) | Client-engine template + Health Connect: read/push round-trip for Weight/Body Fat %/Heart Rate/Sleep; write-back fires only on backend-accepted logs — a 30s-cooldown- or 5000ml-cap-rejected hydration tap produces no Health Connect record; permission groups requested at point of use with non-fatal denied-permission fallbacks; thin-client/SSOT audit passes (no biometric interpretation, no protocol constants in client); both themes + Arabic RTL; ICU strings, none concatenated. | 13 §3/§4/§5; 08 (§4 Health Connect read/write/endpoint sketch, §Open Questions); 01 (Core Opinions, RTL); 04 §7 |
Session count: 4 (backend, web, apple, android).
Notes & gotchas
- Legacy precedents worth reusing. The legacy build already had a
health_samplestable and a Zepp smart-scale flowing weight/body-fat/HR/sleep in through Apple Health — mine its shape and the baseline-from-earliest-reading progress idea (validate the greenfield module against any legacy*_test.gocases first, per doc 12). Keep the greenfield DB conventions (NOT NULL + default) and re-evaluate the legacy anti-patterns (text ids, absent-date-as-empty-text, triggers-as-CSV) rather than porting them. - Thin-client / SSOT is the liability to avoid. The legacy iOS app carried a second protocol implementation (never called
/api/today) — do not repeat it. X3 clients read the device store and render server state only; the baseline/progress math and any interpretation live exclusively in the backend module'score/. The Apple audit specifically greps for embedded rule numbers (250/5000/30/90/240/60/3). - Categorical, not quantitative + no invented certainty. Biometrics are transported verbatim; there is no threshold logic, no "your heart rate is high" copy, and no diagnosis-shaped output on any surface. Any conclusion is backend behavioral-pattern analysis and always defers to "Consult your doctor."
- PENDING DESIGN — open items (do not silently resolve):
- Cross-scope reconciliation of Apple vs Android read scopes: Apple lacks a Sleep read; Android lacks Body Water % and Skin Temp. The unified backend scope must accept partial per-platform coverage without erroring on an absent type.
- Body Water % has no first-party HealthKit type — where it comes from on iOS (vendor-specific HealthKit writes? unsupported on Apple? backend-only via other clients) is unresolved.
- Skin Temp mapping —
appleSleepingWristTemperature,bodyTemperature, or both, and how the backend labels them on ingest. - Nutrition write-back mapping (Android) — Health Connect nutrition records are quantitative while Health Debug is categorical; what (if anything beyond name/timestamp) is written back is undecided (Health Debug holds no nutrient quantities).
- Offline / queuing behavior for pushes (queue with original timestamps vs. reject vs. hybrid) is unresolved project-wide and interacts with server-enforced temporal rules.
- Ingest sync cadence (background sync interval, changes-token differential cadence) is a backend-negotiated setting, unconfirmed.
- Legacy weigh-in window 07:00–11:00 is a legacy precedent, not a spec constant — do not adopt without ratification.
- Ordering. X3 backend must precede all X3 clients. The Apple/Android sessions also depend on their suite scaffold + core-app sessions and on M1 hydration backend for the hydration write-back path (Android nutrition parity additionally leans on M4). Sequence X3 alongside the Apple/Android engine builds, not before them.
Handoff reminder
At session end, follow doc 13 §6: retain one session-handoff memory then memory/edit it to attach metadata (retain cannot set metadata). Use a unique source_ref: handoff/X3-sensors-biometrics/<surface>/<n> (never reuse — a duplicate supersedes the prior). Metadata: {project:"healthdebug", category:"session-handoff", era:"greenfield", status:"current", surface:"<surface>", module:"X3", title:"Handoff <session-id>", tags:[...], next:"<module+surface>"}. Fill the content with the exact SESSION HANDOFF structure (Module/Surface/Status, Summary, Files touched, Decisions, Deviations, Tests run, OPEN THREADS, NEXT SESSION). Then update the single build-ledger memory (source_ref: build-ledger, edited in place) so one recall shows the whole frontier and the recommended next session. Any new architectural decision or open question also gets its own classified memory and, if it changes the plan, an edit to the relevant plan doc.