11 KiB
11 KiB
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
- Phase 1: Foundation & Identity (Passwordless SSO & Encrypted Drift DB)
- Phase 2: Medication Domain, Injection Rotation, Serum Modeling
- Phase 3: Nutrition, Protein Target Tracking, HealthGateway Sync
- Phase 4: Recipe Web Scraper Engine & Meal Planning Calendar
- Phase 5: Pantry Delta Engine & Auto-Shopping Generator
- Phase 6: Hardening, Native Desktop/Web Polish, CI/CD
Phase 2 Deliverables (2026-10-16)
- Domain:
DoseEntryentity, 12-quadrantInjectionSiterotation model,Glp1Compoundvalue object (Sema 168h / Tirze 120h / Lira 13h),IMedicationRepositorycontract. - Domain:
CalculateSerumLeveluse case implementingC(t) = Σ D_i · 0.5^((t − t_i)/t_half). - Data: injection-history CRUD on
AppDatabase+ drift-backedMedicationRepository(record ↔ entity mapping). - Presentation:
MedicationController(Riverpod StateNotifier) with serum estimation, least-recently-used site rotation, and reminder scheduling; refactoredMedicationScreen(serum card, rotation suggestion, interactive log dialog, history with delete). - 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;timezonedependency added. - Regression fix:
SupabaseAuthServicenow resolves its client lazily (baseline widget test no longer throws when Supabase is uninitialized). - 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
- Development auth bypass (2026-10-16): debug-only "Continue Without Signing In" button on the login screen (
kDebugMode-gated, in-memorydevAuthBypassProvider) 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 bytest/features/auth/dev_auth_bypass_test.dart(3 tests).
Phase 3 Deliverables (2026-10-16)
- Domain:
FoodLogEntry(+MealTypeenum),HydrationLogEntry,BiometricEntry(+BiometricSourceenum),INutritionRepository,IHealthSyncRepository— all pure Dart. - Data:
AppDatabaseschema v2 (+ migration for v1 installs) withFoodLogsTable,HydrationLogsTable,BiometricLogsTable, range-filtered watch/get/CRUD helpers,clearSyncedRangefor idempotent re-syncs; drift-backedNutritionRepositoryandHealthSyncRepository(record ↔ entity mapping). - Platform:
HealthGatewayrewrite —IHealthGateway+ conditional-import factory; mobile impl (health_gateway_mobile.dart) wrapspackage:healthforWEIGHT(kg→lb normalization) andACTIVE_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). - Presentation:
NutritionController(RiverpodAsyncNotifier; today's logs, 100 g protein / 96 oz water targets, meal grouping, quick-add water) andBiometricController(weight history, 7/30-day deltas, manual entry, native sync with per-day active-energy aggregation);LogFoodDialogandLogWeightDialog. - Presentation:
NutritionScreenupgraded — 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 runsHealthGatewaysync on Android/iOS and opens the manual dialog on Windows/Web; status snackbars viaref.listen. - Tests: 23 new tests — nutrition repository (in-memory drift: CRUD, day-range filter, watch,
MealTyperound-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). - Regression fix: none required — existing 37 tests untouched and green.
Phase 4 Deliverables (2026-10-16)
- Domain:
Recipe(+MacroProfile),RecipeIngredient(quantity/unit/item parsing: decimals, ASCII + unicode + mixed fractions, known-unit vocabulary),MealPlanSlot(reuses nutritionMealType),IRecipeRepository,IMealPlanRepository— all pure Dart;core/utils/uuid.dartv4 generator (no new dependency). - Domain:
ScrapeRecipeFromUrluse case —package:httpdownload (injectableRecipeHtmlFetcherfor tests),package:htmlDOM parse, JSON-LD@graph/root/array/@type-list navigation,HowToStep/HowToSectioninstruction flattening, ISO-8601prepTime/cookTime,recipeYield, regex nutrition parsing (calories,proteinContent,carbohydrateContent,fatContent), image list/object unwrap, OpenGraph fallback (og:title/og:image/og:description), typedRecipeScrapeException. - Data:
AppDatabaseschema v3 (migration for v1/v2 installs) withRecipesTable(UUID PK, JSON ingredient/instruction columns, macros,createdAt) andMealPlanSlotsTable(UUID PK, plannedDate, mealType, nullablerecipeIdFK withON DELETE SET NULL, customNotes, isCompleted); drift-backedDriftRecipeRepository(URL-deduplicatedimportRecipe,createdAtpreserved on re-import) andDriftMealPlanRepository(half-open[from, to)window, upsert-by-id). - Presentation:
RecipeImportController(RiverpodNotifier; URL submit → loading → preview modal state, live catalog watch stream, confirm-save/dismiss/delete) andMealPlanCalendarController(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). - Presentation:
MealPlannerScreenrefactor — 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) withDragTargetmeal slots, prepared checkboxes, clear menu, per-day protein indicator (progress bar vs. target),AssignSlotDialog(saved-recipe dropdown or custom description), week navigation header. - Tests: 25 new tests —
recipe_scraper_test.dart(JSON-LD@graph/root/array/list-@typeextraction, macro/ingredient/instruction/duration/yield parsing, malformed-JSON fallthrough, OpenGraph fallback,execute()URL validation & error mapping) andmeal_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). - Regression fix: HowToSection flattening now recurses into
itemListElementbefore using the sectionname(parser ordering bug caught by the new test).
Post-Phase-4 Additions
- 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
LayoutBuilderinMealPlannerScreencollapses the page into a single scrollable column (with a fixed-height calendar) whenever the body is shorter than ~520 dp. Covered bytest/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)
- Domain:
PantryItem(+PantryCategoryaisle enum withinfer()keyword heuristic,ExpirationStatuswarning buckets),ShoppingListItem,IPantryRepository, and two pure-Dart value objects —ingredient_name.dart(lowercase, paren-stripped, plural-folded canonical keys:eggs→egg) andmeasurement_units.dart(mass/volume/count dimensions with lb↔oz↔g↔kg and ml/l/tsp/tbsp/cup conversion). - Domain:
CalculateShoppingDeltause case — aggregates the active 7-dayMealPlanSlotwindow'sRecipeingredient manifests by canonical name, appliesdeficit = totalRequired − currentPantryQuantityin dimension base units (dominant recipe unit wins display, 2-decimal rounding), upsertsisAutoGeneratedrows withsourceRecipeTitles(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. - Data:
AppDatabaseschema v4 (migration for v1–v3 installs) withPantryItemsTable(UUID PK, canonical name, quantity/unit, aisle key, nullableexpirationDate,lastUpdated) andShoppingListItemsTable(UUID PK, required quantity/unit, purchased/auto flags, JSONsourceNotesfor recipe titles); drift-backedDriftPantryRepository(watch/get/upsert/delete for both lists, enum/JSON mapping). - Presentation:
PantryInventoryController(live watch stream, quick ± stock steps clamped at zero, aisle-grouping vs. expiration-sort modes) andShoppingListController(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,syncWithMealPlanreading the planner's Monday–Sunday window + recipe catalog). - Presentation:
PantryScreenrefactor — 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: " origin chips, filter segments, check-off → pantry prompt, and Add Pantry Item / Add Shopping Item dialogs. - 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-endsyncWithMealPlan, and inventory canonicalization/quick-adjust clamp.
Active Verification Gates
flutter pub run build_runner build --delete-conflicting-outputsflutter analyze(Zero issues —No issues found!)flutter test(99/99 passing)flutter build web --no-tree-shake-icons(conditional import keepspackage:health/dart:ioout of the web bundle)