diff --git a/.agent/context.md b/.agent/context.md index 82faba8..e2d9e3f 100644 --- a/.agent/context.md +++ b/.agent/context.md @@ -342,3 +342,69 @@ User direction 2026-06-05: "Proceed." Selected from the question menu as the sma - `backend/app/schemas/__init__.py:227-247` — `MealPlanBase` + `MealPlanCreate` schemas - `Review/sprint11-verification.md` — new file (deploy + 4-step browser smoke + race test) +--- + +# Context — Sprint 12 (F8 Spoonacular search) + +## Why Sprint 12 exists + +User direction 2026-06-05: "Proceed." Selected from the question menu as the smallest remaining §Future item. F1 (Sprint 9) shipped, the dead CTA (Sprint 11) shipped, and F8 (Spoonacular search) is the last piece with a clear UI scope. F9 (Ollama local LLM) is a separate full backend proposal (model pull + ollama-py + `/api/llm/plan` endpoint) and remains in the §Future backlog. + +The user can browse ~150 local recipes on `/recipes` (admin seeds them) but has no path to find new ones without leaving the app. Sprint 12 adds a "Search the web" toggle on `/recipes` that hits the Spoonacular `complexSearch` API and lets the user import a result into the local library in one click. + +## Decisions (locked in for Sprint 12) + +- **D1. Reuse the pre-existing `RecipeDiscoveryService`.** `backend/app/services/recipe_discovery.py` (226 lines, already exists) has `_search_spoonacular()`, `_fetch_recipe_info()`, `_normalize_spoonacular()` and a 150/day free-tier quota gate. Sprint 12 does NOT re-implement the API client; it adds a thin HTTP layer in `backend/app/api/recipe_search.py` that calls the same `requests.get(SPOONACULAR_SEARCH_URL, ...)` pattern. +- **D2. Search endpoint = `complexSearch` only, NO info endpoint call.** The pre-existing `_search_spoonacular` calls the info endpoint (1 pt) for every result. For a 10-result search that's 10 extra points — the whole daily quota in one query. Sprint 12's `GET /api/recipes/search` uses just the `complexSearch` summary (1.1 pts/query) for browsing, and the **import** endpoint does the info call once (1 pt) only for the recipe the user actually wants. +- **D3. Ingredient resolution via the existing public `POST /api/ingredients` endpoint.** `backend/app/api/ingredients.py:58-103` is idempotent on `name_lower` + aliases. The import flow upserts each ingredient via this endpoint. No new ingredient-resolution helper needed. +- **D4. NEW endpoints, NOT modification of the pre-existing WIP.** `backend/app/api/recipes.py` (352 lines) and `backend/app/schemas/recipe.py` (93 lines) exist as WIP but are not registered in `main.py`. Sprint 12 does NOT touch them. It creates `backend/app/api/recipe_search.py` (new router) and adds the Pydantic models to `backend/app/schemas/__init__.py` (the canonical location used by all registered endpoints). +- **D5. UI toggle defaults to OFF.** The user explicitly asked for a "Search the web" toggle, not a permanent switch. The local-search UX is preserved for users who don't toggle. The toggle is a real `