Public Access
docs: add HANDOFF.md for new agent takeover
This commit is contained in:
+316
@@ -0,0 +1,316 @@
|
||||
# MealPlanner - Agent Handoff Document
|
||||
|
||||
**Project**: MealPlanner - Self-hosted meal planning system
|
||||
**Last Updated**: 2026-05-04
|
||||
**Last Agent**: OpenCode (MealPlanner session)
|
||||
|
||||
---
|
||||
|
||||
## Project Overview
|
||||
|
||||
A self-hosted meal planning system for a family of 4 (2 adults, 2 children) that integrates with Lucky California grocery store. Key problem: replacing meal kit services (Blue Apron → EveryPlate → HungryRoot → Sunbasket) that suffer from 3x ingredient markup, repetitive meals, and no pantry integration.
|
||||
|
||||
### Core Workflow
|
||||
1. System generates 7-day meal plan based on Lucky California sales, family dietary constraints, budget, and home pantry items
|
||||
2. Email sent to adults with meal proposals and approval links
|
||||
3. Adults click email link → confirmation page → POST vote (Approve/Deny)
|
||||
4. Per-voter tokens (single-use, 72h TTL)
|
||||
5. Majority approve → meal confirmed; any deny → swap
|
||||
6. Shopping list generated after approval
|
||||
|
||||
### Family Constraints
|
||||
- 3 of 4 family members do NOT like mushrooms
|
||||
- No allergies
|
||||
- Budget-conscious but wants tasty food
|
||||
|
||||
---
|
||||
|
||||
## Architecture
|
||||
|
||||
```
|
||||
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 via nginx)
|
||||
- **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, Playwright, BeautifulSoup
|
||||
- Frontend: React 18, TypeScript, Tailwind CSS, React Query, Vite
|
||||
- Database: PostgreSQL 15 with ENUMs and CITEXT
|
||||
- Email: SendGrid
|
||||
- Hosting: Docker Compose, nginx
|
||||
|
||||
---
|
||||
|
||||
## Current Git State
|
||||
|
||||
```
|
||||
git log --oneline -10
|
||||
9458edf docs: update ORIENTATION.md with Phase 3/7 progress and current git state
|
||||
27bca0e docs: update ORIENTATION.md phase table
|
||||
08e196b feat: implement frontend Web UI pages
|
||||
933a0cc feat: implement Lucky California scraper with Playwright + BeautifulSoup
|
||||
c735d21 feat: implement Phase 2 - Alembic migrations, Pydantic schemas, and real API endpoints
|
||||
e8706d3 docs: final ORIENTATION update
|
||||
624b516 docs: update ORIENTATION.md for Phase 1 complete
|
||||
1328ec3 feat: add Phase 1 infrastructure skeleton
|
||||
0c5b0aa docs: add complete project documentation
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Implementation Phases
|
||||
|
||||
| Phase | Description | Status |
|
||||
|-------|-------------|--------|
|
||||
| 1 | Infrastructure (Docker, PostgreSQL, FastAPI, React, nginx) | **Complete** |
|
||||
| 2 | Database & Models (Alembic migrations, Pydantic schemas, API endpoints) | **Complete** |
|
||||
| 3 | Lucky California Scraper (BeautifulSoup + Playwright, ScraperService) | **Complete** |
|
||||
| 4 | Recipe Engine (CRUD, tagging, search) | Not Started |
|
||||
| 5 | Meal Planner Engine (generation algorithm, substitutions) | Not Started |
|
||||
| 6 | SendGrid Email Integration (meal proposal emails) | Not Started |
|
||||
| 7 | Web UI - Core (Dashboard, Meal Detail, Pantry, Shopping List) | **Complete** |
|
||||
| 8 | Web UI - Feedback (Feedback Portal, Learning) | Not Started |
|
||||
| 9 | Meal Planner Generation Algorithm | Not Started |
|
||||
| 10 | Image Strategy (scraped + AI fallback) | Not Started |
|
||||
| 11 | Polish & Future (variety analysis, budget tracking) | Not Started |
|
||||
|
||||
---
|
||||
|
||||
## Key Files and Locations
|
||||
|
||||
### Backend Structure
|
||||
```
|
||||
backend/
|
||||
├── alembic/
|
||||
│ ├── env.py # Alembic configuration
|
||||
│ └── versions/
|
||||
│ ├── 0001_initial_migration.py # Full schema (enums, tables, indexes, constraints)
|
||||
│ └── 0002_seed_data.py # Basic ingredients (70+), family profile, family members
|
||||
├── app/
|
||||
│ ├── api/
|
||||
│ │ ├── admin.py # /api/admin/* (scrape trigger, logs, stats)
|
||||
│ │ ├── meals.py # /api/meals/* (meal plans, voting, approval tokens)
|
||||
│ │ ├── pantry.py # /api/pantry/* (CRUD for home pantry)
|
||||
│ │ ├── profile.py # /api/profile/* (family profile, members)
|
||||
│ │ ├── recipes.py # /api/recipes/* (CRUD, ingredients, filtering)
|
||||
│ │ └── shopping_list.py # /api/shopping-list/* (aggregation, print HTML)
|
||||
│ ├── config.py # Pydantic Settings (reads from .env)
|
||||
│ ├── database.py # SQLAlchemy engine, SessionLocal, get_db
|
||||
│ ├── models/
|
||||
│ │ └── __init__.py # All SQLAlchemy models (FamilyProfile, Recipe, MealPlan, etc.)
|
||||
│ ├── schemas/
|
||||
│ │ └── __init__.py # All Pydantic schemas for API request/response
|
||||
│ ├── scraper/
|
||||
│ │ ├── base.py # BaseScraper with rate limiting, retries, session management
|
||||
│ │ ├── lucky_ca_scraper.py # LuckyCaliforniaScraper with BeautifulSoup + Playwright
|
||||
│ │ └── __init__.py
|
||||
│ └── services/
|
||||
│ └── scraper_service.py # ScraperService to save scraped items to grocery_item table
|
||||
├── alembic.ini
|
||||
├── Dockerfile
|
||||
└── requirements.txt
|
||||
```
|
||||
|
||||
### Frontend Structure
|
||||
```
|
||||
frontend/
|
||||
├── src/
|
||||
│ ├── api/
|
||||
│ │ └── index.ts # mealPlannerApi wrapper for all endpoints
|
||||
│ ├── pages/
|
||||
│ │ ├── Dashboard.tsx # Weekly meal plan grid view
|
||||
│ │ ├── MealDetail.tsx # Recipe display with ingredients/instructions
|
||||
│ │ ├── Pantry.tsx # Add/remove pantry items
|
||||
│ │ └── ShoppingList.tsx # Grouped by aisle with sale highlighting
|
||||
│ ├── types/
|
||||
│ │ └── index.ts # TypeScript interfaces for all models
|
||||
│ ├── App.tsx # React Router with /, /meals/:id, /pantry, /shopping-list
|
||||
│ └── vite-env.d.ts # Vite env types
|
||||
├── Dockerfile
|
||||
├── package.json
|
||||
└── vite.config.ts
|
||||
```
|
||||
|
||||
### Documentation
|
||||
```
|
||||
docs/
|
||||
├── ARCHITECTURE.md # System architecture diagram
|
||||
├── database-schema.md # Complete PostgreSQL schema reference
|
||||
├── implementation-plan.md # Detailed phase-by-phase plan
|
||||
├── ORIENTATION.md # First-stop guide for new agents (READ THIS)
|
||||
├── RUNNING.md # Deployment guide
|
||||
└── SPEC.md # Project specification
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Database Schema Highlights
|
||||
|
||||
### Core Tables
|
||||
- `family_profile` - Household with CHECK(household_size > 0)
|
||||
- `family_member` - Individual 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 enum
|
||||
- `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 CA items with FK to ingredient
|
||||
- `scrape_log` / `email_log` - Operation history
|
||||
|
||||
### Key Enums
|
||||
- `meal_plan_status_enum`: draft, pending_approval, approved, locked
|
||||
- `meal_plan_item_status_enum`: pending, approved, denied, swapped
|
||||
- `approval_token_status_enum`: active, used, expired
|
||||
- `denial_reason_enum`: too_expensive, boring, disliked_ingredient, cultural, other
|
||||
|
||||
### Approval Flow (REDESIGNED)
|
||||
1. Email contains unique token per family member (not shared)
|
||||
2. Token expires after 72 hours
|
||||
3. Token can only be used once (marked USED after voting)
|
||||
4. Email link → GET confirmation page (NOT auto-approve)
|
||||
5. Vote submitted via POST
|
||||
|
||||
---
|
||||
|
||||
## Configuration
|
||||
|
||||
### Environment Variables (.env)
|
||||
```bash
|
||||
DATABASE_URL=postgresql://mealplanner:password@db:5432/mealplanner
|
||||
SENDGRID_API_KEY=SG.xxx
|
||||
FAMILY_EMAIL_1=you@example.com
|
||||
FAMILY_EMAIL_2=spouse@example.com
|
||||
LUCKY_CA_URL=https://luckysupermarkets.com
|
||||
AI_IMAGE_ENABLED=false
|
||||
LOG_LEVEL=INFO
|
||||
SECRET_KEY=change-me-to-a-random-secret-key
|
||||
```
|
||||
|
||||
### Key URLs
|
||||
- Lucky California: https://luckysupermarkets.com (verified scrapeable)
|
||||
- robots.txt: Allows all
|
||||
|
||||
---
|
||||
|
||||
## Verification Commands
|
||||
|
||||
```bash
|
||||
# Docker build verification
|
||||
docker compose config
|
||||
docker compose build backend
|
||||
docker compose build frontend
|
||||
|
||||
# Backend import test
|
||||
docker compose run --rm backend python -c "from app.main import app; print(app.title)"
|
||||
|
||||
# Alembic migration test (requires running db)
|
||||
docker compose run --rm backend alembic upgrade head
|
||||
|
||||
# Frontend build test
|
||||
docker compose run --rm frontend npm run build
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Known Issues / Open Questions
|
||||
|
||||
1. **Lucky California scraping**: Site uses dynamic content (JS rendering). The scraper uses Playwright for browser automation, but actual scraping hasn't been tested with a live database yet. May need adjustment based on actual page structure.
|
||||
|
||||
2. **AI image generation**: Config flag `AI_IMAGE_ENABLED=false`. Not implemented - just a placeholder for future.
|
||||
|
||||
3. **WhatsApp integration**: Planned for future via Twilio. Out of scope for MVP.
|
||||
|
||||
4. **Planned scheduler**: APScheduler with `--workers 1` to avoid duplicate fires. Not yet implemented.
|
||||
|
||||
5. **Meal planner algorithm**: Not implemented. Need to create algorithm that:
|
||||
- Filters out never_suggest ingredients/recipes
|
||||
- Prioritizes home_pantry items
|
||||
- Incorporates sale items from grocery_item
|
||||
- Ensures variety (max 2 same protein/sauce per week)
|
||||
- Respects mushroom preference (3 of 4 don't like)
|
||||
|
||||
6. **SendGrid email integration**: Not implemented. Need:
|
||||
- Email templates for meal proposals
|
||||
- Approval email with per-voter tokens
|
||||
- Reminder emails before deadline
|
||||
- Confirmation emails after approval
|
||||
|
||||
---
|
||||
|
||||
## Next Steps (Priority Order)
|
||||
|
||||
### 1. Implement SendGrid Email Integration (Phase 6)
|
||||
- Create `backend/app/services/email_service.py`
|
||||
- Implement meal proposal email template
|
||||
- Connect to meal plan creation flow
|
||||
- Use approval tokens for email links
|
||||
|
||||
### 2. Implement Meal Planner Generation Algorithm (Phase 9)
|
||||
- Create `backend/app/services/meal_planner_service.py`
|
||||
- Load constraints (family profile, dietary, budget)
|
||||
- Filter never_suggest ingredients/recipes
|
||||
- Prioritize home_pantry items
|
||||
- Incorporate sale items
|
||||
- Ensure variety requirements
|
||||
|
||||
### 3. Implement Recipe Engine (Phase 4)
|
||||
- Recipe CRUD already exists but needs testing
|
||||
- Tag-based filtering implemented in API
|
||||
- Search functionality needed
|
||||
|
||||
### 4. Implement Web UI - Feedback (Phase 8)
|
||||
- Feedback Portal page
|
||||
- Rating submission (1-5 stars)
|
||||
- "Never suggest this" flag
|
||||
- Learning integration
|
||||
|
||||
---
|
||||
|
||||
## 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
|
||||
|
||||
### API Design
|
||||
- RESTful endpoints with proper HTTP methods
|
||||
- Pydantic schemas for request/response validation
|
||||
- Email approval links use GET → confirmation page → POST
|
||||
- Per-voter tokens, not shared household tokens
|
||||
|
||||
### Database
|
||||
- UUIDs for primary keys
|
||||
- Timestamps with timezone (TIMESTAMPTZ)
|
||||
- ENUMs for status fields (not loose VARCHAR)
|
||||
- CITEXT or lowercase-on-write for name matching
|
||||
|
||||
---
|
||||
|
||||
## Contacts
|
||||
|
||||
- **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)
|
||||
|
||||
---
|
||||
|
||||
## File: ORIENTATION.md
|
||||
|
||||
**IMPORTANT**: Read `docs/ORIENTATION.md` first before doing anything else. It contains the authoritative project state, phase table, and next steps.
|
||||
Reference in New Issue
Block a user