Skip to content

The pipeline

One questionnaire goes in, one menu comes out. In between are nine steps that each narrow the problem: from 180 raw answers, to the 30 that matter, to a number of calories, to grams, to portions, to meals, to food, to a page the client can follow.

flowchart TD
    A["answers.json<br/>~180 fields"] --> B["1. Read the intake<br/>→ intake digest"]
    B --> C{"2. Safety screen"}
    C -->|red flag| STOP["Stop & refer<br/>no menu is written"]
    C -->|clear| D["3. Energy target<br/>→ kcal/day"]
    D --> E["4. Macro split<br/>→ g protein / carb / fat"]
    E --> F["5. Portion exchanges<br/>→ daily portion counts"]
    F --> G["6. Meal schedule<br/>→ portions per slot + clock times"]
    G --> H["7. Plate & combinations<br/>→ food choices per slot"]
    H --> I["8. Personalise<br/>→ restrictions, dislikes, logistics"]
    I --> J["9. Assemble & check"]
    J --> K["menu.md<br/>weekly framework"]

Why this order

The order is not arbitrary — each step closes off decisions the next step would otherwise have to guess at.

Screening comes before arithmetic because a red flag makes the arithmetic pointless. There is no version of "calculate the target first and check safety afterwards" that ends well: the effort is wasted at best, and at worst the agent has produced a document it should not have produced.

Energy before macros because the macro split is a set of percentages, and percentages need something to be a percentage of.

Macros before portions because the exchange system is a representation of a macro target, not an alternative to one. Portion counts are derived by dividing grams by the group's grams-per-portion — see step 5.

Portions before the schedule because the daily count is what gets distributed. The schedule moves portions between slots; it never changes how many there are in a day.

Schedule before food because the constraint that dinner carries no starch is a scheduling rule, and it determines which foods are even eligible for the evening slot.

Personalisation last because it is a filter, not a design step. Every substitution it makes must preserve the portion count of the slot it touches — which is only checkable once the counts exist.

What each step may and may not change

This is the invariant that keeps the pipeline honest. A step that quietly revises an earlier step's output produces a menu whose numbers no longer mean anything.

Step May change Must preserve
3. Energy
4. Macros the kcal target (±2%)
5. Portions the macro grams (±5 g protein, ±10 g carb, ±5 g fat)
6. Schedule which slot a portion sits in the daily portion counts, exactly
7. Plate which food fills a portion the portion counts per slot, exactly
8. Personalise which food fills a portion the portion counts per slot, exactly
9. Assemble nothing everything

If step 8 cannot make a substitution without breaking a count — the client cannot eat any of the eligible starches, say — it does not fudge the count. It goes back to step 5 and rebuilds the daily distribution with that group excluded, then re-runs 6 through 8. That is a loop, and it is expected to happen for restrictive patterns.

The load map

The agent does not hold this whole site in context. Each method page names the reference pages it needs; load those and no others.

Running step Also load
1. Read the intake Input contract
2. Safety screen Red flags, Scope & limits
3. Energy target
4. Macro split Menopause
5. Portion exchanges Exchange lists
6. Meal schedule Hydration & eating behaviour
7. Plate & combinations Exchange lists, Food quality, Fibre & the gut
8. Personalise Food quality, Menopause
9. Assemble & check JSON schema, Style guide, Supplements

Scope & limits is loaded at every step. It is short for that reason.

Where the questionnaire itself lives

The questions are not defined here. They are defined in survey/sections/*.json in this repository, in SurveyJS format, and that is the only source of truth for field IDs, answer types and conditional logic. This site consumes those field IDs; it does not get to invent them. See the input contract.