Public Access
fix: address adversarial review blockers
All §1 consensus blockers and §2 high-risk gaps resolved: Schema fixes: - Remove RecipeIngredient join table, use JSONB for ingredients - Add family_member table for per-voter approval tracking - Add all ENUMs for status fields (no loose VARCHAR) - Add CHECK constraints (household_size, rating 1-5, day_of_week) - Add name_lower for case-insensitive ingredient matching - Add grocery_item → ingredient FK - Fix day_of_week to ISO-8601 (1=Monday, 7=Sunday) - Remove calorie_target (nutrition is non-goal) Approval flow redesign: - Email link → confirmation page (GET), not auto-approve - Actual vote is POST from confirmation page - Per-voter tokens (single-use, 72h TTL) - Record which member voted Auth model: - VPN-only for admin endpoints - Session-based for family web UI Docker hardening: - Remove direct port exposure for backend/frontend - nginx is sole entrypoint - Add docker-compose.dev.yml for local dev Skeleton fixes: - Add missing Pantry.tsx page - Add missing index.html (Vite entrypoint) - Add package-lock.json - Fix SQLAlchemy 2 text() for raw SQL - Remove create_all from startup (use migrations) - Configure Alembic properly Docs updates: - Update Lucky URL to luckysupermarkets.com - Add WCAG 2.1 AA accessibility target - Update family profile with correct mushroom preferences - Add external dependencies list to SPEC Verification: - docker compose config: PASS - docker compose build backend: PASS - docker compose build frontend: PASS - backend import: PASS - alembic context: PASS
This commit is contained in:
+90
-91
@@ -18,28 +18,20 @@ The family has been using meal kit services (Blue Apron → EveryPlate → Hungr
|
||||
|
||||
### Current Status
|
||||
|
||||
**Phase**: Phase 1 complete. Phase 2 (Database & Models) next.
|
||||
**Phase**: Post-adversarial-review fixes applied. Ready for verification.
|
||||
|
||||
Infrastructure skeleton is committed:
|
||||
- docker-compose.yml with 4 services (backend, frontend, db, nginx)
|
||||
- FastAPI backend with placeholder API routes
|
||||
- React frontend with Vite + Tailwind + placeholder pages
|
||||
- nginx reverse proxy config
|
||||
- SQLAlchemy models created (not yet connected to real endpoints)
|
||||
|
||||
---
|
||||
|
||||
## Key Files
|
||||
|
||||
| File | Purpose |
|
||||
|------|---------|
|
||||
| `docs/SPEC.md` | Full project specification (goals, constraints, user stories) |
|
||||
| `docs/ARCHITECTURE.md` | System architecture, component descriptions, data flow |
|
||||
| `docs/database-schema.md` | PostgreSQL schema with all tables, indexes, relationships |
|
||||
| `docs/implementation-plan.md` | 12-phase implementation plan with verification commands |
|
||||
| `docs/RUNNING.md` | Deployment guide, troubleshooting, environment setup |
|
||||
| `README.md` | Project overview and quick start |
|
||||
| `meal-planner-plan.md` | Original planning file (may be superseded) |
|
||||
**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
|
||||
|
||||
---
|
||||
|
||||
@@ -49,19 +41,20 @@ Infrastructure skeleton is committed:
|
||||
User (email) ──► SendGrid ───────────────────────────────┐
|
||||
User (web) ───► React UI ──► nginx ──► FastAPI ──────────┼──► PostgreSQL
|
||||
│ │
|
||||
└──► Lucky California scraper ──┘
|
||||
└──► Lucky CA scraper ──┘
|
||||
(luckysupermarkets.com)
|
||||
```
|
||||
|
||||
### Services (Docker Compose)
|
||||
- **backend**: FastAPI Python app (port 8000)
|
||||
- **frontend**: React + Tailwind (port 3000, served via nginx)
|
||||
- **db**: PostgreSQL 15 (port 5432)
|
||||
- **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, Alembic
|
||||
- Frontend: React 18, TypeScript, Tailwind CSS, React Query
|
||||
- Database: PostgreSQL 15
|
||||
- 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
|
||||
@@ -70,39 +63,56 @@ User (web) ───► React UI ──► nginx ──► FastAPI ────
|
||||
|
||||
## Family Profile
|
||||
|
||||
- **Household**: 2 adults, 2 children
|
||||
- **Dietary**: One adult + one child like mushrooms; other adult + one child do NOT
|
||||
- **Goals**: Calorie, budget, and health conscious; tasty but not expensive
|
||||
- **No allergies**
|
||||
### 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
|
||||
|
||||
### Approval Workflow
|
||||
### 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 both adults with meals, images, Approve/Deny links
|
||||
3. One denial → meal swapped with alternative
|
||||
4. No denials (or silence) → meal auto-approved
|
||||
5. After all approvals → shopping list generated
|
||||
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
|
||||
- `recipe` - All recipes with ingredients (JSONB), instructions, image URLs
|
||||
- `ingredient` - Master ingredient list with aisle, price, season
|
||||
- `meal_plan` - Weekly plan (7 days)
|
||||
- `meal_plan_item` - Individual meal with approval_token, status
|
||||
- `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 sale prices
|
||||
- `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_profile` 1:N `home_pantry`
|
||||
- `recipe` N:N `ingredient` (via `recipe_ingredient` junction table)
|
||||
- `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:1 `feedback`
|
||||
- `meal_plan_item` 1:N `meal_plan_vote`
|
||||
- `meal_plan_item` 1:N `approval_token`
|
||||
- `grocery_item` → `ingredient` (FK)
|
||||
|
||||
---
|
||||
|
||||
@@ -111,7 +121,7 @@ User (web) ───► React UI ──► nginx ──► FastAPI ────
|
||||
| Phase | Description | Status |
|
||||
|-------|-------------|--------|
|
||||
| 1 | Infrastructure (Docker, PostgreSQL, FastAPI, React, nginx) | **Complete** |
|
||||
| 2 | Database & Models (SQLAlchemy models, Alembic migrations) | Not Started |
|
||||
| 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 |
|
||||
@@ -125,7 +135,7 @@ User (web) ───► React UI ──► nginx ──► FastAPI ────
|
||||
|
||||
---
|
||||
|
||||
## Environment Variables Required
|
||||
## Environment Variables
|
||||
|
||||
```bash
|
||||
# Database
|
||||
@@ -140,39 +150,51 @@ FAMILY_EMAIL_1=user@example.com
|
||||
FAMILY_EMAIL_2=spouse@example.com
|
||||
|
||||
# Scraping
|
||||
LUCKY_CA_URL=https://www.luckyncal.com
|
||||
LUCKY_CA_URL=https://luckysupermarkets.com
|
||||
|
||||
# AI Images (optional)
|
||||
AI_IMAGE_ENABLED=false
|
||||
|
||||
# Auth
|
||||
SECRET_KEY=change-me-in-production
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Next Steps
|
||||
|
||||
### IMMEDIATE: Phase 2 - Database & Models
|
||||
### IMMEDIATE: Verify skeleton with verification matrix
|
||||
|
||||
1. Set up Alembic for migrations
|
||||
2. Create actual database tables from SQLAlchemy models
|
||||
3. Add seed data (basic ingredients, sample recipes)
|
||||
4. Create Pydantic schemas for API validation
|
||||
5. Implement real API endpoints (not just placeholders)
|
||||
```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
|
||||
```
|
||||
|
||||
### After Phase 2 Complete
|
||||
If any of these fail, fix before proceeding.
|
||||
|
||||
- Verify migrations: `docker-compose exec backend alembic upgrade head`
|
||||
- Test database connectivity: `curl localhost:8000/health/db`
|
||||
- Test API endpoints with real data
|
||||
### After Verification
|
||||
|
||||
### Current Git State
|
||||
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
|
||||
```
|
||||
|
||||
Phase 1 skeleton complete and committed. Phase 2 (Database) is next.
|
||||
**Pending commit**: All adversarial review fixes (models, schema, docker-compose, docs updates)
|
||||
|
||||
---
|
||||
|
||||
@@ -190,12 +212,14 @@ Phase 1 skeleton complete and committed. Phase 2 (Database) is next.
|
||||
### API Design
|
||||
- RESTful endpoints with proper HTTP methods and status codes
|
||||
- Pydantic schemas for request/response validation
|
||||
- JWT-free for MVP (simple token-based auth for email approval links)
|
||||
- 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)
|
||||
- Soft deletes preferred over hard deletes where applicable
|
||||
- Use ENUMs for status fields (not loose VARCHAR)
|
||||
- Use CITEXT or lowercase-on-write for name matching
|
||||
|
||||
### Testing
|
||||
- Write unit tests for services (pytest)
|
||||
@@ -204,37 +228,12 @@ Phase 1 skeleton complete and committed. Phase 2 (Database) is next.
|
||||
|
||||
---
|
||||
|
||||
## Common Tasks
|
||||
|
||||
### Run full stack
|
||||
```bash
|
||||
docker-compose up -d
|
||||
```
|
||||
|
||||
### Create database migration
|
||||
```bash
|
||||
docker-compose exec backend alembic revision --autogenerate -m "description"
|
||||
```
|
||||
|
||||
### Trigger scrape manually
|
||||
```bash
|
||||
curl -X POST http://localhost:8000/api/admin/scrape \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"source": "lucky_california", "type": "weekly_ad"}'
|
||||
```
|
||||
|
||||
### View all logs
|
||||
```bash
|
||||
docker-compose logs -f
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Known Issues / Open Questions
|
||||
|
||||
1. **Lucky California scraping**: May need adjustment if website structure changes. Has fallback to manual input.
|
||||
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.
|
||||
|
||||
---
|
||||
|
||||
@@ -244,4 +243,4 @@ docker-compose logs -f
|
||||
- **Wife**: Non-technical, will use web UI and email
|
||||
- **Children**: 2, eating habits vary (one OK with mushrooms)
|
||||
|
||||
Last updated: 2026-05-04 (Phase 1 complete, Phase 2 next)
|
||||
Last updated: 2026-05-04 (post adversarial review fixes)
|
||||
|
||||
Reference in New Issue
Block a user