Canonical source: docs/claude/app-redesign-data-contracts.md. This page is generated by docs/scripts/sync-handbook.mjs. Edit the source file in the repo; changes appear here on the next build.
App Redesign — Data Contracts & Backend Support
Companion to app-redesign-roadmap.md and app-redesign-wireframes.md. Captures the two backend-facing contracts the redesign UI assumes: (1) how a workout's assigned / logged / synced representations merge into one record, and (2) how every Atlas insight card in the mockups maps to backend detection so we know what to build. The Figma UI (fileKey 2Vs1e9IZVQh8KpMxc0W3PT) renders the result of these — this doc is the contract underneath.
1. Workout auto-merge / auto-complete contract
Problem: one real-world workout can be described by up to three separate inputs. The UI must show one unified summary, not three cards, and the plan must auto-complete without the user tapping "done".
The three inputs
| Input | Source | Carries |
|---|---|---|
| Assigned activity | Protocol / plan (clinician- or Atlas-assigned) | protocolActivityId, type, target params (e.g. 4×6 @ prescribed load), scheduled day/window |
| User-logged detail | In-app during the session (the Strong-style set rows) | actual sets · reps · weight · isometric hold durations · notes · RPE, per exercise |
| Wearable-synced workout | Whoop / Apple Health / Garmin via Terra + HealthKit | HR time-series, HR zones, calories, duration, GPS/route, cadence/power, start/end timestamps, source |
Merge rule (canonical record)
- Resolve the three to one canonical workout record per real session, keyed by
(userId, activityType-family, overlapping time window).- A synced "strength / functional" workout whose window overlaps the assigned strength session → linked.
- A synced "running/cycling/walking" workout with no assignment still produces a canonical record (standalone).
- Auto-complete: when a matching logged or synced workout is detected, mark the assigned protocol activity complete and link it to the canonical record. User can still manually override completion.
- The canonical record renders as the union: sets/reps/weight/notes (from log) + HR graph / zones / calories / route (from wearable) — exactly what the workout-summary screens show.
Precedence & provenance (per-field)
- User-logged is authoritative for strength detail (sets/reps/weight/notes/holds).
- Wearable is authoritative for biometrics (HR series, zones, calories, GPS, cadence/power).
- Duration: prefer the wearable's verified duration; fall back to logged.
- Store per-field provenance (which source supplied each value) so the summary can attribute ("via Whoop") and so dedup is auditable.
Dedup across wearables
- If two devices record the same session (e.g. Whoop and Apple Health both log the run), canonicalize to one — reuse the existing source-attribution / workout-canonicalization path (same machinery as sleep dedup). Only one source contributes each metric; the rest are suppressed, not double-counted.
Timing / re-entrancy
- Matching runs whenever the second representation arrives:
- Terra webhook ingest (biometrics land) → look for same-day assigned activity + in-app log in window → merge.
- In-app log finalize → look for an already-synced workout in window → merge.
- Late-arriving wearable data back-fills an already-logged session in place (don't create a second record).
- Webhooks fire in parallel and retry (see Terra rules in CLAUDE.md) → merge must be idempotent: per-user lock + upsert, never insert duplicates.
- Edit preservation: once the user has edited merged detail, flag those fields and protect them from later wearable re-sync overwrite.
Backend to build
- Canonical
workouts(activity-completion) record: assignment ref + logged-detail blob + biometric blob + per-field provenance. - Merge/matching service invoked from (a) Terra webhook ingest and (b) in-app log finalize; windowed match by
user + type-family + time. - Auto-complete write-back to the protocol activity (completion flag + link to canonical workout).
- Idempotent upsert under per-user lock (reuse Terra per-uid lock pattern); no duplicates on parallel/retried webhooks.
- Back-fill semantics (biometrics merge into existing logged record in place).
- Edit-preservation flags (protect user-edited fields from re-sync clobber).
- Extends existing Terra source-attribution / canonicalization — this is the workout analogue of the sleep canonicalization already shipped.
2. Atlas insight cards → backend support map
Every "helpful insight" in the redesign (e.g. "Your HRV dropped 14pts — take a rest day") is an output of the Atlas observation engine (scan → score → ground → card), which has 3 detector sources:
- Template — curated detectors we ship so every clinic has good defaults day one.
- Clinician-rule — provider-authored conditions (configured in basisflow-web; UI deferred, but the rule schema below is what backend must support).
- Atlas-autonomous — model-detected patterns, gated through a clinician review queue before the client sees them.
Every card is grounded against the client's real data + personal baseline before display, and ships with confidence + source + "why" (non-negotiable per the roadmap).
Insight inventory (from the mockups) → what backend must produce
| Insight (where it appears) | Source | Input signals | Trigger / computation | Baseline | Grounding | Output card + action | Provider config knob |
|---|---|---|---|---|---|---|---|
| "HRV dropped 14pts → take a rest day" (Today hero) | Template | nightly HRV, recent training load | HRV drop ≥ threshold vs rolling baseline | 60-day rolling HRV | HRV series + workout log | DiagnosticCard → adjust plan / log rest | on/off, Δ threshold, tone, action |
| "Skip strength Tuesday?" (Plan Cowork) | Clinician-rule + Atlas | multi-day HRV trend, upcoming schedule | N-day declining trend + high-strain session ahead | HRV baseline | HRV trend + protocol | Cowork card → Approve/Discuss/Decline → mutates protocol on approve, clinician notified | trend length, which activities, require-approval |
| "Deep sleep 20% below baseline" (Sleep) | Template | sleep stages, bedtime, meal-log timing | deep-sleep deficit; correlate late meal/bedtime | per-stage baseline | sleep data + meal log | DiagnosticCard → view sleep tips | deficit %, correlate-with-meals on/off |
| "14ms below personal baseline" (HRV detail) | Derived stat | HRV series | current vs rolling baseline delta | rolling percentile | HRV series | inline baseline callout (no action) | baseline window length |
| "ApoB trending up 4 panels → add fiber, recheck 8wk" (Lab / clinician note) | Clinician-rule + Template | lab panels over time | per-analyte slope across panels → needs-attention | prior panels | lab history | Biomarker card + clinician note → Discuss / order retest | which analytes, out-of-range bands, retest interval |
| "Negative split · Zone 2 economy +4%" (Running) | Atlas / Template | split paces, HR-vs-pace | split analysis + HR efficiency vs history | prior runs | GPS + HR | insight card (no action) | on/off |
| "Climb power held 12 min" (Cycling) | Template | power stream | sustained-power detection | — | power meter | insight card | on/off |
| "Zone 1 walk after dinner → glucose + HRV" (Walk) | Template | activity timing, meal log, next-day HRV | cross-domain correlation | HRV baseline | walk + meal + HRV | insight card | on/off |
| "Volume +8% · new PR" (Strength) | Template | training log history | volume aggregation + PR detection | prior sessions | in-app log | insight card + PR badge | on/off |
| "Recommended for your ApoB — Omega-3" (Shop) | Clinician-rule + Atlas | out-of-range biomarker | biomarker → intervention/product mapping | reference range | biomarker + catalog | product rec card → add to cart (Rx needs clinician approval) | mapping table, require-Rx-approval |
| Booking suggestion / protocol proposal (Chat) | Atlas-autonomous | schedule, recent data | follow-up cadence / drafted change | — | relevant data | inline action card → Book / Approve | review-queue gating |
| "Needs attention: ApoB" (Health hero) | Template | all biomarkers | needs-attention scoring, pick most-off-baseline | reference + personal | biomarker set | RangeBar card → Discuss | scoring weights |
Shared backend primitives (build once, all rules reuse)
- Signal store — normalized time-series across wearable / lab / in-app-log domains.
- Personal-baseline service — rolling baselines/percentiles per metric (HRV, sleep stages, RHR, pace, etc.). Many cards depend on this; it's the highest-leverage primitive.
- Detector framework — the observation engine's 3 lanes: template detectors, clinician rule engine (condition → copy → action → confidence gate), atlas-autonomous.
- Confidence scorer — magnitude × signal-count × data recency/quality → the "% confidence" badge.
- Grounding/citation — attach source refs to each card ("3 signals", "Whoop + sleep log") for the "why" affordance.
- Observation/card store + delivery — persists cards (observations collection) → dashboard card + push; dedup so the same insight doesn't repeat.
- Action framework — each card action maps to a handler:
adjust_plan,book,order,log,notify_clinician,mutate_protocol. (Chat inline-action-card handler already partly scoped in copilot work.) - Provider config — per-clinic template on/off + thresholds + tone + action set (the "knob" column above). This is the data model behind the (deferred) basisflow-web rule-builder.
- Review queue — clinician approves/edits Atlas-drafted insights before clients see them (clinician-side mirror of the Approve/Discuss/Decline card).
Clinician-rule schema (what the deferred basisflow-web builder writes)
Even without the builder UI, backend must accept rules of this shape so clinicians can seed/edit insights:
InsightRule {
clinicId
enabled: bool
name: string
when: { // condition
signal: 'hrv'|'deep_sleep'|'apob'|'training_load'|...
op: 'below_baseline'|'above'|'trend_down'|'slope_over_panels'|...
threshold: number
window: duration
}
ground_on: [signalRefs] // what to cite
say: { title, body, tone } // insight copy (or template ref)
action: 'adjust_plan'|'book'|'order'|'notify_clinician'|'mutate_protocol'|null
confidence_gate: number // suppress below this
require_clinician_approval: bool // route through review queue
}
Map each row in the inventory table to one InsightRule (or template) to enumerate the v1 detector set.
Open decisions (surface before backend build)
- Personal-baseline window lengths per metric (HRV 60d? sleep 30d?).
- Which insights are client-visible immediately vs must pass the clinician review queue.
- Biomarker→intervention mapping table ownership (clinical content) and Rx-approval flow.
- Confidence-score formula weights (magnitude vs signal count vs recency).