Reported by Narong 2026-07-20 from a live Get Coffee receipt. Verified on lumiere.staging
6/6 + unit suite 8/8 + staging smoke 5/5.
The Notion 06.06.2026 spec flagged the exception explicitly — it just wasn't implemented:
…and again as Expected Output #6: "2 Lower Price & 2 Higher Price → Free 1 lower price & free 1 higher price."
(1) Carve-out: multiples of the same drink pair with each other first — every 2 frees 1
of that drink. (2) Default: the leftovers (≤1 per drink) pair across drinks, freeing the cheapest
half. Grouping is by product, so one drink split across lines by modifiers (Iced 50% vs Iced 0%, as on the receipt)
still counts as "the same kind". Pairing moved to a pure, unit-tested module lib/promotions/bogo.ts.
2 files, no migration.
| Case | Expected | Before | After |
|---|---|---|---|
| Spec example A$1.75 / B$2 / C$3.25 | free A | ✓ | ✓ |
| 1. Buy 2 of the same kind | 1 free | ✓ | ✓ |
| 2. 1 lower + 1 higher | free the lower | ✓ | ✓ |
| 3. Buy 3 drinks | free lowest, pay 2 highest | ✓ | ✓ |
| 4. Buy 4 different drinks | free 2 lowest | ✓ | ✓ |
| 5. Buy 1 only | no discount | ✓ | ✓ |
| 6. 2 lower + 2 higher | free 1 lower + 1 higher | ✗ frees 2 lower | ✓ |
| Narong's receipt | free 2 GC + 1 M + 1 L | ✗ $11.80 | ✓ $11.45 |
Only case 6 changes — scenarios 1–5 and the worked example are untouched, which is the containment Narong asked for
("it shouldn't override the previous logic"). Freed units are always floor(N/2), asserted in every case.
Cart = 2× "BOGO Test Tea" ($2) + 2× "BOGO Test Latte" ($5), gross $14. Old logic → total $10 (frees 2 Teas). Spec logic → total $7 (frees 1 Tea + 1 Latte).


| Unit suite — 6 spec scenarios + worked example + the receipt | 8/8 |
| POS: cart total $7 (not $10) after Apply | pass |
| POS: promo badge on BOTH lines + FREE label | pass |
| POS: Remove promotion clears badges | pass |
| Staging smoke | 5/5 |
nix-cafe staging ddd418f
(Worker nix-cafe-staging). Starter tenant required — BOGO eligibility resolves cafe-native product UUIDs,
so Pro/Odoo ids degrade to ineligible. Parked for the scheduled prod promote.