Skip to content

User stories (web) — terminales MVP

Purpose: the authoritative list of what each role must be able to do on the web app (Next.js admin + WebTerminal). Mobile is tracked separately — this file is web-only. Status: 2026-08-26 — verification coverage reconciliation sweep (#618): the parity-audit rows (US-ACC-1, US-PLAT-1) reconciled forward against design-parity-matrix (the coverage source-of-truth companion), and the VAS/Customers rows (US-VAS-1..6, US-CLI-1) reconciled against the terminales-web mock-first suites e2e/us-vas.spec.ts + e2e/us-cli.spec.ts. (2026-07-30 — restructured: split web ⇄ mobile, and replaced the single ambiguous “Coverage” symbol with three explicit status columns (Built · Mock E2E · Live E2E) so it is clear what is implemented, what the hermetic test suite covers, and what has been verified against the real backend.) Companions: mobile-user-stories.md (the mobile app’s stories, screens, and status — the mobile authority) · flows-screens-master-map · design-parity-matrix · roles-permissions-and-labels.md · reporting.md · value-added-services.md

Each area maps to a master-map W-xx flow. A story with a mobile surface is also listed in mobile-user-stories.md with its M## screen + mobile status; this file carries only the web screen and the web status. Two stories are mobile-only (handheld card/top-up) and live solely in the mobile file.

The three status columns are independent — a story can be built but untested, or tested-with-mocks but not yet verified live:

  • Built — is the web feature implemented and design-aligned? ✅ shipped · 🟡 partial / not design-aligned · ⚠ missing / legacy / not built
  • Mock E2E — covered by the hermetic Playwright suite (chromium project, NEXT_PUBLIC_USE_MOCKS=true; screen parity + navigation, and the money-path where mock-reachable). Last run 2026-07-30: 203 passed · 36 skipped · 6 failed. ✅ passing · 🟡 partial (interactive legs fixme behind the admin-session seam #164, or some legs skip) · ⚠ failing · — no mock spec
  • Live E2E — verified against the real backend (--project=live, mocks OFF: local terminales-api + terminales-web + cloud Appwrite). This session 2026-07-29. ✅ passing · 🟡 partial · ⚠ blocked / product finding · 🔄 unblocked, re-run pending · ⏭️ gated (needs creds / a flag / an aggregator) · — no live spec run
  • Notes — one line: the real gap, finding, or the ticket that owns it.

Roles (from roles-permissions-and-labels.md): store-team cashier / supervisor / manager (the merchant owner operates as manager); platform platform_admin / platform_support / platform_developer. customer / guest are reserved (no MVP screens).

Role Primary story areas
cashier ACC, POS, VAS (sell), BAL (register deposits), SHIFT (own), NOTIF, ACCT
supervisor + approvals across POS/SHIFT, CAT/INV (write), REP (store)
manager / owner + STAFF, TERM, WL, REP (portfolio), CAT/INV/CLI full, BAL (wallet — owners only)
platform_admin / support / developer PLAT (merchant + user management, impersonation, white-label assist)

ID Story Roles Screen Built Mock E2E Live E2E Notes
US-ACC-1 Sign in with email + password on the Figma split-screen login. all W01 ✅ ✅ ✅ AuthSplit is wired at /login; real login works — reconciles matrix W01 ⚠ forward (#241 since merged), correcting the old “not wired” claim
US-ACC-2 Second-factor (email/SMS) challenge when enabled. all W02 🟡 ⏭️ ✅ email-factor 2FA login MERGED to main (terminales-web #858, 2026-09-09) — live-proven GREEN on terminales-dev (disposable email-MFA user): challenge → Mailpit OTP → lands /select-store (no bounce) → GET /teams 200. Was BROKEN (LoginForm expected createEmailPasswordSession to THROW the MFA signal; live Appwrite raises it only on the next GET /account). Fix = one shared MFA-complete session (client completes MFA → its cookieFallback secret → bridge validates + sets cookie). SMS leg deferred (no provider)
US-ACC-3 Recover a forgotten password via a link. all W03 · W03b 🟡 ✅ ✅ request / deep-link / invalid-token green; consume-emailed-secret leg now GREEN live on terminales-dev 2026-09-09 via Mailpit (throwaway @terminales.dev user; fixme cleared). Full-suite ordering flake only: AC3 after AC1’s two recovery POSTs hits Appwrite throttling — green in isolation
US-ACC-4 Fast PIN access with a profile selector. cashier W04 ✅ ✅ ✅ profile selector is shipped (corrects “deferred v2”); PIN roster + login live
US-ACC-5 Pick which store/terminal I operate. all W05 ✅ ✅ ✅ single/multi/no-store legs green; WebTerminal terminal-pick leg fixme
US-ACC-6 Onboard my commerce (merchant → first store). manager W06 ✅ ✅ ✅ wizard provisions a merchant + first store live (corrects “post-MVP”)

VER — Merchant verification (KYC) UX — see merchant-verification-ux-design.md

Section titled “VER — Merchant verification (KYC) UX — see merchant-verification-ux-design.md”

Sub-project D (approved 2026-09-02), shipped mock-first. Merged web PRs: #686 (checklist step), #694 (banner + shell slot), #695 (gated actions + blocked dialog), #705 (progress page + notification deep-links), #706 (blocked-flows mock e2e) + FIX-706 #722 (verified-by-default mock baseline), #711 (verification.reasons.ts pinned to frozen contract v1.0.0). Mock coverage lives in e2e/flows/merchant-verification.spec.ts. No live verification spec yet — the deferred live leg (a real merchant walked non-approved → approved on the live backend) is tracked by inspiraCode/terminales-api#534 + the local web-merchant-verification-sandbox-e2e-verification-dev leg. Screens = the approved merchant-verification-ux canvas (no W-nn ids assigned).

Rule (PO 2026-09-25, updated 2026-09-26): default is policy B — business-rules/merchant-verification-policy.md. An unverified merchant sets up (catalog, appearance, 1 store, 1 terminal) but cannot sell, open shifts, register cash movements or invite staff; money-out (liquidaciones, merchant→merchant transfers, wallet→platform top-ups) always needs verification; a platform admin can verify on behalf (attest); the realm-only, admin-only switch merchant.unverifiedCanOperate (default false) restores policy A (the 2026-09-18 money-out-only model). Suspension (admin, audited) is the kill-switch; allowUnverifiedSales is retired.

ID Story Roles Screen Built Mock E2E Live E2E Notes
US-VER-1 See a verification status banner in the app shell, per state (in-review/retry/approved), on admin + POS shells. all (merchant scope) merchant-verification-ux (WebBanner*) ✅ ✅ — banner per-state (owner + staff read-only), POS-shell retry, no banner when approved, session-scoped dismiss, retry not dismissible — all mock-covered; no live spec
US-VER-2 Unverified merchant is gated by policy B (default): sell (Cobrar, VAS Confirmar), open shift, cash movement, invite staff, a 2nd store and a 2nd paired terminal each open the «Comercio no verificado» blocked dialog (owner: «Ir a verificación»; staff: read-only caption), and the API refuses the same actions with 403 merchant_unverified; money-OUT (Disponer saldo / liquidación, merchant→merchant transfer, wallet→platform top-up) is gated in every policy. With the realm switch merchant.unverifiedCanOperate on (policy A) only money-OUT is gated. Catalog, appearance and the verification flow are never gated. manager (owner), staff (read) merchant-verification-ux (WebBlockedDialog*) ✅ ✅ 🟡 Mock: e2e/flows/merchant-verification.spec.ts › gates (policy B invite / 2nd store / 2nd terminal / open_shift / Cobrar refused; policy A not gated). Live: QA re-verify R7-A (2026-09-25) — «Nueva tienda» gated add_store, api 403. Policy point terminales-api/src/pos/merchant-capabilities/merchant-capabilities.service.ts; web mirror src/components/MerchantVerification/merchantVerification.i18n.ts (dialog copy)
US-VER-3 Verification progress page — checklist, rejection reasons, retry loop; staff see a read-only line. manager (owner), staff (read) merchant-verification-ux (WebProgress*) ✅ ✅ — progress per-state, returned→submitted banner, retry loops back in-app, staff read-only — mock-covered; reasons frozen to contract v1.0.0 (#711); no live spec
US-VER-4 A verification notification deep-links into the progress page. manager (owner) merchant-verification-ux ✅ ✅ — merchantVerification notification opens the progress page (mock); mobile mirror = US-VER-4 (#419); no live spec
ID Story Roles Screen Built Mock E2E Live E2E Notes
US-POS-1 Browse/search the catalog and add items (incl. barcode) to a cart. cashier W10 · W11 ✅ ✅ ✅ add-to-cart drives the live cart
US-POS-2 Apply a line/order discount; supervisor approval over threshold. cashier, supervisor W13 🟡 ⚠ 🚧 discount UI intentionally hidden (CartPanel discountsEnabled=false, pending final design); mock W13 overlay + live both fixme’d
US-POS-3 Charge cash and record change. cashier W14 · W15 ✅ ✅ ✅ cash tender computes change and finalizes live
US-POS-4 Charge a card via the paired terminal (AngelPay). cashier W16 🟡 ✅ ⚠ card dispatch needs a paired terminal + the still-open web↔handheld bridge (#137/#138); mock = parity only
US-POS-5 Mixed/split tender across methods. cashier W17 ✅ ✅ ✅ split-cash legs green; card leg fixme (no live handheld)
US-POS-6 Finalize durably across a network blip (no double-charge). cashier W19 🟡 ⏭️ ✅ blip+retry finalizes exactly once live (1 order, 1 movement); AC4 DBOS metric not emitted locally; mock finalize fixme
US-POS-7 Print/render an 80mm receipt. cashier W18 ✅ ✅ ✅ completion receipt (folio, line, total, cash+change, Print/Share/Email) renders on finalize
US-POS-8 Hold a sale and resume it later. cashier W64 🟡 ✅ ⚠ candidate bug: “Guardar en espera” silently no-ops live (no POST, cart not cleared) despite an open shift; mock round-trip passes
US-POS-9 Sale surface blocked with a clear banner when offline. cashier W10 (gap #10) 🟡 — — online-only policy; blocking states pending; no e2e
ID Story Roles Screen Built Mock E2E Live E2E Notes
US-VOID-1 Void a finalized ticket within the current shift (supervisor PIN + reason). cashier, supervisor W31 ✅ ✅ — same-day void shipped (#119); mock drives W29→W30→PIN→approved; no dedicated live money-path spec
US-VOID-2 Card void cancels the linked payment-intent. cashier W31 🟡 — — no POST /pos/sales-orders/:id/void HTTP route; API leg e2e-verified at the service boundary (#260)
US-VOID-3 Refund beyond same-day void. (post-MVP) supervisor W31 🟡 🟡 ✅ refund engine verified live out-of-band (full refund + ledger, idempotent replay, approval-gating); UI-refund chain fixme (seeded prior-day order not surfaced in the sales-orders list)
ID Story Roles Screen Built Mock E2E Live E2E Notes
US-CAT-1 Manage products (variants, barcodes, cost & price). manager, supervisor W20 · W21 · W22 ✅ 🟡 ✅ products list shows the seeded product + price live; mock interactive parity fixme (#164 seam)
US-CAT-2 Manage categories (parent + store scope). manager, supervisor W23 🟡 🟡 ✅ categories list renders the seeded store-scoped category live; parentId free-text gap
US-CAT-3 Manage tax rates. manager W24 🟡 🟡 ✅ tax-rates list renders the seeded rate (16%) live
ID Story Roles Screen Built Mock E2E Live E2E Notes
US-INV-1 See stock levels and movements. manager, supervisor W25 · W27 ✅ ✅ ✅ levels render, store filter narrows, sale-decrement appears as a movement, empty state
US-INV-2 Adjust inventory with a required reason. manager, supervisor W26 ✅ ✅ ✅ adjustment writes a movement + updates the level (corrects “no PR yet”)
US-INV-3 Transfer stock between stores. manager W28 ✅ ✅ ✅ full transfer lifecycle live: pending→receipt→complete, idempotent, over-transfer 409, unknown-ref 404 (corrects “send-side only”)
ID Story Roles Screen Built Mock E2E Live E2E Notes
US-SHIFT-1 Open a shift with an opening float. cashier W34 ✅ ✅ ✅ open → home with an OPEN shift; re-open restores the seed
US-SHIFT-2 Record cash movements (pay-in/pay-out) with a reason. cashier W36 ✅ ✅ ✅ cash movement recorded live in the open→movement→close flow
US-SHIFT-3 Close a shift with a denomination breakdown + see the variance. cashier W35 · W65 🟡 ✅ 🟡 within-tolerance close completes; the variance-preview leg mismatches (~$500 basis vs API close snapshot — likely accumulated drawer state; expected-cash formula is a PO decision, gap #18)
US-SHIFT-4 Approve an over-tolerance cash variance at close. supervisor W35 🟡 ✅ ✅ server refuses an over-tolerance close with no approval; UI approval-modal leg shares the SHIFT-3 preview blocker
US-SHIFT-5 Immutable Z-report per closed shift. supervisor, manager W33 · W58 ✅ ✅ ✅ closed-shift picker auto-selects, GET uses ?shiftId=, single-shift close-out renders (no ZodError)
ID Story Roles Screen Built Mock E2E Live E2E Notes
US-STAFF-1 Manage employees and their PINs. manager W37 · W38 ✅ ✅ — employees + PIN reset shipped; exercised as the entry for STAFF-3 reachability
US-STAFF-2 Invite users and assign a store role. manager W39 ✅ ✅ ⚠ auto-join path only. invite+assign-role membership verified live; but the emailed-accept leg (/invitacion + POST /pos/invites/accept) is UNREACHABLE via the real invite path — the api’s server-key teams.createMembership auto-confirms (confirm=true) and sends no email (Mailpit 0; SMTP works). Real invite delivery is a brainstorm decision (Q-onboarding-3, 2026-09-09). Invite entry now routes to the dedicated /admin/pos/employees/new form (W38 row 96, #857) — the old list dialog is retired
US-STAFF-3 See the role→permission matrix (read-only). manager W40 ✅ ✅ ✅ correct cumulative grants, read-only, reachable W37→W40; pos-roles.ts exists
US-STAFF-4 Cross-store/cross-role access denied server-side. all RLS + assertStoreRole 🟡 — — server-side authz sweep (A01) — API/authz test, not a UI e2e
ID Story Roles Screen Built Mock E2E Live E2E Notes
US-REP-1 Daily Sales summary (tender + hourly breakdown). supervisor, manager W55 · W56 ✅ ✅ ✅ daily-sales dashboard adapts + renders the live aggregate
US-REP-2 Sales by Product/Category with margin. manager W56 🟡 ✅ 🟡 opens scoped to the store live but returns empty rows (product-aggregation/line_items.unitCost data dep)
US-REP-3 Sales by Payment Method and by Employee. manager W56 ✅ ✅ 🔄 store-select drift fixed 2026-07-30 (#497 → PR #503, testid moved to SelectDisplayProps); specs are drivable — needs a live re-run to confirm they pass
US-REP-4 Cash Movements and Voids & Discounts exception reports. manager, supervisor W56 ✅ ✅ 🔄 unblocked by the same #497 fix; needs a live re-run. The voids&discounts endpoint itself is reachable (VOID-3)
US-REP-5 Commissions report. manager W57 🟡 ✅ 🔄 store-select unblocked by #497; still depends on AngelPay commission data that has not flowed yet
US-REP-6 Reports limited to role scope + CSV export. all W55–W58 🟡 ✅ 🔄 first leg unblocked by #497; RLS scoping still to verify on a live re-run

VAS — Value-added services (W-09) — see TDR-0003

Section titled “VAS — Value-added services (W-09) — see TDR-0003”

Mock E2E source (terminales-web): the money-path legs are authored in e2e/us-vas.spec.ts — US-VAS-1 airtime (success + aggregator-error) and US-VAS-2 gift-PIN are honest test.fixme, deferred on the admin-session harness seam (#164) + the Track-C VAS build; nothing is a false green. US-VAS-3’s admin /admin/service-payments money-path case was removed (that admin surface was retired — service payments now live in the POS terminal Servicios tab), so there is no standalone US-VAS-3 mock money-path spec. US-VAS-4/5/6 stay unspecced (surfaces unbuilt). The ⚠ Mock columns below reflect the failing VAS nav-parity specs (Track-C rebuild not wired), distinct from the fixme’d money-path legs.

ID Story Roles Screen Built Mock E2E Live E2E Notes
US-VAS-1 Sell airtime/TAE to a phone number. cashier W42 ⚠ ⚠ ⏭️ legacy TypeORM only, Track C rebuild; money-path e2e/us-vas.spec.ts fixme (seams #164 + VAS test-hooks); mock VAS nav-parity fails; live gated (VB aggregator is a fail-closed 503 stub)
US-VAS-2 Sell gift-card PINs (secure delivery/reprint). cashier W43 ⚠ ⚠ ⏭️ Track C; pin_vouchers custody; money-path e2e/us-vas.spec.ts fixme (gift-PIN surface unbuilt); live gated on aggregator
US-VAS-3 Pay a service/bill (adeudo lookup). cashier W41 ⚠ ⚠ ⏭️ Track C; admin service-payments money-path case removed from e2e/us-vas.spec.ts (surface retired → POS terminal Servicios tab); mock nav-parity still fails; live gated on aggregator
US-VAS-4 Sell a SIM chip from serialized inventory. cashier W12 ⚠ — — Track C; serialized_units; no e2e / no frame
US-VAS-5 Register customer deposits and transfers. cashier, manager W44 · W45 · W46 ⚠ ⚠ ⚠ Track C/K rebuild; mock W44 Transferir→W45 nav fails; live transfer legs fail
US-VAS-6 VAS reporting by kind/provider/commission. manager W57 ⚠ — ⚠ Track C reporting dimensions; live blocked by the report store-select

The card-on-handheld credit (channel E) and handheld autopay top-up are mobile-only surfaces — tracked as US-BAL-4 / US-BAL-5 in mobile-user-stories.md.

ID Story Roles Screen Built Mock E2E Live E2E Notes
US-BAL-1 See the wallet balance and movements — owners only. manager (owner) W47 ✅ ✅ ✅ LIVE GREEN (2026-09-09, all 4 ACs) — balance + movements render with correct minor-unit money ($12,200.00; +$12,450 CREDIT / −$250 DEBIT), empty state, non-owner web-redirect+API-403. The earlier fails were stale minor-units spec assertions (pesos() didn’t ÷ MINOR_SCALE) — fixed in web PR #860 (verified vs local origin/main api).
US-BAL-2 Register deposit channels A–C + manually validate (pending → available). manager W46 ✅ ✅ ✅ LIVE GREEN (2026-09-09, us-bal-2 4/4 vs qa-design). Cap fix (api #685) + declinedAt schema (#688) both verified live; the stale matcher relaxed (integration PR #868). Register A/B/C 201, owner-validate credits once, decline → 200/DECLINED + declinedAt stamped + no credit. (history: cap bug FIXED api #685; declinedAt gap FIXED #688) Cap bug FIXED (api #685 merged): deposit > $100 now registers live — register A/B/C all 201 ($750/$1,500/$2,200), owner-validate credits exactly once (verified 2026-09-09 vs local origin/main api; spec fixes web PR #860). declinedAt schema gap FIXED (api #688 merged + pushed to qa-design). 2026-09-09 re-run vs qa-design: 7/8 — the decline path is functionally correct live (decline → 200/DECLINED, declinedAt stamped, no credit, no validation stamp; OOB-confirmed). Sole remaining red = a stale spec matcher, NOT behaviour: us-bal-2-deposits.live.spec.ts:302 asserts expect(row.validatedBy).toBeUndefined() but Appwrite returns an unset optional column as null → one-line relax (.toBeFalsy() / ?? undefined). Matcher-fix slice in flight → flip to ✅ on 8/8.
US-BAL-3 Direct cash payment (channel D) credits store cash (never the wallet). cashier W36 🟡 🟡 🟡 rides existing cash-movements; channel semantics Track K
US-BAL-6 VAS sale blocked when the wallet lacks funds. cashier W12 · W41–W43 ⚠ ⚠ ⏭️ Track K VAS wiring; live gated on the VAS aggregator
US-BAL-7 Wallet operations serialize; balance = sum of movements. (system) — ⚠ — — TDR-0004 engine + integrity job — API/engine test, not UI e2e
US-BAL-8 Withdraw wallet funds to a bank account. (post-MVP) manager (owner) W47 ⚠ — 🟡 WITHDRAWAL seam only (PDR-0003); “disabled affordance” leg fails a spec detail

CONX — Plataformas de saldo / Conexiones (W-14)

Section titled “CONX — Plataformas de saldo / Conexiones (W-14)”

Built & merged (web PRs #578/#579/#576/#642; api #483–#488). The whole surface is owner-gated and covered by a mock-first Playwright suite, but three test-harness gaps keep the Mock columns honest, not ✅-across-the-board: two authz specs are test.fixme (the mock harness can’t present a non-admin session — same #164-class seam), e2e/registrar-deposito.spec.ts is neutered (the mock harness 307→/login on /admin/*, so it falls back to component tests), and Movimientos pagination is not yet exercised (only the first page is asserted). There is no live (*.live.spec.ts) coverage yet — a separate P1 track owns live authoring; the fixture/harness debt is tracked in the web-platform-connections-verification-followups draft. Top-up (“Agregar saldo”) is ledger-only (gap #50): it records a movement + comprobante, no real funds move.

ID Story Roles Screen Built Mock E2E Live E2E Notes
US-CONX-1 Conexiones tab (Ajustes) + connect flow (choose → credentials → “Probar conexión”). manager (owner) W66 · W67 · W68 ✅ 🟡 — list/connect legs green (e2e/settings-connections.spec.ts + connections-connect.spec.ts); non-owner authz leg test.fixme (e2e/connections-authz.spec.ts — mock harness can’t present a non-admin session); no live spec yet (P1 in progress)
US-CONX-2 Platform detail + Movimientos ledger (movements table). manager (owner) W69 ✅ 🟡 — detail + first-page movements green (e2e/connections-detail.spec.ts + e2e/platform-movements.spec.ts); pagination not exercised (only first page asserted — P7 AC3); no live spec yet (P1 in progress)
US-CONX-3 POS “Saldo de plataforma” tender + platform selector (incl. VAS platform-funded sale). cashier W70 ✅ ✅ — tender + selector spec green (mocks ON); no live spec yet (P1 in progress)
US-CONX-4 Agregar saldo (ledger-only) + comprobante. manager (owner) W71 · W72 ✅ 🟡 — modal + comprobante green; non-owner authz leg test.fixme (e2e/agregar-saldo-authz.spec.ts — no non-admin mock session); top-up is ledger-only (records a movement, no real funds move — gap #50); no live spec yet (P1 in progress)
US-CONX-5 Deposit Destino = plataforma (deposit destination field + column). manager W46 ✅ 🟡 — destination field + column shipped; e2e/registrar-deposito.spec.ts neutered (mock harness 307→/login on /admin/*, falls back to component tests — P7 AC2); no live spec yet (P1 in progress)
ID Story Roles Screen Built Mock E2E Live E2E Notes
US-CLI-1 Look up / quick-create a customer and assign them to a sale. cashier W (233:279/233:383) ⚠ 🟡 🟡 Track D; sales_orders.customerId; 1 live leg green, 1 fails; mock spec e2e/us-cli.spec.ts is a single honest test.fixme (seams #164 + persisted-sale)
US-CLI-2 Customer directory with detail. manager W (231:3/232:157) ⚠ — — Track D; nav item missing (gap #45)

TERM — Commerce, stores, terminals, white-label (W-10)

Section titled “TERM — Commerce, stores, terminals, white-label (W-10)”
ID Story Roles Screen Built Mock E2E Live E2E Notes
US-TERM-1 Manage commerces, stores, and terminals. manager W48–W52 ✅ ✅ ✅ LIVE GREEN (2026-09-09, 4/4, us-term-1-commerce.live.spec.ts) — store-read ✅, terminal-read ✅, and store-CREATE from inside a Comercio → 201 + persists (multi-merchant 400 GONE by construction) ✅. Journey: rows 57–58 aggregation columns wired to api #667 /platform/summaries (merged #856); rows 55/56/59 drill-down IA — Comercio detail Tiendas+Apariencia tabs, Tiendas Comercio column, “Nueva tienda” from a Comercio pre-sets merchantId, stub tabs dropped (merged #859); the store-create-from-Comercio live spec = web PR #862 (rebased + AC2 reworked to the drill-down flow). The old global-path multi-merchant 400 no longer applies (store-create is launched from within a Comercio).
US-TERM-2 Pair a terminal via a code (invalid/expired/success). manager W53 ✅ ✅ ✅ pairing flow + states verified live (corrects “web pairing UI unticketed”)
US-WL-1 Configure white-label appearance + receipt with live preview. manager W54 ✅ — ✅ appearance + receipt live-preview verified live (corrects “designed, unimplemented”); no dedicated mock flow spec
ID Story Roles Screen Built Mock E2E Live E2E Notes
US-ACCT-1 View/edit my account and security (2FA/PIN). all W59 · W60 🟡 ✅ ✅ live re-drive on terminales-dev 2026-09-09: AC1 (edit+save profile) and AC5 (self-scoped to /account/*) PASS — form renders, #account-save persists to Appwrite (verified out-of-band via Users-admin), survives reload, self-restores. The prior ⚠ “hydration” note was stale (the browser SDK IS authenticated at login via login.api.ts, not guest). Security legs AC2/AC3 (MFA) re-authored to the email factor (auth/mfa-email-live-spec), live-run pending
US-ACCT-2 General settings. manager W61 ✅ ✅ ✅ LIVE GREEN (2026-09-09). W61 canvas items already shipped (#795/#797/#799/#808): tabs removed / General-only / title “Ajustes” / max-width / DirtySaveBar. The earlier live fail was drifted spec (clicked a removed testid) — fixed in web PR #865 (dirty-save-bar-save); save persists across reload (durable Appwrite prefs, OOB-confirmed) + save-error toast green.
US-ACCT-3 View the audit log / bitácora. manager, supervisor W62 ✅ ✅ ✅ LIVE GREEN (2026-09-09). W62 items already shipped: Nav-Drawer Bitácora entry, Acción/Entidad i18n, Usuario name (not id) resolution, Hoy/Ayer/full-date. Spec drift (asserted raw id) fixed in web PR #865 (assert the resolved actor). Live: Spanish action label, filter+paginate+expand-diff, CSV export, cross-tenant-empty (RLS), role-gate all green; metrics leg self-skips (API-served).
ID Story Roles Screen Built Mock E2E Live E2E Notes
US-NOTIF-1 Notifications center + detail with unread badge. all W08 · W63 ⚠ ⚠ ⚠ CORE plumbing (#116); mock dashboard→Notificaciones→Detalle nav fails; live first leg fails; screens/deep-link gaps
ID Story Roles Screen Built Mock E2E Live E2E Notes
US-SEARCH-1 ⌘K command palette across products/sales/employees. manager, supervisor W09 🟡 ✅ 🟡 mock ⌘K-from-any-screen passes; live fails a palette focus assertion (timing); search endpoint unticketed
ID Story Roles Screen Built Mock E2E Live E2E Notes
US-PLAT-1 Manage merchants and their users. platform_admin (admin) ✅ ✅ 🟡 #239 RESOLVED on main — dashboard now renders DashboardHomeLive wired to real POS reporting (dashboard.hooks; PRs #573/#762/#773). 2026-09-09 re-verify (merchants TL): mock dashboard parity + dashboard→Notificaciones nav now GREEN (inicio.spec.ts AC1/3); live AC1 (sees live merchants) ✅. AC2 (edit persists): the code path is CORRECT — the earlier “does not persist” was a CONFIG issue, not a bug (RC’d 2026-09-09, S-P1FIX): a PATCH /api/pos/merchants/:id persists live (200 + readback) in a coherent env. The false symptom came from (1) project drift — web .env.local pointed at terminales-qa while readback was from terminales (the write landed in the other project), and (2) write_gateway flag web-ON/api-OFF (api path 403-no-ops; Hard Rule 1 says the flag should be ON). Fix is operational (align web↔api project + FEATURE_FLAG_WRITE_GATEWAY), not merchants code. AC3/AC4 serial-cascade-blocked
US-PLAT-2 Scoped read + audited impersonation. platform_support, platform_developer (admin) 🟡 — 🟡 2026-09-09 re-verify: live did NOT self-skip (operator/owner/cashier seeded). AC1 (cross-merchant read scoped) FAILS because the owner fixture is over-privileged — E2E_OWNER_USER_ID default carries the platformAdmin label, so the API correctly returns platform scope; fix = a merchant-owner fixture WITHOUT platformAdmin (seed/identity gap, not missing creds). AC2/AC4 serial-cascade-blocked
US-PLAT-3 Assist with white-label theming. platform_developer W54 ⚠ — 🟡 rides US-WL-1; 2026-09-09 re-verify: live first leg (owner edits own appearance) fails on a test-side strict-mode selector ambiguity (primaryColor resolves to 2 inputs — color picker + hex field), not a product bug; rest serial-cascade-blocked

  • Reserved roles (customer, guest) intentionally have no MVP stories — they activate with a future customer portal / public storefront (roles-permissions-and-labels.md).

  • Deferred by PDR-0001 (no MVP story): QR CoDi/SPEI tender, cobro-rápido/quick-actions, POS tap-to-add/fullscreen, KPI drill-down.

  • Deferred by PDR-0003: VAS Phase 2/3, bank-statement-import deposit validation, and the wallet withdrawal/payout flow (US-BAL-8).

  • The admin dashboard mock-parity failures — RESOLVED (#239, 2026-09-09). The dashboard (inicio) parity + dashboard → Notificaciones nav previously failed on hardcoded demo data; the dashboard now renders DashboardHomeLive (real POS reporting via dashboard.hooks; PRs #573/#762/#773) and both legs pass in the 2026-09-09 merchants-TL re-verify (inicio.spec.ts). The remaining mock findings are: the VAS nav failures (Track-C rebuild not wired) and the POS-2 (W13 discount) failure matching the intentionally-hidden discount UI. (Separately, inicio.spec.ts ⌘K global-search deep-link now resolves to the product-detail route vs the asserted catalog route — a US-SEARCH-1/#240 routing item, not dashboard.)

  • report-store-select — FIXED 2026-07-30 (issue #497, PR #503). It had been a shared live-spec blocker across US-REP-3/4/5/6 + US-VAS-6: MUI puts data-testid on the hidden native <select> (display:none), so the live specs’ click never became actionable. The testid now forwards to the visible combobox via SelectDisplayProps. Those specs are drivable again but have not been re-run — their Live column reads 🔄 (re-run pending), not ✅. A known latent instance of the same pattern remains on the employee select (deferred to todos.tasks).

  • Live verification was run against a local stack (api :3020 + web :4000, mocks OFF, cloud Appwrite) using the E2E seed store e2e_store (“Sucursal E2E”). apacheco is multi-store and defaults to “Demo Store #251”, so admin-UI specs pin the active store via a shared scopeAdminToE2eStore helper. BAL requires FEATURE_FLAG_MERCHANT_WALLET (+ _DEPOSITS) — now enabled on stage (via the STAGE_BACKEND_ENV secret + a 2026-07-30 stage deploy) and locally.

  • Coverage is a verification claim: Built is a design/implementation claim; Mock E2E and Live E2E are the evidence that turns 🟡/⚠ into ✅ or opens a fix ticket.

  • Layer-2 parity audit executed 2026-07-12 (design-parity-matrix, #253): one row per canonical web screen (W01–W65) comparing the running app to its Figma node + design system. Track A umbrellas: #241 AuthSplit login · #239 admin dashboard · #240 web screens.