# Meal Planner - Orientation Guide This document is the **first stop** for any agent resuming work on this project after context compaction. Read it before doing anything else. --- ## Project Overview **MealPlanner** is a self-hosted meal planning system for a family of 4 (2 adults, 2 children) that integrates with Lucky California grocery store to source ingredients from weekly sales, sends meal proposals via email, generates shopping lists, and learns from feedback. ### Key Problem Being Solved The family has been using meal kit services (Blue Apron → EveryPlate → HungryRoot → Sunbasket) which suffer from: - 3x ingredient markup in cost - Repetitive meals and sauces - Forcing app login to manage meal selection - No home pantry integration ### Current Status **Phase**: Phase 2 (Database & Models) IN PROGRESS. Migrations created, schemas implemented, API endpoints implemented. **Adversarial Review Completed**: 2026-05-04 - All consensus blockers (§1.1 - §1.8) addressed - All high-risk gaps (§2.1 - §2.8) addressed - Key fixes applied: - Recipe-ingredient: JSONB only (removed join table) - Family member: Added `family_member` table for per-voter tracking - Approval flow: Redesigned with confirmation page + POST + per-voter tokens + TTL - Auth: VPN-only for admin endpoints, session-based for family web UI - Schema: ENUMs, CHECKs, CITEXT for ingredients, ISO day_of_week (1=Mon) - Lucky URL: Fixed to `https://luckysupermarkets.com` - Docker: Hardened (no direct port exposure to backend/frontend) - Alembic: Configured with migration policy **Phase 2 Progress** (2026-05-04): - [x] Initial Alembic migration (0001_initial_migration.py) - [x] Seed data migration (0002_seed_data.py) - [x] Pydantic schemas for all models - [x] /api/profile endpoints (CRUD, family members) - [x] /api/recipes endpoints (CRUD, ingredients) - [x] /api/meals endpoints (meal plans, voting, approval tokens) - [x] /api/pantry endpoints (CRUD) - [x] /api/shopping-list endpoints (aggregation, print) - [x] /api/admin endpoints (scrape trigger, logs, stats) --- ## Architecture Summary ``` User (email) ──► SendGrid ───────────────────────────────┐ User (web) ───► React UI ──► nginx ──► FastAPI ──────────┼──► PostgreSQL │ │ └──► Lucky CA scraper ──┘ (luckysupermarkets.com) ``` ### Services (Docker Compose) - **backend**: FastAPI Python app (port 8000, internal only) - **frontend**: React + Tailwind (port 3000, internal only via nginx) - **db**: PostgreSQL 15 (internal only) - **nginx**: Reverse proxy with SSL (ports 80/443) ### Tech Stack - Backend: Python 3.11, FastAPI, SQLAlchemy 2.0, Alembic - Frontend: React 18, TypeScript, Tailwind CSS, React Query, Vite - Database: PostgreSQL 15 with ENUMs and CITEXT - Scraping: Playwright, BeautifulSoup - Email: SendGrid - Hosting: Docker Compose, nginx --- ## Family Profile ### Household - 2 adults, 2 children - 3 of 4 members do NOT like mushrooms - No allergies - Goals: Calorie, budget, and health conscious; tasty but not expensive ### Family Members | Member | Role | Mushroom Preference | |--------|------|-------------------| | Adult 1 | Adult | Does NOT like mushrooms | | Adult 2 | Adult | Likes mushrooms | | Child 1 | Child | Does NOT like mushrooms | | Child 2 | Child | OK with mushrooms | ### Approval Workflow (REDESIGNED) 1. System generates 7-day meal plan (Sunday) 2. Email sent to all adults with meals, images, approval page links 3. Email link → confirmation page (GET, not auto-approve) 4. Adult clicks Approve/Deny → POST with reason 5. Per-member token (single-use, 72h TTL) 6. Majority approve → meal confirmed; any deny → swap 7. After approval → shopping list generated --- ## Database Schema Highlights ### Core Tables - `family_profile` - Household configuration with household_size CHECK - `family_member` - Individual family members with email, role, mushroom preference - `recipe` - Recipes with JSONB ingredients (not join table) - `ingredient` - Master list with name_lower (CITEXT for case-insensitive matching) - `meal_plan` - Weekly plan with ISO day_of_week (1=Mon, 7=Sun) - `meal_plan_item` - Individual meal with approval status - `meal_plan_vote` - Per-member votes (one vote per member per meal) - `approval_token` - Single-use tokens with TTL and status tracking - `home_pantry` - Family's on-hand ingredients - `feedback` - Ratings, denial reasons, never-suggest flags - `grocery_item` - Scraped Lucky California items with FK to ingredient - `scrape_log` / `email_log` - Operation history ### Key Relationships - `family_profile` 1:N `family_member` - `family_profile` 1:N `meal_plan` - `family_member` 1:N `meal_plan_vote` (per-voter tracking) - `recipe` 1:N `meal_plan_item` - `meal_plan` 1:N `meal_plan_item` - `meal_plan_item` 1:N `meal_plan_vote` - `meal_plan_item` 1:N `approval_token` - `grocery_item` → `ingredient` (FK) --- ## Implementation Phases | Phase | Description | Status | |-------|-------------|--------| | 1 | Infrastructure (Docker, PostgreSQL, FastAPI, React, nginx) | **Complete** | | 2 | Database & Models (SQLAlchemy models, Alembic migrations) | **Post-review fixes applied** | | 3 | API Endpoints (CRUD, meal plans, shopping list, feedback) | Not Started | | 4 | Lucky California Scraper (weekly ad, Playwright) | Not Started | | 5 | Recipe Engine (CRUD, tagging, search) | Not Started | | 6 | Meal Planner Engine (generation algorithm, substitutions) | Not Started | | 7 | SendGrid Email Integration | Not Started | | 8 | Web UI - Core (Dashboard, Meal Detail, Approval, Pantry) | Not Started | | 9 | Web UI - Feedback (Feedback Portal, Learning) | Not Started | | 10 | Shopping List & Print | Not Started | | 11 | Image Strategy (scraped + AI fallback) | Not Started | | 12 | Polish & Future (variety analysis, budget tracking) | Not Started | --- ## Environment Variables ```bash # Database DATABASE_URL=postgresql://mealplanner:password@db:5432/mealplanner POSTGRES_PASSWORD=secure_password # SendGrid SENDGRID_API_KEY=SG.xxx # Family FAMILY_EMAIL_1=user@example.com FAMILY_EMAIL_2=spouse@example.com # Scraping LUCKY_CA_URL=https://luckysupermarkets.com # AI Images (optional) AI_IMAGE_ENABLED=false # Auth SECRET_KEY=change-me-in-production ``` --- ## Next Steps ### IMMEDIATE: Verify skeleton with verification matrix ```bash docker compose config docker compose build backend docker compose build frontend docker compose up -d db docker compose run --rm backend python -c "from app.main import app; print(app.title)" docker compose run --rm backend alembic upgrade head docker compose run --rm frontend npm run build ``` If any of these fail, fix before proceeding. ### After Verification 1. Phase 2: Implement real API endpoints (not placeholders) 2. Phase 3: Connect database models to endpoints 3. Phase 4: Spike Lucky California scrape (before committing to full schema) --- ## Current Git State ```bash git log --oneline 624b516 docs: update ORIENTATION.md for Phase 1 complete 1328ec3 feat: add Phase 1 infrastructure skeleton 0c5b0aa docs: add complete project documentation ``` **Pending commit**: All adversarial review fixes (models, schema, docker-compose, docs updates) --- ## Important Conventions ### Code Style - Python: Black formatter, isort for imports - TypeScript: Prettier, ESLint - No comments unless explaining non-obvious logic ### Git Commits - Conventional commits: `feat:`, `fix:`, `docs:`, `refactor:`, `test:` - One logical change per commit where possible ### API Design - RESTful endpoints with proper HTTP methods and status codes - Pydantic schemas for request/response validation - Email approval links use GET → confirmation page → POST - Per-voter tokens, not shared household tokens ### Database - Always use UUIDs for primary keys - Timestamps with timezone (TIMESTAMPTZ) - Use ENUMs for status fields (not loose VARCHAR) - Use CITEXT or lowercase-on-write for name matching ### Testing - Write unit tests for services (pytest) - Integration tests for API endpoints - Frontend: component tests with React Testing Library --- ## Known Issues / Open Questions 1. **Lucky California scraping**: Feasibility not yet spiked. URL is `luckysupermarkets.com`. 2. **AI image generation**: Config flag to enable/disable. Disabled by default. 3. **WhatsApp integration**: Planned for future via Twilio. Out of scope for MVP. 4. **Planned scheduler**: APScheduler with `--workers 1` to avoid duplicate fires. --- ## Contact / Context - **Primary user**: Peter (tech-savvy, hosts the system) - **Wife**: Non-technical, will use web UI and email - **Children**: 2, eating habits vary (one OK with mushrooms) Last updated: 2026-05-04 (all adversarial review fixes committed)