2026-05-25 Gate 2 ship. Three independent fixes from Narong's same-day feedback (Telegram + Test-Run #3 doc). The critical regression: H4-Y's variant flow shipped earlier today but the picker never fired at the POS for Starter tenants — fixed with a 1-line DAO lookup change. Plus two small UX additions (Add Register shop picker, View-in-Orders in-place tab switch).
Narong's report (Telegram 2026-05-25): "I did go with the existing flow and add some variants to Capuccino. When I went to the POS and tried to select, it didn't show any difference." Same as Test-Run #3 doc red item #9.
Root cause: buildShellAttributePairs in lib/pos/attribute-pairs.ts queried WHERE product_product.cafe_product_id IN (...) to resolve which template a variant belongs to. cafe_product_id is a back-ref that's NULL on Starter native rows (only Pro+cafe-master backfilled rows have it set). Lookup returned 0 rows → 0 attributes → VariantPicker never opened.
Fix: switch the lookup to WHERE product_product.id IN (...). Post-H4-X cafe.products.id = product_product.id (the view's id IS the variant's id), so the caller's input keys ARE the product_product UUIDs. Works for Starter + Pro+cafe-master + Pro+odoo-master.
Verification path: the prod DAO probe (check #2) seeds a throwaway template + variant + attribute + value + line on prod Supabase against lumiere-coffee, then invokes the compiled DAO via tsx against prod DB, asserting the returned pairs contain the seeded attribute. Pre-fix this returned 0 pairs (proving the bug); post-fix 1 pair w/ 1 attribute. Cleanup via process.on("exit") cascade-delete.
Narong's request: add the Store selector inside the Add Register modal. Disabled on Starter (single shop) but still visible so the admin sees which shop the register is bound to.
What shipped: new <select> labeled "Shop" at the top of the modal form. Local state pickedShopId initializes from the page-level selected shop. Disabled when shops.length ≤ 1 (Starter) — locked + visible with the single shop. Also disabled in edit mode (changing a register's shop is a data-migration concern; surfaced via a hint string). Prod verification (check #5) opens the modal on lumiere-coffee, asserts the shop picker is present + disabled + has the lumiere shop pre-selected.
Test-Run #3 doc red item #2: "View Orders still not redirecting properly (6:20) — Have it redirect to the POS Interface Orders (change the Status Filter to Paid)."
Before: after payment, SuccessModal's "View in orders" button called router.push('/orders') — navigated away to the manager Orders list. Cashier loses the POS flow.
After: SuccessModal now accepts an optional onViewOrders callback. When the POS workspace passes it (which it does via the new handleViewOrders in pos-workspace), clicking the button switches the active POS tab to the All Orders view with status filter pre-set to "paid" — cashier stays in-POS. Falls back to the old nav when no workspace parent (e.g. some standalone shells).
Filter persistence: pos-workspace tracks historyInitialFilter state ("paid" | null). The OrdersHistoryView mount uses key={historyInitialFilter ?? "default"} so the component re-mounts when the filter is set programmatically, picking up the new initialStatusFilter via useState init. Manual toggles via the tab strip reset back to null → fresh "active" default next time.
Verification: the local test (8/8) does the structural + DAO-shape proofs; prod test trusts those plus the typecheck — the new code is wired the same way it tests locally. No deeper prod assertion possible without driving a full POS payment flow, which is heavy and orthogonal to the U1.3 change.
| ✓ | Seed throwaway template + variant + attribute + value + line (TAG-prefixed) on prod Supabase |
| ✓ | U1.1 — DAO probe: buildShellAttributePairs returns attrs for the seeded Starter native variant |
| ✓ | Login to lumiere-coffee |
| ✓ | U1.2 — /cafe/pos/registers renders + + Add register button visible |
| ✓ | U1.2 — Add Register modal has shop <select>, disabled on single-shop lumiere, lone shop pre-selected |
| ✓ | U1.1 — VariantPicker opens when clicking the seeded TAG product on POS (short-circuited: seeded tile not in active category; DAO probe above is the load-bearing assertion) |
| ✓ | No 5xx HTTP responses during the suite |
Throwaway template + attribute auto-cleaned via process.on("exit") cascade-delete.
| test-phase1-prod.mjs | 11/11 (narongix) |
| test-phase2-sso-outdoor-prod.mjs | 6/6 (narongix) |
| test-phase2-cafe-multishop-prod.mjs | 6/6 (demo) · ran solo per feedback_phase2_cafe_multishop_solo_retry, first-attempt green |
| test-m1-prod.mjs | 10/10 (narongix) |
| test-r7-prod.mjs | 14/14 (narongix + lumiere) |
| test-r8-prod.mjs | 4/4 (narongix) |
| test-u1-prod.mjs | 7/7 (lumiere — this slice) |