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
8.3 KiB
Sprint 7 — Verification
Sprint: Fix webui "empty meal plan" (date-semantics mismatch) Status: code complete, awaiting deploy. Approach: Option C — align orchestrator + frontend on "this week" = upcoming Monday; migrate the existing Friday-keyed plan to its equivalent Monday.
Date of handoff: 2026-06-05.
What changed (recap)
| Layer | File | Change |
|---|---|---|
| Backend | backend/app/services/orchestrator/runner.py:20-35 |
_current_week_start() returns the upcoming Monday (today if Mon). |
| Frontend | frontend/src/lib/utils.ts:43-83 |
isoMonday → upcomingMonday (new logic). isoMonday kept as a deprecated alias. New formatWeekRange(mondayIso) helper. |
| Frontend | frontend/src/components/WeekRangeNav.tsx (NEW) |
Shared component. Renders [<] Jun 8 — Jun 14 [>] with clickable chevrons + a clickable range label (jump home) + an optional This week chip. |
| Frontend | frontend/src/pages/Dashboard.tsx |
Import + 3 call-site updates. Replaces inline segmented control with <WeekRangeNav>. |
| Frontend | frontend/src/pages/ShoppingList.tsx |
Same: import + 5 call-site updates, replaces both inline segmented controls (header + empty-state) with <WeekRangeNav>. |
| Data | backend/scripts/fix_2026_06_05_to_2026_06_08.sql (NEW) |
Guarded UPDATE meal_plan SET week_start_date='2026-06-08' WHERE week_start_date='2026-06-05'; with a SELECT COUNT(*) and a final verification SELECT. Optional commented-out block for 2026-05-29. |
| Docs | Review/ui-nielsen-audit.md + fix-ui-audit.md + Review/handoff-ui-audit.md + docs/HANDOFF.md + .agent/plan.md + .agent/context.md |
All updated with Sprint 7 status blocks. |
No new dependencies. No backend migration. Frontend + backend rebuild only, plus the one-time SQL data fix.
Deploy commands
Run on the deployment host (100.108.224.12):
# 1. Pull
cd ~/MealPlanner
git pull
# 2. SQL data fix (the user's 3-pending-items plan moves to 2026-06-08)
docker compose exec -T db psql -U mealplanner -d mealplanner \
-f /dev/stdin < backend/scripts/fix_2026_06_05_to_2026_06_08.sql
# Expected output:
# rows_to_migrate
# ----------------
# 1
# UPDATE 1
# COMMIT
# If you also uncomment the 2026-05-29 block, you'll see another UPDATE 1.
# 3. Rebuild backend + frontend
docker compose -f docker-compose.yml up -d --build backend frontend
# 4. (Optional) Verify the new function on the running backend
docker compose exec backend python -c "
from datetime import date
from app.services.orchestrator.runner import _current_week_start
print('_current_week_start() →', _current_week_start())
"
# Should print the upcoming Monday's date (e.g. 2026-06-08 if today is Fri 2026-06-05).
Order matters. Pull → SQL fix → rebuild. The SQL fix is idempotent (re-running is a no-op once 2026-06-05 has no rows), so it's safe to retry.
Smoke checklist (browser, on http://100.108.208.56:8082/)
| # | Action | Expected |
|---|---|---|
| 1 | Open / (Dashboard) with no ?week= query param. |
URL is just /. Header shows the new week-range nav: [<] Jun 8 — Jun 14 [>] for week 2026-06-08. Three meal cards visible (Chicken Fajitas Mon, Garlic Shrimp Scampi Wed, Breakfast-for-Dinner Veggie Scramble Fri). Plan status badge: draft. Total cost ~$188.98. |
| 2 | Click the range label (Jun 8 — Jun 14). |
URL is still / (jump home goes to the default week). The This week chip is not visible (we're already on the upcoming week). |
| 3 | Click the right chevron ([>). |
URL becomes /?week=2026-06-15. Header shows Jun 15 — Jun 21. Empty state appears (no plan for next-next week). |
| 4 | Click the left chevron ([<). |
URL becomes /?week=2026-06-08. Header shows Jun 8 — Jun 14. Three meal cards reappear. |
| 5 | Click the This week chip (visible only when off the upcoming week). |
URL clears the ?week= param. Header shows the upcoming week again. |
| 6 | Open /shopping-list (no ?week=). |
Header shows the same [<] Jun 8 — Jun 14 [>] nav. The "Add N to pantry" + "Reset" + "Print List" buttons are still in their normal positions. |
| 7 | Click a checkbox in the shopping list. The "Add N to pantry" button shows. | Button works as in Sprint 6 (no regression). |
| 8 | Open /shopping-list?week=2026-06-15. |
Empty state: "No plan for that week. Go to the Dashboard and generate one, or pick a different week." The week-nav is still clickable. |
| 9 | Open / with ?week=2026-06-01 (a Mon that has no plan). |
Empty state with "Plan the week" dropdown (F4). The week-nav shows Jun 1 — Jun 7 with the This week chip visible (because we're not on the upcoming week). |
| 10 | Reload the page after each navigation. | URL is preserved; the same week is shown. (F5 URL persistence still works.) |
| 11 | Press g d (Sprint 5 keyboard shortcut). |
Navigates to / with the default (upcoming) week. |
| 12 | Press ? (Sprint 5 help shortcut). |
ShortcutHelpBanner slides down with the keyboard help text. (No regression.) |
If any item fails, stop and report with the URL you were on, the action you took, and the observed vs expected behavior.
API smoke (curl, on the deployment host)
# (a) The user's just-migrated plan lives at the upcoming Monday's key
curl -s "http://100.108.208.56:8082/api/meals?week_start=2026-06-08" | python3 -m json.tool | head -30
# Expect: 3 items, all status=pending, total ~$188.98
# (b) The old Friday-keyed date now returns null (post-migration)
curl -s "http://100.108.208.56:8082/api/meals?week_start=2026-06-05"
# Expect: null
# (c) The current calendar week has no plan (was always the case)
curl -s "http://100.108.208.56:8082/api/meals?week_start=2026-06-01"
# Expect: null
# (d) The shopping list is also keyed by the upcoming Monday now
curl -s "http://100.108.208.56:8082/api/shopping-list?week_start=2026-06-08" | python3 -c "
import sys, json
d = json.load(sys.stdin)
print('items:', len(d.get('items', [])))
print('week:', d.get('week_start_date'))
print('total:', d.get('total_estimated_cost'))
"
# Expect: items around 20, week=2026-06-08, total ~$27.35
Things to look for
- The webui is no longer empty. This is the user's original complaint.
- The week-range label is scannable. The header shows
Jun 8 — Jun 14(or whatever the upcoming week is) — the user requested this format. - The chevron brackets are clickable. Mouse or keyboard, they step the week by 7 days.
- The
This weekchip appears only when off the upcoming week. Clean visual signal. - URL persistence still works.
?week=YYYY-MM-DDin the URL is respected. - Keyboard shortcuts (Sprint 5) still work.
g d,g r,g p,g snavigate;/focuses search;?shows help. - Plan-the-week (Sprint 6) still works. The "Plan the week" button is at the same position with the same dropdown.
- Bulk pantry add (Sprint 6) still works. The "Add N to pantry" button on the shopping list is unchanged.
Rollback (if needed)
The Sprint 7 change is small and easy to revert:
- Revert
runner._current_week_start()to return the most recent Friday (the original 5-line body). - Revert
upcomingMonday()to return the current calendar week's Monday. - Revert the frontend
WeekRangeNavswap (put the inline segmented control back in Dashboard and ShoppingList). - Re-run the SQL in reverse:
UPDATE meal_plan SET week_start_date='2026-06-05' WHERE week_start_date='2026-06-08';(and similarly for 2026-05-29 if you migrated it). git revertis also clean — all Sprint 7 work is in one or two commits if squashed.
The pre-Sprint 7 behavior is what the user reported as broken. Only revert if a regression appears that wasn't there before Sprint 7.
Verification log
(Filled in by the operator after deploy + smoke.)
git pullon deployment host → success- SQL fix script ran → expected row count
- Backend rebuild → success
- Frontend rebuild → success
- Browser:
/shows 3 meals on the upcoming week - Browser:
[<]and[>]chevrons step the week - Browser:
This weekchip is visible only when off the upcoming week - API:
/api/meals?week_start=2026-06-08returns 3 items - API:
/api/meals?week_start=2026-06-05returns null - No regression in Sprints 1-6
Last updated: 2026-06-05 — code complete, awaiting deploy.