Google Fit REST API was closed to new sign-ups on 2024-05-01 and shuts down
end of 2026, surfacing as "Access blocked: this app's request is invalid"
when starting the OAuth flow.
- Swap the 10 fitness.* OAuth scopes for the 3 googlehealth.*.readonly
scopes (activity_and_fitness, health_metrics_and_measurements, sleep).
- Replace fitness/v1 dataset:aggregate + sessions calls with
health.googleapis.com/v4/users/me/dataTypes/{steps,total-calories,
heart-rate,sleep}/dataPoints, filtered to today's window.
- Read the v4 DataPoint union defensively (the per-type schema is sparsely
documented) and log the first raw sample at debug so we can refine field
paths after the first real OAuth.
- Output Signal contract is unchanged — agents and downstream consumers
see the same steps/activity/heart_rate/sleep signals.
Cloud Console still needs: enable Google Health API, add the 3 scopes to
the consent screen, add test user (all googlehealth scopes are Restricted).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
services/
Backend modules. Each owns a contract and ships its own README.md. In Phase 0 these are internal packages inside a single Node process (ADR-0003); they extract to their own processes as pressure justifies.
| Dir | Role | Phase-0 shape | Extracts when |
|---|---|---|---|
gateway/ |
BFF for clients; auth check; fan-out | in-proc router | never (stays as the edge) |
auth/ |
Google OAuth (Apple in M1), sessions, JWT | Auth.js behind OIDC shape | mobile native ships (M3) |
profile/ |
user profile, preferences, consents | in-proc module | team ownership diverges |
integrations/ |
connectors + encrypted token vault | in-proc module | credential blast-radius isolation |
recommender/ |
POST /recommend — policy-driven tip selection |
in-proc; calls ml/serving from M1 |
scaling hotspot |
events/ |
event bus + signal log | in-proc emitter; bridges to NATS JetStream when NATS_URL set (ADR-0010) |
always a library + broker, not a service |
notifier/ |
push/email delivery + quiet hours | in-proc; web push in M1 | SLA divergence or mobile push scale |
Contracts that cross module lines (HTTP or events) come from packages/shared-types/. In-module imports across modules are forbidden by import lint.