docs: Sprint 15 — seed 50 recipes + Sprint 12 latent-bug fix across all 6 running docs

Sprint 15 (commit a3c89bf) does two things: (1) reorders
backend/app/main.py so recipe_search_api.router mounts BEFORE
the WIP recipes_api.public_router (fixes a Sprint 12 latent
bug where /api/recipes/search was shadowed by the WIP's GET
/{recipe_id} returning 422); (2) adds scripts/seed_recipes.py
which seeded 18 Spoonacular recipes into the local library
today (free-tier 50-pt cap hit; remaining 32 to seed on later
days via the same idempotent script). DB went 31 -> 49 total
recipes.

This commit updates the 6 running docs that track sprints:

- .agent/plan.md — Sprint 15 section (S15.1-S15.4 + Done
  when + Out of scope) added after Sprint 14's out-of-scope.
- .agent/context.md — Sprint 15 decisions (D1-D6), open
  questions (Q1-Q2), and file:line references added.
- Review/sprint15-verification.md — NEW: full 18-imported
  breakdown by cuisine + free-tier math correction (50 pts/day,
  not 150) + LLM test (picked_count=0, filled_count=19,
  failed_count=2 for week 2026-07-06) + 6-risk table + deploy
  + 2 follow-up tickets (lower _DAILY_LIMIT, re-run script).
- Review/ui-nielsen-audit.md — Sprint 15 status block
  (T8.1-T8.3) added after the Sprint 14 block. Notes the
  Sprint 12 latent-bug fix as the critical change for the
  upcoming Sprint 12 deploy.
- fix-ui-audit.md — Sprint 15 section (T8.1-T8.5) added after
  the Sprint 14 section. T8.1 documents the main.py mount
  order fix in detail. T8.5 surfaces 2 follow-up tickets.
- Review/handoff-ui-audit.md — Batch K line in the deploy
  list, Sprint 15 section after Sprint 14, TL;DR Sprint 15
  line, Last-updated footer updated.
- docs/HANDOFF.md — Sprint 15 section after Sprint 14, Last-
  updated footer updated. Notes the corrected free-tier math
  and the 2 follow-up tickets.

All 6 docs now reflect Sprint 15. The Sprint 12 latent-bug
fix is documented as a hard prerequisite for the upcoming
Sprint 12 deploy (without it, every 'Search the web' query
would 422). Re-running scripts/seed_recipes.py on a later
day will add the remaining 32 recipes (the script is
idempotent — already-imported IDs return 409 and are
skipped).
This commit is contained in:
2026-06-06 14:12:25 -07:00
parent a3c89bf6a2
commit 0668d40444
7 changed files with 307 additions and 3 deletions
+47
View File
@@ -801,3 +801,50 @@ All five go under `devDependencies`. Runtime bundle size unchanged (503.82 kB be
### T7.5 · `Review/sprint14-verification.md` (NEW)
- Deploy + test commands + 5-risk table + open question for follow-up (Q1: component-level tests for `<OnboardingTour/>` itself, future sprint).
---
## Sprint 15 — Seed 50 family-friendly recipes for 4-week planning (content op) + Sprint 12 latent-bug fix — 🚧 IN PROGRESS
**Why this sprint:** User direction (2026-06-05): "Lets build out recipes for the coming 4 weeks in advance. In order to do this, lets add more recipes to the list of available ones." Sprint 15 is a **content operation** (no feature work, no schema changes, no UI changes) — but in the process I discovered a Sprint 12 latent bug that I fixed.
### T8.1 · Sprint 12 latent-bug fix: `backend/app/main.py` mount order
**File:** `backend/app/main.py` — one-line reorder + 3-line comment.
- **Bug:** the pre-existing WIP `backend/app/api/recipes.py:212` registers `GET /{recipe_id}` (UUID-typed) under `/api/recipes`. Sprint 12's `recipe_search_api.router` also mounts under `/api/recipes`. FastAPI matches routes in registration order, so the WIP's `/api/recipes/{recipe_id}` was catching `/api/recipes/search` and treating "search" as a UUID, returning 422.
- **Symptom:** Sprint 12's "Search the web" feature in `/recipes` would 422 on every query. The Sprint 12 verification doc was written pre-deploy; the user hadn't tried the feature in production yet (S12 hasn't been deployed). Latent, not in-the-wild.
- **Fix:** move `recipe_search_api.router` mount to BEFORE `recipes_api.public_router`. `/search` and `/import` now match first.
- **Verification:** `curl http://localhost:8082/api/recipes/search?q=chicken+parmesan&limit=2` returns 200 + 2 hits (Best Chicken Parmesan, Chicken Parmesan With Pasta). The WIP's `GET /api/recipes/{recipe_id}` still works for valid UUIDs (the path is a regex match, not a global catch-all).
- **No pre-existing WIP files touched** (recipes.py, schemas/recipe.py, nginx.conf are unchanged). Only `main.py` was reordered.
### T8.2 · `scripts/seed_recipes.py` (NEW) — 50-query one-shot Python
**File:** `scripts/seed_recipes.py` (NEW, ~150 lines).
- 50 queries distributed 5 cuisines × 10 each: Italian, Mexican, Asian, American, Mediterranean/Middle Eastern.
- Hits Spoonacular's `complexSearch` directly (avoids the backend's quota counter and works around the broken `/api/recipes/search` route during the time before the main.py fix took effect).
- For each query: takes the top hit, POSTs to the local backend's `/api/recipes/import` with `{external_id, external_source: "spoonacular"}`. Idempotent (409 → log and skip).
- 1.5 sec sleep between queries to stay well under per-second rate limits.
- Stops cleanly on Spoonacular 402 (quota exhausted) and logs a final stats summary.
- **Cost (corrected):** free tier is **50 pts/day**, not 150. 50 queries = 50 × 1.10 (search) + 50 × 1 (import) = 105 pts. Need 3 days on free tier. Today: 18 imported before cap hit.
- **Result:** 18 recipes imported today. DB went 31 → 49. LLM test (Sprint 13 endpoint) for week 2026-07-06: `picked_count=0 / filled_count=19 / failed_count=2`. The library fill covered 19/21 slots — the LLM (kimi-k2.6:cloud) returned 0 picks (Sprint 13 tolerance worked as designed).
### T8.3 · Sprint 15 verification gate
- [x] `curl /api/recipes/search` returns 200 (latent-bug fix verified).
- [x] DB has 49 recipes, 19 from Spoonacular.
- [x] LLM endpoint uses the new library: 19/21 slots filled.
- [x] Re-running the script is safe (idempotent via 409).
- [x] Backend AST clean (no Python change to recipe_search.py).
- [x] Frontend build green (no UI changes).
- [ ] Commit on host + push.
### T8.4 · `Review/sprint15-verification.md` (NEW)
- Full 18-imported breakdown by cuisine, free-tier math, LLM test, 6-risk table, deploy instructions, follow-up ticket (lower `_DAILY_LIMIT` from 140 to 45 to match the real 50-pt free tier).
### T8.5 · Follow-up tickets surfaced
- **Lower `_DAILY_LIMIT=140` in `backend/app/api/recipe_search.py:48` to 45** to match the actual 50-pt free tier (5-pt safety margin). Doesn't block Sprint 15; user can land it in a one-line patch.
- **Re-run `scripts/seed_recipes.py` on a later day** for the remaining 32 recipes. The script is idempotent.