Technical Design TD-002 — Quick Meals & Consumption¶
| Field | Value |
|---|---|
| ID | TD-002 |
| Title | Quick Meals & Consumption |
| Status | Draft |
| Owner | @souperk |
| Audience | contributors |
| Created | 2026-02-07 |
| Last Updated | 2026-02-07 |
| Reviewers | TBD |
| Approvers | TBD |
| Tracking | TBD |
| Related | v0.2 Roadmap, TD-001 |
1. Summary¶
- Logging common meals (e.g., yogurt + granola + honey) currently requires multiple API calls
- Quick meal templates save ingredient+quantity combos for one-tap consumption
- One-tap consumption deducts from inventory via FIFO without navigating the recipe→cook flow
- The "almost cookable" enhancement surfaces recipes where only 1–2 items are missing
2. Context and Problem Statement¶
- Users eat the same simple meals repeatedly (breakfast combos, snacks, quick lunches)
- The full recipe→cook flow is too heavyweight for these everyday meals
- Users want a "just deduct it" action without creating recipes or tracking prepared food portions
- The binary cookability check misses recipes that are almost cookable, reducing meal planning utility
2.1 Goals¶
- Quick meal templates with saved ingredient+quantity combos
- One-tap consumption endpoint that deducts via FIFO from active inventory
- "Almost cookable" threshold on cookability check
- Recipe suggestions endpoint returning cookable + almost-cookable recipes
2.2 Out of Scope¶
- Prepared food creation from quick meals (quick meals are consumption-only)
- Shopping suggestions for missing ingredients (v0.3)
- Nutrition tracking for quick meals (v0.5)
2.3 Assumptions and Constraints¶
- Quick meals are simpler than recipes — no instructions, no tags, no servings
- One-tap consumption uses the same FIFO logic as cook-recipe
- "Almost cookable" threshold is specified as a count of missing ingredients (not percentage)
3. Design Overview¶
- New
QuickMealentity withQuickMealItemchildren - One-tap consumption endpoint that accepts a quick meal ID and deducts all items
- Enhancement to
RecipeService.check_cookabilitywithmissing_thresholdparameter - New recipe suggestions endpoint combining fully cookable and almost-cookable recipes
3.1 Key Components¶
| Component | Purpose |
|---|---|
QuickMeal | Entity storing a named set of ingredient+quantity combos |
QuickMealItem | Child entity with ingredient_id, quantity, unit |
QuickMealRepository | Data access for quick meals |
QuickMealService | CRUD + one-tap consumption logic |
QuickMealRouter | FastAPI router at /quick-meal |
3.2 Architecture¶
graph TB
QMRouter[QuickMealRouter] --> QMService[QuickMealService]
QMService --> QMRepo[QuickMealRepository]
QMService --> InvService[InventoryService]
RecipeRouter[RecipeRouter] --> RecipeService[RecipeService]
RecipeService --> InvRepo[InventoryBatchRepository] 3.3 Interface Impact (Before → After)¶
| Area | Before | After |
|---|---|---|
| Inventory consumption | Only via cook-recipe or manual consume | Also via one-tap quick meal |
| Cookability check | Binary: cookable or not | Supports missing_threshold parameter |
| Recipe discovery | No suggestion endpoint | GET /recipe/suggestions with cookable + almost-cookable |
4. Implementation Details¶
4.1 Core Contracts and APIs¶
QuickMeal entity:
class QuickMeal(BaseAggregate):
name: Mapped[str]
user_id: Mapped[str]
items: Mapped[list[QuickMealItem]]
QuickMealItem entity:
class QuickMealItem(BaseEntity, AuditMixin):
quick_meal_id: Mapped[int]
ingredient_id: Mapped[int]
quantity: Mapped[Decimal]
unit: Mapped[str]
API endpoints:
| Method | Path | Description |
|---|---|---|
POST | /quick-meal | Create quick meal template |
GET | /quick-meal | List quick meals |
GET | /quick-meal/{id} | Get single quick meal |
PATCH | /quick-meal/{id} | Update quick meal |
DELETE | /quick-meal/{id} | Delete quick meal |
POST | /inventory/quick-consume | One-tap consumption (deduct all items) |
GET | /recipe/{id}/cookability | Enhanced with missing_threshold query param |
GET | /recipe/suggestions | Cookable + almost-cookable recipes |
4.2 Component Architecture¶
QuickMealextendsBaseAggregatewith standard column helpersQuickMealItemextendsBaseEntity+AuditMixin- Parent-child via
relationship(cascade="all, delete-orphan", lazy="selectin") - Repository extends
BaseRepository
4.3 Integration Points¶
- One-tap consumption: Reuses
InventoryServiceFIFO consumption logic - Cookability enhancement: Modifies existing
RecipeService.check_cookabilityto accept threshold - Recipe suggestions: New method on
RecipeServicequerying all user recipes and checking cookability
4.4 Error Handling and Edge Cases¶
| Error | When | Response |
|---|---|---|
QuickMealNotFoundError | ID doesn't exist | 404 |
InsufficientInventoryError | Not enough stock for one-tap consumption | 400 (with details of which items are short) |
EmptyQuickMealError | Quick meal has no items | 400 |
Edge cases:
- One-tap consumption is atomic: if any item has insufficient stock, the entire operation fails
- Quick meal items can reference ingredients that no longer exist; validate on consumption, not on template save
- Almost-cookable threshold of 0 is equivalent to "fully cookable" (backward compatible)
5. Semantics¶
- Quick meals are templates, not recipes — they have no instructions, servings, or tags
- One-tap consumption creates
InventoryActivityrecords of typeCONSUMEDfor each deducted item - The
missing_thresholdparameter counts distinct missing ingredients, not total missing quantity - Recipe suggestions are ordered: fully cookable first, then almost-cookable sorted by missing count ascending
6. Examples¶
6.1 Create Quick Meal¶
# POST /quick-meal
{
"name": "Morning Yogurt Bowl",
"user_id": "user-123",
"items": [
{"ingredient_id": 1, "quantity": "200", "unit": "g"},
{"ingredient_id": 2, "quantity": "50", "unit": "g"},
{"ingredient_id": 3, "quantity": "1", "unit": "tbsp"}
]
}
6.2 One-Tap Consumption¶
# POST /inventory/quick-consume
{
"quick_meal_id": 5,
"user_id": "user-123"
}
# Response: list of InventoryActivity records created
6.3 Almost-Cookable Check¶
# GET /recipe/42/cookability?user_id=user-123&missing_threshold=2
{
"cookable": false,
"missing_ingredients": [
{"ingredient_id": 10, "name": "Basil", "required": "10g", "available": "0g"}
],
"missing_count": 1,
"within_threshold": true # 1 missing <= 2 threshold
}
7. Test Matrix¶
- Unit: QuickMeal entity, threshold logic in cookability check
- Integration: Quick meal CRUD, one-tap consumption with FIFO, cookability with threshold, recipe suggestions
- Edge cases: Empty quick meal, insufficient stock (partial), threshold=0, ingredient deleted after template save
8. Acceptance Criteria¶
- Quick meal CRUD endpoints functional
- One-tap consumption deducts from inventory via FIFO
- One-tap consumption is atomic (all-or-nothing)
- Cookability check supports
missing_thresholdparameter - Recipe suggestions returns ordered list of cookable + almost-cookable
-
InventoryActivityrecords created for each deduction - All error cases return appropriate HTTP status codes
- Migration v08 applies cleanly
9. Rollout and Migration¶
9.1 Phased Plan¶
Phase 1: QuickMeal aggregate, repository, service, router, migration v08
Phase 2: One-tap consumption endpoint with FIFO integration
Phase 3: Cookability threshold enhancement and recipe suggestions endpoint
9.2 Migration Strategy¶
- New tables only (
quick_meals,quick_meal_items) - No changes to existing tables
- No new enums required
10. Risks and Mitigations¶
| Risk | Mitigation |
|---|---|
| One-tap consumption atomicity under concurrent access | Use database-level row locking on batch consumption |
| Recipe suggestions query slow with many recipes | Limit to user's recipes, add index on user_id, consider pagination |
| Quick meal templates referencing deleted ingredients | Validate on consumption, return clear error with ingredient details |
11. Progress¶
- Entity design
- Repository implementation
- Service implementation
- Quick meal API endpoints + schemas
- One-tap consumption endpoint
- Cookability threshold enhancement
- Recipe suggestions endpoint
- Migration v08
- Tests
12. Changelog¶
| Date | Change |
|---|---|
| 2026-02-07 | Initial draft |