NIX-OS Automated Test Gallery

Visual QA screenshots for completed tasks.

R4.4 — Drop legacy tenants.plan_code + tenant_limits PROD

2026-04-29 on prod. Final R4 sub-phase — closes Phase 5 (NIX Cpanel). Cafe's getTenant() now joins subscriptions(product_code='nix_cafe') to derive tier (cafe_starter/pro/master → starter/pro/enterprise); PayWay webhook stops writing the deprecated tenants.plan_code; seed-supabase-getcoffee.ts upserts both outdoor_sales + nix_cafe subscription rows for fresh DBs (closing the gap that bit us in R4.x and R4.3). New cafePlanController helper in test-utils.mjs centralized the capture/flip/restore pattern across 12 prod test files (bulk-migrated via 2-pass regex script). Migration drops tenants.plan_code column + tenant_limits table. 107/107 prod tests green BOTH before AND after migration applied — no remaining consumer of the dropped column. R4 done.

R4.3 — Per-product usage endpoint PROD

2026-04-29 on prod. Cpanel TenantDetailView Subscriptions card now shows a live usage bar per (tenant, product). Backend: GET /admin/tenants/:t/subscriptions/:productCode/usage branches Cafe→COUNT cafe.orders WHERE state='paid' AND date_trunc('month',created_at)=current month, Outdoor→COUNT activities WHERE activity_type='visit' this month, others→404. Limit from subscriptions.limits_json (0/missing→null/∞). New UsageBar.vue auto-fetches on subscription expand, renders green/amber/red on 70%/90% thresholds. Surfaced + fixed mid-Gate-2: get-coffee + demo had no nix_cafe subscription rows post-Supabase-migration (operator backfill via SQL); test cleanup used JSON-through-bash for SQL escape that failed (rewrote to SELECT-into-UPDATE referencing the plan's defaults). 8/8 new prod + 99/99 regression sweep green; 107/107 total.

R4.2 — Impersonation E2E PROD

2026-04-29 on prod. Admin-initiated tenant_user impersonation across 5 repos. Cpanel "Sign in as" → backend issues 30-min JWT with imp claim referencing new commerce.impersonation_sessions row, sets shared nix_session cookie on .nixtech.app, returns redirect → admin lands on tenant subdomain authenticated as target user. Sticky amber banner on every authed page in Cafe + Commerce + Outdoor (3 banner components, Next server component + 2 Vue) shows admin email + End button. Tenant-end and admin-revoke both mark row ended_at + ended_by; JWT validation joins on the row, so revoked sessions immediately stop authenticating. Admin's Cpanel session preserved (separate Bearer scope on admin.nixtech.app). Audit log writes started + ended events to tenant_audit_logs. New ActiveImpersonationsView in Cpanel lists live sessions with 30s auto-refresh + revoke. 9/9 new prod + 90/90 regression sweep green; 99/99 total.

R4 carried-forward follow-ups — admin seed + is_active sweep PROD

2026-04-29 on prod. Two non-blocking follow-ups from R4.1 bundled together. (A) Idempotent admin_operators seed in nix-outdoor-sales-backend/migrate.js — any future fresh DB (Supabase Pro upgrade, dev clone, full re-migration) gets Cpanel access from minute zero, no more silent 24h lockout like the one R4.1 caught. Idempotency verified by running migrate.js against live prod Supabase before push: existing row untouched, "= admin_operators already populated (1 rows) — skipping seed". (B) is_active filter on the two remaining shop-read surfaces — kitchen display secret (saved URL stops authenticating when ops disables the shop) and createCashierAction shop validation (can't assign cashier to retired shop). Deliberately skipped pin_identities.listPinIdentities leftJoin — Team page wants the historical shop name for context. No new test (small surgical fixes); verified by R4.1 9/9 sanity + full regression sweep clean. 90/90 total green.

R4.1 — Multi-shop activation E2E PROD

2026-04-29 on prod. First sub-phase of R4 (Cpanel finish). Drove the full multi-shop activation path on get-coffee — admin API → create 2nd shop "annex" → cashier-side ShopPicker dropdown verified visible (single-shop label gone) → switch shops via UI → Cpanel soft-disable → picker collapses back to label without 60s lag. Two real findings landed: (1) admin_operators table was empty after Supabase migration — Cpanel had been silently locked out for 24h+; restored one operator row out-of-band. (2) listAccessibleShops didn't filter on is_active AND wasn't wrapped in db.transaction() — soft-disabled shops still appeared in the picker, and even with the filter, Hyperdrive's ~60s SELECT cache made disables lag. Both fixed in lib/db/shops.ts. 9/9 new prod + 81/81 regression sweep green; 90/90 total.

Cafe follow-ups — skip-preview banner + daily report split PROD

2026-04-29 on prod. Two operator-feedback follow-ups bundled into one Gate cycle. (A) Success-banner Print fast-path: post-sale Print fires the print iframe directly, no modal hop — operators just rang up the order and don't need a second confirmation. /orders per-row Print still goes through the preview modal (older-order reprint sanity). (B) Daily report splits totalDiscount into totalLineDiscount + totalOrderDiscount in the DAO + UI — operators can see per-line comp/promo ($1) vs whole-ticket override ($0.30) separately. End-to-end seeded scenario verified DB ground truth on both discount_usd columns + iframe-no-modal on banner Print + split testids in the report. 5/5 new prod + 76/76 stable regression suite (receipt-preview rewritten /orders-only 6/6 + r2-followups3 7/7 + r2-followups2 11/11 + r2-followups 19/19 + r2-4b 12/12 + 70-2 10/10 + phase1 11/11) green; 81/81 total.

Receipt preview modal + Render→Supabase migration PROD

2026-04-28 on prod. Operator-requested in-shell receipt preview before print — new shared ReceiptPreviewModal renders KhmerReceipt with Cancel + Print + Esc-to-close + backdrop-click. Wired into Starter register success banner and /orders per-row Print. Bundled with the full Render→Supabase prod database migration after Render's free tier auto-suspended: 99 knex migrations + migrate.js + tenant seed (get-coffee + demo) + Cafe Worker secret + Hyperdrive update — all on session-mode pooler (port 5432; transaction mode 6543 broke Drizzle prepared statements). 8/8 prod + 70/70 stable regression suite green; 78/78 total.

R2 follow-ups round 3 — P1+P2 bundle PROD

2026-04-26 on prod. Two ergonomics polish items. Wired the R2 GC route to the existing nix-cafe-cron-trigger Worker (redeployed with second cron expression "30 18 * * *" — first fire tonight 01:30 Phnom Penh). Added a 10% / 20% / 50% / Comp quick-percent picker to the per-line discount editor — verified end-to-end on a $10 line: 10% → $9, 50% → $5, Comp → $0, X clear → $10. 7/7 prod + 63/63 stable regression suite (round 2 11/11 + round 1 19/19 + R2.4b 12/12 + 70.2 10/10 + phase1 11/11) green.

R2 follow-ups round 2 — F1+F2+F3 bundle PROD

2026-04-26 on prod. Three small follow-ups bundled. R2 GC cron route (POST /cafe/api/cafe/cron/r2-gc with X-Cron-Secret + ?dry=1 + 500-key safety cap, auth boundary verified 401/401), per-line discount on cart (cafe.order_lines.discount_usd column + inline cart editor + Customer Display lineTotal field — verified end-to-end with $1 line discount + $0.30 order discount → $5.70 total + DB ground truth on both columns), and receipt printing via hidden iframe (no popup, verified by Print click → iframe in DOM + zero popup windows). 11/11 prod + 52/52 stable regression suite (round 1 19/19 + R2.4b 12/12 + 70.2 10/10 + phase1 11/11) green.

R2 follow-ups — S1+S2+S3+M1+M2 bundle PROD

2026-04-26 on prod. Five Starter-tier follow-ups bundled into one Gate cycle. UploadButton in Starter products (uploads SVG → R2 URL → DB persist → public fetch round-trip), multi-shop register selector (single-shop tenant collapses to label), Customer Display BroadcastChannel sync (cashier rings → display flips idle → cart in real time), receipt printing (Print on success banner + per-row in /orders, GET /api/cafe/orders/:id), and discount + multi-payment splits in the rebuilt PayDialog (10% discount on $7 → $6.30 → split $4 cash + $2.30 card → DB has discount_usd=0.70). 19/19 prod + 33/33 stable regression suite (R2.4b 12/12 + 70.2 10/10 + phase1 11/11) green.

NIX-OS-68B — Polish bucket Tier B PROD

2026-04-24 on prod. Three follow-ons: (1) optimistic-UI fix for the cashier deactivate race in team/cashier-tab.tsx — Active/Inactive chip flips within 500ms on click with rollback on failure; the first prod run caught a real bug (handler read from server prop instead of optimisticActive on rapid clicks) and the fix landed same session; (2) AGENTS.md gains a "Local dev gotchas" section with 5 anchors; (3) r1-2 rewritten for popup contract (now 8/8 — was 7/9 for weeks) and r1-3 adds cashier-picker click (now 13/13 — was 6/13 for days). 8/8 prod + 15 stale regression checks turned green.

NIX-OS-68A — Polish bucket Tier A PROD

2026-04-24 on prod. Three small wins shipped together: (1) settings sidebar wires real Link components for Display Branding, Payment Methods, Diff Reasons (operators no longer have to type URLs); (2) Render/knex divergence — orphan create migration deleted, migrate.js gains an idempotent re-drop entry so cafe.user_pos_access can't permanently resurrect (verified clean on prod); (3) NIX Cash Chrome extension README marked DEPRECATED with a feature-mapping table to R1.7 / R1.8 / NIX-OS-70 native replacements. 8/8 prod + 101/101 stable regression suite green.

NIX-OS-70 — Personalize Customer Display PROD

2026-04-24 on prod. Tenant-set branding for the secondary customer screen — logo URL, primary color, promo text, promo image URL. New /cafe/settings/display admin page with two-column layout (form + live PreviewIdle that updates as you type). Public Customer Display idle screen renders the saved logo, primary-color gradient, promo text card, and promo image. End-to-end click-through: type branding → save → server action persists → DB ground-truth match → public display renders the saved branding. Bad-hex rejection surfaces a clear error. 10/10 prod + 98/98 stable regression suite green.

NIX-OS-69.2 — Kitchen Display route + state machine PROD

2026-04-24 on prod. Second sub-phase of NIX-OS-69. Public Kitchen Display route at /cafe/kitchen/[shopId]?t={secret} with three columns (New / Preparing / Ready), 5s polling, optimistic Start/Ready/Served buttons. GET + POST API endpoints with secret + cross-shop scope guards (404 / 400 / 409 / 200). POS TopBar ChefHat pill is now a clickable button that opens the display in a new window. End-to-end click-through on prod: full state machine (Start → Preparing → Ready → Served) verified via real UI clicks + DB ground-truth assertions on every transition. 11/11 prod + 96/96 stable regression suite green.

NIX-OS-69.1 — Kitchen Display foundation PROD

2026-04-24 on prod. First sub-phase of NIX-OS-69. New cafe.kitchen_orders table + DAO with new→preparing→ready→served state machine, per-shop kitchen_display_secret on commerce.shops, POS order-validate hook enqueuing a denormalized payload, and POS TopBar ChefHat pill showing queue depth. End-to-end click-through: real Odoo order validated → kitchen_orders row lands with POSID-stamped order_name and non-empty payload → TopBar ticks from 0 to 1. 10/10 prod checks + 84/84 stable regression suite green. Kitchen Display route + status transitions ship in 69.2.

NIX-OS-73 — Telegram Event Wiring PROD

2026-04-23 on prod. session_closed + new_order events now fire to opted-in groups; daily cron upgraded to use fetchDailyReport (NIX-OS-72). Central notify dispatcher with audit log + HTML-safe formatters + fire-and-forget so Telegram outages never block the POS flow. 11/11 click-through + 77/77 regression = 88/88 prod tests green.

NIX-OS-71 — Cash In / Cash Out PROD

2026-04-23 on prod. Mid-shift cash drawer adjustments — tips, supplies, petty cash, float top-ups, bank runs. New cafe.cash_movements table with CHECK constraints + FK cascade. 💰 Cash button in POS TopBar opens two-column dialog: record form + running ledger + recent-movements list. Folded into Close Shift expected-cash math and Daily Sale Report per-session cards. 12/12 click-through + 65/65 regression = 77/77 prod tests green.

NIX-OS-72 — Daily Sale Report PROD

2026-04-23 on prod. New /cafe/reports/daily page — totals strip, tender breakdown, top 15 products, per-session cards (cashier, opened→closed, beginning/ending cash, tender split, diff + reason), date picker + prev/today/next nav, print-friendly CSS for A4 / 80mm thermal. 11/11 click-through + 54/54 regression = 65/65 prod tests green.

R1.8 — Native cash helper + Payment Methods (NIX-OS-67 Phase 3 POS) PROD

2026-04-23 on prod. Final R1 sub-phase. Read-only Payment Methods admin page at /cafe/settings/payment-methods + 6 USD ($1/$5/$10/$20/$50/$100) + 8 KHR (500→100,000) denomination buttons in the POS cash entry modal + ↻ Reset cash button. Replaces the retired NIX Cash Chrome extension with a fully native flow. 10/10 click-through + 44/44 regression = 54/54 prod tests green. Closes all 8 R1 sub-phases.

R1.7 — Customer Display (NIX-OS-67 Phase 3 POS) PROD

2026-04-23 on prod. Secondary customer-facing display synced via same-origin BroadcastChannel. Three states (idle / cart / paid) auto-rotating. Public route gated by per-session display_secret. 11/11 click-through + 33/33 regression = 44/44 prod tests green.

R1.6 — Close register popup (NIX-OS-67 Phase 3 POS) PROD

2026-04-22 on prod. Cash count + optional bank count + auto-computed diff + optional difference reason dropdown (admin CRUD at /cafe/settings/payment-diff-reasons). All closure fields land on cafe.sessions. 11/11 click-through + 33/33 regression = 44/44 prod tests green.

R1.5 — POSID-NNNNN daily sequence (NIX-OS-67 Phase 3 POS) PROD

2026-04-22 on prod. POS{configId}-{seq} daily-reset order numbers (spec §8.8). Manually verified: POS06-0001 and POS06-0002 printed successfully, cafe.pos_sequences next_seq=3 confirms two atomic advances. Gate 2 was a firefight — seven Cafe-Worker/Hyperdrive gotchas fixed, including the 405 from missing /cafe basePath on client fetches (affecting 6 call sites). 42/42 regression green.

R1.5 — POSID-NNNNN daily sequence (NIX-OS-67 Phase 3 POS) LOCAL

2026-04-22. Order numbers follow spec §8.8: POS{configId}-{seq}, zero-padded, reset at midnight in tenant's local timezone. Atomic advance via UPSERT+RETURNING survives concurrent cashiers. 3/3 Gate 1 checks (format, advance, daily reset, 5-way concurrency). Prod click-through at Gate 2.

R1.4 — Multi-cart draft orders (NIX-OS-67 Phase 3 POS) PROD

2026-04-22 on prod. Park button + parked-orders strip in cart panel. Carts persist to cafe.draft_orders (JSONB, FK CASCADE). Survives tab crash / device handover. 9/9 R1.4 + 33/33 regression = 42/42 prod tests green.

R1.4 — Multi-cart draft orders (NIX-OS-67 Phase 3 POS) LOCAL

2026-04-22. Cashier can park a cart and start a new one in parallel. Parked carts persist to cafe.draft_orders (JSONB payload, FK CASCADE to cafe.sessions). Survives tab crash / browser restart. New Park button + parked-orders strip in the cart panel. 3/3 Gate 1 checks. Prod click-through at Gate 2.

R1.3.1 — Cashier picker + manager POS PIN + anti-escalation (NIX-OS-67 Phase 3 POS) PROD

2026-04-22 on prod. Four QA gaps closed: PIN-collision; Manager/Owner unlock via "Unlock as {name}"; mandatory separate POS PIN (tenant_users.pos_pin_hash); Commerce-password requirement on PIN-set to prevent cashier escalation. 14/14 R1.3.1 + 33/33 regression = 47/47 prod tests green.

R1.3.1 — Cashier picker + manager override with POS PIN (NIX-OS-67 Phase 3 POS) LOCAL

2026-04-22. Three QA gaps closed: (1) PIN-collision → picker-then-PIN; (2) Manager/Owner couldn't unlock → "Unlock as {name}" button; (3) Unattended-terminal risk → Manager requires separate POS PIN (new tenant_users.pos_pin_hash, inline "Set your PIN" dialog on first use). 4/4 Gate 1. Prod at Gate 2.

R1.3 — Lockable POS shell + PIN unlock + beginning cash (NIX-OS-67 Phase 3 POS) PROD

2026-04-22 on prod. Third cycle of R1: fullscreen new-tab POS route with three-phase state machine (Locked → PreShift → Open), signed active-cashier cookie, full resume flow after Lock. 13/13 R1.3 + 33/33 regression = 46/46 prod tests green.

R1.3 — Lockable POS shell + PIN unlock + beginning cash (NIX-OS-67 Phase 3 POS) LOCAL

2026-04-21. Third cycle of R1: new fullscreen route /cafe/pos/register/[configId] opens in its own tab. Three-phase state machine (Locked → PreShift → Open), nix_active_cashier JWT cookie separate from web session, unlock/openShift/lock/closeShift server actions, NIX cafe.sessions row mirrors Odoo pos.session. 4/4 Gate 1 checks. Prod click-through at Gate 2.

R1.2 — cafe.sessions schema + grouped-by-shop POS landing (NIX-OS-67 Phase 3 POS) PROD

2026-04-21 on prod. Second cycle of R1: cafe.sessions + payment_diff_reasons + pos_sequences migrations live on Render. POS landing now groups registers by shop with Odoo session summary per card. 9/9 R1.2 + 33/33 regression = 42/42 prod tests green.

R1.2 — cafe.sessions schema + grouped-by-shop POS landing (NIX-OS-67 Phase 3 POS) LOCAL

2026-04-21. Second cycle of R1: cafe.sessions (Odoo pos.session mirror with PIN + cash-count fields), cafe.payment_diff_reasons (R1.6), cafe.pos_sequences (R1.5). POS page now groups registers by shop with session-state summary cards. 8/8 Gate 1 checks. Prod UI click-through at Gate 2.

R1.1 — Cashier tab + PIN identities (NIX-OS-67 Phase 3 POS) PROD

2026-04-21 on prod. First cycle of R1: PBKDF2 PIN hashing + Cashier tab. Full server-action click-through (login → create → PIN reveal → reset → deactivate). 9/9 R1.1 + 11/11 phase 1 regression green. Mid-gate fix: db.transaction() wrap to bypass Hyperdrive cache.

R1.1 — Cashier tab + PIN identities (NIX-OS-67 Phase 3 POS) LOCAL

2026-04-21. First cycle of R1: PBKDF2 PIN hashing, commerce.pin_identities DAO, cashier CRUD server actions, Cashier tab on Team page. 5/5 local Gate 1 checks passed — unit tests, schema shape, DAO round-trip, SSR smoke. Prod click-through at Gate 2.

M3 — NIX-OS-73 Telegram Integration (Cafe) PROD

2026-04-21 on prod. Encrypted bot token + groups + webhook + cron live. 8/8 M3 + 33/33 regression = 41/41 prod tests green.

M3 — NIX-OS-73 Telegram Integration (Cafe) LOCAL

2026-04-21. Per-tenant Telegram bot: encrypted token, groups CRUD, inbound /report + /chatid webhook, scheduled daily push. 7/7 local checks passed.

M1 — Cafe shop scoping on Dashboard/Orders/Reports PROD

2026-04-21 on prod. Shop-scope helper + OR-fallback live, validated on get-coffee.nixtech.app/cafe (real Odoo data). 10/10 M1 + 23/23 regression = 33/33 prod tests green.

M1 — Cafe shop scoping on Dashboard/Orders/Reports LOCAL

2026-04-21. Dashboard/Orders/Reports in nix-cafe now shop-scope their Odoo order queries through the shared getPosConfigFilterForSelection helper, with an OR-fallback for unmapped configs so empty-mapping tenants don't blank out. 8/8 Playwright + unit checks passed.

Phase 2.1 S3+M2 — themeStore persist v4 + pin_identities migration PROD

2026-04-21 bundle on prod. Outdoor frontend persistedstate v4 fix + commerce.pin_identities live on Render nix-db. 6/6 bundle + 23/23 regression = 29/29 prod tests green.

Phase 2.1 S3+M2 — themeStore persist v4 + pin_identities migration LOCAL

2026-04-21 bundle. Outdoor frontend persistedstate v4 key fix + commerce.pin_identities schema (Phase 3 POS prereq). 8/8 Playwright checks passed; schema snapshot inline.

NIX Commerce MVP — tenant-facing portal PROD

2026-04-20 build. Launchpad, read-only subscriptions, team management, invoices stub, branding self-service. 11 step-by-step screenshots. 13/13 Playwright checks passed.

NIX Cpanel Pivot — tenant-first nav + per-product subscriptions PROD

2026-04-20 refactor. Tenants subscribe to multiple products with per-(tenant, product) plan/limits/billing. 13 step-by-step screenshots. 14/14 Playwright checks passed.

NIX-OS-36 — Basic HR Module PROD

Attendance, leave, expenses, settlements, salary, employee records. 8 pages × 2 resolutions.

NIX-OS-56 — Wide Monitor Responsiveness PROD

Content capped at 1400px on ultrawide screens. 6 resolutions × 5 pages. Captured from demo.nixtech.app.

NIX-OS-56 — Wide Monitor Responsiveness LOCAL

Same test against local dev environment.