Meal Plan Generator — Design Specification
Purpose
The meal plan generator is a constraint satisfaction system. Given a user’s suspected foods, sensitivity category data, and dietary context, it produces a structured meal schedule that maximizes the statistical power of the correlator output. The plan is the experimental design — if it’s poorly constructed, the statistics downstream are weaker regardless of how sophisticated the correlator is.
Inputs
- A user (with their suspected foods)
- A start date
- A duration in days (default 14)
- The food pool (seeded foods, optionally filtered by user dietary restrictions)
Output
A MealPlan record with:
- MealPlanSlots (3 per day: breakfast, lunch, dinner)
- Associated Meals and MealIngredients for scheduled slots
- WashoutWindow records for each category used in the plan
Spec 1: Plan Structure
What
The generator creates a plan spanning the requested duration with 3 meal slots per day.
Behavior
- Creates a MealPlan with
starts_onandends_onspanning the duration. - Creates
duration × 3MealPlanSlots, one for each (date, meal_time) combination. - Each slot has a
meal_timeof breakfast, lunch, or dinner. - Slots may have a nil
meal_idif no recipe/food is assigned (skeleton slot). - The plan is associated with the requesting user.
Spec 2: Minimum Exposure Guarantee
What
Each suspected ingredient must appear in the plan at least MIN_EXPOSURES times (5).
Why
The correlator needs at least 5 observations per ingredient to compute a meaningful correlation. Fewer than 5 observations only detects very large effects. The meal plan is the primary mechanism for ensuring this minimum is met — if the plan doesn’t schedule enough exposures, the correlator will return insufficient_data for that ingredient and the testing period is wasted.
Behavior
For each food in the user’s suspect list, the generator ensures its primary ingredient appears in at least MIN_EXPOSURES distinct meal slots across the plan. If the plan duration is too short to accommodate all suspects at 5 exposures each (given washout constraints), the generator should prioritize suspects in order of addition (oldest suspects first) and flag under-scheduled foods.
Constant
MIN_EXPOSURES = 5
Spec 3: Per-Category Washout Enforcement
What
Same-category foods must be separated by at least the category-specific washout period. No two meals containing foods from the same sensitivity category should occur within the washout window.
Why
Washout periods exist to prevent mediator load stacking. If a user eats histamine-heavy meals on Monday and Tuesday, Tuesday’s symptoms are contaminated by Monday’s load still clearing. The washout ensures each exposure is evaluated against a clean baseline.
Different categories have different pharmacokinetics (see Washout Windows):
Category-Specific Washout Durations
WASHOUT_DAYS_BY_CATEGORY = {
"histamine" => 3, # DAO depletion recovery + mast cell resensitization
"fodmap" => 2, # Fermentation clears in 24-48h
"salicylate" => 3, # COX recovery (reversible inhibition at dietary doses)
"oxalate" => 2, # Acute GI effects; crystal effects are cumulative (different model)
"lectin" => 4, # Delayed reactions up to 48h; need extra buffer
"glutamate" => 2, # Fast clearance
"capsaicin" => 3 # TRPV1 re-sensitization after desensitization
}
Behavior
When scheduling a food that belongs to category X, the generator checks whether any food in category X was scheduled within the preceding WASHOUT_DAYS_BY_CATEGORY[X] days. If so, the food cannot be placed in that slot.
For foods belonging to multiple categories, ALL category washouts must be satisfied. A food in both histamine (3-day washout) and salicylate (3-day washout) can only be placed if neither category had an exposure within the past 3 days.
The generator creates WashoutWindow records for each category used in the plan, covering the period after the last exposure to facilitate hypothesis engine queries.
Spec 4: Ingredient Combination Decorrelation
What
The generator should vary which ingredients appear together in meals across different days, so that frequently co-occurring ingredients can be statistically distinguished.
Why
If two suspected ingredients always appear in the same meal (soy sauce always with rice), the correlator cannot determine which one drives symptoms. They’re perfectly collinear. The generator should intentionally break these co-occurrences by scheduling ingredients in different combinations on different days.
Behavior
For any pair of suspected ingredients, they should NOT co-occur in every exposure. Specifically: if ingredient A appears on 5 days and ingredient B appears on 5 days, they should share at most 60% of their exposure days. This isn’t a hard constraint — it’s a soft optimization target. The generator should prefer schedules where co-occurrence is lower, all else being equal.
Implementation approach: after placing required exposures for each suspect food, check the pairwise co-occurrence matrix. If any pair is fully correlated, swap one exposure to a different day (respecting washout constraints).
Spec 5: Exposure Distribution Across the Plan Window
What
Suspected food exposures should be spread across the plan duration, not clustered in the first few days or the last few days.
Why
Temporal clustering introduces order effects. If all histamine tests happen in week 1 and all salicylate tests happen in week 2, any change in baseline health, stress, or hormonal state between weeks confounds the comparison. Latin square-inspired designs balance order effects by distributing each treatment across the full experimental window.
Behavior
For each suspected food with N scheduled exposures across a D-day plan, the exposures should span at least 50% of the plan window (measured as days between first and last exposure). The ideal is even spacing: one exposure every D/N days, with jitter to respect washout constraints.
The generator should not place all exposures for a single suspect in the same half of the plan.
Spec 6: Graceful Degradation Under Constraint Pressure
What
When constraints conflict (too many suspected foods, too short a plan, too many washout conflicts), the generator should produce the best possible plan rather than failing.
Why
A family with MCAS suspecting 10+ foods on a 14-day plan may not be able to give every food 5 exposures with 3-day washouts. That’s a mathematical impossibility. The generator should still produce a useful plan — prioritizing the most suspected foods, scheduling as many exposures as possible, and clearly communicating which foods are under-scheduled.
Behavior
When the constraint solver cannot satisfy all requirements simultaneously:
- Prioritize suspected foods by order of addition (the user suspects these most strongly).
- Schedule the top-priority foods at full MIN_EXPOSURES first.
- Schedule remaining foods with as many exposures as constraints allow.
- Never violate washout constraints — these are hard constraints (scientific validity) while exposure count is a soft constraint (statistical power).
- Report which foods are under-scheduled and recommend extending the plan duration.
The generator should never crash or return an error for valid inputs. An empty food pool should produce a plan with all-empty slots (the user will log off-plan meals anyway).
Spec 7: Safe Food Fill
What
Meal slots not occupied by suspected foods should be filled with safe (low-sensitivity) foods from the food pool.
Why
The user needs to eat something at every meal. Filling non-test slots with known-safe foods ensures that background meals contribute minimal mediator load, creating the cleanest possible baseline for evaluating the suspected foods.
Behavior
Safe foods are those with no high-severity category memberships, or no category memberships at all. The generator selects from the safe pool for non-test slots, preferring variety (don’t repeat the same safe food every day).
Safe foods also contribute to the correlator: if a “safe” food consistently co-occurs with symptoms, the correlator will flag it. The meal plan’s categorization doesn’t prevent the correlator from detecting unexpected associations.
Spec 8: Configurable Plan Duration
What
The default plan duration is 14 days, but it should accept any duration from 7 to 60 days.
Why
A 7-day plan is useful for a quick initial screen with 2-3 suspect foods. A 60-day plan accommodates users with many suspects and provides enough data for the mixed effects model (v0.2+). The family use case (3 family members with different suspects) may benefit from longer plans that stagger testing across family members.
Behavior
The generator accepts a duration_days parameter. It validates the range (7-60) and adjusts the exposure distribution math accordingly. Longer plans allow more exposures per food and more decorrelation between ingredient pairs.
The generator should recommend a minimum plan duration based on the number of suspected foods and their category overlaps. Formula: recommended_days = max(14, suspect_count × 3) — enough time for each suspect to get 5 exposures with washout spacing.
Dependencies
- UserSuspectFood — which foods to prioritize
- FoodCategoryMembership — category membership and severity for washout scheduling
- FoodSensitivityCategory — washout durations per category
- Food pool (all seeded foods)
- ANOVA and Experimental Design — Latin square and washout window theory
Consumers
- MealPlanSlot / Meal / MealIngredient — the created plan records
- WashoutWindow — created for the hypothesis engine to query
- User-facing weekly plan display
- The correlator — the plan’s quality directly determines the correlator’s statistical power