60 lines
11 KiB
Markdown
60 lines
11 KiB
Markdown
# Build Execution Ledger
|
||
|
||
## Active Target
|
||
- **Current Milestone:** Phase 5: Pantry Delta Engine & Auto-Shopping Generator — COMPLETE
|
||
- **Status:** Phase 5 Complete (next: Phase 6)
|
||
|
||
## Phase Checklist
|
||
- [x] Phase 1: Foundation & Identity (Passwordless SSO & Encrypted Drift DB)
|
||
- [x] Phase 2: Medication Domain, Injection Rotation, Serum Modeling
|
||
- [x] Phase 3: Nutrition, Protein Target Tracking, HealthGateway Sync
|
||
- [x] Phase 4: Recipe Web Scraper Engine & Meal Planning Calendar
|
||
- [x] Phase 5: Pantry Delta Engine & Auto-Shopping Generator
|
||
- [ ] Phase 6: Hardening, Native Desktop/Web Polish, CI/CD
|
||
|
||
## Phase 2 Deliverables (2026-10-16)
|
||
- [x] Domain: `DoseEntry` entity, 12-quadrant `InjectionSite` rotation model, `Glp1Compound` value object (Sema 168h / Tirze 120h / Lira 13h), `IMedicationRepository` contract.
|
||
- [x] Domain: `CalculateSerumLevel` use case implementing `C(t) = Σ D_i · 0.5^((t − t_i)/t_half)`.
|
||
- [x] Data: injection-history CRUD on `AppDatabase` + drift-backed `MedicationRepository` (record ↔ entity mapping).
|
||
- [x] Presentation: `MedicationController` (Riverpod StateNotifier) with serum estimation, least-recently-used site rotation, and reminder scheduling; refactored `MedicationScreen` (serum card, rotation suggestion, interactive log dialog, history with delete).
|
||
- [x] Platform: `INotificationService` / `PlatformNotificationService` (flutter_local_notifications 17.x; Android/iOS/macOS scheduled reminders, graceful no-op on Web/Windows/Linux); Android boot-receiver manifest entries; `timezone` dependency added.
|
||
- [x] Regression fix: `SupabaseAuthService` now resolves its client lazily (baseline widget test no longer throws when Supabase is uninitialized).
|
||
- [x] Tests: 33 new unit/widget-fake tests (PK calculator, rotation policy, repository w/ in-memory drift, controller); baseline smoke test green.
|
||
|
||
## Post-Phase-2 Additions
|
||
- [x] Development auth bypass (2026-10-16): debug-only "Continue Without Signing In" button on the login screen (`kDebugMode`-gated, in-memory `devAuthBypassProvider`) lets reviewers enter the app without a Supabase session; the router guard honors the flag and the shell shows an orange "Development mode - not signed in" banner while active. Covered by `test/features/auth/dev_auth_bypass_test.dart` (3 tests).
|
||
|
||
## Phase 3 Deliverables (2026-10-16)
|
||
- [x] Domain: `FoodLogEntry` (+ `MealType` enum), `HydrationLogEntry`, `BiometricEntry` (+ `BiometricSource` enum), `INutritionRepository`, `IHealthSyncRepository` — all pure Dart.
|
||
- [x] Data: `AppDatabase` schema v2 (+ migration for v1 installs) with `FoodLogsTable`, `HydrationLogsTable`, `BiometricLogsTable`, range-filtered watch/get/CRUD helpers, `clearSyncedRange` for idempotent re-syncs; drift-backed `NutritionRepository` and `HealthSyncRepository` (record ↔ entity mapping).
|
||
- [x] Platform: `HealthGateway` rewrite — `IHealthGateway` + conditional-import factory; mobile impl (`health_gateway_mobile.dart`) wraps `package:health` for `WEIGHT` (kg→lb normalization) and `ACTIVE_ENERGY_BURNED` (joule→kcal) with double-guarded try/catch; fallback impl (`health_gateway_fallback.dart`) for Web/Windows/macOS never touches platform channels (reads served from the drift cache, writes via manual entry).
|
||
- [x] Presentation: `NutritionController` (Riverpod `AsyncNotifier`; today's logs, 100 g protein / 96 oz water targets, meal grouping, quick-add water) and `BiometricController` (weight history, 7/30-day deltas, manual entry, native sync with per-day active-energy aggregation); `LogFoodDialog` and `LogWeightDialog`.
|
||
- [x] Presentation: `NutritionScreen` upgraded — interactive protein/hydration progress meters, +8/+16/+24 oz quick-add buttons, meal-grouped food diary with delete, weight tracker card (latest weight, 7/30-day trend chips, latest active burn) whose "Log Weight / Sync" button runs `HealthGateway` sync on Android/iOS and opens the manual dialog on Windows/Web; status snackbars via `ref.listen`.
|
||
- [x] Tests: 23 new tests — nutrition repository (in-memory drift: CRUD, day-range filter, watch, `MealType` round-trip), nutrition controller (targets, grouping, validation, quick-add, delete, failure retention), biometric controller (manual entry, deltas, fallback short-circuit, permission denial, weight+energy sync idempotency).
|
||
- [x] Regression fix: none required — existing 37 tests untouched and green.
|
||
|
||
## Phase 4 Deliverables (2026-10-16)
|
||
- [x] Domain: `Recipe` (+ `MacroProfile`), `RecipeIngredient` (quantity/unit/item parsing: decimals, ASCII + unicode + mixed fractions, known-unit vocabulary), `MealPlanSlot` (reuses nutrition `MealType`), `IRecipeRepository`, `IMealPlanRepository` — all pure Dart; `core/utils/uuid.dart` v4 generator (no new dependency).
|
||
- [x] Domain: `ScrapeRecipeFromUrl` use case — `package:http` download (injectable `RecipeHtmlFetcher` for tests), `package:html` DOM parse, JSON-LD `@graph`/root/array/`@type`-list navigation, `HowToStep`/`HowToSection` instruction flattening, ISO-8601 `prepTime`/`cookTime`, `recipeYield`, regex nutrition parsing (`calories`, `proteinContent`, `carbohydrateContent`, `fatContent`), image list/object unwrap, OpenGraph fallback (`og:title`/`og:image`/`og:description`), typed `RecipeScrapeException`.
|
||
- [x] Data: `AppDatabase` schema v3 (migration for v1/v2 installs) with `RecipesTable` (UUID PK, JSON ingredient/instruction columns, macros, `createdAt`) and `MealPlanSlotsTable` (UUID PK, plannedDate, mealType, nullable `recipeId` FK with `ON DELETE SET NULL`, customNotes, isCompleted); drift-backed `DriftRecipeRepository` (URL-deduplicated `importRecipe`, `createdAt` preserved on re-import) and `DriftMealPlanRepository` (half-open `[from, to)` window, upsert-by-id).
|
||
- [x] Presentation: `RecipeImportController` (Riverpod `Notifier`; URL submit → loading → preview modal state, live catalog watch stream, confirm-save/dismiss/delete) and `MealPlanCalendarController` (Monday–Sunday window, next/previous/today navigation, slot assign/upsert/clear, prepared toggle, per-day protein rollup against the nutrition 100 g target, deleted-recipe slot normalization, stale-window refresh guard).
|
||
- [x] Presentation: `MealPlannerScreen` refactor — URL import bar with spinner, `RecipePreviewDialog` (image, meta chips, macro tiles, ingredients, steps, save with error surface), `SavedRecipesTray` (draggable cards with thumbnail + protein badge + quick-assign), `WeeklyCalendarView` (≥900 dp: 7-column grid; below: tabbed pager) with `DragTarget` meal slots, prepared checkboxes, clear menu, per-day protein indicator (progress bar vs. target), `AssignSlotDialog` (saved-recipe dropdown or custom description), week navigation header.
|
||
- [x] Tests: 25 new tests — `recipe_scraper_test.dart` (JSON-LD `@graph`/root/array/list-`@type` extraction, macro/ingredient/instruction/duration/yield parsing, malformed-JSON fallthrough, OpenGraph fallback, `execute()` URL validation & error mapping) and `meal_plan_repository_test.dart` (`startOfWeek`, recipe CRUD/URL-dedup/watch, slot window filter/upsert/delete, calendar rollup/assignment/navigation/custom entries/prepared toggles/recipe-deletion normalization, import-controller empty-URL guard).
|
||
- [x] Regression fix: HowToSection flattening now recurses into `itemListElement` before using the section `name` (parser ordering bug caught by the new test).
|
||
|
||
## Post-Phase-4 Additions
|
||
- [x] Desktop resize overflow fix (2026-10-16): the hatched yellow/black stripes (RenderFlex overflow) shown when shrinking the Windows window are gone — wide-mode day columns now scroll vertically (matching the compact pager), and a `LayoutBuilder` in `MealPlannerScreen` collapses the page into a single scrollable column (with a fixed-height calendar) whenever the body is shorter than ~520 dp. Covered by `test/features/meal_planner/meal_planner_screen_test.dart` (4 widget tests pumping the screen at short-compact, short-wide, tall, and extremely-short window sizes; overflow errors fail widget tests, so green proves no stripes).
|
||
|
||
## Phase 5 Deliverables (2026-10-16)
|
||
- [x] Domain: `PantryItem` (+ `PantryCategory` aisle enum with `infer()` keyword heuristic, `ExpirationStatus` warning buckets), `ShoppingListItem`, `IPantryRepository`, and two pure-Dart value objects — `ingredient_name.dart` (lowercase, paren-stripped, plural-folded canonical keys: `eggs` → `egg`) and `measurement_units.dart` (mass/volume/count dimensions with lb↔oz↔g↔kg and ml/l/tsp/tbsp/cup conversion).
|
||
- [x] Domain: `CalculateShoppingDelta` use case — aggregates the active 7-day `MealPlanSlot` window's `Recipe` ingredient manifests by canonical name, applies `deficit = totalRequired − currentPantryQuantity` in dimension base units (dominant recipe unit wins display, 2-decimal rounding), upserts `isAutoGenerated` rows with `sourceRecipeTitles` (stable ids, checked-state kept only while the requirement is unchanged), clears stale auto rows (satisfied by stock or superseded by a manual row), and never modifies or removes manual rows.
|
||
- [x] Data: `AppDatabase` 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); drift-backed `DriftPantryRepository` (watch/get/upsert/delete for both lists, enum/JSON mapping).
|
||
- [x] Presentation: `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?" prompt performing a unit-converting stock merge or aisle-inferred row creation, `syncWithMealPlan` reading the planner's Monday–Sunday window + recipe catalog).
|
||
- [x] Presentation: `PantryScreen` refactor — 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, auto-row "From: <recipe>" origin chips, filter segments, check-off → pantry prompt, and Add Pantry Item / Add Shopping Item dialogs.
|
||
- [x] Tests: 10 new tests in `test/features/pantry/delta_calculator_test.dart` — the three required scenarios (300 g vs 800 g → +500 g entry; sufficient stock → no entry; manual rows preserved across repeated syncs) plus 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.
|
||
|
||
## Active Verification Gates
|
||
- [x] `flutter pub run build_runner build --delete-conflicting-outputs`
|
||
- [x] `flutter analyze` (Zero issues — `No issues found!`)
|
||
- [x] `flutter test` (99/99 passing)
|
||
- [x] `flutter build web --no-tree-shake-icons` (conditional import keeps `package:health`/`dart:io` out of the web bundle) |