الصفحات · Health Debug
12 — Legacy Reference Map (E:\Sites\healthdebug)
Status: REFERENCE ONLY. The greenfield build carries no code forward from this tree (per
00-MASTER-PLAN.md). Fady has cleared referencing anything that matches the plan from the old code. This document maps where things live so rebuild work can consult concrete precedents instead of re-deriving them.Legacy root:
E:\Sites\healthdebug· Go 1.26.4 / togo v0.21.0 · modulegithub.com/fadymondy/healthdebug· live at health.fadymondy.com. The.ref/subfolder holds 4 further reference checkouts (togo framework, the pre-rebuild Apple-only app, sibling projects dr/rtx).
1. Top-level layout
| Path | What it is |
|---|---|
cmd/ | Entrypoints: cmd/api/main.go (serve + OpenAPI export), cmd/migrate, cmd/seed |
internal/ | Go backend (see §2) |
db/atlas/migrations/ | 13 hand-written Atlas migrations + atlas.sum |
api/ | openapi.yaml / openapi.json — the committed contract 3 clients generate from |
web/ | Vite 6 + React 19 + TanStack Router/Query SPA |
ios/ | SwiftUI: iOS + watchOS + macOS + WidgetKit + CarPlay |
android/ | Kotlin/Compose: app/, core-api/ (generated client), wear/ |
desktop/ | Electron tray app (loads the live web app) |
extension/ | Chrome MV3 extension |
docs/ | BUSINESS.md, architecture.md, sync.md, deployment.md (see §7) |
scripts/ | Codegen pipeline (gen-rest.mjs, gen-graphql.mjs, pg-sync.mjs, check-i18n.mjs), Windows ops, backup |
togo.resources.yaml | 24.6 KB resource manifest (20 resources) driving schema/query generation |
No CI workflows exist (.github/ has only templates).
2. Backend — where the old engines and rules live
internal/protocols/ — the engines as pure functions (no I/O, injected clock). internal/protocols/*_test.go is the executable protocol specification — the new build's engine plugins should be validated against those cases first.
Exact legacy constants (valuable precedents for the greenfield spec's open questions):
| Engine (legacy file) | Constants as implemented |
|---|---|
Hydration (hydration.go) | GlassMl=250, MaxDailyMl=5000, LogCooldown=30s; default goal 2500ml; expected intake paced linearly across the work window (not the whole day); statuses on_track / slightly_behind (deficit≤500) / dehydrated / goal_reached |
Caffeine (caffeine.go) | CaffeineBlockMinutes=90; wake inferred = work_start − 1h (never asked); MaxDailyCaffeineLogs=8, CaffeineLogCooldown=1min; sugar-based = red bull/monster/energy drink/soda/cola; clean = cold brew/matcha/green tea/espresso/black coffee/americano; unknown kinds treated as clean |
GERD (shutdown.go) | window = shutdown_window_hours before target sleep, default 4h; allowed = water/chamomile/anise; high-risk keywords list (spicy, fried, dairy, chocolate, citrus, tomato, coffee, soda, alcohol, mint); past sleep time rolls to tomorrow |
Pomodoro (pomodoro.go) | work 25min / short break 5min / long break 15min / long break every 4 cycles / daily target 8 — a direct legacy precedent for the greenfield's open "interval lengths" question |
Trigger families (nutrition.go) | ibs_gerd: whole eggs, falafel, deep fried, raw onion, raw garlic, cheddar, yellow cheese; gout: red meat, liver, duck, beans, lentils, legumes; fatty_liver: refined sugar, honey, nutella, jam, white flour, mixed carbs; safe lists for proteins/carbs/fats; lowercased substring matching (errs toward flagging) — seed data for the greenfield food catalog |
Medications (medications.go) | GraceMinutes=60; schedules fixed_times / interval / as_needed; overdue is advisory only; skipped counts as kept; adherence with 0 decided = 100% |
Cycle (cycle.go) | MinCyclesForPrediction=3, IrregularThresholdDays=9 (refuse to predict); averages last 6 cycles; ovulation = next start −14d, fertile window −5/+1d; BBT confirmation = 3 readings ≥ +0.2°C over prior 6-day mean; Naegele +280d for pregnancy; SafetyProvider interface with NoSafetyData{} default — no drug database wired up, by design |
Notices (notify.go) | caffeine-window-opened alert 20min, shutdown warning 30min; weigh-in only 07:00–11:00; one notice per kind per user per local calendar day |
REST surface (internal/rest/, Huma v2): GET /api/today returns the whole computed day (TodayState); POST /api/today/{water|caffeine|meal|pomodoro} (429 on cooldown/cap); GET|PATCH /api/profile, /api/sleep_config; idempotent partial PUT /api/summaries/{date} rollups; per-user CRUD collections (water_logs, meal_logs, caffeine_logs, pomodoro_sessions, notification_items, health_samples, devices, medications + nested schedules, medication_intakes, cycles, cycle_days, pregnancies); shared reference data (/api/drinks, /api/food_items + /classify, /api/site); POST /api/ai/{insight|chat}; /api/app/version update gate; admin surface; SSE at GET /api/events.
Auth: togo auth plugin — HttpOnly cookie + CSRF for web, personal access tokens for native (POST /api/auth/tokens, plaintext once, per-device revoke); deny-by-default guard (internal/httpx/guard.go) with a short method-scoped allowlist; owner only ever from the session identity; foreign rows 404 not 403; isolation_test.go proves it over real HTTP.
Database conventions (kept in the greenfield spec): every column NOT NULL with a default. Legacy-specific choices to re-evaluate: text ids (not uuid), absent dates as empty text, no FK to the auth-owned users table, triggers as CSV text. Notable defaults in profiles (46 cols): work 9→19, water goal 2500, timezone UTC, locale en; daily_summaries unique (user_id, date); pregnancies partial unique one-ongoing-per-user; drug_safety_facts table exists with zero implementations (the deliberate empty seam).
3. Clients — state and precedents
| Surface | State | Notable for the rebuild |
|---|---|---|
Web (web/) | Full SPA | TanStack Query as the only state layer + SSE-driven invalidation; design tokens in web/src/app.css (light #016b93/#269761, dark bg 200 45% 7%, defaults to dark); hand-rolled i18n (~315 EN→AR pairs, English strings as keys); RTL via dir + Tailwind logical properties; IBM Plex Sans + Plex Sans Arabic self-hosted; drag-reorderable Today cards (8 card ids) |
iOS (ios/) | Full app, 3 tabs, 9.8k lines | Liability, do not repeat: a complete second protocol implementation in HealthDebugKit/Health/ (iOS never calls /api/today), plus a second identity system (Firebase remnants). Riches to mine: 11 local notification schedulers, App Intents/Siri, FoodRegistry.swift (origin of the Go trigger lists), HealthKit manager reading steps/energy/HR/weight/body-fat/sleep (Zepp scale arrives through Apple Health) |
| WidgetKit | Most complete non-phone surface | 11 home + 6 lock-screen widgets, interactive actions, App Group data store |
| watchOS | Working standalone app (463 lines) | 6 screens incl. AI score; no complications |
Android (android/) | Full phone app, 7.3k lines | Generated API client from openapi.json; Health Connect with point-of-use permission groups (16 permissions, hydration/nutrition write-back); update gate; 225/225 en/ar string parity; no notification implementation at all (only an unread badge) — confirms Divergence #3 |
| Wear OS | Shell only (136 lines) | Token push from phone doesn't exist |
Electron (desktop/) | Working scaffold (424 lines) | Loads live web app; tray, global shortcut Ctrl/Cmd+Shift+W, idle detection, PAT in safeStorage, electron-updater |
Chrome ext (extension/) | Working MV3 (428 lines) | Minimal permissions (storage/alarms/notifications + one host, no content scripts); chrome.alarms for everything; token in chrome.storage.session; badge = hydration % / focus minutes / red ! on violation; _locales/{en,ar} |
4. Cross-cutting precedents
- Notifications: pure rules (
notify.go) → in-process 15-min sweeper (internal/notifier/) →notification_itemsrows withvarsJSON beside the sentence (never interpolated — Arabic reorders). Routing (internal/realtime/route.go): live SSE watcher suppresses push; else most-recent device (12h window) → primary → none, every decision carrying aReason. Delivery never shipped — no FCM/APNs tokens registered. - Realtime: per-user SSE hub replacing togo's broker (which leaked events across users). Events carry resource names, no state — clients refetch.
- AI: server-side insight/chat (
internal/ai/), domains validated not trusted,DayContextflattened struct (only token-worthy fields), system prompt forbids diagnosis/doses/inferring absent numbers, 2–3 sentence cap, honest local fallback with no key. iOS additionally ran its own on-device AI stack — another duplication to not repeat. - i18n: three independent hand-maintained systems (web dict / Android XML / iOS String Catalog) + extension locales; backend emits English source strings + vars. The greenfield's single ICU-based pipeline replaces all of this.
- Gamification: none exists in the legacy tree (exhaustively searched) — greenfield future-state starts from zero.
5. What the legacy answers for the greenfield's open questions
- Pomodoro intervals — legacy precedent: 25/5/15, long break every 4, daily target 8 (open question in
03-protocol-engines.md; needs ratification, plus the kinetic wearable-validation the legacy never had). - Trigger-family catalog seed — the legacy lists in
nutrition.go/FoodRegistry.swiftplus thefood_items+drinkstables (withname_ar) are a ready seed dataset with admin CRUD precedent. - Hydration window — legacy paces the goal across the work window, not the calendar day, and caps at 5000ml/day; informs the day-boundary/anchor open question.
- Wake anchor — legacy inferred wake = work_start − 1h rather than asking; a candidate answer for the Caffeine Block anchor question.
- Offline/no-wearable pomodoro — legacy kept countdowns on-device and only counted completed work sessions; greenfield's kinetic validation is new territory.
- Push transport — legacy built routing but never registered FCM/APNs; the greenfield notification plan must include actual delivery from day one.
- Update gate —
app_releases+ publicGET /api/app/version(server-owned, unauthenticated by necessity) is a proven pattern worth carrying into the plan.
6. Liabilities the greenfield explicitly avoids
- Duplicated protocol logic on iOS (full second implementation; iOS never called the API) — the #1 correctness liability; the greenfield thin-client rule exists because of this.
- Two identity systems on iOS (Firebase + server PATs).
- Design tokens duplicated by hand in three files (
app.css,Theme.kt,Theme.swift) with no shared source — greenfield uses a single token source (PENDING DESIGN). - Three hand-maintained i18n systems — replaced by one ICU pipeline.
- Shells shipped as features (Wear OS without token transport; push routing without delivery) — greenfield phase exit criteria require end-to-end function.
7. Keys & configuration
Credentials, service configs, and environment values extracted from the legacy tree are stored in the cabrain secrets vault (healthdebug brain) rather than in this document. See the memory entries under source_ref greenfield-spec-2026-09-01-* for the inventory.