Go to file
Mike Kell 837b4434f0 feat(app,exercise,recipes,settings): complete Phase 7 navigation and domain expansion
- Exercise feature (lean-mass preservation): MuscleGroup enum + ExerciseEntry
  entity, IExerciseRepository contract with session-scoped in-memory
  implementation, ExerciseController with weekly distinct-session rollup vs.
  the recommended 3 sessions/week target, ExerciseScreen (weekly strength
  frequency card, day-grouped working-set log with swipe-to-delete) and the
  LogExerciseDialog (muscle group / name / sets / reps / weight).
- Recipe Vault: new features/recipes screen with pinned URL import bar wired
  to RecipeImportController, search field + macro filter chips (Protein >
  30g / > 20g / Under 500 kcal / All), responsive GridView.builder
  (SliverGridDelegateWithMaxCrossAxisExtent) with chunked infinite scroll,
  read-only details bottom sheet, and delete confirmation.
- Meal planner refactor: strip the screen to week navigation + the 7-day
  calendar (Mon-Sun) with daily protein rollups; replace the inline import
  bar, SavedRecipesTray, and AssignSlotDialog with the searchable
  AssignRecipeSheet modal bottom sheet (Recipe Vault search + custom-entry
  fallback); empty slots render a "+ Assign Recipe" affordance; drop the
  drag-and-drop tray plumbing.
- Settings: new features/settings screen with Identity (email, Supabase
  status badge, sign-out), Targets (daily protein/water inputs feeding the
  new NutritionController.updateTargets), Security & Storage (SQLCipher
  AES-256 badge with web-degradation note), and Integrations (Apple Health /
  Health Connect / HealthGateway toggles via IntegrationsController through
  the IHealthGateway boundary).
- Shell: register /exercise, /recipes, /settings routes; expand the desktop
  NavigationRail to the 7 specified destinations (medication_liquid,
  restaurant, fitness_center, calendar_month, menu_book, inventory_2,
  settings) with labelType.all inside a height-constrained scroll view;
  mobile keeps 4 core bottom-bar tabs plus a "More" tab opening the
  secondary-destinations drawer.
- Android toolchain fixes (first-ever Android build): enable core library
  desugaring + desugar_jdk_libs 2.1.4 for flutter_local_notifications, set
  kotlin.incremental=false (pub cache on C: vs project on D: breaks the
  incremental compiler), and raise minSdk to maxOf(flutter, 26) for the
  health plugin.
- Mobile overflow fix: LogInjectionDialog compound dropdown gains
  isExpanded + ellipsis so long labels ("Tirzepatide (Mounjaro /
  Zepbound)") no longer paint stripes at phone widths.
- Web drift assets: commit web/sqlite3.wasm (sqlite3-3.7.1 release, matching
  the locked package) and web/drift_worker.js (drift-2.35.2) so the WASM
  database opens at runtime; both were missing and every drift operation
  failed in Chrome.
- Recipe import hardening: browser-like Accept/Accept-Language/User-Agent
  headers on native (403 anti-bot bypass; UA stripped on web), optional
  --dart-define=RECIPE_PROXY_URL routing downloads through a server-side
  proxy, actionable CORS guidance in the web error path, and a deployable
  Supabase Edge Function relay (supabase/functions/recipe-proxy) with
  http(s)-only validation, SSRF host guard, 2 MiB cap, and 15 s timeout.
- Tests: 123 -> 135 (exercise controller/screen, log-injection dialog
  320dp/390dp overflow regressions, scraper header/proxy/error-path
  hardening, meal planner calendar-only rewrite); navigation integration
  tests now cover all 7 rail routes and the mobile More drawer.
- Docs: README web recipe-import (CORS proxy) section; BUILD_STATE.md
  Phase 7 deliverables, Post-Phase-7 additions, and checklist updated.

Verified: dart analyze lib test clean, flutter test 135/135,
flutter build windows --debug, flutter build apk --debug, and
flutter build web --no-tree-shake-icons all succeed.
2026-10-16 19:19:02 -04:00
.forgejo/workflows feat(platform,tests,ci): complete Phase 6 cross-platform hardening, integration tests, and Forgejo CI/CD 2026-10-16 14:59:15 -04:00
android feat(app,exercise,recipes,settings): complete Phase 7 navigation and domain expansion 2026-10-16 19:19:02 -04:00
docs feat(init): baseline multiplatform architecture with clean arch shell, clinerules, and docs 2026-10-15 21:01:52 -04:00
ios feat(init): baseline multiplatform architecture with clean arch shell, clinerules, and docs 2026-10-15 21:01:52 -04:00
lib feat(app,exercise,recipes,settings): complete Phase 7 navigation and domain expansion 2026-10-16 19:19:02 -04:00
macos feat(auth): complete Phase 1 passwordless identity and encrypted DB foundation 2026-10-16 05:01:54 -04:00
supabase/functions/recipe-proxy feat(app,exercise,recipes,settings): complete Phase 7 navigation and domain expansion 2026-10-16 19:19:02 -04:00
test feat(app,exercise,recipes,settings): complete Phase 7 navigation and domain expansion 2026-10-16 19:19:02 -04:00
web feat(app,exercise,recipes,settings): complete Phase 7 navigation and domain expansion 2026-10-16 19:19:02 -04:00
windows feat(auth): complete Phase 1 passwordless identity and encrypted DB foundation 2026-10-16 05:01:54 -04:00
.clinerules feat(medication): complete Phase 2 GLP-1 tracking, reminders, and dev bypass 2026-10-16 06:20:23 -04:00
.env.example feat(platform,tests,ci): complete Phase 6 cross-platform hardening, integration tests, and Forgejo CI/CD 2026-10-16 14:59:15 -04:00
.gitignore feat(platform,tests,ci): complete Phase 6 cross-platform hardening, integration tests, and Forgejo CI/CD 2026-10-16 14:59:15 -04:00
.metadata feat(init): baseline multiplatform architecture with clean arch shell, clinerules, and docs 2026-10-15 21:01:52 -04:00
BUILD_STATE.md feat(app,exercise,recipes,settings): complete Phase 7 navigation and domain expansion 2026-10-16 19:19:02 -04:00
README.md feat(app,exercise,recipes,settings): complete Phase 7 navigation and domain expansion 2026-10-16 19:19:02 -04:00
analysis_options.yaml feat(init): baseline multiplatform architecture with clean arch shell, clinerules, and docs 2026-10-15 21:01:52 -04:00
pubspec.lock feat(medication): complete Phase 2 GLP-1 tracking, reminders, and dev bypass 2026-10-16 06:20:23 -04:00
pubspec.yaml feat(medication): complete Phase 2 GLP-1 tracking, reminders, and dev bypass 2026-10-16 06:20:23 -04:00

README.md

GLP-1 Companion

Cross-platform GLP-1 medication, nutrition, and pantry tracking companion for people on Semaglutide (Ozempic/Wegovy), Tirzepatide (Mounjaro/Zepbound), and Liraglutide (Saxenda/Victoza) therapy.

Targets: Android · iOS · Web · macOS · Windows Stack: Flutter 3.x / Dart 3.x · Riverpod · GoRouter · Drift (SQLCipher) · Supabase Auth

Feature highlights

  • Pharmacokinetics & dose logistics — single-compartment serum-level modeling (C(t) = Σ D_i · 0.5^((t−t_i)/t_half)) with per-compound half-lives (Sema 168 h / Tirze 120 h / Lira 13 h), titration logging, a 12-quadrant injection-site rotation model, and offline dose reminders.
  • Lean-mass preservation — daily protein (100 g) and hydration (96 oz) targets with reactive progress meters and a weight-trend tracker.
  • Recipe scraping & meal planning — schema.org/JSON-LD recipe importer (@graph, HowToSection, ISO-8601 durations, OpenGraph fallback), weekly meal-plan calendar, and portion-aware macro rollups.
  • Pantry delta engine — canonical ingredient keys, dimension-aware unit conversions (mass/volume/count), and one-tap "Sync with Meal Plan" that generates, refreshes, and clears a shopping list while preserving manual rows.
  • Decoupled biometric ingestion — HealthGateway wraps Health Connect (Android) and HealthKit (iOS) behind a platform guard; Windows/macOS/Web degrade to manual entry served from the encrypted local cache.
  • High-privacy persistence — SQLCipher AES-256 encrypted drift database with an OS-vaulted master key; passwordless Supabase auth (magic links, OTP) plus Google/Apple/Microsoft OAuth.

Architecture

Feature-first Clean Architecture — every bounded context owns its domain, data, and presentation layers, and core/ never imports feature code:

lib/
├── app/                  # Bootstrap, GoRouter routes + auth guard, adaptive scaffold
├── core/                 # Shared foundation (no feature imports)
│   ├── database/         # Drift schema (SQLCipher) + conditional web/native connections
│   ├── error/            # Failure models
│   ├── network/          # Supabase availability probe
│   ├── platform/         # HealthGateway, notification service (OS adapters)
│   ├── security/         # CryptoManager (SQLCipher key vault)
│   ├── theme/            # Material 3 / Cupertino adaptive theming
│   └── utils/            # Date/time helpers, UUID generation
└── features/             # Modular bounded contexts
    ├── auth/             # Passwordless SSO, OAuth, debug sign-in bypass
    ├── health_sync/      # Biometric cache, trends, HealthGateway sync
    ├── meal_planner/     # JSON-LD scraper, recipe catalog, weekly calendar
    ├── medication/       # Doses, injection rotation, serum modeling
    ├── nutrition/        # Food/hydration logs, protein + hydration targets
    └── pantry/           # Inventory, shopping delta engine, unit conversions

Each feature follows the same internal layout:

<feature>/
├── domain/               # Pure Dart: entities, value objects, repository
│   ├── entities/         #   interfaces, use cases (ZERO framework imports)
│   ├── value_objects/
│   ├── repositories/
│   └── usecases/
├── data/                 # Drift-backed repository implementations
└── presentation/         # Riverpod notifiers, screens, adaptive widgets

Platform-awareness rules:

  • Presentation code never imports dart:io-bound packages; OS APIs sit behind core contracts (IHealthGateway, INotificationService) selected by conditional imports and kIsWeb / defaultTargetPlatform guards.
  • Web/desktop builds gracefully degrade (manual entry, no-op reminders) instead of crashing on missing platform channels.
  • Responsive layout: mobile (<600 dp) uses NavigationBar, desktop/web (≥1024 dp) uses a NavigationRail; Apple targets lean Cupertino.

Getting started

Prerequisites

  • Flutter SDK ≥ 3.16 (stable channel; Dart 3.x) — flutter doctor must be clean for the platforms you intend to run.
  • VS Code + Cline (or another MCP-capable agent): the repository ships .clinerules with the architecture contract and BUILD_STATE.md as the milestone ledger. Ask the agent to "read BUILD_STATE.md" first so it resumes at the correct phase.
  • A Supabase project (optional for local review): without it the app boots into the login screen; debug builds can tap Continue Without Signing In to explore with an in-memory session.

Environment configuration

pubspec.yaml bundles a .env asset, so the file must exist before flutter run/flutter build:

cp .env.example .env   # then fill in your project values
SUPABASE_URL=https://YOUR_PROJECT.supabase.co
SUPABASE_PUBLISHABLE_KEY=sb_publishable_...

Missing values are tolerated at runtime (the Supabase client stays uninitialized and the app falls back to the dev bypass), but the file itself must exist for asset bundling.

Run it

flutter pub get
dart run build_runner build --delete-conflicting-outputs   # regenerate drift code
flutter run -d windows        # or: chrome / macos / android / ios

Web recipe imports (CORS proxy)

Browsers cannot download recipe pages cross-origin — recipe sites never send Access-Control-Allow-Origin, so the Recipe Vault's URL import needs a server-side relay on web builds (desktop/mobile import directly and need nothing extra). The repository ships one as a Supabase Edge Function:

supabase functions deploy recipe-proxy

flutter run -d chrome \
  --dart-define=RECIPE_PROXY_URL=https://<project>.supabase.co/functions/v1/recipe-proxy

Without the define, in-browser imports surface an explanatory CORS error instead of a raw client exception.

Verify

flutter analyze --fatal-infos     # zero tolerance lint gate
flutter test --coverage           # unit + widget + integration suites
flutter build web --no-tree-shake-icons

Database encryption (SQLCipher AES-256)

All health markers (doses, weight, nutrition logs, pantry) live in a Drift database compiled as SQLCipher through the sqlite3 build-hook define in pubspec.yaml (hooks.user_defines.sqlite3.source: sqlcipher).

Key lifecycle (lib/core/security/crypto_manager.dart):

  1. A random 256-bit master key is generated on first launch with Random.secure().
  2. The key is vaulted in the OS secret store with platform-hardened options:
    • Android — Jetpack Crypto EncryptedSharedPreferences (encryptedSharedPreferences: true).
    • iOS / macOS — Keychain with KeychainAccessibility.first_unlock, so background services can open the database after a reboot.
    • Windows — DPAPI-sealed file (CryptProtectData) managed by flutter_secure_storage's FFI backend.
    • Linux — libsecret / Secret Service (GNOME Keyring).
  3. On every open, the key is applied via PRAGMA key before drift issues its first statement (lib/core/database/connection/native.dart).
  4. If the vault is unavailable (headless CI, locked-down hosts), the CryptoManager degrades to an ephemeral in-memory key instead of crashing — the database stays encrypted for the session and re-keys on the next launch.
  5. Web builds fall back to WASM SQLite (SQLCipher cannot run in a browser); treat the web target as a demo surface, not the privacy-grade target.

CI/CD (git.kellsupport.com/mtkell/glp-companion)

Forgejo Actions workflows live in .forgejo/workflows/. The ci.yml pipeline runs on every push to main and on all pull requests inside the ghcr.io/cirruslabs/flutter:stable container:

  1. actions/checkout@v4
  2. Mock .env with placeholder SUPABASE_URL / SUPABASE_PUBLISHABLE_KEY (the real file is git-ignored; the asset must exist for builds)
  3. flutter pub get
  4. dart run build_runner build --delete-conflicting-outputs (drift codegen)
  5. flutter analyze --fatal-infos
  6. flutter test --coverage
  7. Build sanity: flutter build bundle + flutter build web

Repository: https://git.kellsupport.com/mtkell/glp-companion

Testing strategy

  • test/features/** — domain unit tests (PK calculator, rotation policy, unit conversions, delta engine), data-layer tests over in-memory drift, and controller tests with fakes.
  • test/features/**/**_screen_test.dart, test/features/nutrition/ nutrition_widget_test.dart — widget tests exercising adaptive layouts and reactive progress meters.
  • test/integration/app_navigation_test.dart — shell-level navigation across all core routes under authenticated, dev-bypass, and unauthenticated conditions, on both rail and bar layouts.

License

Private project — all rights reserved.