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_on and ends_on spanning the duration.
  • Creates duration × 3 MealPlanSlots, one for each (date, meal_time) combination.
  • Each slot has a meal_time of breakfast, lunch, or dinner.
  • Slots may have a nil meal_id if 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:

  1. Prioritize suspected foods by order of addition (the user suspects these most strongly).
  2. Schedule the top-priority foods at full MIN_EXPOSURES first.
  3. Schedule remaining foods with as many exposures as constraints allow.
  4. Never violate washout constraints — these are hard constraints (scientific validity) while exposure count is a soft constraint (statistical power).
  5. 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