Public Access
feat(meals): enforce three lower-calorie dinners with media
This commit is contained in:
@@ -0,0 +1,5 @@
|
||||
# Friday Email Catalog Filter
|
||||
|
||||
Quarantined historical recipes are filtered before all Friday proposal, reminder, and finalization email rendering. Active recipes continue to render; historical meal-plan rows and APIs remain unchanged.
|
||||
|
||||
Verification: 17 focused PostgreSQL tests passed, Python compilation and scoped diff checks passed, and final read-only review found no P1/P2 issues. Production backend and scheduler were rebuilt/recreated; backend is healthy, scheduler is running, and both loaded the filter at runtime.
|
||||
@@ -0,0 +1,5 @@
|
||||
# Lower-Calorie Recipe Catalog Phase 1
|
||||
|
||||
Catalog v2 reformulates the 50 app-owned recipes to an estimated 310-500 kcal per serving and enforces a hard 500-kcal loader ceiling. An explicit atomic update mode preserves Recipe IDs and historical references. Exact operator quarantine findings now survive future catalog audits.
|
||||
|
||||
Verification: 9 offline tests and 34 disposable-PostgreSQL tests passed; compilation and scoped diff checks passed; final review found no P1/P2 issues. Production remains unchanged pending Phase 2 approval.
|
||||
@@ -0,0 +1,5 @@
|
||||
# Lower-Calorie Recipe Catalog Phase 2
|
||||
|
||||
Production was backed up to `backups/pre_lower_calorie_catalog_20260828T004802Z.dump`, backend and scheduler were rebuilt, and the catalog v2 loader atomically updated 29 drifted bundled rows in place. A second dry-run reports no remaining updates.
|
||||
|
||||
All 50 bundled recipes are active at an estimated 310-500 kcal per serving. Recipe IDs, all 790 MealPlanItem rows, and 4 bundled historical references are unchanged. Wikibooks Rigatoni is durably quarantined with its staging link preserved; a future audit simulation retains that state. Backend is healthy and scheduler is running.
|
||||
@@ -0,0 +1,7 @@
|
||||
# Production Recipe Replacement Phase 1
|
||||
|
||||
- Replaced the unsuccessful Wikibooks bulk strategy with 50 original bundled starter recipes.
|
||||
- Added strict catalog validation and an atomic, advisory-locked, idempotent PostgreSQL loader.
|
||||
- Preserved the existing approved Wikibooks recipe and all quarantined historical recipes by contract and test.
|
||||
- Verified 3 offline tests and 79 focused disposable-PostgreSQL tests; compilation and scoped diff checks passed.
|
||||
- Production backup already exists. No bundled recipe has been loaded into production yet.
|
||||
@@ -0,0 +1,8 @@
|
||||
# Production Recipe Replacement Phase 2
|
||||
|
||||
- Rebuilt the production backend and atomically loaded 50 original bundled starter recipes.
|
||||
- Loader dry-run predicted 50 creates; actual load created 50 recipes and 58 ingredients; idempotent rerun created zero.
|
||||
- Deleted 484 unreviewed, unpublished staging rows while preserving the approved Rigatoni provenance link.
|
||||
- Verified 51 active recipes, 82 quarantined legacy recipes, and all 792 historical references intact.
|
||||
- Real generation persisted `Beefy Stuffed Bell Peppers` in MealPlanItem `0fb303f4-5e6e-459c-a5c0-a8271d1c0ec5`.
|
||||
- Backup and operation report are stored under production `backups/`.
|
||||
@@ -0,0 +1,9 @@
|
||||
# Recipe Catalog Import Phase 1
|
||||
|
||||
- Added the global `recipe_import_staging` model and Alembic revision `0017` from `0016`.
|
||||
- Preserved source revisions, licensing and attribution, raw and normalized JSON payloads, validation results, and review metadata.
|
||||
- Enforced revision uniqueness, lower-case SHA-256 shape, paired review metadata, and immutable raw payloads.
|
||||
- Local verification: 2 static tests passed and Python compilation/diff checks were clean.
|
||||
- Live verification: all 4 focused tests passed against a disposable PostgreSQL database; database cleanup succeeded.
|
||||
- Added a `0017 -> 0016 -> 0017` test guarded to `TEST_DATABASE_URL` databases ending in `_test`; it restores Alembic head in `finally` and never downgrades across `0015`.
|
||||
- Known pre-existing limitation: `backend/tests/test_alembic.py` downgrades to base, but `0015_normalize_pantry_aisles` intentionally raises on downgrade because it cannot restore normalized source values.
|
||||
@@ -0,0 +1,25 @@
|
||||
# Recipe Catalog Import Phase 2
|
||||
|
||||
## Scope
|
||||
|
||||
Added a dedicated synchronous Wikibooks staging service and offline tests without changing recipe ingestion, recipe APIs, or existing dirty recipe tests.
|
||||
|
||||
## Implementation
|
||||
|
||||
- Fixed `https://en.wikibooks.org/w/api.php` client with bounded category continuation, required caller User-Agent, sequential rate limiting, timeouts, streamed payload cap, HTTP `Retry-After` retries, and MediaWiki `maxlag`/`ratelimited` exponential backoff.
|
||||
- Validated MediaWiki response shapes and restricted page fetches to bounded `Cookbook:` titles.
|
||||
- Normalized rendered HTML into text-only recipe candidates: intro, infobox yields/times, ingredient and ordered instruction subsections, quantity/unit/source fields, and no images.
|
||||
- Added deterministic validation and quarantine for invalid servings, insufficient ingredients/instructions, invalid quantities, placeholders, and distinctive title-token ingredient mismatches.
|
||||
- Staged exact API `parse` payloads with canonical checksums, immutable provenance inputs, CC BY-SA 4.0 attribution/history, and source/page/revision deduplication. The service flushes only.
|
||||
|
||||
## Verification
|
||||
|
||||
- Offline focused run: `22 passed, 2 deselected in 1.60s`.
|
||||
- Full run against disposable PostgreSQL: `24 passed in 3.08s`, including the two-session concurrent deduplication test.
|
||||
- The disposable database was removed after verification.
|
||||
- `backend/venv/bin/python -m py_compile backend/app/services/wikibooks_import.py backend/tests/test_wikibooks_import.py`: passed.
|
||||
- Scoped `git diff --check`: clean for the service, test, and two fixtures.
|
||||
|
||||
## Status
|
||||
|
||||
Phase 2 is verified. Phase 3 has not started.
|
||||
@@ -0,0 +1,25 @@
|
||||
# Recipe Catalog Import Phase 3
|
||||
|
||||
## Scope
|
||||
|
||||
Added the admin review API and atomic staged-recipe publication boundary.
|
||||
|
||||
## Implementation
|
||||
|
||||
- Added paginated/filterable queue and provenance detail endpoints under `/api/admin/recipe-imports`.
|
||||
- Added approve/reject transitions protected by the existing admin bearer dependency.
|
||||
- Revalidates immutable Wikibooks payloads and maps exact-name canonical ingredients without fuzzy matching.
|
||||
- Publishes global text-only recipes and review metadata in one transaction.
|
||||
- Handles idempotent approval/rejection, deleted-recipe republication, page-level publication deduplication, checksum conflicts, integer bounds, and concurrent revision races.
|
||||
|
||||
## Verification
|
||||
|
||||
- Combined Phase 1-3 PostgreSQL tests: `45 passed in 9.11s`.
|
||||
- Offline Wikibooks tests: `23 passed, 2 deselected in 1.70s`.
|
||||
- Python compilation and scoped `git diff --check`: passed.
|
||||
- Follow-up adversarial review: no remaining P1/P2 findings.
|
||||
- Disposable PostgreSQL databases were removed.
|
||||
|
||||
## Status
|
||||
|
||||
Phase 3 is complete. Phase 4 is in progress.
|
||||
@@ -0,0 +1,27 @@
|
||||
# Recipe Catalog Import Phase 4
|
||||
|
||||
## Outcome
|
||||
|
||||
- Automatic and admin acquisition now stage Wikibooks candidates; no production caller publishes Spoonacular data directly.
|
||||
- Legacy search/import endpoints return HTTP 410 and frontend vendor-search controls were removed.
|
||||
- Existing recipes have active/quarantined catalog state, deterministic audit findings, and active-only catalog selection boundaries.
|
||||
- Historical meal-plan reads remain unfiltered; direct creation rejects quarantined recipe IDs.
|
||||
- Spoonacular write scripts and deployment key wiring were retired.
|
||||
|
||||
## Verification
|
||||
|
||||
- Python compilation passed for the Phase 4 backend scope.
|
||||
- Frontend production build passed with only the existing Vite chunk-size warning.
|
||||
- Offline Wikibooks tests: `26 passed, 2 deselected`.
|
||||
- Disposable PostgreSQL catalog suite: `61 passed in 11.80s`.
|
||||
- Disposable PostgreSQL finalizer subset: `5 passed, 26 deselected in 2.26s`.
|
||||
- Nullable admin-email regression plus admin import API: `19 passed in 5.78s`.
|
||||
- Adversarial review: one in-scope blocker fixed; remaining findings were pre-existing, false positives, or outside the approved scope.
|
||||
|
||||
## Incident
|
||||
|
||||
An intermediate test command exported only `TEST_DATABASE_URL`; Compose's existing `DATABASE_URL` therefore kept request-scoped sessions on the live DB. The exact synthetic artifacts were audited and removed transactionally: 23 staging rows, 7 recipes, and 2 ingredients. A post-cleanup query returned zero artifacts. Future container test commands must set both database variables to the disposable database.
|
||||
|
||||
## Remaining
|
||||
|
||||
Phase 5 documents licensing and operations, runs final scoped/full verification, and records unrelated suite blockers without modifying them.
|
||||
@@ -0,0 +1,25 @@
|
||||
# Recipe Catalog Import Phase 5
|
||||
|
||||
## Outcome
|
||||
|
||||
- Added operator documentation for Wikibooks licensing, attribution, staging, review, quarantine, audit, and safe testing.
|
||||
- Added configurable MediaWiki User-Agent wiring to backend and scheduler Compose services.
|
||||
- Updated README, environment example, and project handoff to supersede historical Spoonacular write instructions.
|
||||
|
||||
## Verification
|
||||
|
||||
- Scoped PostgreSQL catalog tests: `57 passed in 11.54s`.
|
||||
- Orchestrator finalizer tests: `5 passed, 26 deselected in 2.17s`.
|
||||
- Frontend tests: `11 passed`.
|
||||
- Frontend TypeScript/Vite build passed; existing 504.08 kB chunk warning remains.
|
||||
- Python compileall, Compose config validation, and scoped diff check passed.
|
||||
- Full backend suite: `211 passed, 13 failed`; isolation confirmed no catalog regression and left seven unrelated legacy expectation failures.
|
||||
|
||||
## Blockers Outside Scope
|
||||
|
||||
- Frontend lint cannot start because no ESLint configuration exists.
|
||||
- Legacy backend failures cover vote transaction fixtures, matcher score expectations, obsolete admin-orchestrator routes, planner cost semantics, and ingredient singular/plural naming.
|
||||
|
||||
## Safety Incident
|
||||
|
||||
One intermediate container command exported only `TEST_DATABASE_URL`, leaving request sessions on the live Compose `DATABASE_URL`. Exact synthetic artifacts were removed transactionally and verified absent. All subsequent commands set both variables to the disposable database.
|
||||
@@ -0,0 +1,9 @@
|
||||
# Three-Dinner Meal Media
|
||||
|
||||
Completed 2026-09-01 with one-time approval.
|
||||
|
||||
- Root causes: bundled starter recipes intentionally had null image fields; calories were present in catalog, DB, and API but absent from Dashboard and meal-detail rendering.
|
||||
- UI: meal cards display kcal; meal details display kcal/serving; all recipe image surfaces resolve local static URLs through the active Home Assistant ingress prefix.
|
||||
- Backend: successful OpenAI generation records `image_source=openai:<model>` alongside the persisted local URL.
|
||||
- Production: generated and persisted `gpt-image-1` PNGs for Baked Ziti with Spinach, Beefy Stuffed Bell Peppers, and Red Bean Rice Pot only. Existing historical/quarantined rows were untouched.
|
||||
- Verification: 13 frontend tests, TypeScript, Vite build, backend image test, Python compilation, and scoped diff checks passed. Live API/DB assertions confirmed calories, image URLs, active status, and provenance; all images returned HTTP 200 `image/png`. Backend and DB are healthy, and all five services are running.
|
||||
@@ -0,0 +1,7 @@
|
||||
# Three-Dinner Planner Routing
|
||||
|
||||
Root cause was two user-facing bypasses around the canonical planner: Dashboard bulk generation called the random seven-day `fill-empty-slots` endpoint, and LLM generation inserted up to seven picks before alphabetic fallback.
|
||||
|
||||
Added session-authenticated `POST /api/meal-plans/generate` and routed Dashboard bulk generation through it. The endpoint derives family ownership from the signed session, replaces only an empty same-week shell, and returns 409 for populated plans. LLM picks are now limited to three unique dinners and are accepted only through the canonical planner; infeasible picks fall back to full canonical selection. Planner config and API schemas enforce `set_size == 3`.
|
||||
|
||||
Regression coverage proves exact dinner days 1/3/5, ACTIVE and calorie eligibility, invalid persisted override fallback, session auth, populated-plan preservation, empty-plan replacement, and LLM schedule limits. Verification passed: 11 focused backend tests on disposable PostgreSQL, 11 frontend tests, TypeScript/Vite build, Python compilation, and scoped diff checks. Remote test resources were removed.
|
||||
@@ -0,0 +1,9 @@
|
||||
# Wikibooks Live Compatibility
|
||||
|
||||
- Fixed live title validation for plural dish names and bounded Roman recipe variants while preserving strict quantity, serving, checksum, and distinctive ingredient checks.
|
||||
- Added safe staged-row revalidation: unreviewed Wikibooks rows only, `FOR UPDATE SKIP LOCKED`, checksum mismatch quarantine, no commit, and reviewed-state exclusion.
|
||||
- Added a captured live Apple Pie payload and regressions for valid/invalid live title patterns and revalidation transitions.
|
||||
- Verification: 36 offline tests; 55 combined tests on disposable PostgreSQL; compile and scoped diff checks passed.
|
||||
- Deployed backend and scheduler to `docker-willester`; backend became healthy and loaded the new validator/revalidation method.
|
||||
- Revalidated and expanded the bounded queue. User approved only `Rigatoni Pasta with Ricotta in Tomato Cream Sauce`.
|
||||
- Production now has one active licensed Wikibooks recipe. The exact Thursday dinner Generate request returned 200 and persisted the linked meal item; legacy recipes remain quarantined.
|
||||
Reference in New Issue
Block a user