- DEFAULT_SUBSTITUTES_BY_INGREDIENT in seed-recipes.ts: ~50 mappature
riusabili (pesci interscambiabili, latticini lactose-free, manzo→tacchino,
pollo→tacchino, frutta secca, condimenti, legumi inter-cambiabili)
- 12 ricette signature ora dichiarano variants reali (1-2 ciascuna):
• Frittata di spinaci → senza lattosio / più saziante
• Yogurt greco con noci → vegana / con frutti di bosco
• Uova strapazzate con pancetta → senza maiale / con verdure
• Insalata di pollo, avocado e pomodorini → vegetariana / con feta e olive
• Salmone al forno con asparagi → versione fredda / sgombro
• Bistecca con zucchine → tacchino / con melanzane
• Insalatona con tonno e olive → Niçoise / sgombro
• Branzino al cartoccio → mediterranea / orata
• Polpette di pollo e ricotta → in sugo / tacchino-spinaci
• Tagliata con rucola → con pomodorini / bresaola
• Caprese → con avocado / con prosciutto crudo
• Pancake proteici, Smoothie verde, Tagliolini di zucchine,
Insalata di lenticchie, Quinoa con pollo (varianti analoghe)
- seed.ts ora propaga r.variants al campo Recipe.variants e applica
ing.substitutes (override) o DEFAULT_SUBSTITUTES_BY_INGREDIENT[name]
alle righe RecipeIngredient
Smoke verificato live: /recipes/<frittata> mostra "VARIANTI SUGGERITE"
con "Versione senza lattosio (-40 kcal)" e "Più saziante (+90 kcal)";
gli ingredienti hanno la riga "Sostituzioni: bietole, Grana Padano…".
Catalog: 46 ricette, 164 link RecipeIngredient, ~50 mappature substitutes.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Quattro completamenti del PRD §5.1: catalogo, varianti/sostituti, fase 2,
free meal.
Catalogo ricette 16 → 46
- seed-recipes.ts: +30 ricette italiane keto bilanciate
(8 colazione · 8 pranzo · 6 spuntino · 8 cena)
- seed-ingredients.ts: 33 → 55 ingredienti
(verdura/carne/pesce/latticini/condimenti + categorie nuove `legumi` e
`cereali` per Fase 2)
- seed.ts: ora fa upsert anche degli ingredienti pre-esistenti per
propagare campi nuovi (es. phase2Week)
Varianti + sostituti
- Recipe.variants Json (array { name, description, kcalDelta? })
- RecipeIngredient.substitutes String[]
- /recipes/[id]: nuova sezione "Varianti suggerite" + riga "Sostituzioni:"
sotto ogni ingrediente quando presenti
Reintroduzione progressiva Fase 2
- Ingredient.phase2Week (settimana minima di reintroduzione, NULL=sempre)
- User.phase2StartedAt: settato automaticamente quando si passa a TRANSITION
- @ketopath/shared/planner/phase-progression: currentPhase2Week,
isRecipeAllowedForPhaseWeek, weeksSince
- plan.routes filtra fuori le ricette con ingredienti non ancora reintrodotti
- POST /me/profile/phase per avanzare di fase (idempotente sul timestamp)
- 12 unit test su phase-progression
- Esempi seed: lenticchie phase2Week=3, ceci=4, mela=5, pera=6, quinoa=7
Free Meal pianificato Fase 3
- MealSlot.isFreeMeal Boolean (default false)
- POST /me/meal-plans/slots/:id/free-meal toggle, valido solo in MAINTENANCE
- @ketopath/shared/planner/phase-progression: freeMealKcalAdjustment
(compensazione clamp -200/+50 kcal per pasto sui rimanenti)
- /plan: bottone ☆ in ogni SlotCard quando phase=MAINTENANCE; lo slot
marcato come pasto libero rende "Pasto libero pianificato" + ~750 kcal
stimate; macro tracker ne tiene conto
Indicatori di fase nel piano
- /plan ora include currentPhase + phase2Week dalla API
- Header del piano: "Settimana N di Fase 2" o "Mantenimento — Fase 3"
i18n: namespaces Plan + Recipe arricchiti
Schema migration 20260429XXXXXX_add_meal_plan_extras.
66/66 unit test verdi (12 nuovi). Smoke verificato live: API ritorna
correctly i campi nuovi, UI rende badge fase, bottoni ☆/↻, free-meal slot.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Recipe detail
- Seed catalog of 33 italian ingredients with macros, allergens, avg price.
- Each seed recipe now declares its RecipeIngredient links (qty + unit).
- /recipes/[id] page: editorial layout with prep meta, ingredients list,
macros block, preparation, chef's note. Plan-week meal names linkable.
Shopping list
- GET /me/shopping-list aggregates RecipeIngredient quantities of the
ACTIVE plan, grouped by ingredient category, sorted alphabetically.
- /shopping page: total items, estimated cost, per-category checklist
with check-off persisted in localStorage per plan id.
- Home gets a "Spesa" nav item; /plan links the list when a plan exists.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@ketopath/shared:
- tracking/schema.ts — Zod schemas for WeightEntry input (weight, optional
measurements/notes/energy/sleep/hunger, photo URLs) and FastEvent start /
update, plus PROTOCOL_DEFAULT_MINUTES table
- planner/macros.ts — macrosForPhase(): protein 1.6-1.8g/kg, netCarb 25/60/120g
by phase, fat = remainder (PRD §9.3)
- planner/matchmaking.ts — pure matchMeals() that filters by exclusion tags,
phase compatibility and meal category, then scores by euclidean distance
from the meal's macro target with a 1.5 penalty for recently-consumed recipes;
DEFAULT_MEAL_SHARE constant (25/35/10/30 %)
- 5 new unit tests cover exclusion, phase, recency, topN, category mismatch
apps/api:
- modules/tracking/weight.routes.ts — GET /me/weight-entries (last 60),
POST /me/weight-entries with upsert on (userId, date); encrypted fields
serialised to/from JSON strings
- modules/tracking/fast.routes.ts — GET, POST (start) and PATCH (update) on
fast events; symptoms stored as encrypted JSON
- modules/plan/plan.routes.ts — POST /me/meal-plans:
- reads profile + preferences, computes BMR (Mifflin-St Jeor), TDEE, daily
kcal target with the phase-dependent deficit, then macros via macrosForPhase
- upserts a MealPlan rooted at Monday-of-this-week, wipes prior slots, and
fills 28 slots running matchMeals per (day, meal) with a recent-2-day
rolling exclusion list to keep variety
- selected recipe + 4 alternatives per slot
- GET /me/meal-plans/current returns the active plan with selected/alternatives
@ketopath/db:
- prisma/seed-recipes.ts — 16 italian keto recipes (4 per meal type) with
estimated macros per serving and phase compatibility
- prisma/seed.ts — idempotent insertion (skip on existing name match)
Verified end-to-end with sign-up → create profile (Michele PRD persona) →
generate plan: 28 slots filled, daily target 1399 kcal / 137 P / 83 F / 25 C,
matchmaking distributes 4 different breakfasts across the first 4 days as
the recent-consumed penalty kicks in.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>