Release: a restaurant gets a restaurant, and a kitchen can say how long it takes - #786
Merged
Merged
Conversation
It got a coffee shop. "restaurant" pointed at the cafe pack - espresso, a smoothie, a croissant and a litre of milk - because that was the closest catalogue that existed, and the note said so in as many words: "Closer than groceries, which is where it landed before." Closer is not right. A restaurant had to delete fifteen coffee-shop products before it could type its first dish. The pack alone would not have been enough. The installer builds each item from a fixed object literal, and a field the pack carries that the literal does not name is dropped on the way in, silently - the third time this shape has cost this program a feature, after the ordering catalogue dropped the dish facts and then the market price. So diet, prep time, nutrition, tags, marks and the spice choice are carried through, cleaned by the same rules the item form uses so a pack cannot smuggle in a claim. That matters because a menu is not a price list. Without those fields the ordering page has nothing to filter by, no prep time to show, no badge to earn and no spice choice to offer, and the demo demonstrates a column of names - which is what the restaurant already had on paper. The dishes carry what a real half-filled menu carries: veg marks on all of them, prep times on nearly all, nutrition on six, and a spice choice on seven. Deliberately not on the dal or the biryani, which a kitchen makes once in one pot and cannot vary by the portion - the menu shows a curry that cannot be made mild sitting beside ones that can. Also wires dish-icons into the seeder, which was written for this and never connected. Every demo item without a photograph now gets an icon read from its own name, right 22 times out of 22 on this pack and on every cafe and bakery product too. The restaurant pack has no photographs. There is no licensed food photography for these dishes in the manifest and a code change cannot invent any, so the test that asks for real images now exempts it by name and says why, rather than quietly passing.
A restaurant that signs up gets a restaurant
Step 1 of Stage 2 in the print roadmap, and the step usually skipped: "Write print_jobs rows and drain nothing. Compare, for a week, what the queue says should print against what actually printed. Any disagreement is a bug found before it can cost anything." WHY IT IS WORTH A WHOLE CHANGE TO PRINT NOTHING Ninety shops feed their kitchens through multiKitchenPrint. The failure mode of a bad cutover is not an error message - it is a dish nobody made, or two dishes where one was ordered. Owner: "printing dont bring me new issues. keep changes safely." So the queue records what it believes should print, the old path keeps printing exactly as it does, and the two are compared. Nobody can currently say whether duplicates happen four times a week or forty; this can answer that without touching a printer. HOW IT CANNOT PRINT Rows are written with status `shadow`, which nothing claims - claimPrintJobs matches `queued` only, and the till asks for bills. A test drives the real claim and fails if that filter ever widens. They carry no payload either, so there is nothing to print even by accident. WHAT IT RECORDS Tickets named the way the TILL names them, both schemes - a sale with no print_jobs array is named by the fallback rather than skipped, because skipping it would make the shadow blind to a whole class of ticket and then report perfect agreement, which is the worst outcome a measurement can have. One row per ticket, enforced by a partial unique index rather than by a read-then-write: two tills polling at once would both read "absent" and both insert. An amended order is a second row, because it is a second ticket. CLOSING A ROW, AND THE BUILD THAT DOES NOT KNOW ABOUT IT The till now sends printedKeys - the names of what actually reached paper, only when paper actually came out. A failed ticket reported as printed would close a row that should be showing up as a disagreement. But ticket names are new on the wire, and closing only by name would leave every row from every older till open for ever - the first report would say the whole estate is failing, which is worthless exactly when it is meant to establish a baseline. So an older till still closes its rows by sale. That is less precise and understates disagreement rather than inventing it, which is the safe direction for a number nobody has yet. It is a bystander throughout: every failure is swallowed inside, and a test drives it against a database that throws on every call to prove a broken shadow cannot fail a service. API 10715 pass, desktop 2541 pass.
…-shadow The kitchen queue watches, and prints nothing
Counted on the live shop the day this was written: 272 dishes, none with a prep time. The field has been on the item form all along. It is empty because filling it means opening 272 dishes, and nobody does that. Two customer-facing things go quiet without it. The dish sheet cannot say "takes about 20 minutes" so somebody can decide before ordering, and the busy-kitchen notice shipped last release uses the shop's median prep time as its round length - so on every shop in the world it can say the kitchen is behind and never by how much. That is the weakest sentence it knows and the only one it can currently say. A kitchen already thinks in sections: breads come off the tawa in eight minutes, a biryani is thirty, a papad is three. So the scope is the one the price, stock and spice tools already use. Only the empty ones by default. A shop that has hand-tuned a few dishes has done the most valuable work on this field, and a blanket write would erase exactly that; overwriting is available and has to be asked for. The check says out loud how many it is sparing, so a shop can see its own numbers are safe rather than trusting the wording of a checkbox. Writing the test found a real one: Number(null) is 0, so a client that sent no minutes at all - or a form that failed to read its box - would have arrived as a confident request to set every dish in the shop to zero minutes, which reads on a menu as "ready instantly". Refused now at both ends, with the reason written down.
Stage 3 of the print roadmap: "Replace 20 seconds x 15 then silence with:
acknowledge stops it, escalate in reach rather than volume, back off, then the
shop's declared default fires."
WHAT WAS THERE
const REPEAT_EVERY_MS = 20000;
const MAX_REPEATS = 15; // five minutes of asking, then it stops nagging
Five minutes, then silence, with the order still unanswered and the customer
still waiting. The comment beside it is honest about why: "An alarm that never
stops is one somebody mutes at the speaker, and then it is gone for every future
order too."
That reasoning is right and the conclusion is wrong. The choice was never
between nagging for ever and giving up; it is between the same volume for ever
and BACKING OFF. An order arriving at 19:05 was never mentioned again, which is
how one sits until closing time - the roadmap names it: "no order sits in
pending overnight. Today they can."
FOUR RULES
It backs off rather than stopping: 20s, a minute, five, then every fifteen for
as long as it takes. A slow heartbeat is still a signal; silence is not. At two
hours it is still speaking.
It escalates in REACH, not volume - till, then handsets, then the owner.
Louder is how an alarm gets muted at the speaker; further is how it gets
answered.
Acknowledging stops the noise, and nothing else does. "I have seen this" is a
different act from "I have accepted it" and a shop mid-rush needs the first
without the second. But acknowledged is NOT answered: the shop's declared
default still fires on time, or acknowledging becomes a way to park an order for
ever by tapping a button - the same bug wearing a different hat.
Then the shop's declared default fires. Owner: "let restaurent owner decide
that. give option auto cancel or auto accept. based ont time he defines it. by
default dont accpept or reject." Absent means nothing happens, and a value the
code does not recognise is treated as no choice - a product that cancels a
customer's order because a shop never opened a settings page has made a decision
that was not its to make.
AND TWO THINGS THAT ARE NOT SETTINGS
An aggregator's clock is not ours. Swiggy and Zomato reject on their own timer
and count it against the shop, so a decision of ours landing after theirs is
worse than none: the order is already gone and we have recorded the opposite. A
shop can only act inside their window.
The customer can always retry or contact the shop. If a shop never sets a timer
the order sits, so the one thing that must always hold is that the person
waiting is not trapped in silence. A shop chooses what IT does; it cannot choose
to leave a customer with no way out.
The deciding is pure and lives in waiting-order-policy.js, so it can be tested
without a clock. The alarm announces a decision on the process bus and never
performs one - a sound module that could cancel a customer's order would be a
surprising place to find that power.
An existing test asserted the opposite ("the alarm gives up rather than becoming
background noise") and now pins the new behaviour, with the reversal written
down rather than quietly edited.
Desktop 2638 pass.
…lence An order is never left in silence
How long a section takes, said once rather than 272 times
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Promotes develop to main. Four merged pull requests now, not one - three landed on develop after this was opened, so the description is refreshed to match what the button would actually ship.
Merge with a merge commit, never a squash. The repository ruleset enforces that, so the button should only offer it.
The online ordering channel
#785 - a restaurant that signs up gets a restaurant. It got a coffee shop:
"restaurant"pointed at the cafe pack, so a new restaurant had to delete espresso, a smoothie, a croissant and a litre of milk before typing its first dish.It also fixes why the whole restaurant feature set has been invisible in the product's own demo data. The installer builds each item from a fixed object literal, and a field the pack carries that the literal does not name is dropped silently - the third time that shape has cost this program a feature.
diet,prep_minutes,spice_choice,nutrition,food_tagsandmenu_marksnow travel, cleaned by the same rules the item form uses. Anddish-icons, written for the seeder and never wired to it, now gives every photo-less demo item a picture read from its own name.#788 - how long a section takes. The busy-kitchen notice is live and working on production - checked while writing this:
capacity: 13, open: 0, busy: false, the whole chain answering on real data. But that shop has 272 dishes and not one prep time, so if the kitchen did fall behind it could say so only in words and never quote a figure. The field has always been on the item form; it is empty because filling it means opening 272 dishes.Tools → How long a section takes, with the same scope and check-then-apply as the price, stock and spice tools. Only the empty ones by default, because a shop that has hand-tuned a few dishes has done the most valuable work on this field and a blanket write would erase exactly that.
The print roadmap, from another session
#787 - the kitchen queue watches and prints nothing, the shadow step usually skipped. #789 - an order is never left in silence.
Nothing changes for an existing shop
Demo packs are installed at signup or on an explicit reseed, so no live catalogue is touched by this merge. The two new catalogue tools do nothing until a shop opens them.
Verified before opening
Root suite 2,633 of 2,636 on the develop tip; the three remaining are the release-artifact tests that need
frontend/publicbuilt, which CI does. API suite 10,741 of 10,741.