Nutrition multi-service analysis proposal
Summary
This proposal recommends expanding the nutrition analysis workflow from a mostly single-path process into a multi-service comparison pipeline.
The core change is simple:
- persist the athlete nutrition item as usual
- generate a common analysis input from the athlete photo and any athlete text
- send that same input to multiple analysis sources
- store each source's returned estimates side by side in a structured comparison table
- compute a derived aggregate estimate, initially focused on calories, protein, carbohydrate, and fat
- write both the per-service outputs and the aggregate result into the athlete nutrition log record
The immediate motivation is to reduce over-reliance on any one interpretation path.
Right now the workflow is effectively:
- local or in-agent interpretation of the photo and food item
- optional or partial Edamam support
- a single final estimate
That is workable, but it hides disagreement between sources and makes it harder to improve macro accuracy over time.
The proposed design makes disagreement explicit, preserves the raw outputs, and creates a better foundation for a future averaging or weighting algorithm.
Objective
Build a nutrition analysis architecture that can compare multiple nutrition-estimation services on the same athlete input and record the returned results in a structured, auditable way.
The first target state should support three analysis paths:
- Local analysis
- current Coach Luca or nutrition-agent interpretation path
- updated to use the shared nutrition analysis prompt so the local and Claude paths follow the same output contract
- Edamam analysis
- current external nutrition lookup path
- Claude CLI analysis
- a new local external-service path using the same saved nutrition analysis prompt and the athlete's photo and text
The workflow should then store:
- the photo description used for the analysis
- the macro estimate from each service
- confidence or uncertainty notes when provided
- any useful supporting details returned by the service
- a derived aggregate estimate for the key macros
Why this change is needed
1. Single-path estimates are fragile
Nutrition estimates from photos are inherently noisy.
Portion size, hidden ingredients, preparation method, and visual ambiguity all create uncertainty. A single local estimate may be directionally useful, but it does not expose how uncertain the estimate really is.
2. Cross-checking should be first-class, not incidental
The current workflow sometimes uses Edamam as support, but that comparison is not yet structured into the record. The system should preserve the fact that multiple services looked at the same input and should show how their outputs differ.
3. Macro averaging can improve practical robustness
Even if no service is perfectly accurate, a simple aggregate across multiple independent estimates may produce a more stable practical estimate than relying on one source alone.
The workspace should be able to evolve from:
- one estimate with implicit judgment
to:
- multiple estimates with explicit comparison
- a transparent aggregation method
- a better final practical estimate
4. The system needs a reusable comparison substrate
This is not only about one extra Claude call.
The right architecture should allow additional services later, such as:
- another CLI agent
- another nutrition API
- a vision-focused model
- a specialized label OCR step
That argues for a service-agnostic comparison design rather than a one-off Claude special case.
Current state
The current durable understanding is:
- local analysis remains the main interpretation path
- Edamam is currently used as a nutrition lookup layer, not the sole decision-maker
- the Edamam image-analysis path is URL-based and not a fully confirmed direct local-file upload path
- a reusable shared nutrition analysis prompt now exists at
team/nutrition/nutrition-analysis-prompt.md - Claude CLI is available locally and authenticated on this machine
- the prompt should become the common contract for both the local analysis path and the Claude CLI path
This proposal builds on those facts rather than replacing them.
Proposed feature
Core idea
For each nutrition intake item that is suitable for analysis, create a normalized analysis package and run it through multiple services.
That normalized analysis package should include:
- athlete message text, if present
- local path to the saved photo, if present
- a canonical short photo description generated locally when needed
- relevant metadata such as received time, local time, and item position in the day's nutrition log
Each service then returns a structured result that can be normalized into a shared comparison schema.
Initial service set
Service 1, local analysis
This is the current internal analysis path.
It should be updated to use the shared prompt at team/nutrition/nutrition-analysis-prompt.md so the local analysis and Claude CLI analysis are working from the same prompt and expected structure.
It should produce a structured output instead of only freeform reasoning.
Expected fields:
- food identified
- observed evidence
- estimated portions
- estimated macros by item, if applicable
- estimated total calories, protein, carbohydrate, and fat
- confidence
- assumptions
- uncertainty notes
- optional extra notes
Service 2, Edamam
This is the current external nutrition support path.
Depending on the item, Edamam may be driven by:
- identified named foods plus quantity estimates
- image-analysis path when a supported image path is actually available and working
Expected fields:
- matched foods or candidate foods
- calories
- protein
- carbohydrate
- fat
- any labels, categories, or nutrient metadata that are useful
- service limitations or call-path notes
Service 3, Claude CLI
This is the proposed new path.
The system should call the local Claude CLI and provide:
- the shared nutrition analysis prompt from
team/nutrition/nutrition-analysis-prompt.md - the athlete text, if present
- the saved nutrition photo, if present
Claude should then return a structured estimate in the prompt's required format.
Expected fields:
- food identified
- observed evidence
- estimated portions
- estimated macros by item, if applicable
- estimated total calories, protein, carbohydrate, and fat
- confidence
- assumptions
- uncertainty notes
Proposed workflow
Phase 1, intake and persistence
No change to the high-level intake rule:
- the nutrition item must still be persisted first
- the canonical athlete daily log remains the durable record
This proposal changes the analysis layer, not the intake-first rule.
Phase 2, normalized analysis input creation
Create a normalized analysis input object for the current nutrition item.
Suggested fields:
The goal is that every downstream service receives the same effective evidence base.
Phase 3, multi-service execution
Run each enabled service against the normalized input.
The first implementation should allow services to succeed or fail independently.
If one service fails:
- the others should still run
- the comparison table should record the failure cleanly
- the nutrition workflow should not collapse unless all required analysis paths fail
Phase 4, normalization of returned outputs
Each service returns different raw shapes. Those must be normalized into a shared schema.
Suggested normalized result fields:
Phase 5, comparison table generation
After normalization, write a side-by-side table into the athlete nutrition record.
Comparison-table design goal
The user asked for one column per service.
That is the right display model for human review because it makes cross-service disagreement immediately visible.
Proposed primary table
This is the minimum required table.
Proposed secondary information table
Because not all useful outputs are numeric, add a second table or structured bullet section for supporting information.
Suggested format:
This keeps the macro comparison compact while still preserving each service's interpretation.
Phase 6, aggregate estimate
Once per-service outputs are normalized, compute an aggregate estimate.
First-version recommendation
Start simple.
Use an arithmetic mean across successful services for:
- calories
- protein
- carbohydrate
- fat
Rules:
- exclude failed or skipped services
- exclude clearly non-numeric outputs
- round the aggregate to practical values
- if only one service succeeds, the aggregate equals that service result
- if two or more services succeed, note how many services were included in the aggregate
Second-version recommendation
Once the basic comparison path is working, consider weighted averaging.
Possible weighting inputs later:
- service confidence
- presence of visible label data
- structured versus heuristic source path
- known service strengths by item type, such as packaged product versus mixed meal versus supplement
Do not start with weighting unless there is a clear validation basis.
Proposed output location in the athlete log
The comparison and aggregate result should be saved with the nutrition item inside the canonical daily log under ## Nutrition.
The goal is not to replace the existing item entry, but to enrich it.
Recommended additions to each analyzed nutrition item:
Photo descriptionAnalysis services usedMacro comparisonService notesAggregate estimate
A possible shape:
### 13:10
- Received: 2026-04-22 13:11 MDT
- Local: 2026-04-22 13:10 MDT
- Type: nutrition photo
- Photo description: bowl with white rice, grilled chicken pieces, avocado, and light sauce
- Analysis services used: local, Edamam, Claude CLI
#### Macro comparison
| Metric | Local | Edamam | Claude CLI | Aggregate |
|---|---:|---:|---:|---:|
| Calories (kcal) | 620 | 580 | 640 | 613 |
| Protein (g) | 34 | 31 | 36 | 34 |
| Carbohydrate (g) | 58 | 54 | 61 | 58 |
| Fat (g) | 22 | 20 | 24 | 22 |
| Confidence | moderate | moderate | moderate | derived |
| Status | ok | ok | ok | 3 services |
#### Service notes
| Field | Local | Edamam | Claude CLI |
|---|---|---|---|
| Food description | chicken rice bowl with avocado | chicken, rice, avocado candidates | grilled chicken rice bowl with avocado and sauce |
| Portion summary | medium bowl | ingredients estimated from named-food matches | medium takeaway bowl |
| Assumptions | light sauce | avocado quantity uncertain | sauce amount uncertain |
| Uncertainty notes | hidden oil unknown | mixed-dish decomposition imperfect | sauce and rice depth not visible |
| Raw-output reference | inline | raw/edamam/...json | raw/claude-cli/...md |
#### Aggregate estimate
- Calories: 613 kcal
- Protein: 34 g
- Carbohydrate: 58 g
- Fat: 22 g
- Aggregate method: simple mean across 3 successful services
Architecture recommendations
1. Keep service adapters separate from canonical log writing
Do not mix provider-specific calling logic directly into the daily-log writer.
Recommended separation:
- one layer builds normalized analysis input
- one adapter per service performs the call and captures the raw result
- all configured nutrition analysis services are run for every nutrition log request
- one normalizer converts each raw result to the shared schema
- one writer updates the canonical daily log format
This will make later service additions much easier.
2. Preserve raw outputs when practical
For debugging and future validation, store raw returned outputs in a durable location when feasible.
Suggested raw-output archive path:
athlete/data/nutrition-analysis/raw/YYYY-MM-DD/
Possible files:
item-01-local.jsonitem-01-edamam.jsonitem-01-claude-cli.mditem-01-normalized.json
The daily log should remain readable and concise, while raw artifacts can live in the archive.
3. Make the local and Claude CLI paths prompt-driven
Do not hard-code the prompt in code.
Both the local analysis adapter and the Claude CLI adapter should read:
team/nutrition/nutrition-analysis-prompt.md
This keeps prompt revision separate from execution code, aligns the two analysis paths on the same structure, and ensures that differences between local and Claude results come from the service or model behavior rather than from two different prompt contracts.
4. Support partial success cleanly
The system should treat the analysis stage as a multi-source collection step, not as a single brittle dependency.
Examples:
- if Edamam fails, local plus Claude can still produce a comparison table
- if Claude fails, local plus Edamam can still produce a comparison table
- if only local analysis succeeds, the record should still be updated, but it should note that no cross-service comparison was available
Open design questions
1. What should count as the canonical final estimate?
Options:
- aggregate estimate always becomes the canonical final estimate
- Coach Luca still approves or adjusts the final estimate after seeing the comparison table
- nutrition specialist owns the final adopted estimate while the table remains evidence
Recommendation for first version:
- keep the aggregate estimate explicit
- keep Coach Luca or nutrition specialist judgment as the final interpretation layer
- do not silently replace coaching judgment with a blind average
2. Should the photo description be generated once or per service?
Recommendation:
- create one neutral local photo description for the item record
- allow each service to also return its own food-identification description
- keep both, because the neutral description and the service interpretation serve different purposes
3. How much of the raw service output belongs in the daily log?
Recommendation:
- keep the daily log concise
- store numeric comparison and short service notes in the log
- archive the full raw outputs elsewhere
4. Should all nutrition items use all services?
Yes.
Current user direction is to run all configured nutrition analysis services for every nutrition log request.
Rationale:
- the system needs broad comparison data before it can judge which services are most useful or most accurate
- early selective routing would hide disagreement patterns and reduce the validation dataset
- consistent all-service execution will make downstream comparison, averaging, and service evaluation much cleaner
Recommendation:
- run all configured nutrition analysis services for every nutrition log request in the current design
- defer any future service-reduction or selective-routing logic until there is enough evidence to justify it
Implementation phases
Phase 1, proposal-aligned prototype
Build a prototype that:
- reads the nutrition analysis prompt from
team/nutrition/nutrition-analysis-prompt.md - runs local analysis, Edamam, and Claude CLI on the same nutrition item
- normalizes calories, protein, carbohydrate, and fat
- writes a comparison table plus aggregate estimate into a test or staging record
Success criteria:
- one real nutrition item can be analyzed through all available services
- the comparison table is readable
- partial failures are handled cleanly
Phase 2, canonical daily-log integration
Integrate the comparison table into the canonical athlete nutrition log format.
Success criteria:
- the result is durable and readable in
athlete/logs/daily/YYYY-MM-DD.md - raw outputs are archived separately when appropriate
- the aggregate estimate is explicit and traceable
Phase 3, weighting and validation
Once enough comparison data exists, evaluate:
- whether simple averaging is useful enough
- whether weighting improves practical estimates
- which services overestimate or underestimate specific food classes
Recommendation
Proceed with this feature.
The recommended first step is not to redesign the whole nutrition system at once.
Instead, build a narrow but real prototype around one nutrition item flow:
- local analysis
- Edamam analysis
- Claude CLI analysis using the saved prompt
- comparison table
- simple aggregate macro estimate
- all configured services executed on that item, with no selective routing
That is enough to validate the architecture before broader rollout.
Proposed file and workflow impacts
This proposal likely affects:
skills/nutrition-analysis/skills/nutrition-intake/, if the comparison results are written during intake-adjacent flowteam/nutrition/, for service prompt and service policy- canonical athlete daily-log nutrition item format
- a new raw-output archive path for multi-service results
Final principle
The system should stop treating nutrition estimation as a one-answer task.
It should treat it as an evidence-comparison task:
- multiple services
- explicit disagreement
- transparent aggregation
- preserved raw outputs
- better practical final estimates