Ground Truth

Restaurant AI Fails on Three Numbers

Mostafa DhouibMostafa Dhouib·10 min read
The short answer

Sales, labour and cost of goods each mean something specific in food and beverage, and a generalist gets all three wrong in directions that look plausible. Sales taken gross rather than net of comps, voids and discounts. Labour taken as wages rather than burdened with taxes and benefits. Cost of goods recognised from a stock count rather than from consumption, so product sitting in the walk-in is treated as sold. If the figures do not tie back to the source systems, no insight should be produced.

Restaurant AI Fails on Three Numbers

The short answer. Three figures carry almost all the meaning in a restaurant, and a generalist building analytics on top of a point-of-sale feed gets all three wrong in directions that look entirely reasonable. Sales taken as gross rings rather than net of comps, voids and discounts, which overstates revenue and flatters every ratio built on it. Labour taken as wages on the roster rather than burdened with payroll taxes and benefits, which understates the true cost of an hour. And cost of goods recognised from a stock count rather than from consumption timing, so product bought and still sitting in the walk-in is treated as sold. Each error is plausible, and the three move prime cost in different directions, so they do not cancel. The gate is reconciliation. Figures that do not tie back to the source systems should produce no insight.

Hospitality looks like an easy analytics domain from outside. A point-of-sale system with every transaction in it, a payroll system with every shift, an invoice pile from suppliers. Three feeds, a dashboard, some summarisation over the top. The reason it goes wrong is not the modelling. It is that the three inputs do not mean what a generalist assumes, and nothing in the data announces the difference.

Sales taken as gross ringsoverstatedcomps, voids, discounts, refunds and tax-inclusive pricing all sit between the ring and the revenue, so every ratio built on it is flattered
Labour taken as the roster wageunderstatedthe burdened cost adds payroll taxes, workers compensation, accrued leave, benefits and agency margin, and scheduled hours are an intention rather than what happened
Cost of goods taken from a stock countmistimedproduct in the walk-in reads as sold, and waste, spoilage and theft land in the same difference as consumption
Where the deductions clusterby shift, staff and locationso the wrong total also produces a wrong comparison, and comparisons are the entire output
What the three do togetherprime cost reads lowwith month to month movement driven by delivery schedules rather than by anything an operator did
What the deduction data actually isthe signal operators wantvoids rising on one terminal, comps under one authorisation code, discounts applied after the order closed
A language model summarising these figures cannot detect the problem, because nothing in them is anomalous. They are internally consistent and wrong.
FigureThree figures carry almost all the meaning in a restaurant, and a generalist gets all three wrong in directions that look entirely reasonable. Each error is plausible on its own, and because they push prime cost in different directions they do not cancel. The composite reads optimistic on two components and noisy on the third.

Sales is not what the till rang

The point-of-sale system records what was ordered. That is not revenue.

Between the ring and the revenue sit several deductions specific to this industry and routine within it. Comps, where an item is given away, for a complaint, for a regular, for a staff meal, for a marketing arrangement. Voids, where an item was entered and then removed, either a mis-key or a cancelled order after preparation. Discounts, from promotions, loyalty schemes, happy-hour pricing and manager overrides. Refunds after the fact. And sales tax, which in many jurisdictions is included in the displayed price and must be stripped out before anything is called revenue.

Take gross rings as sales and three things follow.

The revenue line is overstated, which is the obvious one. Less obvious is that every ratio built on it is flattered, because the denominator is inflated. Cost of goods as a percentage of sales looks better than it is. Labour as a percentage of sales looks better than it is. Prime cost, the figure operators actually manage against, comes out low, and low prime cost reads as a healthy restaurant.

Worse, the deductions are not evenly distributed. Comps and discounts concentrate in specific shifts, staff, items and days. So the gross-sales model does not just produce a wrong total, it produces a wrong comparison between locations and between periods, and comparisons are the entire output of an analytics product.

The deduction data is also the most interesting data in the system. A sustained rise in voids on one terminal, comps concentrated under one manager's authorisation code, discounts applied after the order was closed: those patterns are why operators look at this data at all. A model that nets them out silently, or never sees them because it reads a summary endpoint, discards the signal along with the error.

Labour is not the wage on the roster

The second figure is the cost of an hour worked, and the roster does not contain it.

The roster contains a wage rate. What the business pays is the wage plus everything attached to it. Employer payroll taxes and statutory contributions. Workers compensation premiums, which in hospitality are not trivial. Paid leave accrual, a real cost incurred in the period the hour is worked even though it is paid later. Benefits where they exist. Agency margin on agency staff.

The term for the total is burdened labour, and the gap between the wage and the burdened cost is large enough to change decisions. A schedule optimisation that reduces wage cost while increasing burdened cost, by shifting hours onto staff with a different cost structure, is a real outcome of using the wrong figure.

There is a second trap inside the same number, the difference between scheduled and actual. The roster is a plan. The time clock records what happened. Shifts run over, people clock in early, breaks are taken or are not, overtime triggers at thresholds that vary by jurisdiction and sometimes by award. Analytics built on the schedule describes an intention, and the variance from actual is often the thing worth managing.

So labour done properly is actual hours from the clock, costed at a burdened rate per employee, with overtime computed under the applicable rules. Labour done the generalist way is scheduled hours times a base wage, understating cost in a consistent direction while hiding the variance.

Cost of goods is consumption, not purchases

The third figure is most often wrong and least often noticed.

Cost of goods sold is the cost of the product consumed in producing the period's sales. What is usually available is purchase invoices, which record what was bought, and a stock count, which records what is on hand.

The relationship is opening stock, plus purchases, less closing stock, equals cost of goods sold. Skip that and use purchases, and the figure is wrong whenever buying and consumption do not coincide in a period, which in practice is always. A large delivery on the last day of the month charges that month with product that sells next month. A week of running down stock without reordering charges nothing for product that was sold. The error looks like a cost spike or an improvement and is neither. It is a timing artefact.

The failure mode these briefs usually contain is specific. Recognise cost from the stock count alone, treat the difference between counts as consumption, and several things are conflated. Product sitting in the walk-in has been bought and not sold, and if the count is taken wrongly, or on a different date from the period boundary, it shows up as sold. Waste, spoilage, breakage and theft all appear in the same difference and are indistinguishable from sales, so the number meant to measure cost efficiency is partly measuring loss.

Doing it properly needs more than invoices. It needs counts taken on the period boundary rather than when somebody had time, consistent units between the purchase unit and the recipe unit, which is where most of the real work lives, a theoretical usage figure from recipes multiplied by items sold, and the variance between theoretical and actual reported as its own number rather than buried. That variance is the output operators want. A model producing a cost percentage without it has produced the less useful of the two figures available.

The three errors do not cancel

It would be convenient if the three errors offset each other. They do not, and this is worth being precise about.

Overstated sales pushes every cost ratio down. Understated labour pushes the labour ratio down again. Timing-distorted cost of goods pushes the cost ratio in whichever direction the period's purchasing went, which varies month to month.

So the composite picture is biased optimistic on two of three components and noisy on the third. A dashboard built this way will consistently report prime cost below the real figure, with month-to-month movement driven by delivery schedules rather than by anything an operator did. Acting on it means managing the noise.

This is why the layered approach fails. A language model summarising these figures produces a fluent, confident narrative about cost control, and the narrative is about an artefact. The generation step cannot detect the problem, because nothing in the numbers is anomalous. They are internally consistent and wrong.

The gate

The conclusion is a design rule rather than a better model.

Before any figure is presented, it has to tie back to the source system it came from. Net sales for a period must equal the point-of-sale system's own net sales report for the same period, to the cent. Labour cost must equal what payroll recorded. Purchases must equal the supplier invoice total. Each is a check with a known right answer, because the source system produces its own version of the number.

If a check fails, the correct output is not a flagged insight or a confidence caveat. It is no insight. The pipeline stops, says which reconciliation failed and by how much, and offers nothing else until it ties.

The gate has to be absolute because the consumer cannot tell the difference. An operator reading a figure cannot distinguish one that reconciles from one that does not, and a plausible narrative over unreconciled figures is indistinguishable from a correct one. Everything downstream inherits the trust the gate either earned or did not.

The gate
Net sales matches the point of sale system's own report, labour matches payroll, purchases match the supplier invoice total. Does every check tie?
Produce the insight
everything downstream now inherits trust that was actually earned, and the variance figures become reportable in their own right.
Produce no insight
not a caveat, not a confidence score, not a flagged result. State which reconciliation failed and by how much, and offer nothing else until it ties.
A plausible narrative over unreconciled figures is indistinguishable from a correct one, which is why a caveat is not a substitute for a stop.
FigureThe conclusion is a design rule rather than a better model. Each of the three figures has a known right answer, because the source system publishes its own version of the number. The gate has to be absolute because the reader cannot tell a reconciled figure from an unreconciled one.

The honest limit

The three numbers are the ones I keep finding wrong. They are not the complete list. Tips and service charges have their own tax handling, delivery platform commissions raise the same gross-versus-net question again, inter-location transfers within a group break both stock and sales if unhandled, and multi-jurisdiction operators face different payroll rules per site.

The definitions here are general rather than jurisdictional. What counts as burdened labour, how sales tax is presented, and which deductions are permitted vary by country and sometimes by state. Anyone building this needs local accounting input rather than this article, which is about which numbers to distrust rather than about what the local rules say.

And the gate has a cost I should not understate. A product that sometimes refuses to say anything is harder to sell and harder to keep funded than one that always produces a confident paragraph. That is a real organisational problem, not a technical one, and it is where this approach usually dies.

FAQ

Why is point-of-sale revenue the wrong figure for sales? Because the till records what was ordered, not what was earned. Comps, voids, discounts, refunds and, where prices are tax-inclusive, sales tax all sit between the two. Using gross rings overstates revenue and flatters every ratio built on it, so cost of goods and labour both look better as a percentage of sales than they are. The deductions also cluster by shift, staff member and location, so comparisons are distorted and not just totals.

What is burdened labour and why does the wage rate not suffice? Burdened labour is the full cost of an hour worked: the wage plus employer payroll taxes and statutory contributions, workers compensation premiums, accrued paid leave, benefits, and agency margin on agency staff. The gap is large enough to reverse decisions. A schedule change that lowers wage cost can raise burdened cost by moving hours onto staff with a different cost structure, which a wage-based model reports as an improvement.

How does a stock count produce a wrong cost of goods figure? Cost of goods sold is product consumed, and a count tells you what is on hand. Without the opening-plus-purchases-less-closing calculation, product bought and still sitting in the walk-in is treated as sold, and a delivery landing near a period boundary shows up as a cost spike that is purely a timing artefact. Waste, spoilage and theft also fall into the same difference and become indistinguishable from consumption.

Do the three errors cancel each other out? No. Overstated sales pushes every cost ratio down, understated labour pushes the labour ratio down again, and timing-distorted cost of goods moves in whichever direction that period's purchasing went. The result is biased optimistic on two components and noisy on the third, so prime cost reads consistently low with month-to-month movement driven by delivery schedules rather than by operational decisions.

Why can a language model not detect the problem? Because the figures are internally consistent. Nothing about them is anomalous, so there is no signal for the generation step to catch. The output is a fluent and confident narrative about cost control that happens to describe an artefact of three definitional errors. Fluency is the one property that is guaranteed regardless of whether the inputs tie back to anything.

What should the pipeline do when a figure does not reconcile? Produce no insight. Not a caveat, not a confidence score, not a flagged result. State which reconciliation failed and by how much, and offer nothing else until it ties. The reason it has to be absolute is that the reader cannot tell a reconciled figure from an unreconciled one, so a plausible narrative over numbers that do not tie is indistinguishable from a correct one.

Free worksheet
The Seam Register

The five signs that a failure is in the seams, then one register per boundary covering units, ranges, timing, retry ownership and partial-success behaviour. Filled in separately by both sides, which is the whole method.

One email, the resource, and nothing else unless you reply.
Carrying a program like this one?

Tell us the system, the stakes, and the date that matters. You get a straight technical reply from the person who would lead the work, within 24 hours.

Bring us the program