Build MealWise

YESreplaces $7.99/mosaves $95.88/yrback to the verdict

0%0 of 13 items done

Saved on this device only. Tick prerequisites first, then work the phases in order · do not start one until the checks above it pass.

A one-page recipe generator whose hard problems are refusing to render a half-parsed model response, doing the cost arithmetic itself from prices you maintain, and taking every safety-relevant number from a published chart rather than from generated text.

estimated effort one sittingthe files for this build are in the project pack

RuntimeNext.js 15 and TypeScript, mobile first, one pageModelOne call with a strict schema, the key held server side in a route handlerStorageIndexedDB in the browser for saved recipes, with an export to fileSafety dataA checked-in table of cooking temperatures with its source URL and copy dateArithmeticCosts computed in the application from unit prices you entered

Before step 1

Everything below is assumed from the first step. Tick each one when you actually have it, not when you plan to.

  1. installfree

    Why Runs the application and its tests.

    Get it nodejs.org, or your version manager. open ↗

    Verify node --version prints v22 or higher

  2. API keypay per request; a few recipes a week is negligible

    Why The recipe itself is one call, and structured output is what makes the costing and safety matching possible at all.

    Get it Create a key with your chosen provider and set a spend limit in their console.

    Verify One request returns a response that validates against the schema

  3. have readyfree

    Why This is the one part of the output that must not be generated, and copying it yourself with the date is the whole safeguard.

    Get it Open the chart published by the authority for your country, copy the figures into the table file, and record the URL and today's date in it. open ↗

    Verify The table file contains the figures, the URL and the copy date, and the build fails without it

  4. have readyfree

    Why The cost panel is arithmetic over this table, and with an empty table every recipe is entirely unpriced, which is honest but useless.

    Get it Write down the price and unit for the staples in your kitchen from a recent receipt.

    Verify A generated recipe prices most of its ingredients and counts the rest

Data model

Create these before the first phase that stores anything. Changing a table later is the expensive kind of change.

- recipes(id, created_at, request_text, model_id, prompt_version, schema_version, raw_response)
- ingredients(recipe_id, ordinal, name, quantity, unit, matched_price_key, unit_price_minor, est_cost_minor)
- steps(recipe_id, ordinal, text, temp_key, flagged) · temp_key points into the safety table or is null
- safety_temps(key, food, celsius, fahrenheit, rest_minutes, source_url, copied_on) · checked into the repository, never generated
- pantry_prices(key, name, unit, unit_price_minor, updated_at) · your prices, because the model does not know what a tin costs where you shop
- flags(recipe_id, kind, detail) · kind is allergen_match, unconfirmed_temperature or missing_price
- There is no field anywhere holding a claim that a recipe is safe for a person, and no field holding a cost the model produced.

Environment variables

These go in a .env file the app reads at startup. The pack's .env.example is this table as a file · copy it, never commit the filled-in version.

VariableNeededExampleWhere the value comes from
MODEL_API_KEYsecretrequiredsk-...redactedHeld in the route handler only. A test asserts it is absent from the client bundle.
MODEL_IDrequireda text model identifierStored with every recipe, because a different model writes different steps.
SAFETY_TABLE_PATHrequired./data/safety-temps.jsonThe checked-in temperature table. A missing file fails the build.
CURRENCYrequiredGBPUsed for every cost figure and for the smallest-unit arithmetic.
EXPORT_PROMPT_AFTER_SAVESrequired10How many saves before the app asks you to export. Browser storage is not a backup.
REQUESTS_PER_SESSIONrequired30Cap on model calls from one session, so a stuck retry loop is not a bill.

The build, in order

  1. One request, one schema, no half-parse

    A meal request produces a validated structured recipe or a clear failure, never something in between.

    1. Everything downstream needs a number and a unit.

      Files src/lib/schema.tssrc/app/api/recipe/route.ts

    2. A partial recipe is worse than no recipe.

      Files src/lib/generate.tstests/parse.test.ts

    3. So an odd recipe can be explained rather than argued about.

      Files src/lib/store.ts

    done when · tick each as it passes
    watch out
    • Falling back to rendering the model's prose when the structured response fails to parse gives you a recipe that reads perfectly and has no machine-readable quantities, so every cost is missing and no safety step can be matched, and the user cannot tell the difference. Render nothing on a parse failure: show what came back, offer a retry, and keep the failure visible.
  2. The costs are yours, not the model's

    Every figure in the cost panel is arithmetic the application did over prices you entered.

    1. The model does not know what a tin costs where you shop.

      Files src/lib/schema.ts

    2. A number a test can check is a number you can argue with.

      Files src/lib/cost.tstests/cost.test.ts

    3. A missing price is not a free ingredient.

      Files src/components/CostPanel.tsx

    done when · tick each as it passes
    watch out
    • Letting the model return a cost per ingredient gives you a total that changes between two runs of the same shopping list, cannot be reconciled against a receipt, and is presented with the same confidence as the cooking time. Ask for quantities and units only, look the prices up yourself, and make the arithmetic something a test can check to the penny.
    • Converting between weight and volume for an ingredient silently assumes a density and produces a price that is wrong by a factor rather than a percentage. Convert only within a measurement family, and leave anything else unpriced with a visible reason.
  3. The safety layer

    Cooking temperatures come from the checked-in table and allergen handling never claims safety.

    1. The figures change and a stale table is worse than none.

      Files data/safety-temps.json

    2. 'Until the juices run clear' is not a measurement.

      Files src/lib/safety.tstests/safety.test.ts

    3. A string test is the only way this stays true.

      Files tests/no-safety-claims.test.ts

    done when · tick each as it passes
    watch out
    • A generated step saying to cook chicken until the juices run clear is a sentence models write constantly and it is not a temperature; a reader following it has no way to know the difference between that and the step that gives a number. Match the food against your table, show the published figure and rest time beside the step, and flag the vague step rather than letting it stand alone.
    • Writing that a recipe is safe for someone with an allergy, in any wording, is a claim about a dish nobody has cooked, produced from a sentence, with no knowledge of cross-contamination in the kitchen it will be made in. The interface reports matches against a list the user typed and says exactly that, and there is no string in the codebase asserting safety.
what this build does not replace
after v1, if you want it

Need the files? The project pack on the verdict page hands your agent the whole brief · more home.