Skip to content

Mobile user stories — the derived subset

Purpose: the explicit, reviewable list of which MVP user stories the mobile app (terminales, Android / Kotlin / Jetpack Compose) must satisfy, and the mobile authority for their screens + status. Each row is a story shared with the web list (user-stories.md) — the story text and roles are common — but the mobile screens, owning flow, Pass C ticket, and mobile status are tracked here (the web file is web-only since the 2026-07-30 split). Two stories are mobile-only (US-BAL-4 card-on-handheld credit, US-BAL-5 handheld autopay top-up) and exist only here. Status: 2026-09-10 (late) — terminales PR #452 open (pending gate): PB-1 + PB-2. PB-1 was filed as “the drawer opens from Inicio but not again” and that framing was wrong: an isolation repro showed the area-root hamburger was dead on every non-Inicio root, so with the bottom nav bar retired and the drawer the only way to switch areas, a user who entered M25 Reportes or M24 Mi cuenta on current main could not leave them for another area, with nothing in logcat when the tap died. Android system back still escaped to Inicio (Alfredo confirmed that 09-09), so it was not a hard trap — but every in-app route out was gone. Inicio masked it by coincidence of layout, not design: the M06 dashboard happens to place its hamburger clear of the 48px strip; nothing was special-cased. That is why the 53-flow sweep walked past it and why a position-dependent bug looked ordering-dependent. Cause: the drawer’s left-edge swipe zones were overlay boxes carrying pointerInput drawn over the content, and Compose hands a tap to the topmost hit sibling and stops, so a full-height 48px strip swallowed every tap in the left margin; production leaves the Scaffold topBar slot empty so each screen’s own bar sat underneath. The sub-screen back arrow had the identical defect and is fixed in the same commit. PB-2 (owner “Cerrar sesión” below the N86 fold) is fixed structurally — the cashier margin was only ~13dp and flaked pre-fix, so the fold was marginal for every role, not just the owner. Gate run on the merge commit: 1466 tests / 0 failures, lint unchanged. Neither fix is confirmed on the physical N86 (not attached). Prior: 2026-09-10 — T-11 rehearsal on the manual realm (Alfredo driving the N86, terminales-n86 watching live): pairing, PIN, shift open, catalog, cash sale + printed receipt, and a REAL approved card charge ($1.00, chip, auth 895897) all passed on current main; evidence in terminales/docs/ui-evidence/n86-t11-rehearsal/. Two defects came out of it — receipt printing fails on the CARD path (3 successful prints in the session, every one after CASH; 4 card payments, none printed; root cause NOT yet established) and the pre-shell pairing gate’s dead roster link. The web→handheld bridge leg was not reached. Prior: 2026-09-09 (late) — fixes from the N86 session shipped to main in terminales PR #446 (dc8a265c): BUG-1 Home shift card (S11), BUG-5/6/7 Historial folio · store-timezone times · reload after void (S12), BUG-11 magic-link error mapping (S16), UX-9 Reintentar feedback + reachability probe (S15), UX-14 coin rows, UX-15 “Abierto desde” label. PR #447 (S9: drawer hijack from the Sell FAB, “Más opciones” drawer entry, “Terminal lista (pago remoto)” Home tile, drawer close) merged 2026-09-10 (575d81a3). PR #448 (pending gate): S10 direct singular VAS sale, no cart (US-VAS-1/2/3 → Built ✅ on the new path; Live stays gated on the aggregator); S14 catalog empty-grid (BUG-4); S17 session tenant guard; S13 N86 design fit (UX-2/3/4/6/7/8/10/12/13). Earlier: N86 device session (Alfredo + terminales-n86, qa-design realm, 14 legs) results folded in as N86 09-09 notes; full evidence terminales/docs/ui-evidence/n86-s4-2026-09-09/, bug list in team/workers/terminales-n86.md. Earlier the same day: delta pass by terminales-n86 (T-8) against terminales origin/main 7053943e: rows below carry a Δ 09-09 note where the 07-31 status drifted (VAS rebuilt on the api path, BAL-4 live inside the unified tail, BAL-5 dormant, CLI flags retired, POS-2 disabled, POS-4 unified + device-verified, POS-8 restore-only on the handheld). Full ground truth: team/workers/terminales-n86.md. Prior status (2026-07-31) — restructured to mirror the web file: the single ambiguous “Coverage (mobile)” symbol is replaced by three explicit status columns (Built · Mock E2E · Live E2E), and the Live E2E column now carries the results of the first mobile Pass D live-backend run (this session). Prior status note (2026-07-30): the PO’s 2026-07-22 review admitted US-VAS-5 Transferir saldo into mobile MVP and deferred eight items with TODOs (see § Deferred TODOs). Companions: user-stories.md (the web stories — shared story text) · flows-screens-master-map (M-1…M-8 flows + navigation edges) · README (per-screen Figma nodes + code cross-refs) · design-parity-matrix · roles-permissions-and-labels.md

Mirrors user-stories.md. The columns are independent — a story can be built but unverified, or design-aligned but blocked live:

  • Built — is the mobile feature implemented and design-aligned? ✅ shipped · 🟡 partial / behind a flag / not design-aligned · ⚠ missing / legacy / not built
  • Mock E2E — covered by a Pass C parity + navigation Maestro flow (.maestro/flow-*.yaml, mock-first / screenshot harness): ✅ screen parity + nav pass · 🟡 partial · ⚠ failing · — no flow
  • Live E2E — verified against the real backend this session (local terminales-api :3020 + cloud Appwrite + the app on an emulator, driven by Maestro): ✅ money-path/nav passes live · 🟡 partial (core proven, a leg blocked or not driven) · ⚠ blocked / product finding · 🔌 hardware-bound (card-present, not automatable on an emulator) · ⏭️ gated (needs a flag) · — not run (VAS/BAL deferred this session)
  • Notes — one line: the real gap, finding, or the fix that unblocked it.

Live E2E run — 2026-07-31 (groups ACC · POS · VOID · SHIFT · CLI · ACCT · NOTIF; VAS + BAL deferred). Stack lifted from verify/user-stories-functionality worktrees of both repos. Seed: scripts/seed-e2e.ts (cashier e2e_cashier@terminales.dev PIN 1234, supervisor 4321, store “Sucursal E2E”, product “Coca 600 E2E”). Bugs found + fixed and flow drift repaired are recorded in the Notes and in § Live E2E run — findings.

The mobile app is the operator device: a cashier (with supervisor approvals) standing at a counter or walking a floor with a handheld. The web app is the operator device plus the back office — it additionally carries catalog authoring, inventory, staff administration, reporting, fleet/terminal configuration, white-label, and the whole platform-administration surface. That split is not accidental; it follows the role index in roles-permissions-and-labels.md: cashier and supervisor journeys are almost entirely present on mobile, manager-only journeys are mostly web, and the platform roles (platform_admin / platform_support / platform_developer) have no mobile presence at all.

Stories
Total MVP stories (user-stories.md) 66
In mobile scope (this document) 31
Web-only 35

Of the 31 mobile stories, 28 name at least one canonical M## Figma node; the remaining 3 (US-POS-9, US-VAS-4, US-BAL-5) are mobile-scoped in the source but have no canonical mobile frame yet — they are flagged (no M frame) below and are also design gaps.

  • ID — the story id from user-stories.md. E2E specs and cases reference this id; the mobile Pass D drafts will do the same.
  • Mobile screens — the canonical M## frame(s) with their Figma node ids, exactly as the source cites them. M27a–M27e are the shared POS overlays backfilled as flow M-7 (they are the mobile side of the same 135:* nodes the web uses).
  • Flow — the master-map mobile flow (M-1…M-8) the screens belong to.
  • Pass C ticket — the mobile UI parity + navigation issue that owns those screens today (the UI-only / mock phase). These are the seven Mobile UI … Flow Screens Parity And Navigation issues on inspiraCode/terminales.
  • Built · Mock E2E · Live E2E · Notes — the three status columns defined above. Live E2E carries this session’s Pass D result.
Flow Screens Pass C ticket
M-1 · Acceso y Onboarding 01–05 #221 Mobile UI MobileAuth Access Flow Screens Parity And Navigation
M-2 · Inicio y Turno 06, 07, 28, 08, 09, 10, 29 #222 Mobile UI ShiftManagement Shift And Cash Flow Screens Parity And Navigation
M-3 · Venta POS 11–14 #223 Mobile UI PosCheckout Sale Flow Screens Parity And Navigation
M-4 · Cobro y Recibos (+ M-7 overlays) 15–19, 27 #224 Mobile UI PosPayments Charge And Receipt Flow Screens Parity And Navigation
M-5 · Categorías especiales (VAS) 20–23 #225 Mobile UI Vas Special Categories Flow Screens Parity And Navigation
M-6 · Cuenta y Reportes 24–26 #226 Mobile UI AccountReports Account And Reports Flow Screens Parity And Navigation
M-8 · Clientes y v2 30–35 #227 Mobile UI Customers Clients Flow Screens Parity And Navigation

All seven are blocked_by the Maestro screenshot harness (#215) and the Pass B design-system component families (#216–#220, #247–#252).


ACC — Access & onboarding (M-1) — 5 stories

Section titled “ACC — Access & onboarding (M-1) — 5 stories”
ID Story Roles Mobile screens Flow Built Mock E2E Live E2E Notes
US-ACC-1 Sign in with email + password on the Figma split-screen login. all M01 52:27 M-1 ✅ ✅ 🟡 N86 09-09: email/password ✅ on the device; magic link ✗ on qa-design (“Correo o contraseña incorrectos” — BUG-11: wrong error mapping on mobile + the App-Link host is not a registered web platform on the self-hosted realm, Q-n86-6). Prior: flow-auth.yaml — invalid creds → mapped error, no session; valid → real Appwrite session → shell. Magic-link (AC3) also driven live (2026-08-01): createMagicURLToken rejected the terminales:// scheme (same Appwrite-Cloud limitation as recovery), so it too was broken in prod; migrated MAGIC_LINK_REDIRECT_URL to the verified https App Link (https://links.io.inspiracode.com/magic-link). flow-auth-magic-send+-complete request the link, read userId/secret from Mailpit, open the App Link and land on M05 store select
US-ACC-2 Second-factor (email/SMS) challenge when enabled. all M03 55:59 M-1 ✅ ✅ ✅ valid-code leg now driven live — cloud Appwrite’s custom SMTP points at the IONOS Mailpit, so flow-auth-mfa-send+flow-auth-mfa-enter read the OTP from Mailpit’s API and complete the challenge (session elevates to M05). Wrong-code rejection also covered
US-ACC-3 Recover a forgotten password via a link. all M04/M04b 539:1215 M-1 ✅ ✅ ✅ valid-link leg now driven live (2026-08-01). Root cause of the send-400 was not a missing platform: Appwrite Cloud rejects the terminales:// custom scheme in createRecovery (accepts only http(s) / appwrite-callback-…), so recovery was broken in prod too. Fixed by migrating RECOVERY_REDIRECT_URL to a verified https App Link (https://links.io.inspiracode.com/recovery; assetlinks.json hosted on the IONOS box, host verified on-device). flow-auth-recovery-send+-reset request the mail → read userId/secret from Mailpit → open the App Link → set the new password (“Tu contraseña se actualizó”). Invalid/expired token still rejected. gap #33
US-ACC-4 Fast PIN access with a profile selector. cashier M02 300:4256 M-1 ✅ ✅ ✅ N86 09-09: PIN unlock + sign-out → “Acceso rápido con PIN” re-entry both green on the device. Prior: wrong PIN refused server-side; correct PIN (1234) unlocks through POST /pos/pin-login. Profile selector deferred v2 (gap #31)
US-ACC-5 Pick which store/terminal I operate. all M05/M05b 546:1229 M-1 ✅ ✅ ✅ live store list; pick scopes the session tenant; unpaired terminal blocks the sale surface, pairing lifts it. gap #36 closed this session: a new “Seleccionar terminal” roster screen (TerminalSelectionScreen → GET /pos/terminals) lists the store’s terminals with online status, reachable from “Registro de terminal” → “Ver terminales de la tienda” (flow-terminal-select.yaml verifies it renders the seeded terminals)

Not on mobile: US-ACC-6 (merchant owner onboards a commerce, W06) — a web onboarding journey, and post-MVP.

Node-id discrepancy to resolve — deferred, TODO T-8: for US-ACC-3 and US-ACC-5 the source cites the M04b/M05b variant nodes (539:1215, 546:1229) while the Pass C ticket (#221) lists the canonical frames 56:88 (04 Recuperar contraseña) and 57:85 (05 Seleccionar tienda/terminal). Both are recorded here as the source has them; the parity ticket is the one that will settle which frame is canonical.

VER — Merchant verification (KYC) UX (M-1) — 4 stories

Section titled “VER — Merchant verification (KYC) UX (M-1) — 4 stories”

Sub-project D (approved 2026-09-02). Merged mobile PRs: #416 (module + gate), #418→#421 (banner / blocked dialog / progress), #419→PR 423 (notification category + deep link). Mirrors the web merchant-verification-ux canvas. Maestro / live coverage is not yet recorded here — the marks below reflect merge status only. Δ 09-09 (terminales-n86): confirmed — no .maestro/flow-* covers VER, no live run recorded; the Custom-Tab deep-link return is deferred by design (spec §15: the progress screen refreshes on foreground + “Actualizar estado”). Scheduled in the T-8 emulator sweep + N86 device plan. (Web mirror = the VER section in user-stories.md.)

ID Story Roles Mobile screens Flow Built Mock E2E Live E2E Notes
US-VER-1 Verification status banner in the shell, per state. all (merchant scope) merchant-verification-ux M-1 ✅ — — shipped #418→#421; banner mirror of web US-VER-1; e2e/live coverage unrecorded
US-VER-2 Merchant actions gated until verified (blocked dialog). manager (owner) merchant-verification-ux M-1 ✅ — — shipped #416 (gate) + #418→#421 (dialog); e2e/live coverage unrecorded
US-VER-3 Verification progress screen (checklist + reasons). manager (owner) merchant-verification-ux M-1 ✅ — — shipped #418→#421; e2e/live coverage unrecorded
US-VER-4 Verification notification deep-links into progress. manager (owner) merchant-verification-ux M-1 ✅ — — shipped #419→PR 423 (notification category + deep link)

POS — Point-of-sale sale (M-3, M-4, M-7) — 8 stories

Section titled “POS — Point-of-sale sale (M-3, M-4, M-7) — 8 stories”
ID Story Roles Mobile screens Flow Built Mock E2E Live E2E Notes
US-POS-1 Browse/search the catalog and add items (incl. barcode scan) to a cart. cashier M11 67:303 · M13 300:4196 M-3 ✅ ✅ ✅ flow-pos-venta/flow-cobro-recibos — catalog renders live, product → M12 detail → add to cart. (Seed had 0 stock → “Agotado”/not addable; fixed by seeding a stock_levels row)
US-POS-2 Apply a line/order discount, requesting supervisor approval over threshold. cashier, supervisor M27a 135:738 · M27b 135:929 M-7 ⏭️ ✅ 🟡 Δ 09-09: discounts DISABLED for MVP (Alfredo 2026-09-01) — UI hidden (#411→#412, FEATURE_FLAG_DISCOUNTS=false); story parked, not a gap. Prior: the shared M27b supervisor-approval sheet is verified live (proven end-to-end by US-VOID-1); the discount-entry leg (cart → “Aplicar”) not driven standalone. over-threshold reject state gap #12
US-POS-3 Charge cash and record change. cashier M27d 135:1047 M-7 ✅ ✅ ✅ N86 09-09: two cash sales green on the device (folios 6aa1e592, 6aa20a2d); design asks UX-2 (cash sheet does not fit 360×640) + UX-3 (success CTAs below the fold). Prior: flow-cobro-recibos — “Exacto” → finalize; server pos.sales.finalized total=1800 tenders=1, stock decremented. Fixed a crash: finalize threw IllegalAccessError (a private companion on the @Serializable response DTO)
US-POS-4 Charge a card via the paired terminal. cashier M15/M16 71:403/72:407 M-4 ✅ ✅ ✅🔌 N86 09-10 (realm manual, owner session): real card charge APPROVED end-to-end — SALE.charge ok code=00 auth=895897 ref=260910162721 APRO, chip, $1.00, serial N860W175796; server shows one CARD payment + one COMPLETED SALE transaction, no duplication. Gotcha: the credential’s environment is asserted against the build’s ANGELPAY_ENV (defaults QA), so a PROD credential on a QA build renders “Terminal no habilitada” — re-provision the credential, do not rebuild. Δ 09-09: built on the unified settlement pipeline (NativeCardTender + CardTenderViewModel + SettlementJobObserver, CardChargeMode REAL/EMULATE_* — #397) and live-verified with a real $1 charge on the Nexgo N86 2026-08-24 (single card; web→handheld bridge live 2026-08-19, #379/#393). Still card-present (🔌) for evidence; the $1 cash+card exactly-once split drive is liquidaciones’ activation bundle (device-gated)
US-POS-5 Mixed/split tender across methods. cashier M27c 135:978 M-7 ✅ ✅ ✅ Multi-tender settlement proven live — server pos.settlement.tender_applied twice against a running balance (leg 1 $1 → remaining $1799; leg 2 → remaining ~$0). Took clearing a 4-layer stack: sales_orders.amountPaid/amountRemaining + status enum OPEN + order_payments.clientId (cloud schema) and 2 real app bugs — the tender DTO omitted storeTeamId/sent non-whitelisted changeGiven (→403), and the shared TenderResponseDto read remaining/status where the server returns amountRemaining/finalized (balance always read 0, swallowing tenders). Both fixed in PosOrderGateway/CardChargeCoordinator. Residual: the 8-nines saturation leg leaves a 1-cent remainder so it doesn’t auto-finalize — a coerce-rounding follow-up, not a settlement failure. Δ 09-09: the split CARD leg on the unified pipeline is shipped dormant on web (#754) and referenced by SplitPaymentViewModel on mobile — unverified on the handheld until activation
US-POS-7 Print/render an 80mm receipt. cashier M17 73:439 M-4 ✅ ✅ ✅ N86 09-10: prints after a CASH sale, FAILS after a CARD sale (“Error de impresión”). 3 successful prints in the session, every one after cash; 4 card payments, not one print after any. Root cause OPEN — the failure emitted no telemetry in a 924k-line capture, so either logcat dropped it or the printer layer is never reached; deliberate repro (cash control → card) is the next step, hardware-blocked. Prior: M17 “¡Venta exitosa!” renders folio/total/Cambio/Imprimir from the finalized order. print/email error state gap #14 RESOLVED + ACCEPTED ON PAPER 09-12. Root cause was NOT a printer fault: PosCheckoutFlowContent.kt:203 was onPrint = onPrint ?: { localPrintFailed = true } — documented placeholder scaffolding. Only the cash path was ever wired (#275); card, manual-charge and split passed no print action, so the error was fabricated in the UI and the printer was never called. That is why a 924k-line capture held no telemetry: nothing to log. Fixed in #459 (all four paths wired; the placeholder DELETED and onPrint made a required parameter, so a future tender cannot acquire a fake failure by omission). Accepted on hardware 09-12: card sale folio #6aa5db6c, auth 361329, full printer success sequence, paper out and renders correctly — both confirmed by Alfredo, which is the only evidence that can settle 80mm rendering. Still unprinted anywhere: the manual-charge and split receipt paths (newly wired, never exercised on paper).
US-POS-8 Hold a sale and resume it later (mine/all + author). cashier M34 300:4185 M-8 🟡 🟡 ✅ Δ 09-09: RESTORE-ONLY on the handheld — “Guardar en espera” (create-a-hold) was removed from the handheld by PO decision 2026-09-05 (#438); holds are created on the web POS and resumed on the handheld. HeldOrdersApiClient wire contract pinned to live OpenAPI (#385). flow-pos-hold-resume.yaml still scripts the removed create step → drift to repair. Prior: flow-pos-hold-resume — “Guardar en espera” parks the cart, “Reanudar venta” restores it. (Web flags this as a candidate bug; mobile works)
US-POS-9 Sale surface blocked with a clear banner when offline. cashier (no M frame) — M-3, gap #35 M-3 ✅ ✅ ✅ N86 09-09: airplane mode → banner + Cobrar disabled ✅; reconnect recovers only after ~30 s and “Reintentar” gives no feedback (UX-9); two stacked banners (UX-8). Offline cash queue = POST-MVP (Alfredo 09-09). Prior: flow-pos-offline.yaml (new) — airplane-mode → M13 CartOfflineBanner (InlineAlert “Sin conexión — no es posible cobrar esta venta.”) renders and “Cobrar” is withheld (canCheckout = lines && !isOffline); “Reintentar” restores it. The blocking state was already built (no figma frame); this run drove it live

Not on mobile: US-POS-6 (durable finalize across a network blip, W19) — a DBOS/backend durability story with a web-only screen citation; it protects the mobile sale path too but has no mobile surface of its own.

ID Story Roles Mobile screens Flow Built Mock E2E Live E2E Notes
US-VOID-1 Void a finalized ticket within the current shift with a supervisor PIN + reason. cashier, supervisor M27e 135:1078 M-7 ✅ ✅ ✅ N86 09-09: two voids approved on the device (SALE_VOID GRANTED), reached via Reportes → Ventas del día because the Sell-FAB “Anular venta” opens the nav drawer over the ticket search (BUG-2, S9); approval sheet opens half-height (UX-6); Historial does not refresh after the void (BUG-7). Prior: flow-void.yaml (new) — M19 → Cancelar → reason → M27b supervisor PIN 4321 → M19 “Cancelado”. Server pos.approval.granted action=SALE_VOID + pos.sales.voided. reject state gap #17

Not on mobile: US-VOID-2 (cancel the linked card payment-intent — backend behaviour of the same void) and US-VOID-3 (refunds beyond same-day void — post-MVP).

SHIFT — Shifts & cash (M-2) — 4 stories

Section titled “SHIFT — Shifts & cash (M-2) — 4 stories”
ID Story Roles Mobile screens Flow Built Mock E2E Live E2E Notes
US-SHIFT-1 Open a shift with an opening float. cashier M08 61:212 M-2 ✅ ✅ 🟡 N86 09-09: $500 open accepted by the server but the Home card keeps “Sin turno” until the next PIN login (BUG-1, S11). Prior: flow-inicio-turno — $500 float opens a real shift; a duplicate open → server 409 “Ya hay un turno abierto”
US-SHIFT-2 Record cash movements (pay-in/pay-out) with a reason. cashier M10/M29 63:258/291:951 M-2 ✅ ✅ ✅ N86 09-09: −$125 / +$200 with reasons green on the device; reached via the Sell FAB only through the drawer hijack (BUG-2). Prior: pay-out −$125 + pay-in +$200, each with a required reason; reason-required rejection enforced; movements persist in M10. concept picker undesigned (gap #38)
US-SHIFT-3 Close my shift with a denomination breakdown and see the variance. cashier M09 300:4229 M-2 🟡 🟡 🟡 N86 09-09: driven live — count $610 vs expected $575 → “Descuadre detectado” → supervisor approval → mobile_shift_close_total{result=success}. But Ventas brutas is hardcoded $0 on mobile and the server’s expected cash excluded the shift’s $3 cash sale (BUG-10, Q-n86-4 = gap #18); no coin rows in the count grid (UX-14); Home still shows the shift open afterwards (BUG-1)
US-SHIFT-4 Approve an over-tolerance cash variance at close. supervisor M27b 135:929 M-7 ✅ 🟡 ✅ N86 09-09: mobile_approval action=SHIFT_VARIANCE result=GRANTED on the device, shift closed. Prior: the shared M27b approval sheet is verified live (US-VOID-1); the shift-close variance leg not driven (depends on SHIFT-3)

US-SHIFT-4 is a story whose screens and flow ticket disagree (sequencing note, TODO T-7): the journey is shift-close (M-2 / #222) but the approval overlay it renders is the shared M27b, owned by #224. Both tickets must land before it can be exercised end-to-end. US-VAS-5 has the same shape across #225/#224.

Not on mobile: US-SHIFT-5 (immutable Z-report per closed shift, W33/W58) — the report artifact is a web/back-office surface, even though mobile M09 performs the close that produces it.

VAS — Value-added services (M-5, M-4) — 5 stories

Section titled “VAS — Value-added services (M-5, M-4) — 5 stories”

Deferred this session — VAS was not part of the 2026-07-31 Pass D run (Built/Mock E2E carried forward from the prior review; Live E2E = —). It requires FEATURE_FLAG_VAS_MOBILE_CATEGORIES (app, committed on) + a VAS-configured API (aggregator).

ID Story Roles Mobile screens Flow Built Mock E2E Live E2E Notes
US-VAS-1 Sell airtime/TAE to a phone number. cashier M21 80:545 M-5 ✅ 🟡 ⏭️ Δ 09-09: rebuilt on the api path (vas/VasApiClient.kt, TAE tab in-place in the M11 picker — #356, robustness #375→#383). Live = gated on the aggregator (fail-closed 503 stub until Mantarys lands); TAE tab never device-checked
US-VAS-2 Sell gift-card PINs with secure delivery/reprint. cashier M22 81:589 M-5 ✅ 🟡 ⏭️ Δ 09-09: rebuilt on the api path (Pines tab, same client). Live gated on the aggregator; Pines tab never device-checked
US-VAS-3 Pay a service/bill (with adeudo lookup). cashier M20 76:503 M-5 ✅ 🟡 🟡 Δ 09-09: rebuilt on the api path; Servicios tab driven live on the N86 2026-08-12 (form → aggregator stub); adeudo robustness #375→#383
US-VAS-4 Sell a SIM chip from serialized inventory (ICCID/NIP). cashier (no M frame) — M-5 M-5 ⚠ — — Track C; serialized_units (deferred)
US-VAS-5 Register a customer transfer (financial services) and confirm it with a comprobante. cashier, manager M23 82:631 · M18 74:456 M-5 → M-4 ⚠ 🟡 — Δ 09-09: still UNBUILT — no transfer capture/confirm screens on main (connections/AddBalance* is Conexiones top-up, a different flow)

US-VAS-5 is in mobile MVP — PO decision 2026-07-22. Mobile carries the transfer leg only: M23 Transferir saldo → M18 Confirmar transferencia / Comprobante. Registering deposit channels A–C and manually validating them (pending → available) stays web-only — that is US-BAL-2. The wallet balance itself is never shown on mobile (owner-only, PDR-0003). Note the story spans two flow tickets: M23 sits in M-5 (#225) and its confirmation screen M18 in M-4 (#224) — both must land before the journey is exercisable end-to-end.

Not on mobile: US-VAS-6 (VAS reporting by kind/provider/commission, W57) — back-office reporting.

BAL — Merchant wallet (M-4, M-5) — 4 stories

Section titled “BAL — Merchant wallet (M-4, M-5) — 4 stories”

Deferred this session — BAL was not part of the 2026-07-31 Pass D run. Requires FEATURE_FLAG_MERCHANT_WALLET* (backend) + the Track-K wallet wiring.

ID Story Roles Mobile screens Flow Built Mock E2E Live E2E Notes
US-BAL-3 Customer’s direct cash payment (channel D) credits store cash, never the wallet, with a printable receipt. cashier M27d 135:1047 M-7 🟡 🟡 — rides existing cash-movements; channel semantics Track K (deferred)
US-BAL-4 Card payment on the handheld (channel E) credits the merchant wallet immediately. cashier M15/M16 71:403/72:407 M-4 ✅ 🟡 ✅🔌 Δ 09-09: the channel-E deposit is inside the unified settlement tail (order_payment → finalize → deposit, exactly-once via the deposits UNIQUE) proven live on the N86 2026-08-24 with real $1 charges. Card-present for evidence
US-BAL-5 Top up the wallet by card on the handheld (autopay) — confirms success without revealing the balance to non-owners. manager, cashier (no M frame) — new handheld screen, Track K — 🟡 🟡 🔌 Δ 09-09: built but DORMANT — TopupViewModel + wallet-topup.yaml shipped (#259); gated by FEATURE_FLAG_MERCHANT_WALLET_TOPUP_MOBILE=false (BuildConfig). Flag-minimalism: PO decides keep-and-flip vs drop before launch
US-BAL-6 VAS sale blocked when the merchant wallet lacks funds. cashier M20–M23 M-5 🟡 ⚠ — Δ 09-09: Conexiones balance sheet + POS platform-balance tender selector shipped (#357→#362, #359→#361, #373→#381); the insufficient-funds block itself is unverified

US-BAL-5 is the only mobile story with no Pass C parity ticket (deferred, TODO T-6) — it has no canonical Figma frame, so it never entered the flow audit. It is already covered by the pre-existing E2E draft backlog/drafts/mobile-merchant-balance-autopay-topup-e2e.md → issue #259. A frame must be designed before it can be parity-audited.

Not on mobile: US-BAL-1 (wallet balance + movements — owners only, W47), US-BAL-2 (register and manually validate deposit channels A–C, W46), US-BAL-7 (serialize wallet operations — system/backend), US-BAL-8 (withdraw to bank — post-MVP). Per PDR-0003 the wallet balance is deliberately owner-only and therefore deliberately absent from the cashier-facing mobile app.

CLI — Clientes / customers (M-8) — 2 stories

Section titled “CLI — Clientes / customers (M-8) — 2 stories”
ID Story Roles Mobile screens Flow Built Mock E2E Live E2E Notes
US-CLI-1 Look up / quick-create a customer and assign them to a sale. cashier M32/M33 300:4086/300:4129 M-8 ✅ ✅ 🟡 Δ 09-09: mobile flag RETIRED (#387). Prior: wired live this session: the cart FAB “Asignar cliente” now loads the real directory (CartCustomerPickerViewModel → CustomersApiClient, not mock recents); picking stores the customer on CartStateHolder and it threads through finalize onto sales_orders.customerId (flow-clientes-assign.yaml picks the seeded “Ana López” + a cash sale persists order.customerId=e2e_customer_ana). Gated on the two CLI flags
US-CLI-2 Customer directory with detail. manager M30/M31 300:4041/300:4052 M-8 ✅ ✅ 🟡 Δ 09-09: mobile flag RETIRED (#387) → no longer gated on the app side; follow-ups #369 open (detail page-scan, 20-row truncation, unassign analytics). Prior: flow-clientes — M30 renders the live seeded directory (Ana/Beto/Celia) + server-side search. Gated on TWO flags: FEATURE_FLAG_CUSTOMERS_MOBILE (app, default OFF → no nav row, gap #45) and backend FEATURE_FLAG_CUSTOMERS_MODULE (default OFF → 403 El módulo de clientes no está habilitado). With both on, the directory works live

Mobile is ahead of web here: the master map records that the v2 Clientes module (230:2) was absorbed into the mobile flow M-8 but not yet into web.

ACCT — Account & settings (M-6) — 1 story

Section titled “ACCT — Account & settings (M-6) — 1 story”
ID Story Roles Mobile screens Flow Built Mock E2E Live E2E Notes
US-ACCT-1 View/edit my account and security (2FA/PIN). all M24 83:647 M-6 🟡 ✅ ✅ flow-cuenta-reportes — M24 “Mi cuenta” renders Datos personales / Soporte / Cerrar sesión live. N86 09-10 (PB-2, fix in #452): “Cerrar sesión” sat below the 640dp fold for an OWNER — the owner-only “Plataformas de saldo” row (#358) costs 71dp and pushed it to top 586dp in a 568dp body. Measured, not estimated. The cashier margin was only ~13dp and flaked to a failure once in six pre-fix runs, so read this as the fold was marginal for every role and the owner crossed it first. Now top bar → weight(1f) scrolling band → pinned footer (the S13 cash-sheet/receipt shape); sign-out also raised from 40dp to 52dp, having been below the 48dp a11y floor independently. PB-1 also applied to this screen — its hamburger was dead, so Mi cuenta could not be left via the drawer. Edit + 2FA/PIN legs not driven (security via 2FA #124)

Not on mobile: US-ACCT-2 (general store settings, W61) and US-ACCT-3 (audit log / bitácora, W62) — manager back-office.

ID Story Roles Mobile screens Flow Built Mock E2E Live E2E Notes
US-NOTIF-1 Notifications center + detail with unread badge. all M07/M28 60:177/164:937 M-2 ✅ 🟡 ✅ N86 09-09: list (4 unread, day groups), detail, “Ver producto” deep link → M12, mark-all → badge 0 (read state via api) all green; UX-12 raw payload keys in Detalles. Prior: flow-notificaciones.yaml (new) — the top-bar bell opens M07, which renders live seeded notifications day-grouped (unread + read). detail/deep-link gap #34

Gaps follow-up — “missing designs” are mostly built-but-unframed (2026-07-31)

Section titled “Gaps follow-up — “missing designs” are mostly built-but-unframed (2026-07-31)”

A pass over the design/frame gaps flagged in this file found that, for the in-scope groups, the “missing figma design” screens are already implemented in code (mock-first, from the design system + string resources) — what they lacked was a canonical figma frame and a live driving. Confirmed by building nothing and simply driving them:

  • US-POS-9 (offline-blocked sale surface, “no M frame”) — the M13 block already exists: CartScreen.CartOfflineBanner (an InlineAlert over pos_cart_offline_*) renders when CartViewModel.isOffline, and canCheckout = lines && !isOffline withholds “Cobrar”. Authored flow-pos-offline.yaml (airplane-mode → banner + block → “Reintentar” restores) — now ✅ Live.
  • US-CLI-1 assign-on-sale — wired live this session (was mock): the cart FAB now uses the real directory and threads customerId through finalize onto the order (see the US-CLI-1 row). AssignCustomerFlow (the order-PATCH variant) still exists for assign-after-finalize.
  • Terminal-selection screen (gap #36) — built this session. Rather than a “become another terminal” picker (which conflicts with the claim-one-terminal handheld model), the new TerminalSelectionScreen is a roster/visibility screen: it lists the store’s terminals via GET /pos/terminals (store-scoped server-side) with each one’s online status and marks this device’s own, reachable from “Registro de terminal”. Binding still happens by pairing code. See the US-ACC-5 row.
  • Still built-but-unframed and drivable (not yet driven this pass): US-SHIFT-3 close-shift M09 (ShiftCloseScreen), the gap #12 discount over-threshold state (verified indirectly via US-VOID-1’s M27b approval), and gap #33 new-password step (driven in flow-auth).
  • Email stories (US-ACC-2 MFA, US-ACC-3 recovery): Mailpit added to terminales-api/docker-compose.yaml (SMTP :54325 matching .env, inbox http://localhost:8025). It catches mail the api sends; it does not catch the MFA/recovery OTP+reset emails, which cloud Appwrite sends. Runbook to close those legs via the IONOS Mailpit + Appwrite project SMTP + terminales:// platform registration: terminales-api/docs/runbooks/appwrite-mailpit-email-e2e.md (the console-SMTP + platform steps need Appwrite console access + the CI-secret Mailpit host — a hand-off).

First mobile Pass D run (groups ACC · POS · VOID · SHIFT · CLI · ACCT · NOTIF; VAS + BAL deferred). The app talks to cloud Appwrite for domain data + auth and to a local terminales-api :3020 for the POS API (the app reaches it via adb reverse tcp:3020); Postgres holds only DBOS/cache, so an empty public schema is expected. Work landed on the verify/user-stories-functionality branch of each repo.

App bug found + fixed (real defect):

  • Cash finalize crashed the app — java.lang.IllegalAccessError: Field 'FinalizeOrderResponseDto.Companion' is inaccessible (SaleFinalizeApiClient). The @Serializable response DTO carried a private companion object (for its JSON key constants), so the reified decodeFromString<FinalizeOrderResponseDto> — inlined into a synthetic lambda — could not reach the generated serializer(). Every cash sale crashed at the finalize response. Mobile Pass D had never run, so it was never caught. Fix: make that companion non-private. Unblocks US-POS-3/POS-7 (and any decode of that DTO).

Backend / cloud-schema drift found (needs a fix to unblock):

  • US-POS-5 split tender — a chain of settlement schema drift + one app bug. Peeling it back layer by layer:
    1. sales_orders missing amountPaid + amountRemaining (double) — added (createFloatAttribute).
    2. sales_orders.status enum allowed only (FINALIZED, VOID); settlement opens as OPEN — OPEN added to the enum.
    3. With settlement now opening live, the tender leg 403’d — a real app bug: CashTenderRequestDto omitted storeTeamId (which the server’s role check + AddTenderDto require) and sent changeGiven (rejected by forbidNonWhitelisted). Fixed in PosOrderGateway/CardChargeCoordinator (thread storeTeamId from the resolved sale context, drop changeGiven) — a manual POST …/tender with storeTeamId confirmed the 403 is cleared.
    4. The tender then 500’d on order_payments missing clientId (the per-tender idempotency key it queries + writes) — added via scratchpad/fix-order-payments.js (it was the only missing column).
    5. With the tender reaching the server, it silently no-op’d: the shared TenderResponseDto read remaining/status/orderPaymentId but the endpoints return amountRemaining/finalized (open returns the row’s top-level $id; tender returns a nested order). The balance therefore always decoded to 0, so the split never advanced. Fixed the response model + accessors to decode both shapes (amountRemaining, finalized, top-level $id).
    • Result: split settlement now works live — two pos.settlement.tender_applied events against a server-recomputed running balance. Residual: the flow’s 8-nines saturation leg leaves a 1-cent remainder (amountRemaining=1) so it doesn’t auto-finalize — a coerce-rounding follow-up worth a small ticket, not a settlement failure.
    • The shared-cloud-schema writes (steps 2 + 4) were run by the user (the auto-mode classifier blocked the agent from writing to the shared cloud project); the float attributes (step 1) went through directly.
  • Non-fatal rollup error on finalize — pos.rollup.update_failed … Unknown attribute: "salesByKind" — the reporting rollup writes a salesByKind attribute the cloud collection lacks. The sale still commits (rollup is best-effort), but the report aggregate silently misses it.

Environment gating discovered (not bugs — needed to exercise the stories):

  • CLI needs two flags — FEATURE_FLAG_CUSTOMERS_MOBILE (app, default OFF → the “Clientes” nav row is absent, gap #45) and backend FEATURE_FLAG_CUSTOMERS_MODULE (default OFF → the API 403s “El módulo de clientes no está habilitado”). Both were flipped on to verify US-CLI-2 live; the app flag must be reverted before merge (CI nav-gate tests enforce OFF).
  • Seed gap — FIXED — scripts/seed-e2e.ts seeded the product but no variant/stock_levels row, so the default variant read quantity 0 → “Agotado” → not addable and the whole POS money-path was blocked. The seed now upserts a default variant + a stock_levels row (quantity 100) so the catalog is sellable on a fresh seed (verified: seed:e2e runs clean).

Maestro flow drift repaired (the flows had never run against the live app): product taps by coordinate → by name; the cash keypad id:numpad_key_* → text taps (its NumericKeypad testTag is a non-merged semantics node id can’t see) and cash tendered via “Exacto” (the seeded price is high); a contentDescription: selector (invalid in Maestro 2.5.1) → text; the action-hub rows made scroll-robust for the taller VAS-on layout (centerElement); the M27b approval keypad uses bare digits (not M02’s "Tecla <n>"), its first tap needs waitForAnimationToEnd, and on 1080×2400 the keypad pushes the [Cancelar|Aprobar] row below the fold → must scrollUntilVisible "Aprobar" (a real M27b layout finding affecting every approval: void / discount / variance). New helper subflows/claim-terminal.yaml (idempotent pair + PIN after clearState, which wipes the local pairing) and the runner’s in-app login is the cashier (whose PIN matches) while the pairing code is minted with the manager (issuer-only).

New flows authored: flow-void.yaml (US-VOID-1, emulator-runnable — the combined flow-cobro-recibos reaches void only through a card-present leg) and flow-notificaciones.yaml (US-NOTIF-1).

Section titled “US-ACC-3 password recovery — driven green via https App Links (2026-08-01)”

Real product defect found + fixed. US-ACC-3’s send half had been logged as “infra — register the terminales:// platform”. That premise was wrong, proven live by probing createRecovery with a non-existent email (URL validation runs before user lookup, so 404 user_not_found = URL accepted, 400 general_argument_invalid = URL rejected):

url result
terminales://auth/recovery 400 rejected — arbitrary custom scheme never accepted
https://<anything>/recovery 404 accepted (a * Web platform covers any https host)
appwrite-callback-terminales://… 404 accepted (Appwrite’s standard mobile scheme)

So no platform registration can fix it — Appwrite Cloud’s redirect-url validator accepts only http(s):// or appwrite-callback-<projectId>://. Recovery was therefore broken in production, not just the E2E.

Fix (best-practice, mobile): migrate the recovery callback to a verified Android App Link (also the right security posture — a custom scheme carrying a single-use reset secret is hijackable; an App Link is bound to the app’s signing cert):

  • RECOVERY_REDIRECT_URL → https://${BuildConfig.MAGIC_LINK_HOST}/recovery; magic.link.host=links.io.inspiracode.com.
  • /recovery added to the autoVerify https <intent-filter>; AuthDeepLinks routes verified https://<host>/recovery → Recovery. Contracts re-pinned in AppwriteRecoveryRepositoryTest + AuthDeepLinksTest (full unit suite + spotless/detekt/lint green).
  • Infra: assetlinks.json (package + debug cert SHA-256) served at https://links.io.inspiracode.com/.well-known/assetlinks.json via a small nginx behind the shared Traefik on the IONOS box (/opt/terminales-assetlinks/, TLS auto-issued). The host reports verified on the emulator (pm get-app-links).
  • Verified end-to-end: M04 Enviar enlace → createRecovery 201 → mail in Mailpit → App Link opens the reset screen → password set. run-recovery.sh orchestrates send → Mailpit read → reset.

Magic-link + verify-email — same fix applied (2026-08-01). Both shared the root cause and were migrated in the same way:

  • Magic-link (US-ACC-1 AC3): MAGIC_LINK_REDIRECT_URL → https://${MAGIC_LINK_HOST}/magic-link. Driven green end-to-end (M01 → createMagicURLToken → Mailpit → App Link → session → M05): flow-auth-magic-send+-complete.
  • Verify-email (#93 AC1): EMAIL_VERIFY_REDIRECT_URL → https://${MAGIC_LINK_HOST}/verify-email, /verify-email added to the autoVerify filter, routed in AuthDeepLinks. URL acceptance proven live (a signed-in createVerification returns 409 user_email_already_verified for the https url on a verified account vs 400 general_argument_invalid for the custom scheme) and the routing is unit-tested. Full UI run staged (flow-auth-verify-email.yaml): its send screen is playground-only (Routes.Playground.APPWRITE_MFA_SETTINGS) and needs an unverified account, so it isn’t a production journey yet.

AuthDeepLinks now routes all three callbacks on both transports (verified https + the terminales:// custom-scheme fallback for in-flight emails). US-ACC-2 MFA is unaffected (it uses the email OTP challenge, not this deep link). Before shipping: add the release signing-cert fingerprint to assetlinks.json, and give verify-email a production entry point.


These areas have zero mobile stories. They are the back-office and platform surfaces that make web the larger app.

Area Stories Why web-only
CAT — Catalog US-CAT-1…3 (3) Product/variant/barcode/price authoring, categories, tax rates — manager/supervisor authoring, not counter work. Mobile consumes the catalog (US-POS-1).
INV — Inventory US-INV-1…3 (3) Stock levels, adjustments with reason, inter-store transfers — manager/supervisor. (Note gap #34: a notification deep-link points at a mobile inventory flow that does not exist.)
STAFF — Staff & access US-STAFF-1…4 (4) Employees + PINs, invites, role→permission matrix, server-side cross-store deny — administration.
REP — Reports US-REP-1…6 (6) Daily sales, margin, tender/employee mix, exception reports, commissions, role-scoped CSV export. Mobile has a Reportes screen (M25) but no story claims it — PO deferred 2026-07-22, TODO T-1.
TERM / WL — Commerce, stores, terminals, white-label US-TERM-1, US-TERM-2, US-WL-1 (3) Fleet configuration, terminal pairing, brand/receipt theming — manager.
PLAT — Platform administration US-PLAT-1…3 (3) Merchant/user management, audited impersonation, white-label assist — platform roles only; no mobile surface by design.
SEARCH — Global search US-SEARCH-1 (1) ⌘K command palette — a desktop interaction pattern.
Remaining web-only rows in mobile-touching areas US-ACC-6, US-POS-6, US-VOID-2/3, US-SHIFT-5, US-VAS-6, US-BAL-1/2/7/8, US-ACCT-2/3 (12) Listed under their areas above.

Coverage inversion — mobile screens with no user story

Section titled “Coverage inversion — mobile screens with no user story”

Resolved 2026-07-22: M23 Transferir saldo and M18 Confirmar transferencia / Comprobante are now claimed by US-VAS-5 (PO: in mobile MVP) — see VAS.

Seven canonical mobile frames remain owned by a Pass C parity ticket but not claimed by any story. All are deferred with a TODO — tracked as T-1…T-5 in § Deferred TODOs. None blocks the UI parity tickets (#221–#227); each must be settled before the mobile E2E (Pass D) batch is finalized.

Screen Node Flow Pass C ticket Status Assessment
M06 Inicio (Dashboard) 300:4317 M-2 #222 TODO T-2 Hero-flow entry point; no story. Bottom-nav IA still undecided (gap #37, 4 vs 5 items).
M12 Detalle de producto 68:344 M-3 #223 TODO T-3 Detail step implied by US-POS-1, named by no story.
M14 Cobrar / Método de pago 70:389 M-3 #223 TODO T-3 The tender picker every US-POS-3/4/5 passes through; implied, never named.
M19 Detalle de ticket 75:475 M-4 #224 TODO T-4 Adjacent to US-VOID-1 and US-POS-7. Missing Cancelado/Reembolsado states (gap #35).
M25 Reportes 85:699 M-6 #226 TODO T-1 REP is entirely web-only, yet a mobile Reportes screen exists. PO deferred which reports and at what role scope.
M26 Historial / Buscar ticket 86:740 M-6 #226 TODO T-1 Ticket lookup feeding M19; adjacent to US-VOID-1. Deferred with M25.
M35 FAB · Acceso rápido 300:4289 M-8 #227 TODO T-5 Quick-action FAB; “Abrir menú” destination undesigned, active-cart rule undecided (gap #44).

Do not author mobile E2E tickets for these seven screens — there is no story to give them acceptance semantics. #221–#227 already cover them as UI parity + navigation only, which is sufficient for the current phase.

Role Mobile stories
cashier 23 named explicitly (+ the 6 all stories = 29 reachable) — the bulk of the app: sale, tenders, receipt, shift, cash movements, VAS, transfers, customer assign, hold/resume, PIN access
supervisor 3 named explicitly — approval overlays only (US-POS-2 discount, US-VOID-1 void, US-SHIFT-4 variance) — plus everything cashier and all reach
manager / owner 3 — US-CLI-2 (customer directory), US-BAL-5 (handheld wallet top-up), US-VAS-5 (transfers, shared with cashier); everything else managerial is web
platform_admin / support / developer 0 — by design
customer / guest 0 — reserved roles, no MVP stories on either platform

This is the concrete evidence for the “mobile = operator, web = operator + back office” framing: three manager-touching stories out of thirty-one, and none of them manager-exclusive back-office work.

The mobile track completed Pass A (Maestro harness, #215), Pass B (component families, #216–#220 / #247–#252), and Pass C (the seven flow parity + navigation tickets, #221–#227) — all UI-only / mock. Pass D — the mobile E2E batch: backend + frontend lifted together against the live stack — began 2026-07-31: the seven in-scope groups (ACC · POS · VOID · SHIFT · CLI · ACCT · NOTIF) were driven live and their Live E2E column filled above; VAS + BAL remain to run. See § Live E2E run — findings for the bug/schema/flow fixes that unblocked it.

The 31 stories above are that batch’s scope list — one E2E ticket per story, or fewer if stories sharing a screen and a money path are collapsed (the M27 overlay cluster, US-POS-2/3/5 + US-VOID-1 + US-SHIFT-4, is the obvious candidate).

The scope list is settled enough to author against: the PO’s 2026-07-22 review admitted US-VAS-5 and deferred everything else with TODOs T-1…T-8. Those deferrals are all unclaimed screens (T-1…T-5), a missing frame (T-6), a sequencing note (T-7), and a node-id cleanup (T-8) — none of them removes a story, and only T-1 (mobile reports) could add one. So Pass D can be authored for the 31 now, with M25/M26 explicitly excluded until T-1 lands.

Deferred TODOs — PO mobile scope review (2026-07-22)

Section titled “Deferred TODOs — PO mobile scope review (2026-07-22)”

Raised while deriving this file (relocated here in the 2026-07-30 web ⇄ mobile split — all are mobile-scope). All deferred, not dismissed. None blocks the mobile UI/parity tickets (#221–#227); all must be settled before the mobile E2E (Pass D) batch is finalized, because an E2E ticket for a screen with no story has no acceptance semantics to test against.

# Item Deferred decision
T-1 M25 Reportes + M26 Historial / Buscar ticket have mobile frames but every REP story is web-only. Which reports does mobile get, at which role scope?
T-2 M06 Inicio (Dashboard) (300:4317) — the hero-flow entry point — is claimed by no story. Bottom-nav IA is also still undecided (master-map gap #37, 4 vs 5 items). Does the mobile dashboard need its own story, or is it purely navigational chrome? Settle alongside gap #37.
T-3 M12 Detalle de producto (68:344) and M14 Cobrar / Método de pago (70:389) are implied by US-POS-1 and US-POS-3/4/5 but named by neither. Extend the existing stories’ Screens columns, or accept them as implied steps. Low risk either way.
T-4 M19 Detalle de ticket (75:475) is adjacent to US-VOID-1 / US-POS-7 but unclaimed, and is missing its Cancelado/Reembolsado states (gap #35). Attach to a story once the void/refund state package is designed.
T-5 M35 FAB · Acceso rápido (300:4289) is unclaimed; its “Abrir menú” destination is undesigned and the active-cart rule is undecided (gap #44). Rides on gap #44 — PO decides the menu destination and the discard-vs-block rule for an active cart.
T-6 US-BAL-5 (handheld wallet top-up / autopay) is mobile-scoped but has no Figma frame, so it never entered the Pass C parity audit. It is covered by E2E draft backlog/drafts/mobile-merchant-balance-autopay-topup-e2e.md → #259. Design the handheld top-up frame so the story can be parity-audited, or accept it as E2E-verified only.
T-7 US-SHIFT-4 straddles two tickets: the journey is shift-close (M-2 → #222) but the approval overlay it renders is the shared M27b, owned by #224. No decision needed — just sequencing: both tickets must land before the story is exercisable end-to-end.
T-8 Node-id discrepancy: US-ACC-3 / US-ACC-5 cite the M04b/M05b variants (539:1215, 546:1229) while #221 lists canonical 56:88 / 57:85. Settle which frame is canonical during #221; then correct the Screens column here.

Mobile screens + mobile status are tracked here; the shared story text and roles live in user-stories.md (web). When either changes:

  • a story gains or loses an M## screen → add or remove its row here, and update the counts in Why mobile is a subset;
  • a story’s mobile implementation/coverage changes → update its Built · Mock E2E · Live E2E columns here directly (no longer mirrored from the web file);
  • a shared story’s text or roles change → mirror the wording here (the web file owns the story text);
  • a new mobile frame is designed → check it against the coverage inversion table and either attach it to a story or record it as an open question;
  • a new mobile-only story is added → add it here directly (US-BAL-4 / US-BAL-5 are the current mobile-only stories).