- Add GET /{plan_id}/prep-session endpoint so frontend can retrieve existing sessions without creating
- Fix list_plans response_model from list[dict] to list[PlanSummary] with proper _plan_summary() mapping
- Replace assert in store.update_prep_task with ValueError (assert is stripped under python -O)
- Add day_of_week 0-6 validation to upsert_slot endpoint
- Remove MagicMock sqlite artifact files left by pytest (already in .gitignore)
- upsert_slot: raise 422 immediately if meal_type not in VALID_MEAL_TYPES
- update_prep_task: assert whitelist safety contract after dict comprehension
- CreatePlanRequest: week_start typed as date with must_be_monday validator; str() cast at call site
- PrepTask: frozen=True; build_prep_tasks rewired to use (priority, kwargs) tuples so frozen instances are built with correct sequence_order in one pass (no post-construction mutation)
- Move deferred import json to file-level in meal_plans.py
- Fix test dates: "2026-04-14" was a Tuesday; updated request bodies to "2026-04-13" (Monday)
- Add migration 021: recipe_browser_fts FTS5 table on category + keywords
columns, eliminating LIKE '%keyword%' full sequential scans on 3.1M rows
- _count_recipes_for_keywords now uses FTS5 MATCH (O(log N) vs O(N))
- browse_recipes reuses cached count, eliminating the second COUNT(*) scan
per page request; ORDER BY r.id replaces the unindexed ORDER BY title sort
- Module-level _COUNT_CACHE keyed by (db_path, keywords) means domain-switch
category counts are computed once per process lifetime
feat(find): dietary preset grid, Big 9 allergen pills, Hard Day Mode surface
- Dietary constraints replaced with toggle-button preset grid (8 options)
+ free-text "Other" field; removes dense freeform text input
- Allergies replaced with Big 9 pill picker (peanuts, tree nuts, shellfish,
fish, milk, eggs, wheat, soy, sesame) + "Other" for custom entries
- Hard Day Mode surfaced as a standalone aria-pressed button above the
dietary collapsible; no longer buried inside a collapsed section
- Active-state dot indicators on both collapsibles show filter engagement
at a glance without expanding
fix(a11y): aria-describedby wiring for wildcard checkbox and tag inputs (#40)
- Persistent hint spans replace placeholder-only instructions for constraint
and allergy fields (WCAG 3.3.2)
fix(browse): auto-select highest-count category on domain switch (#41)
- Eliminates the 3-decision cold start (domain → category → content)
- Surprise Me button added for zero-decision random navigation