Dev.to AI 🤖 Ai 👁 0 📖 3 min read

Model the review step before saving AI output

An AI response can identify a meal and suggest nutrition values. That response still leaves a product decision unresolved: which information does the person want in their diary? If the response is treated as a saved mea

An AI response can identify a meal and suggest nutrition values. That response still leaves a product decision unresolved: which information does the person want in their diary?

If the response is treated as a saved meal immediately, a successful model request can change a daily total before the person has checked the food or portion. A review screen added afterwards cannot restore the distinction the data model has already lost.

FoodWarz's domain vocabulary separates an analysis from a meal. Analysis is reviewable interpretation. A meal is a saved diary snapshot containing the amount, nutrition and source information the user chose to record. This post uses that boundary to describe a small modelling approach that also applies to other products which turn generated suggestions into durable records.

Give each object one job

The following TypeScript is an illustrative sketch, not FoodWarz's production schema. Identifiers, validation and storage details are deliberately omitted.

type Nutrition = Readonly<{
  calories: number | null;
  protein: number | null;
  fat: number | null;
  carbs: number | null;
}>;

type AnalysisResult = {
  kind: "analysis";
  suggestedNutrition: Nutrition;
};

type ReviewDraft = {
  kind: "review";
  original: AnalysisResult;
  editedNutrition: Nutrition;
};

type SavedMeal = {
  kind: "meal";
  nutritionSnapshot: Nutrition;
};

type PlannedDish = {
  kind: "planned";
  proposedNutrition: Nutrition;
};

The useful part is the separate names. A function that accepts a SavedMeal should not also accept an AnalysisResult simply because both happen to contain calories. A production boundary needs runtime validation too: types cannot establish that a client request is valid or that the person confirmed it.

A simplified flow is:

Analysis completes → open a review draft
User edits         → update the draft
User confirms      → validate and persist a meal
Persistence succeeds → include that saved meal in diary totals

The draft can also be abandoned. That must remain an ordinary outcome of analysis, with no new consumed-food record.

Write invariants before wiring UI events

These invariants make event handlers easier to review:

Event Expected effect Boundary to preserve
Analysis completes A suggestion becomes available for review Analysis completion alone does not add consumed food
A portion is edited The review draft changes The original analysis remains distinguishable from the correction
A meal is confirmed and saved A reviewed snapshot enters the diary The saved estimate retains its basis and provenance
A source recipe or catalogue item changes The source can be used for future entries An existing historical snapshot is not silently recalculated from it
A dish is added to a plan Planning state changes Planned food does not enter consumption totals

The snapshot rule is easy to miss. Suppose a catalogue item is corrected tomorrow. If yesterday's diary reads its nutrition by joining the current catalogue row every time, yesterday's total can change without a diary edit. Store the reviewed nutrition on the historical record and keep the source reference for traceability.

A deliberate correction to an existing meal is a separate user action. Keeping a snapshot does not mean preventing the owner from correcting a mistake.

Keep correction evidence useful

FoodWarz's saved-meal contract distinguishes the original analysis from the reviewed estimate. Corrections can be represented as user-corrected values while retaining links to the analysis context. That supports a concrete debugging question: what did the system suggest, and what was actually saved?

An apparently plausible calorie total is insufficient evidence. The amount, unit and nutrition basis matter together. A value per 100 grams has a different meaning from a total for the eaten portion. Source and uncertainty also need to survive the transition into the diary.

This is a contract to validate at the boundary. It is not proof that every upstream interpretation is correct, and review does not turn an estimate into an exact measurement.

Test the transitions that could change history

Useful regression scenarios start from actions a person can take:

  • Complete an analysis, close review, and verify that no meal was added.
  • Change the portion during review and verify that the saved snapshot contains the reviewed result.
  • Change a reusable source record and verify that an existing meal keeps its recorded nutrition.
  • Create or edit a planned dish and verify that consumption totals stay unchanged until an ordinary meal is saved.

These scenarios check observable meaning rather than the names of internal helper functions. They are suggested coverage for this model, not a report of a production test run.

The FoodWarz diary overview shows the user-facing context for this engineering note. The modelling lesson is to decide when a suggestion becomes a record, then make that decision explicit in types, validation and state transitions.

Disclosure: Published by the FoodWarz team. This article was prepared with AI assistance using the project's domain documentation and selected code contracts. The code above is illustrative.

📰 Read the original article on Dev.to AI

Originally published by Dev.to AI. Aggregated on AIWithGhost for educational purposes — full credit and traffic to the original publisher.