Mise Engine · Working Cheat Sheet

How We Build the Engine — and What the Data Taught Us

For: Edith · From: Vash · Updated: July 13, 2026 · Companions: MISE_ENGINE_PLAYBOOK.md (the spec) · PLAYBOOK_AMENDMENTS.md (16 verified traps) · mise-build-map.html (roadmap)

Law 1 — Money math never comes from a model.

Every dollar is a deterministic, tested function. AI reads photos in and phrases words out. If a model output ever appears in a dollar figure, that's a highest-severity bug.

Law 2 — The engine never returns a bare number.

Every answer is cost + ledger: the list of assumptions, sources, and fallbacks behind it. The ledger is the contract between the engine, the screens, and the agent.

01Where we are right now

In progress
Conversion engine
This week · blocker
Ledger session (90 min)
Sept 1
Beta kitchen opens
Build order = dependency order, and it doesn't bend: conversions → catalog & matching → pricing (yields inside it) → recipe costing → margins & batch → questions & memory. The finish line that counts: recompute a real customer's book and match ChefTec's own cached numbers (the three well-kept books QueryMan identified, 99%+ cache coverage). Clean diff = engine proven.

02Pricing: why our data, and when to use the web

There are three different "prices" for the same tomato, from three altitudes of the supply chain. We tested this live in July with Roma tomatoes — our database vs. USDA wholesale reports vs. retail:

AltitudeRoma tomatoes, July 2026What it really isUse for costing?
Terminal / commodity (USDA)$0.52–1.44 /lb by regionWhat distributors pay at the wholesale marketNo — two steps upstream of a kitchen
Settled prices (our DBs)$1.16–1.63 /lb, deliveredWhat real kitchens actually paid, with vendor, date, placeYes — this is the only altitude a recipe can honestly be costed at
Retail / web list~$1.90–2.20 /lbSticker prices from the wrong channelNo — nobody pays this wholesale

Our numbers sat above terminal and below retail at exactly the expected markup ratios — three independent sources agreeing is how we know we're close. And the two sites' prices differed ($1.16 vs $1.63) while moving together (both peaked in May, both fell) — which is the whole corpus model in one example: levels are personal, trends are shared.

When we use ChefTec data: always, for any number a dish gets costed with. Their own invoices first; other kitchens' settled prices (anonymized, as a range) only to bridge cold-start and to power market comparisons.
When we use the web: never for costing. USDA-style commodity reports serve two supporting roles only — calibration (periodically check our medians sit in sane supply-chain order) and, later, trend context ("butter futures jumped — expect your dairy invoice up"). Always labeled, never inside a dollar figure.

The price ladder — how one ingredient's price resolves, in order:

  1. This kitchen's own purchase history — their invoices, their exact prices. exact
  2. Prices the chef told us — quotes, stated prices, ask-once answers. exact-ish
  3. Market range from the corpus — median + range across nearby kitchens, recent only, junk excluded. Shown as "market estimate," never as their price. honest estimate
  4. Nothing found → ask the chef. Never invent a number. The answer becomes rung 2 forever. question
Bulk pricing — measured, settled, done: we tested 669 cases of the same kitchen buying the same item at very different quantities. 80% flat. Volume discounts are baked into the account's contract, not applied per order. So: ingredient cost scales linearly in batch simulation — no bulk curve, ever. Labor is where per-plate cost genuinely falls with batch size. (The "bigger buyers pay less" effect is real but lives between accounts — that's a corpus insight for later, not a costing input.)

03Matching: the three-rail waterfall

Connecting "what the recipe says" and "what the invoices say" to one catalog identity is the engine's judgment layer. The data gave us three rails, each cheaper and more certain than the next. Resolution order:

0 · firstKitchen memory

This kitchen answered before → same answer forever. Never ask twice.

1Seed-ID + name

Low ItemIDs (<~2000) are a shared factory catalog across sites. ID match + name agreement = auto-link. Name disagreement → review (sites rename seeds!).

2Vendor SKU

80% of purchase lines carry product codes; Sysco codes are identical across sites. A join, not a guess.

3Fuzzy name

Trigram/embedding candidates with scores. Suggests, never decides.

4 · lastHuman review

Low confidence → review queue. Every confirmation becomes memory (rail 0).

Why the guardrails matter: unguarded fuzzy matching paired toilet tissue with OIL and an IPA with CREAM in our own staples test. Fuzzy is the fallback, never the authority — and nothing auto-links below the confidence threshold.

04The components — how to attack each one

Conversions

in progress
Approach
Three tiers: exact math → stored facts → chains of stored facts (only within one ingredient, max 3 hops). Two routes disagreeing by >1% = contradiction: flag + question, never a silent pick.
What the data said
  • 157K conversion facts fleet-wide; vendor-scoped facts dominate item-generic 4.5:1.
  • Every site invents custom units beyond the 61-unit master (IDs to 668) — the master list is a floor. Unit IDs are never portable across sites; always resolve through the site's own Units table.
  • ConvMissing: 62.7K logged conversion failures = a frequency-ranked list of what real kitchens got stuck on. Import it — it seeds the question queue and prioritizes reference data.
Done means
Spec tests green on every constant and tier · fact-health command running (round-trips, physics bands, contradictions) · stress agent can't find a case where the engine silently guesses.
How we test it
Full methodology — the LLM chef-simulator, the five-layer verification stack, the silent-assumption metric: Conversion Testing Playbook →

Ingredients & catalog

next
Approach
One namespace per tenant (recipes can BE ingredients). Matching = the waterfall above. Every confirmed match persists forever.
What the data said
  • Inv.Sku is empty everywhere — ProdCo is the real SKU store; import it fully.
  • Inv carries flags we want: CatchWeight (always cost per weight), IsStaple, preferred vendor, cost-method override.
  • Recipe lines need a third case: separators are physical Inv rows recipes point at — ingredient XOR sub-recipe XOR separator.
Watch out
Everything in the source is keyed per profit center — every join carries (ProfCentID, ItemID), or counts silently multiply.

Pricing

next
Approach
The crown jewel stays: no price field anywhere — price is derived at query time from append-only purchase facts, through the ladder in §02.
What the data said
  • Cost methods are a monoculture (>99.9% on the default at every site): port ONE strategy + a per-item dispatcher, stub the rest.
  • Cost column is a line TOTAL, proven with data — unit price is always total ÷ qty.
  • Zero-qty (0.4%), zero-cost (0.4%), no-unit (1.3%) rows do the real exclusion work; IgnoreCost is ~dead but port it.
  • Outlier guard required: our own fixture has a $0.70-vs-$50.94 same-unit entry error. Wild deviation from an item's own history → still return it, but ledger caution + question.
Watch out
Sub-recipe fake purchases (up to 21.8% of a site's Trans!) can only be excluded by the SubRecipe=1 join per profit center — NEVER by vendor name. "In-house" is ordinary data and varies per site.

Yields & waste

rides with pricing
Approach
Effective cost = unit cost ÷ usable factor (an avocado at 0.70 costs ~43% more per usable pound). Factor NULL = unknown → ledger caution on produce/protein, never a silent 1.0.
What the data said
  • Legacy yield data barely exists: 575 real entries in the whole fleet (99.6% of rows are the untouched 1.0 default). Import 1.0 as NULL — confirmed at maximum scale.
  • Seeding comes from the 3 sites that actually maintained yields + a chef review pass (session doc already prepared), not from migration.
  • Factors >1.0 are legal and meaningful — rice and dried goods gain weight when cooked (one site deliberately uses 2.0–3.0). Don't cap at 1.0 in validation; ledger-annotate as a yield gain.
  • Per-line yield factors: unused fleet-wide — stage the column, build nothing.

Recipe costing (cost_recipe)

mid-august target
Approach
The heart. Sum of line costs (children first for sub-recipes, circular refs rejected at write time) + adjustments + optional labor; portion cost = total ÷ portions. Every line gets a status; problems escalate the recipe to "incomplete" but the engine still returns the partial cost + ledger — never an exception for data gaps.
What the data said
  • Sub-recipe dual identity verified exactly (4,435 = 4,435 = 4,435) — model it as an explicit either/or line reference, killing the legacy shared-ID hack.
  • RecpItems looks like recipe lines and is a decoy — RecpInv is the only truth for composition.
  • Legacy labor is dead fleet-wide: stub the port; build fresh simple labor (per-batch + reserved per-portion column) for the beta kitchen.
  • Q-factors are alive at 4 sites — port the math (it's one line).
Watch out
The diff harness answer key (RecpCost) has no timestamp and carries failure codes: diff only result-code-0 rows of active recipes; bucket code-1 as expected mismatches.

Margins & batch

thin layer on costing
Approach
Food-cost % / margin / target price off one portion cost. Batch simulation is a view, never an edit: ingredients scale linearly (measured — no bulk curve), labor scales by batch count, ledger says which is which.
What the data said
  • Menu-price slots 2/3: empty at all 14 sites. Migrate slot 1 only; our labeled-price-list model stands, the "three mystery slots" question answered itself.
  • Price-change history (RecpPrice) is rich where used (to 772K rows) — harvest for price features later.

Questions & memory (the ask-once loop)

threaded throughout
Approach
Questions are generated only from ledger entries — never invented. Ranked by dollar impact (what's blocked + how much the answer swings the plate). Deduped so nothing is ever asked twice. Friction budget: median ≤3 questions per recipe.
What the data said
  • ConvMissing's 62.7K real-world failures are the calibration set — build the question taxonomy against what kitchens actually hit, frequency-ranked, before a single user exists.

05The fleet, in numbers

80%
of purchase lines carry a vendor product code — matching is a join first
1
cost method in real use (of 11) — port one + dispatcher
575
real yield entries fleet-wide; 71% at 3 sites — seed + chef review, not migration
62.7K
logged conversion failures (ConvMissing) — the ask-once starter inventory
0
sites using menu-price slots 2/3, or legacy labor — stub both
6,600
items with enough history for price-trend series — the alert engine's fuel
<2000
ItemIDs = shared factory seed catalog — free cross-site matches (verify names!)
21.8%
of one site's "purchases" are internal fakes — the SubRecipe join is not optional

06The 16 amendments — one-line index

Full version with evidence: PLAYBOOK_AMENDMENTS.md (self-contained, fold into playbook §9). This is the quick index:

#TrapRule
1"In-house" vendor is data, not a constant; fakes flow through real vendors tooPseudo-purchase = SubRecipe=1 join per (PC, ItemID). Never by vendor.
2Same ItemID repeats per profit center inside one DBEvery join carries (ProfCentID, ItemID). UsFacts is the exception.
3Per-line yield column existsStage it; build nothing (unused fleet-wide).
4Custom unit IDs are site-local inventionsResolve units through each site's own Units table; never map raw IDs across sites.
5Unit aliases keyed by import format, not vendorModel alias source as format/maker; migrate all rows; audit before trusting.
6ConvMissing failure log (62.7K rows)Import it — question-queue seed + per-site gap metric.
7RecpCost: no timestamp, carries failure codesDiff only code-0 rows of active recipes; code-1 = expected mismatch bucket.
8Separators are physical Inv rows in recipesLine = ingredient XOR sub-recipe XOR separator; separators never cost.
9RecpItems mimics recipe linesRecpInv only for composition; RecpItems never feeds RecipeLine.
10Inv.Sku empty; ProdCo is the SKU storeImport ProdCo fully; (vendor, code) is a first-class cross-site match key.
11Seed catalog: low ItemIDs shared across sites — but renameableSeed-ID + name agreement auto-links; disagreements to review.
12IgnoreCost ~deadPort the flag; zero-row filters do the real work.
13Feature families empty or monoculturalOne cost method + dispatch · slot-1 prices only · port Q-factors · import bids/stub strategy · stub labor.
14Yields: 1.0 is the whole table; >1.0 is deliberate1.0 → NULL; allow >1.0 (hydration gains) with ledger annotation.
15Cloned template books at multi-PC sitesPer-PC fingerprint; duplicate PCs imported but quarantined from the corpus.
16Last-purchase-date lies about activityActivity = trailing-12-month line volume, never max date.