MVP gap inventory — N86 / mobile initiative
From: terminales-n86 (TL) · Date: 2026-09-12 · For: PO → Alfredo
Purpose: one honest cross-lane inventory so the verification sweep runs against a complete surface and finds defects rather than absences.
Method: every claim below was read from origin/main or from a PR, not recalled. Where I could not verify, it says so.
⚠️ Correction to the ask, first
Section titled “⚠️ Correction to the ask, first”The PO expected settlement_unified split-tender and the write-gateway card bridge from me as flag-gated dormant mobile code. Neither is mine, verified rather than assumed:
FEATURE_FLAG_WRITE_GATEWAYwas retired in6d99cad7(#408). Mobile’s write-gateway path is permanently ON with no flag.git grep -i write_gateway origin/main -- app/returns zero.FEATURE_FLAG_SETTLEMENT_UNIFIEDhas never existed in the mobile build. No-Shistory inapp/build.gradle.kts, andgit grep -i settlement_unified origin/main -- app/returns zero. It is an api/web flag; ask those lanes.
Stating this rather than reporting on flags I do not own.
1. Flag-gated dormant (mobile has exactly five flags)
Section titled “1. Flag-gated dormant (mobile has exactly five flags)”Correction/addition 2026-09-13 — mobile has TWO flag planes, and the second gates nothing. Beyond the five build-time
BuildConfigflags below, there is a full remote runtime evaluator (FeatureFlagsRepository): reads the Appwritefeature_flagstable, paginates past the 25-row default, reconciles remote-wins into a persisted offline-first cache, subscribes to Realtime so a flip reaches live sessions with no restart, and fails closed on every error path. It is started at app boot and consumed by nothing — its only production caller isTerminalesApplication:90 featureFlagsRepository.start(...); no feature asks it whether a flag is on. Every real gate uses the build-time plane, so no mobile flag is a live kill switch today, despite the capability being fully built.This also resolves the empty-
feature_flags-table puzzle. Mobile’s repository is the only reader I have found; web reads env vars and never touches that table. Since nothing reads mobile’s result, an empty table currently has no observable effect anywhere — so seeding it would change nothing. Recorded because seeding is the natural and wrong next move on seeing it empty.
| Flag | Value | What is behind it | State | MVP? |
|---|---|---|---|---|
MERCHANT_WALLET_TOPUP_MOBILE |
OFF | Card top-up of the merchant wallet | Finished pending review — session seam + attribution in PR (see §4) | Alfredo’s call. He ruled finish it over cutting; when it ships is explicitly a separate decision |
STORAGE_CANONICAL_BUCKETS |
OFF | Legacy bucket vs canonical product-images |
Unknown, and deliberately frozen. Collapsing as-is would permanently enshrine the legacy path. Open question with the merchants lane: has the server-side migration run? | Needs the answer before anyone touches it |
DISCOUNTS |
OFF | DiscountSheet + VM intact; the M27a route is not registered, so no discount surface is reachable (#411) |
Finished but deliberately hidden. The one documented exception to the all-flags-ON directive | PO decision — re-enable or cut. It is built |
DEVICE_MODE |
OFF | Terminal/Móvil differentiation (#85). OFF = always TERMINAL (baseline); ON auto-detects | Unknown to me. 3 files; I have not driven it | Probably not — baseline is the N86 behaviour we ship |
DEMO |
OFF | Dev/demo scaffolding, 2 files | Not product | No |
2. Specified but unbuilt
Section titled “2. Specified but unbuilt”| Item | State | MVP? |
|---|---|---|
| S10 VAS direct sale | BUILT and merged — VasDirectSaleScreen/Form/Panels/State are on main. The PO’s list has this stale |
n/a |
| Offline cash queue | Unbuilt, and correctly so. Alfredo ruled it POST-MVP; MVP is block-all-offline (POS-9) | POST-MVP |
| Ticket-detail reprint wiring | Unbuilt — see §4, it is worse than unbuilt | See §4 |
I am not aware of other specified-but-unbuilt mobile work. That is an absence of knowledge, not a proof of none.
3. Built but not wired / not deployed
Section titled “3. Built but not wired / not deployed”| Item | State | Unblocks on |
|---|---|---|
| Category id migration | Mobile needs zero changes (verified: CatalogViewModel is plain id equality, chips carry category.id). api #706 / web #888 open. My job is a post-migration verify pass |
The onboarding lane running the migration |
manual realm is one realm only |
Everything I verified this week ran on manual. Other realms’ schema/state not checked by me |
n/a — flagging scope, not a defect |
4. Known broken or knowingly partial
Section titled “4. Known broken or knowingly partial”| Item | What actually happens | Ref | MVP? |
|---|---|---|---|
| Ticket-detail “Reimprimir” | Fabricates a print failure. TicketDetailScreen.kt:302 is onClick = { reprintFailed = true } — the printer is never called. Same defect class as #459, still on main. Its KDoc admits it |
Needs its own slice | Yes if reprint is MVP. Two product questions first: may a cancelled/refunded ticket reprint, and is a reprint marked as such on the paper |
| Wallet top-up session seam | Fixed, PR open (re-based and re-opening after its stacked parent merged) | was #461 | Ships with the flag decision |
| Home KPI does not refresh in-session | After a sale, Home still reads Ventas hoy $0.00 / Transacciones 0 while “Actividad reciente” on the same screen lists the sales. Correct after a restart. Data is right throughout — this is a refresh gap, not an aggregation defect |
Observed 09-11, unfixed | Probably yes — a merchant checking takings mid-shift sees zero |
| Activity timestamps look ~6h off | Sales made ~16:45 device time (America/Mexico_City) displayed as 11:20 p. m. Possible timezone regression (S12 territory) |
Observed 09-11, not diagnosed | Yes if confirmed — wrong times on a sales list is a trust problem |
| Back from “Terminal lista” exits the app | Rather than returning to the shell | Observed 09-11, not confirmed | Minor |
e2e_terminal_unpaired shown pre-login |
A fixture id that exists nowhere in production code is displayed before authentication. Retracted as a blocker (post-login it is correct); origin still unexplained | Open question, undiagnosed | Low, but unexplained |
5. Blocked on a human, a device, or a third party
Section titled “5. Blocked on a human, a device, or a third party”| Item | Blocked on | What unblocks it |
|---|---|---|
| DONE 2026-09-12 | ✅ Accepted on paper. Card sale folio #6aa5db6c, auth 361329, full printer success sequence; paper out and renders correctly, both confirmed by Alfredo. Same tap that produced a fabricated error with zero printer lines the day before |
|
| Manual-charge and split receipt paths — never printed | The physical N86 | Both were wired by #459 and neither has ever printed on paper. They do not inherit the card path’s acceptance: the card receipt is the only one whose 80mm rendering has been seen. One sale each on the device settles it |
| Web→handheld bridge (R10) | N86 + web on :4007 |
The web env var is routed to the web lane; then a card tap |
| PB-4 verification CTA end to end | N86 + the endpoint deployed (it now is) | A device session |
| 11 Maestro flows repaired-but-not-re-run | Credentials + a live stack — never capacity | Someone with the credentials runs them. Not me; I do not handle them |
| VAS aggregator activation | Third party | Outside my lane |
The judgement asked of me: is the handheld MVP at the same depth as web?
Section titled “The judgement asked of me: is the handheld MVP at the same depth as web?”No. Web is the MVP surface; the handheld should follow. I have driven both and I think this for three reasons, none of them about code quality.
1. The handheld’s defect density is still falling fast, and that is information. In roughly thirty-six hours of driving it, this lane found: a dead navigator on every non-Inicio root, sign-out below the fold, two dead links on one onboarding screen, receipt printing never wired for three of four tenders, a terminal fault reported as a customer’s card being declined, and a top-up path with no session handling at all. Those were not found by a sweep; they were found by using it. A surface still yielding defects of that severity, at that rate, has not been exercised enough to call it MVP-ready at web’s depth.
2. Every one of them was invisible from the outside. The print failure looked like a broken printer. The false decline looked like a refused card. The dead hamburger logged nothing. This surface fails convincingly, which means absence of reports is not evidence of correctness — and the handheld has had far fewer real users than web.
3. The handheld’s hard dependencies are the ones we cannot rehearse. Card taps, a physical printer, a payment SDK session, a paired terminal. Those cannot be covered by tests or emulators, and each needs a person at a device. Web has no equivalent gate, so web can reach confidence on a schedule the handheld structurally cannot.
What I would propose instead of equal depth: MVP on the handheld = the cash-and-card sale path end to end with a printed receipt, plus shift open/close and the onboarding gate. That is what a merchant cannot trade without. Wallet top-up, VAS breadth, discounts, reprint-from-history and the remote bridge are all real work that can land after, because a merchant can complete a day’s trading without any of them.
Caveat, stated plainly: this is a judgement about readiness, not about effort or value. And it is one lane’s view — the web lanes may have their own gaps I cannot see, which would change the comparison. I would not want this read as “web is done”.