The screen that said you had no kitchens
Sign in, land on Store, and the office told seven live venues they had no kitchens yet. The list it read had not arrived. An empty answer is not an answer.
Sign in to the back office, land on the Store screen, and read: no kitchens yet. The account in question runs seven live venues. Nothing had been deleted. The screen was telling the truth about a list it had never received.
How a working venue reads as empty
Four venue screens work out which venue they are showing from the list of sites the app holds at the moment they draw. If that list is empty the venue is zero, and zero means the empty state: the friendly card you see on day one, before you have set anything up. The trouble is that an empty list has two causes. Either you really have no venues, or the list has not arrived yet. The screen could not tell them apart, and it chose the reading that is wrong for every customer who has been trading for a year.
The exact path
It only happened on the reload straight after signing in. Signing in marks you as signed in and reloads the page, and the next boot takes the fast path: paint from what is cached, fetch the fresh list in the background. On a browser that had never cached the list there was nothing to paint from, so the router ran against an empty list, drew the empty state, and the real list landed a moment later with nothing left on screen that would redraw. A fallback that hides its own failure is a pattern we have met before, and this was it again. The fast path is meant to buy an instant paint. With a cold cache it buys nothing and costs a wrong screen.
So the app now waits for the list when the cache is cold, and keeps the instant paint exactly as it was when the cache is warm, which is the case the fast path was built for in the first place.
The proof that nearly proved nothing
We drove it both ways in a real browser. Before the fix: Store, heading, no kitchens yet. After: same path, same heading, the venue form. The first run of that check passed before the fix had even landed. The local database had come back without any users, so the browser was sitting on the first-run setup page, which also contains no mention of kitchens, and the probe read that as success. A check that passes on the wrong screen is not a check. It now refuses to give a verdict unless it is actually on the Store screen.
One more thing fell out. A test guarding an earlier fix sliced a fixed number of characters out of the sign-in code, and it raised an alarm when a new comment pushed the thing it was looking for past the edge. It was crying wolf about behaviour nobody had changed. It now reads to the end of the branch instead of to a character count, and it was re-proved to bite by putting the old reload back.