| Direction | Types |
|---|---|
| Write | bodyMass, bodyMassIndex, bodyFatPercentage, leanBodyMass |
| Read (only what Coach / charts / algorithms use) | Weight + fat % for charts; heartRate, restingHeartRate, heartRateVariabilitySDNN, respiratoryRate, appleSleepingWristTemperature, oxygenSaturation, vo2Max, stepCount, activeEnergyBurned, appleExerciseTime, sleepAnalysis (stages when Watch writes them), distanceWalkingRunning, workouts |
Writes happen only on Confirm to Health or Manual β Save. Manual entries set HKMetadataKeyWasUserEntered.
Weigh any time (1.0.95): BLE auto-open is not limited to the first weigh of the day. A stabilized, plausible broadcast presents the live sheet at any hour. Home Weigh now / Weigh again calls beginScaleDetection() (scan + live sheet). Foreground re-arms Core Bluetooth. The morning π© drill still skips once a valid today sample exists β that gate is notifications only.
Monday morning card (2.8.0+ / 2.50.4): after a successful write, if local time is Monday 04:00-12:00, the app presents the weekly instructor card (progress + Sunday target + meals + Grok diagnostic). Cached per ISO week. Progress week-start kg = last Monday Health body mass (prefer morning 04:00β12:00; else any Monday sample; else last weigh before Tuesday); anchor date is always local Monday 00:00. Weekly % / Sunday target pace from that Monday kg. Preview anytime: Settings β Legal β Dev (ephemeral β does not reset live weekly progress).
In-app only (no HealthKit quantity): muscle mass, bone mass, water %, visceral index, raw ohms.
Skipped vanity reads (no coaching decision): flights climbed, stand hours, basal energy, etc.
Every Coach chat turn calls refreshFitnessDigestForCoach() before Grok sees the brief. That is separate from background fitness-monitor jobs.
Digest is a dated snapshot (Generated at) and includes:
Important: FATNAG reads HealthKit only. Apps like AllTrails or Strava appear only after they write workouts/distance into Apple Health.
Settings β Health β Grok monitoring shows Health status, Allow Health access, and Open Health. After upgrading, tap Allow again so iOS can grant new read types (schema bump). Ask Coach about sleep / recovery / HRV / last workout.
Documented in code (HealthScienceMath, TargetFeasibility, HealthChartMath) and summarized here. Coaching heuristics only β not clinical diagnosis.
| Algorithm | Formula / rule | Rationale |
|---|---|---|
| Safe weight loss/gain caps | Loss β 0.7% body weight/wk (midpoint of 0.5β1%), clamped 0.25β1.0 kg/wk; gain β 0.5%/wk capped 0.5 kg/wk | Common ACSM / obesity-medicine sustainable-loss ballpark |
| History projection | OLS on adaptive Health weight window; temper slope to safe caps; soft slowdown if elevated RHR, short sleep, low HRV vs median, or red recovery band | Sparse weigh-ins β OLS; tempering avoids unsafe βcrashβ rays on charts |
| Pre-sleep HR | Flag if avg HR in N min before sleep onset β₯ RHR+Ξ or β₯ absolute floor; borderline + depressed HRV vs 7d median also flags | Sleep-onset window + autonomic load; HRV relative check when present |
| Watch-wear | Movement (steps / workout / energy / walking-running km) with sparse HR samples β likely not worn | HR vs activity mismatch |
| Recovery / load band | Additive score from sleep duration, deep sleep, HRV vs median, RHR, recent workout load β green β₯70 / yellow 45β69 / red <45 / unknown if thin | Transparent coaching band; factors listed in digest |
revealProgress so the target date stays in frame. Fat chart has no projection.session.weeklyGoalSurface.weekStartKg, built by WeeklyGoalSurfaceEngine from persisted weeklyGoal after ensureWeeklyGoalBaseline() β MondayCardEngine.reconcileWeekStart (Health/history last-Monday rule above).All local (UNUserNotificationCenter). Toggles live in Settings. 2.9.0+ ships iOS 27-era chrome:
| Surface | Title / subtitle / body | Category actions | Thread | Interruption |
|---|---|---|---|---|
| Coach wake | Name + wake Β· clock/before Β· Coach line | Open Coach, Snooze 10 min | thescale.coach |
Time Sensitive |
| Coach reminder | Name + reminder Β· fire time Β· Coach line | Open Coach, Snooze | thescale.coach |
Active |
| Morning weigh drill | Keel Β· π© drill Β· 06:30 local fallback (or ASAP after Health wake); window closes 08:00; skips if already weighed today | Open Coach / Weigh | thescale.coach |
Time Sensitive |
| Bad trend | Name + scale check Β· kg Β· reason | Open History, Progress, Snooze | thescale.trend |
Active |
| Monday mini-goal | Name + weekly Β· goal title Β· nudge | Open Progress, Coach | thescale.trend |
Passive |
| Weigh miss ladder | Soft evening / day 2β3 | Open Weigh, Snooze | thescale.trend |
Active |
| Monday skip | Mon noon if still empty | Open Progress | thescale.trend |
Passive |
| Sunday wrap | Soft week wrap (β₯1 weigh) | Open Progress | thescale.trend |
Passive |
| Fitness interval | Name + check Β· interval Β· open hint | Open Coach | thescale.fitness |
Passive |
| Watch / pre-sleep HR | Name + signal Β· kind Β· message | Open Coach, Snooze | thescale.fitness |
Active |
| Dev sample | Name + sample Β· kg Β· QA line | Open Coach, Progress | thescale.sample |
Active |
Also: Communication-style Coach avatar when appropriate (INSendMessageIntent / UNNotificationAttributedMessageContext), PNG attachment visuals, relevanceScore, targetContentIdentifier, App Intents (OpenCoachIntent, OpenProgressIntent, OpenHistoryIntent). Foreground: wake gets banner+sound+list+badge; interval/weekly stay quieter (banner+list).
| Trigger | Gate | Copy |
|---|---|---|
| Bad trend (above ideal + rising, or sharp weekly gain) | Algorithmic + β₯3 distinct weigh days/7d + β₯3d between fires + daily budget | FM polish after schedule (optional suppress) |
| Monday mini-goal | Pref + Mon weigh still missing before noon (one-shot 08:15, not repeating) | Schedule first, FM polish optional |
| Morning weigh drill (2.50.0+) | Pref on + auth; before 08:00 local; skip if already weighed today; idempotent fallback (no re-add spam) | Default 06:30 local (migrates legacy 07:30); sleep-wake ASAP when Health has wake; max once/day; vulgar π© Keel copy |
| Weigh miss ladder (1.0.45+) | Pref morning drill; evening same-day after 08:00 empty; day 2β3 soft drift; β€1/day β€3/week; daily budget | Gentle, never Time Sensitive |
| Monday skip (1.0.45+) | Pref weekly; Mon β₯12:00 still empty; once/ISO week | Soft Progress |
| Sunday wrap (1.0.45+) | Pref weekly; Sun β₯18:00; β₯1 weigh this week; once/ISO week | Soft Progress |
| Global daily budget (1.0.45+) | β€2 non-morning banners/day (morning / Coach wake / spike exempt) | β |
| Coach-scheduled wake / reminder | Coach parse β schedule first | FM polish optional (never blocks) |
| Fitness (Watch wear / pre-sleep HR) | Algorithm + cooldown + daily budget | FM judgment + polish |
| Fitness interval nudge | Monitor interval | FM polish |
Rule: algorithms decide whether work exists and schedule local notifications; Foundation Models may refine wording afterward and may drop weak trend/fitness pings. Coach wake/timed reminders and morning weigh always schedule with algorithmic copy first. If Apple Intelligence is off or ineligible, algorithmic strings still fire (no silent drop regression).
Keel Β· π© drill, body Go drop a π© and use FATNAG after! (Keel/sergeant, Time Sensitive unchanged).-650g) + βYouβre a winnerβ on post-weigh hero, home, Progress, and History.Fallback schedule is idempotent: if a pending thescale.morning-weigh-fallback already targets the same local fire instant (Β±60s), consider() does not remove/re-add (stops DEBUG log spam on every digest/observer wake).
Date debug prints show UTC (+0000). For Asia/Hong_Kong, 06:30 local next morning appears as 22:30 UTC the prior calendar day. Triggers use Calendar.current date components + timeZone.
Rules:
Already weighed detection: any bodyMass sample dated today in recentHealthWeights (HealthKit refresh), historyWeights, or historyTrendWindowWeights.
Root cause of βno pings all dayβ: sleep-wake path required a live HealthKit wake inside a 3 min-2.5 h window and BG wakes skipped when only morning coaching was on. No calendar fallback existed.
Fix:
runBackgroundHealthWake runs when morning weigh is on (fetches sleep digest).thescale.morning-weigh-fallback always pending when the toggle is on (next eligible local morning clock).Verify:
morning fallback with tomorrow/today fire time.remind me in 2 minutes for timed reminder path.UNUserNotificationCenter.add first β optional FM polish of pending content.Coach reminder verify (quick):
remind me in 2 minutes.remind me at 8am (or remind me tomorrow morning at 8am). Confirm the locked local time in chat + Settings pending list.Dev sample SOTA ping: Settings β Legal β Dev β Fire sample SOTA notification. Expect ~1.5s later: titled sample with subtitle kg, visual, Coach chrome, actions.
UIBackgroundModes: healthkit, fetch, processing.com.apple.developer.healthkit.background-delivery.HealthKitBackgroundDelivery calls enableBackgroundDelivery + HKObserverQuery for workouts (immediate), sleep (immediate), body mass (immediate), steps/energy/HR/HRV/RHR (hourly β Appleβs floor for steps).runBackgroundHealthWake: refresh digest, evaluate Watch-wear / pre-sleep triggers, schedule local notifications (cooldowns apply), refresh bad-trend when weight samples change. Full Grok/FM coach reply only when forced or interval-due.BGAppRefreshTask (app.thescale.ios.fitness-check) + BGProcessingTask (app.thescale.ios.fitness-processing).Verify background Health wake:
main 2.9.0, Clean Build, Run on iPhone 15.HealthKitBackground.Medical disclaimer: onboarding + Settings β Legal only. Notification and Coach copy must not recite it.
main 2.7.4, Clean Build, Run on iPhone.how was my sleep? / how's my recovery?
ping me tomorrow morning before 7:30 to wake up.Parent overview: README. AI routing: coach-and-ai.md.