Flow
Two old tie rules, the new hash, and the test
1Store ID order
Same answer every time, but one ranking for all products, so the same stores win every leftover.
2Row key first
Looks fair when test keys rotate; falls back to store ID order when keys start with the store.
3Product and store hash
Same answer for one product, a new order for the next, no matter how rows are fed in.
4Reverse and count
Feed rows in reversed, check totals add up, and confirm leftovers reach several stores.
Favoring one store was something the ordering system did, and nobody had told it to. The cause was a sort that broke ties by store ID.
How the split works
The stores share supplier cases. The planner adds up what each store wants for one product, rounds up to whole cases, and then hands each case back out in small legal pieces, like a quarter of a case. The first extra piece goes to one store set up to take the extra. Each piece after that goes to the store that is furthest short.
Lots of code calls this same planner, including the checks that rerun it later to confirm a saved cart is right. So it has to give the same answer every time.
Where the bias came from
Ties probably happen a lot, with many stores wanting the same small amount. When they tie, the planner needs a rule to pick one. Mine used the store ID. That gives the same answer every time, which is good. But it's the same order for every product, so the same stores won every tie and the rest never did. I never measured how much that skewed real orders.
The test that fooled me
My first change sorted by the row key before the store ID. The test passed, but only because the test built row keys that rotated. In the real exact-product path the row key starts with the store ID, so nothing changed. I missed that the test was doing the work.
Hashing the product with the store
Now each store gets a tie key made by hashing the product together with the store ID. With made-up names:
const fingerprint = (sku: string, shopId: string) => hashHex(`${sku}|${shopId}`)
For one product the order never changes, so reruns agree. For the next product it's a new order, so a store that lost one tie can win the next. The same order is used when pieces get taken back.
The product is now a required input, and a blank one throws an error. If one caller left it out, or used a different value, its rerun would come out different from the plan and a good cart would get rejected. So I updated every caller at once.
Reversing the rows and counting winners
The new test runs many fake products. It feeds each one in forward and reversed and expects the same totals. It checks that units, cases and value add up, that every share is a whole piece, that no store gets every leftover, and that more than one store gets some. It doesn't ask for perfectly even counts. A hash won't give you that.
So the test answers one question: can a single store grab every leftover? No. And because every caller passes the same product, a rerun lands on the same split as the plan.