2026-05-13 on Supabase prod. Second of two bundles toward multi-register per shop + UUID-decoupling from Odoo's bigint. Bundle 2 ships the user-visible feature: /cafe/settings/registers admin, Starter POS landing renders N cards per shop, top-bar register picker, sequence_prefix on order numbers, dual-write of pos_config_uuid on session+order INSERTs.
31b2fdb (backend Bundle 2 migration) · f13d8fe (cafe main — admin page + Starter wiring + dual-write across 19 files) · 1f4569a (backend remap fix — relocate 171 orders off redundant Register 1 + retry delete) · 9cc59ec (cafe Hyperdrive wrap on read-after-write DAOs) · 0d45713 + d4ae2f1 (cafe duplicate-name detection via pre-check).SELECT pc.name, pc.odoo_pos_config_id, pc.pos_config_int_id, pc.shop_id, sess, ord FROM cafe.pos_configs pc + counts WHERE tenant = get-coffee: ┌─────────────────────────────────────┬─────────┬───────────────────┬──────┬──────┐ │ name │ odoo id │ pos_config_int_id │ sess │ ord │ ├─────────────────────────────────────┼─────────┼───────────────────┼──────┼──────┤ │ Bakery Shop │ 4 │ 4 │ 4 │ 9 │ │ Get Coffee CM (Chhuk Meas) │ 5 │ 5 │ 5 │ 29 │ │ Get Coffee TK (Toul Kork) │ 6 │ 6 │ 9 │ 87 │ │ Get Coffee TSP (Toul Svay Prey) │ 7 │ 7 │ 49 │ 45 │ │ Fresh Clean Shop │ 8 │ 8 │ 2 │ 1 │ └─────────────────────────────────────┴─────────┴───────────────────┴──────┴──────┘ All 5 rows shop_id = 809e3f7f-... (single get-coffee shop, enriched by step 1). Orders sum: 9 + 29 + 87 + 45 + 1 = 171 (re-mapped by step 6 from redundant Register 1's UUID; sums match the original count of orders that previously landed on the wrong row).
| test-multi-register-bundle2-prod.mjs (NEW) | 10/10 |
| test-phase1-prod.mjs | 11/11 |
| test-phase2-sso-outdoor-prod.mjs | 6/6 |
| test-phase2-cafe-multishop-prod.mjs | 6/6 |
| test-m1-prod.mjs | 10/10 |
| test-r7-prod.mjs | 14/14 |
| test-r8-prod.mjs | 4/4 |
| Total | 61/61 |
1. Bundle 1's OR-clause backfill (match by Odoo id OR by shop_id) could non-deterministically pick either when both branches matched — 171 orders landed on the wrong UUID on prod get-coffee. Bundle 2 added a remap step that scans for "rows currently pointing at a redundant Register 1 with a matching Pro alternative" and re-targets them. Lesson: in any future multi-table backfill, prefer NOT-EXISTS ordering over OR conditions. 2. Drizzle wraps pg errors in a "Failed query: ..." Error whose message doesn't carry the pg constraint name. The .cause chain *does* have the pg error with code='23505' + .constraint, but it's not 100% reliable. For unique-violation translation (e.g. duplicate names), pre-check the collision before INSERT rather than parsing the thrown error. Cleaner user-facing message, no error-format coupling. 3. Hyperdrive caches every SELECT for ~60s; admin pages that mutate then render need their DAOs wrapped in db.transaction(). The Bundle 2 admin page kept failing read-after-write until listRegistersForShop + getRegisterById + getRegisterByIntId were all wrapped. Mirrors the R9 + telegram-token incidents documented in project_hyperdrive_cache_stale_reads.md. 4. Demo's plan_code is 'cafe_pro' (not Starter as I assumed pre-test). The Starter tenant on prod is **lumiere-coffee** (5 shops, no Odoo). For any new Starter prod test, use owner@lumiere-coffee.com. 5. Bundle 1's INSERT-Register-1 check (`NOT EXISTS shop_id = s.id`) fired wrongly for tenants whose pos_configs had NULL shop_id. Bundle 2 added a "single-shop tenant enrichment" step (`pos_configs.shop_id = the single shop`) BEFORE the seed in subsequent runs — same idempotent behavior, cleaner outcome.
- Set NOT NULL + FK on pos_config_uuid for sessions/shop_pos_configs/pos_sequences. - Drop legacy int pos_config_id columns from those tables. - Rename pos_config_uuid → pos_config_id throughout. - Drop shopToConfigId helper. Can sit indefinitely in the dual-column state — Bundle 2 dual-writes the UUID on every new INSERT, so the legacy int is increasingly redundant but not breaking anything. Bundle 3 is "tidy up when convenient", not urgent. Separately out-of-scope (separate future task): The Odoo connector that pushes new NIX-created Pro registers to pos.config.create. Bundle 2 sets sync_state='pending_create' on those rows; today they're usable in NIX but invisible in Odoo until the connector drains them (mirrors R11.5's session-close-move drain).