User report 2026-06-05: 'webui Meal Planner page is empty' on Friday
morning after the Friday email went out. Root cause: the orchestrator
keyed plans by the most-recent-Friday while the frontend's isoMonday()
returned the most-recent-Monday — a 7-day mismatch on Fridays.
Fixes (one semantic across the stack):
- runner._current_week_start() returns the upcoming Monday (today if
Mon, else the next Mon). The Friday email subject
('Meal plan for week of <date>') automatically picks up the new
value via run.week_start_date.
- frontend isoMonday -> upcomingMonday (same logic; renamed for
intent). isoMonday kept as a deprecated alias.
- New WeekRangeNav component (Dashboard + ShoppingList share it).
Renders [<] Jun 8 - Jun 14 [>] with clickable chevrons and a
clickable range label that jumps to the upcoming week. Replaces
the Sprint 5 inline segmented control on both pages.
- New formatWeekRange(mondayIso) helper (UTC-stable; uses
timeZone: 'UTC' so the rendered date matches the stored ISO date
regardless of viewer TZ; closes a latent bug in formatIsoDate too).
- New SQL fix script that retargets the user's 3-pending-items plan
from 2026-06-05 (Friday-keyed) to 2026-06-08 (upcoming Monday).
Idempotent + transaction-wrapped. Optional block for 2026-05-29.
No backend migration. No new dependencies. Deploy is git pull +
run the SQL fix + docker compose up -d --build backend frontend.
See Review/sprint7-verification.md for the full deploy + smoke flow.
Files:
- backend/app/services/orchestrator/runner.py:20-35
- backend/scripts/fix_2026_06_05_to_2026_06_08.sql (new)
- frontend/src/lib/utils.ts:43-130
- frontend/src/components/WeekRangeNav.tsx (new)
- frontend/src/pages/Dashboard.tsx (3 call sites + 1 segmented control)
- frontend/src/pages/ShoppingList.tsx (5 call sites + 2 segmented controls)
- Review/{sprint7-verification,ui-nielsen-audit,handoff-ui-audit}.md
- fix-ui-audit.md
- docs/HANDOFF.md
- .agent/{plan,context}.md
- Drop the empty batch_alter_table block and the meaningless
set_config call from migration 0015. Temp tables still persist
for the migration's session (Alembic's transactional_ddl).
- New backend/scripts/persist_aisle_backup.sql creates
public.ingredient_aisle_backup_0015 and
public.grocery_item_aisle_backup_0015 permanent tables for
operators who want a recoverable record beyond the migration.
- Update Review/sprint2-verification.md, Review/ui-nielsen-audit.md
and fix-ui-audit.md with the correct container-based deploy
steps: docker compose exec db psql -U mealplanner -d mealplanner
-f /dev/stdin < ...sql. Host psql is not available on the
deployment host; the db runs inside the container.
- Dashboard MealCard: title truncate -> line-clamp-2, image shrinks to
40x40 on <md to give the title room (B6).
- MealDetail: hero reworked to normal flow with stronger gradient;
description runs through new cleanDescription() helper that strips
14 spoonacular SEO patterns and trims to the last full sentence.
Raw description moved to a 'Notes from source' disclosure (B7).
- Pantry: free-text aisle/unit replaced with <Select> populated from
the new PANTRY_AISLES canonical enum; ingredient name field marked
required. New PANTRY_AISLES export + PantryAisle type in types (B8).
- backend: alembic 0015_normalize_pantry_aisles maps free-text
ingredient.aisle and grocery_item.aisle to canonical labels in a
single transaction; downgrade raises (restore from snapshot).
backend/scripts/dry_run_aisle_migration.sql is the read-only
preview helper.
- ShoppingList: human-readable AISLE_LABEL map replaces raw snake_case
aisle keys; 3-col stat grid with compact mobile sizing (B9 + S3.3).
- Pantry table: role/aria-label region and a right-edge white
gradient hint at mobile horizontal overflow (B10).
- Recipes: pending/applied filter split, Apply and Reset buttons,
active-count chip on the Filters button, role=region + aria-label
on the panel (B11).
- Review/sprint2-verification.md and fix-ui-audit.md updated.
Build: npm run build (tsc + vite) green. tsc emits 0 errors.
Co-located audit + plan docs kept in sync: Review/ui-nielsen-audit.md
gains a Sprint 2 status block; fix-ui-audit.md has implementation
notes for each Sprint 2 task.
- api/meal_plans.py: /regenerate now passes exclude_recipe_ids into generate_meal_plan
- planner/generate.py: filter recipe_dicts by exclude_recipe_ids set
- image_generation.py: OpenAI gpt-image-1 client with prompt building, b64_json handling
- main.py: StaticFiles mount at /static for generated images
- admin.py: POST /api/admin/trigger-images endpoint for batch generation
- scripts/generate_images.py: CLI for batch image generation
- docker-compose.yml + nginx: volume mounts for static/images persistence
- Verify MealPlanItem.votes ↔ MealPlanVote relationship is correct; no model bug exists