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