0668d404443d2f321ec05e791ec8056947723647
14
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
0668d40444 |
docs: Sprint 15 — seed 50 recipes + Sprint 12 latent-bug fix across all 6 running docs
Sprint 15 (commit
|
||
|
|
af4ec793c7 |
docs: Sprint 14 — Vitest for useOnboarding (Q4) across all 6 running docs
Sprint 14 (commit |
||
|
|
8cb4d4198c |
docs: Sprint 13 — F9-lite (Ollama Cloud free-text plan synthesis) across all 6 running docs
Sprint 13 (commit
|
||
|
|
e939c96961 |
docs: Sprint 12 — F8 Spoonacular search across all 6 running docs
Sprint 12 (commit
|
||
|
|
dac1364c29 |
docs: Sprint 11 — wire the dead "Generate Meal Plan" CTA across all 6 running docs
Sprint 11 (commit
|
||
|
|
4c85c929d3 |
docs: record Sprint 9 post-deploy dismiss-bug fix across all 6 running docs
Sprint 9 (commit |
||
|
|
0b6c5dcfb7 |
feat(ui): Sprint 10 — Deny Forever on Recipes (card overlay + detail button + undo toast)
User-driven follow-up to Sprint 8: surface the Sprint 1-3 NeverSuggest
infrastructure on the Recipes surface so a family can pre-emptively
mark a recipe as never-suggest before it appears in a plan.
Backend (3 changes):
- POST /api/never-suggest (public, webui-facing). Idempotent on
(family, recipe, reason). Returns the row joined with recipe_name.
- DELETE /api/never-suggest/{ns_id} (public, webui-facing). Row-level
ownership check (403 if cross-family), 404 if absent.
- NeverSuggestRead.recipe_name + .ingredient_name server-side joins
via _attach_names() helper (one LEFT OUTER JOIN per kind).
- Admin path (POST/DELETE /api/admin/never-suggest) unchanged.
Frontend (4 changes):
- New NeverSuggestButton component (~290 lines). Two variants: card
(overlay on RecipeCard) and detail (text buttons in RecipeDetail
top bar). Popover with Allergy (red, window.confirm) + Dislike
(neutral, no confirm). Undo toast via showToast.undo() (Sprint 3
B12 pattern, 6s window). Pre-existing block detection shows a
Blocked state with an Unblock path.
- mealPlannerApi.neverSuggest.list/add/remove in api/index.ts.
- Recipes.tsx overlay: RecipeCard has position: relative; button is
opacity-0 group-hover:opacity-100 focus:opacity-100. e.preventDefault
+ e.stopPropagation prevents accidental navigation.
- RecipeDetail.tsx top bar: new Deny forever button group to the left
of Add to Plan.
Build: npm run build green (tsc 0 errors, vite 0 errors) on
docker-willester. Bundle 487 -> 495 kB. No new dependencies. No
migration (NeverSuggest table exists from prior sprints).
Tracking: Review/sprint10-verification.md (9-step browser smoke +
5 API curls + undo test + a11y check).
|
||
|
|
6e386baf6e |
feat(ui): Sprint 9 — F1 onboarding tour (4-step welcome)
Hand-rolled 4-step tour (no react-joyride) anchors to existing [data-tour="<id>"] attributes. localStorage key mealplanner:onboarding-complete is the source of truth; ?reset-tour=1 clears the key and re-shows. Steps: Dashboard / Pantry / Recipes / Shopping List. Keyboard: 1-4 jump, ←/→ step, Esc dismiss. Off-route fallback renders a centered card with an 'Open <page>' CTA. A11y: role=dialog, aria-modal=true, focus captured on open and restored on close. 5 lines of code across 4 pages; 1 new component (~420 lines). No new dependencies. No backend changes. No migration. Frontend-only deploy. Tracking: Review/sprint9-verification.md (8-step browser smoke + a11y check + reset-link test). |
||
|
|
efd1fc695f |
feat(ui): explicit Deny semantics with 2-denial hard-filter escalation (Sprint 8)
User policy decision (2026-06-05, exact): 'Hard filter. If it is denied
this week twice, it should be considered denied for good.'
The planner had no cross-week memory of denials: a denial on
meal_plan_item.approval_status was never consulted by the planner,
and NeverSuggest (the per-family permanent blocklist) was empty for
the user. The 'Roasted Sweet Potato and Chickpea Bowl' the user
denied on 2026-05-15 was still in the planner's pool 3 weeks
later.
Implements C + Z (explicit two-button model + soft-decay +
hard-filter escalation):
- Approve: untouched.
- Deny this week (1st in 90d): denial_expires_at = now() + 90d.
- Deny this week (2nd in 90d, server-side auto-escalation):
denial_expires_at = NULL + a NeverSuggest row written.
- Never again (explicit): same as the 2nd-time auto-escalation.
Both soft and permanent denials are hard filters in the planner
(per user). A denied recipe never reappears until either the 90d
window expires or the user un-blocks via the NeverSuggest API.
Changes:
- Migration 0016: meal_plan_item.denial_expires_at (partial index)
and meal_plan_vote.denial_scope.
- 3 backend helpers (_apply_denial, _ensure_never_suggest_recipe,
_has_prior_active_soft_denial) — single source of truth for the
deny path.
- POST /api/meals/items/{id}/deny?scope=this_week|never_again
(default this_week). Returns promoted_to_permanent.
- POST /api/meals/vote/{id} extended: vote=approve|deny|never_again.
Returns denial_scope + promoted_to_permanent.
- GET /api/meals/vote/{id} HTML page renders 3 buttons; supports
one-click ?scope=... for email direct-action links.
- Email template (step_email): 3 direct-action links per recipe
plus a secondary 'open vote page' link.
- Planner: _load_blocklists returns 3 sets; soft_denied_recipes
is hard-filtered (union with blocked_recipes at the call site).
- Frontend: MealCard renders 3 buttons (Approve / Deny this week
/ Never again) for pending items. handleDeny is scope-aware;
toast reflects promoted_to_permanent. window.confirm on
'Never again' prevents accidental permanent blocks.
Verification:
- npm run build green.
- 21/21 planner tests pass (1 pre-existing test_filter_blocks_by_cost
failure is NOT introduced by Sprint 8 — verified via git stash).
- Review/sprint8-verification.md: 11-step browser smoke + 4 API
curls + email-render procedure + rollback.
Files:
- backend/alembic/versions/0016_denial_decay_and_scope.py (new)
- backend/app/models/__init__.py:221-242, 250-269
- backend/app/schemas/__init__.py:204-219, 248-269
- backend/app/api/meals.py:30-138 (helpers), 240-330 (HTML page),
380-455 (submit_vote), 486-552 (deny_meal_item)
- backend/app/services/orchestrator/steps.py:283-300
- backend/app/services/planner/generate.py:59-99, 150-194
- frontend/src/api/index.ts:48-58
- frontend/src/pages/Dashboard.tsx:38-50, 385-410
- Review/{sprint8-verification,ui-nielsen-audit,handoff-ui-audit}.md
- fix-ui-audit.md
- docs/HANDOFF.md
- .agent/{plan,context}.md
Deploy (user runs on deployment host):
cd ~/MealPlanner && git pull
docker compose exec backend alembic upgrade head
docker compose -f docker-compose.yml up -d --build backend frontend
|
||
|
|
09c7525a12 |
fix(ui): align 'this week' to upcoming Monday (Sprint 7)
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
|
||
|
|
a616138e7c |
docs(review): Sprint 6 verification log + plan/handoff/audit updates
Sprint 6 (F3 + F4) is now documented across the project: - Review/sprint6-verification.md: new deploy + smoke-check doc. Backend + frontend deploy (no migration). 5 smoke-check sections: A) ShoppingList bulk-add end-to-end, B) F3 partial- failure edge case, C) F4 'Plan the week' button + dropdown, D) F4 edge cases (no empty slots, all recipes used, invalid meal_types), E) Sprints 1-5 regression spot-check. Rollback section covers revert (no migration to undo). - fix-ui-audit.md: new Sprint 6 section (S6.1 F3, S6.2 F4, S6.3 verification gate). 'Done when' block updated to 6 sprints / 10 commits / 20 findings closed. - Review/handoff-ui-audit.md: updated to a 6-sprint cycle. TL;DR table includes the |
||
|
|
2029e80c5a |
docs(review): Sprint 5 verification log + plan/handoff/audit updates
Sprint 5 (F5 + F2 + 0015 cast fix) is now documented across the project: - Review/sprint5-verification.md: new deploy + smoke-check doc. Backend + frontend deploy (one batch with Sprints 2-4). Migration 0015 MUST be run as part of this deploy (the cast fix is what makes it runnable). 7 smoke-check sections: A) curl tests for ?week_start=, B/C/D) URL week nav on Dashboard and Shopping List with query-key isolation, E) keyboard shortcut matrix, F) post- migration canonical-aisle verification query, G) Sprints 1-4 regression spot-check. Rollback section covers reverts + the persist_aisle_backup recovery path. - fix-ui-audit.md: new Sprint 5 section (S5.0 critical 0015 fix, S5.1 F5 implementation, S5.2 F2 implementation, S5.3 verification gate). 'Done when (overall)' block updated to 5 sprints + 9 commits + 18 findings closed + the 0015 fix unblocks Sprint 2. - Review/handoff-ui-audit.md: updated to a 5-sprint cycle. TL;DR table includes the |
||
|
|
62dfc1eb4a |
docs(review): Sprint 4 verification log + plan/handoff/audit updates
Sprint 4 (F7 + F6) is now documented across the project: - Review/sprint4-verification.md: new 100-line deploy + smoke-check doc. Frontend-only deploy (git pull + docker compose up -d --build frontend). 5 smoke-check tables: A) success toasts still work for all 11 actions, B) error path shows FastAPI detail (network-down is the easiest test; full Pydantic 422 verification via curl + DevTools 'Edit and resend'), C) pre-flight toasts still fire without a network call, D) plan-status Badge has correct aria-label in DevTools, E) Sprint 1-3 regression spot-check. Includes rollback instructions (single-commit revert). - fix-ui-audit.md: new Sprint 4 section with full per-task notes (S4.1 F7 implementation details, S4.2 F6 aria-label, S4.3 verification gate). 'Done when (overall)' block updated to 4 sprints + 7 commits + 16 findings closed. No new commit in fix-ui-audit.md for the F8/F9 §Future addendum (those are noted in Review/handoff-ui-audit.md but live in the doc/proposals/ tree, not in the UI-audit plan). - Review/handoff-ui-audit.md: updated to a 4-sprint cycle. TL;DR table includes the |
||
|
|
427d8ac352 |
docs(review): add handoff document for UI audit work
Review/handoff-ui-audit.md is a focused handoff for a fresh agent taking over the UI/UX audit and fix cycle (Sprints 1, 2, 3). It complements docs/HANDOFF.md (project-wide) rather than duplicating it. Covers: - Commit table ( |