← All posts

August 8 - the price field that looked like history and wasn't

The thing that clicked today: a passing test suite over fixtures you wrote yourself proves the code agrees with your assumptions, not that your assumptions agree with the data.

Breadcrumbs exports my own grocery orders, item by item, from the store's order pages. With 95 orders over seventeen months in hand, the obvious next thing was price history: what am I actually paying for the things I buy every week, and when does it jump.

Built / shipped

Two findings that shaped the schema. First, the per-unit price field the store exposes is a capture-time snapshot, not a historical price: twelve of the thirteen most-purchased items carry exactly one distinct value across the whole seventeen months, while the price actually paid moves a lot (one staple ranged from $11.97 to $20.99). And the line price does not equal unit price times quantity in 198 of 398 resolvable lines. The obvious design, charting the unit-price field, would have shipped flat lines and looked like a working feature. The authoritative signal is price paid divided by quantity. Second, there is no product identifier at all, not unstable, absent. Identity has to rest on the verbatim item name, which turned out to hold cleanly: 26 products recur four or more times byte-identical across the corpus.

A small local service. SQLite, bound to the machine's own address only, 12 of 12 tests, importing the existing export format and computing per-product series and alerts.

The alert rule, replayed against reality. After the suite passed, the rule was replayed against the real 95-order corpus rather than trusting the green. It produced 36 alerts, and the loudest was a +157.7% jump on red grapes, which was false. A fourth grammar for the unit-price field had been missed: a wrapper marking variable-weight items, present on 39 line items, for which the quantity column is a weight rather than a package count. The grapes series had silently switched from per-pound to per-package on its last observation. The shipped variable-weight heuristic tested for an empty field and therefore classified zero products as variable-weight on real data; the intended safety skip never fired. The synthetic fixtures passed because they encoded the same wrong assumption as the code. Corrected rule: 7 variable-weight products detected, the grapes false positive suppressed, and the true headline surfaced as eggs up 48%.

Promo-cycle suppression. Reviewing the alerts against the source showed 9 of 18 high alerts were promotions ending, not price increases; the price fell back afterwards. One staple alerted three times at +62.8% just because a promo ended, because the trailing median sits between the promo price and the regular price. Raising the threshold provably cannot fix it: a sweep from 7% to 25% only cuts 35 alerts to 18, because promo swings run 49 to 63%, far above any sane threshold; it would discard real signal and still fire three times on the staple. The fix classifies promo-cycling products from their full history (a spread test, sticky, because a rolling gate flickers and lets promo returns through) and baselines those against the 75th percentile, the regular price, instead of the median. High alerts 18 to 10, promo artifacts among them 9 to 4. The staple's three phantom alerts became two real ones at +7.7%, catching an actual regular-price rise from $19.49 to $20.99. Eggs survived and improved, baselined at the true regular price: +26.8% instead of +48%.

Problems & fixes

A one-alert divergence from prediction, 11 versus 10, chased rather than waved through: one product sits at +25.06%, six hundredths over the severity cutoff, and lands on opposite sides in the two languages' rounding. Cosmetic, same alerts, same items, recorded.

The alert distribution after a real import matched an independent local replay exactly, 35 alerts, which is the check that the import path and the replay path compute the same thing.

Decisions

Price paid divided by quantity is the signal; the unit-price field is a snapshot and is never charted as history.

Identity is the verbatim name, because there is nothing else, and the corpus shows it holds.

Replay every rule against the real corpus before trusting the suite.

Promo suppression by classifying the product from full history and baselining against the regular price, not by raising the threshold.

Learned

A passing suite over self-authored fixtures proves internal consistency, not correctness. The fixture and the code shared the same wrong assumption about an empty field.

Raising a threshold cannot fix a structural false positive. Measure the swing sizes first; here they were above any threshold that keeps real signal.

"Looks like history" is not history. Twelve of thirteen items had one value for seventeen months.

Still open / next

Threshold tuning after the first real import, the gap between the 95 orders exported and the 126 the store shows, and parity for a second store's format, which a friend's orders will test.