Tre completamenti del PRD §5.1 lasciati indietro fino a oggi.
1. Macro tracker giornaliero
- GET /me/meal-plans/current ora include proteinG/fatG/netCarbG/prepMinutes
per ogni recipe selected e alternative
- /plan: nuova summary card settimanale (medie kcal/proteine/grassi/carb
sui giorni attivi)
- Header per ogni giorno: "1390 KCAL · P 100 · G 92 · C 17"
2. cookingTime nel matchmaking
- Nuovo prepMinutes su RecipeCandidate
- MatchOptions accetta maxPrepMinutes (soft cap, non hard filter)
- Score: penalty 0.01 per minuto di sforamento — preserva la diversità
ma orienta le scelte
- maxPrepMinutesFor(level): LOW=15, MEDIUM=30, HIGH=60
- plan.routes deriva la soglia da Preferences.cookingTime
3. Rigenera singolo pasto
- POST /me/meal-plans/slots/:slotId/regenerate
- Riusa l'intero pipeline di calcolo (BMR, deficit, condizioni, training,
protocollo, mealsPerDay, cookingTime) e ricalcola top5 per quello slot
- Passa consumedSoFar (kcal/macros già scelti negli altri pasti del
giorno) al matchmaking → coerenza dei macros giornalieri
- Aggiunge la ricetta corrente al recentlyConsumedIds per forzare il
cambio
- UI: pulsante ↻ in ogni SlotCard, accessibile via aria-label
Smoke verificato live: 21 bottoni rigenera (3 pasti × 7 giorni), summary
"Media kcal/giorno 1397", header giornaliero "1390 KCAL · P 100 · G 92 · C 17".
54/54 unit test verdi. Lint, typecheck, build verdi.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Tre leve aggiuntive che migliorano la personalizzazione del piano dopo
le tre del commit precedente (BF%, deficit, condizioni mediche).
1. Storia delle diete precedenti (adattamento metabolico)
- Profile.dietHistory ∈ {NONE,SOME,EXTENSIVE,SEVERE} cifrato at-rest
- bmrAdjustForDietHistory: 0.97 / 0.93 / 0.85 — aggiustamento BMR per
riflettere l'adattamento metabolico osservato in studi a lungo termine
(Fothergill 2016 Biggest Loser, Rosenbaum 2008)
- Select in ProfileForm con copy diretto sulla "questione yo-yo"
2. Schedule allenamento (kcal extra training-day)
- Preferences.trainingDays Int[] (0=Lun..6=Dom)
- Preferences.trainingType ∈ {CARDIO,STRENGTH,MIXED,SPORT}
- Preferences.sessionMinutes Int?
- extraKcalForSession: equazione MET (Ainsworth 2011) con MET prevalenti
8/5/6/7 a tipo. Output arrotondato a 25 kcal per evitare false-precisioni
- Plan generation aggiunge l'extra solo nei training days
- Chips Lun-Dom + select tipo + input durata in PreferencesForm
3. Frequenza pasti preferita
- Preferences.mealsPerDay Int? (1..4)
- mealShareForFrequency: 1=solo cena, 2=pranzo+cena, 3=no spuntino, 4=default
- Quando mealsPerDay è impostato e non c'è fastingProtocol attivo,
sostituisce la share del protocollo (il digiuno IF ha precedenza)
- Slot a share=0 saltati alla creazione (niente colazione/spuntino "vuoti")
Schema migration 20260429220XXX_add_lifestyle_fields.
Smoke verificato via API: con mealsPerDay=3 e nessun protocollo IF, ogni
giorno del piano ha esattamente 3 pasti (Colazione+Pranzo+Cena, niente
Spuntino). 54/54 unit test verdi (11 nuovi).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Finora il fastingProtocol era una preferenza salvata ma ignorata dal
planner — ogni giorno generava 4 pasti. Ora cambia struttura per protocollo
e per giorno della settimana.
@ketopath/shared/planner
- protocolPlanForDay(protocol, dayOfWeek) → { share, kcalMultiplier }
- 14:10 → tutti e 4 i pasti, share default
- 16:8 → niente colazione: pranzo 50%, spuntino 10%, cena 40%
- 18:6 → niente colazione: pranzo 55%, spuntino 5%, cena 40%
- 20:4 → solo spuntino+cena (10/90)
- ESE 24h → mercoledì kcalMultiplier=0 (giorno saltato), altri default
- 5:2 → lunedì + giovedì kcalMultiplier=0.25, share 100% cena
- 7 unit test in protocol.test.ts
apps/api/plan
- POST /me/meal-plans: legge preferences.fastingProtocol, deriva il piano
per ogni giorno, salta MealSlot con share=0 e giorni a kcalMultiplier=0
- GET /me/meal-plans/current: include fastingProtocol nella risposta
- baseDailyTarget restituito invariato per il client
apps/web
- /plan mostra "Finestra alimentare 16:8" sotto il titolo della settimana
- plan-week distingue i giorni "fasting" (digiuno completo) e "leggeri"
(5:2 fasting day) con copy dedicato; rimuove totali calorici inappropriati
- i18n: Plan.protocolLabel + Plan.fastingDay/lightDay
Smoke tested: settato 16:8 da /profile, rigenerato il piano, verificato
che l'etichetta appaia e che ogni giorno abbia esattamente 3 pasti
(Pranzo / Spuntino / Cena, niente Colazione).
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>