Identity: rename the application identity to com.companionglp.glpcompanion on every platform (Android applicationId/namespace + MainActivity package, iOS/macOS bundle IDs incl. RunnerTests, Windows CompanyName/copyright, web manifest id, Supabase OAuth redirect scheme).
Deep links: register the custom scheme com.companionglp.glpcompanion:// in iOS/macOS Info.plist (CFBundleURLTypes), the Android VIEW/BROWSABLE intent-filter, and Windows per-user HKCU protocol registration. Server-side steps (Supabase uri_allow-list, Entra optional email claim) are recorded in BUILD_STATE.md.
Windows runner: single-instance mutex (Local\GLPCompanionSingleInstance) so login callbacks are delivered deterministically; SendAppLinkToInstance forwarding with foreground/taskbar-flash fallback so protocol activation can never look like a hang; protocol diagnostics appended to TEMP glpcompanion-protocol.log.
macOS: add the missing com.apple.security.network.client entitlement so the sandboxed runner can reach Supabase at all.
Auth UX: surface deferred OAuth/magic-link handshake failures as snackbars on the login screen; make isAuthenticatedProvider reactive (watches authStateProvider) with a microtask-deferred router refresh so live sign-in and sign-out redirect correctly; add a debug package:logging listener so supabase_flutter diagnostics are visible under flutter run.
Field-verified on Windows: Google, Microsoft (Entra ID), and email magic-link sign-in round trips plus sign-out redirect to /login.
Implements architecture section 4.7: IHealthGateway now exposes the Phase 3B contract (HealthProviderType, HealthConnectionStatus, HealthDataPayload, checkStatus/requestAuthorization/fetchLatestBiometrics/disconnect) with defaults derived from the existing primitives. Drivers and test fakes switch from implements to extends so Dart inherits the default bodies.
- Mobile driver: BLOOD_GLUCOSE added to iOS/Android read sets (mmol/L to mg/dL normalization); checkStatus probes hasPermissions plus Health Connect SDK readiness; samsungHealth aliases the Health Connect binding on Android (federated Samsung/Pixel feeds); disconnect clears the cached SDK probe.
- Fallback driver: safe dummy states on Windows/Web/macOS (manualFallback connected, enterpriseFhir notConfigured, native unsupportedPlatform); fetchLatestBiometrics serves the encrypted drift cache; zero platform-channel calls.
- Settings: IntegrationsController rewritten from toggles to status orchestration (refreshStatuses, connect/disconnect round-trips, denial to permissionDenied plus snackbar); SettingsScreen gains a Health Integrations card with Apple Health, Health Connect (Samsung & Pixel), and SMART on FHIR rows showing live status badges and Connect/Disconnect buttons with desktop-safe disabled states.
- Manifests: Android adds READ_BLOOD_GLUCOSE, healthdata package query, ACTION_SHOW_PERMISSIONS_RATIONALE intent, and ViewPermissionUsageActivity alias; iOS adds NSHealthShareUsageDescription and NSHealthUpdateUsageDescription.
- Tests: +13 (fallback dummy-state matrix, integrations controller transitions, Settings desktop disabled-states and mobile connect/disconnect round trip); existing fake switched to extends; navigation test updated for the renamed card title.
Verified: build_runner 191 outputs; flutter analyze clean; flutter test 148/148; flutter build windows debug succeeded.
- Master asset assets/icon/app_icon.jpg (2048x2048) plus idempotent
assets/icon/generate_icons.py (Python/Pillow, Lanczos resampling) that
regenerates every platform icon size; preview_contact_sheet.png
documents the rendered variants.
- Web: favicon.png (32px) and icons/Icon-192/512.png gain a feathered
circular alpha mask (removes the master's white JPEG corners); new
Icon-maskable-192/512.png place the logo in the 80% safe circle over
brand sage #D4E4C7; manifest.json and index.html swapped from Flutter
placeholders to "GLP-1 Companion" naming with theme #375A53 /
background #D4E4C7.
- Android: legacy mipmap-*/ic_launcher.png (48-192px) with transparent
corners, plus first-ever adaptive icons: mipmap-anydpi-v26
ic_launcher{,_round}.xml, a sage vector background, and 5-density
foreground PNGs confining the logo to the 66/108dp safe circle.
- iOS: all 15 AppIcon.appiconset PNGs (20px-1024px) rendered opaque RGB
with no alpha channel per App Store validation.
- macOS: app_icon_{16..1024}.png with transparent corners.
- Windows: multi-resolution app_icon.ico (16/24/32/48/64/128/256px).
- Docs: BUILD_STATE.md Post-Phase-7 icon branding entry.
Verified: PIL assertions on dimensions, color mode, alpha, and ICO
frames; adaptive-icon XML well-formedness; flutter build web
--no-tree-shake-icons and flutter build apk --debug package the new
assets.
- 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.
Domain (pure Dart):
- Add PantryItem and ShoppingListItem entities plus the PantryCategory aisle
enum (produce/meat_seafood/dairy/dry_goods/spices/other) with a keyword
infer() heuristic and ExpirationStatus warning buckets (expired /
expiringSoon within 3 days)
- Add measurement_units value object (mass/volume/count dimensions with
lb/oz/g/kg and ml/l/tsp/tbsp/cup conversion onto a shared base scale) and
ingredient_name canonicalizer (lowercase, parenthetical stripping,
plural folding: eggs -> egg, lbs -> lb)
- Define IPantryRepository contract covering inventory and checklist CRUD
- Add CalculateShoppingDelta use case: aggregates the active 7-day meal-plan
slot window's recipe ingredient manifests by canonical name, computes
deficit = totalRequired - currentPantryQuantity in base units (dominant
recipe unit wins display, 2-decimal rounding), upserts isAutoGenerated
rows with stable ids and source recipe titles, clears stale auto rows once
stock covers the plan, and never modifies or removes manual rows
Data:
- Bump AppDatabase to schema v4 (migration for v1-v3 installs) with
PantryItemsTable (UUID PK, canonical name, quantity/unit, aisle key,
nullable expirationDate, lastUpdated) and ShoppingListItemsTable (UUID PK,
required quantity/unit, purchased/auto flags, JSON sourceNotes for recipe
titles) plus watch/get/upsert/delete helpers
- Implement DriftPantryRepository (record <-> entity mapping, enum keys,
JSON recipe-title round-trip)
Presentation:
- Add PantryInventoryController (live watch stream, quick +/- stock steps
clamped at zero, aisle-grouping vs expiration-sort modes) and
ShoppingListController (manual add, All/To Buy/Checked filter, check-off
with "Add to Pantry?" unit-converting stock merge or aisle-inferred row
creation, syncWithMealPlan reading the planner's Monday-Sunday window and
recipe catalog)
- Refactor PantryScreen: Inventory/Shopping tab header with remaining-count
badge, Aisles/Expiration segmented control, categorized tiles with +/-
counters and expired/expiring badges, swipe-to-delete on both lists,
prominent "Sync with Meal Plan" banner with run summary + snackbar,
"From: <recipe>" origin chips on auto rows, checklist filters, check-off
-> pantry prompt, and Add Pantry Item / Add Shopping Item dialogs
Testing:
- 10 new tests in delta_calculator_test.dart: 300 g on hand vs 800 g
requirement -> +500 g entry, sufficient stock -> no entry, manual rows
preserved across repeated syncs, stale auto cleanup, cross-recipe
plural/unit normalization (4 eggs + 2 egg - 2 -> +4 egg), lb/oz scale
unification (0.75 lb), check-off -> pantry transfer (merge +
aisle-inferred create), end-to-end syncWithMealPlan, and inventory
canonicalization / quick-adjust clamp; full suite green (99/99)
- flutter analyze clean (No issues found!) after codegen; BUILD_STATE.md:
mark Phase 5 complete and record all verification gates
Domain (pure Dart):
- Add Recipe, MacroProfile, and RecipeIngredient entities (quantity/unit/
item parsing: decimals, ASCII/unicode/mixed fractions, known-unit
vocabulary) plus MealPlanSlot reusing the nutrition MealType enum
- Define IRecipeRepository and IMealPlanRepository contracts
- Add ScrapeRecipeFromUrl use case: package:http download with injectable
fetcher, package:html DOM parse, JSON-LD @graph/root/array/list-@type
navigation, HowToStep/HowToSection flattening, ISO-8601 prep/cook times,
recipeYield, regex nutrition parsing, OpenGraph fallback, and typed
RecipeScrapeException
- Add core/utils/uuid.dart RFC-4122 v4 generator (no new dependency)
Data:
- Bump AppDatabase to schema v3 (migration for v1/v2 installs) with
RecipesTable (UUID PK, JSON ingredient/instruction columns, macros) and
MealPlanSlotsTable (UUID PK, nullable recipeId FK ON DELETE SET NULL)
- Implement DriftRecipeRepository (URL-deduplicated importRecipe, createdAt
preserved on re-import) and DriftMealPlanRepository (half-open [from, to)
window, upsert by id)
Presentation:
- Add RecipeImportController (URL submit -> loading -> preview modal state,
live catalog watch stream, confirm-save/dismiss/delete) and
MealPlanCalendarController (Monday-Sunday window navigation, slot
assign/upsert/clear, prepared toggles, per-day protein rollup against the
100 g target, deleted-recipe slot normalization)
- Rework MealPlannerScreen: URL import bar with spinner, RecipePreviewDialog
(macros, ingredients, steps, save with error surface), draggable
SavedRecipesTray with protein badges, responsive WeeklyCalendarView
(>=900 dp: 7-column grid, below: tabbed pager) with DragTarget slots and
daily protein indicators, AssignSlotDialog quick-assign modal
Fixes:
- Eliminate desktop resize overflow (RenderFlex hatched stripes): wide-mode
day columns scroll vertically, and a LayoutBuilder collapses the page
into a single scrollable column with a fixed-height calendar when the
window body is shorter than ~520 dp
Testing:
- 29 new tests: recipe scraper (JSON-LD variants, macro/ingredient/
instruction/duration parsing, OG fallback, execute error mapping),
meal-plan repositories (window filter, upsert, dedup), calendar
controller (rollup, navigation, prepared/custom entries), and screen
widget tests pumping short-compact/short-wide/tall/extremely-short
window sizes to prove no overflow; full suite green (89/89)
- BUILD_STATE.md: mark Phase 4 complete and record all verification gates
Domain (pure Dart):
- Add 12-quadrant InjectionSite anatomical model with a least-recently-used
rotation policy, DoseEntry entity, and Glp1Compound value object
(Semaglutide 168h, Tirzepatide 120h, Liraglutide 13h)
- Add CalculateSerumLevel use case implementing
C(t) = sum(D_i * 0.5^((t - t_i) / t_half))
- Define IMedicationRepository contract; retire superseded MedicationDose
Data:
- Add injection-history CRUD to AppDatabase (watch/get/insert/update/delete/
latest-administered), ordered newest first
- Implement drift-backed MedicationRepository with record-to-entity mapping
Presentation and local alerts:
- Add MedicationController (StateNotifier) managing serum estimation, site
rotation suggestions, and best-effort reminder scheduling
- Rework MedicationScreen: serum card, suggested-site card, interactive log
dialog (compound, dosage, timestamp, quadrant), history with delete, and a
60s decay ticker
- Add INotificationService / PlatformNotificationService wrapping
flutter_local_notifications (one-shot scheduling on Android/iOS/macOS,
graceful no-op on Web/Windows/Linux), Android boot receivers for reminder
restoration, and the timezone dependency
Development tooling:
- Add debug-only "Continue Without Signing In" bypass on the login screen,
backed by an in-memory devAuthBypassProvider; the router guard honors the
flag and the shell shows a dev-mode banner while active
Fixes:
- Resolve the Supabase client lazily in SupabaseAuthService so widget tests
and pre-initialization builds no longer throw
Testing:
- 36 new tests covering PK decay math, rotation determinism, repository CRUD
on an in-memory drift database, controller behavior with fakes, and the
end-to-end dev bypass flow; full suite green (37/37)
- BUILD_STATE.md: mark Phase 2 complete and record all verification gates