Web MVP completeness — reconnaissance for the go-live scan
- Status: Draft for PO ruling
- Date: 2026-09-12
- Author: web verification lane (pre-sweep reconnaissance)
- Scope lens: PDR-0001 MVP go-live scope + PDR-0003 VAS phasing & wallet visibility
- Sources:
docs/product/user-stories.md,docs/design/flows-screens-master-map.md,backlog/,manifests/, 365 terminales-web issues, and a route-level read of all 79src/app/**/page.tsxatterminales-web@main(4361cddc).
Why this exists. The go-live sweep was deferred until MVP work lands, so that a sweep finds defects rather than absences. This document is the completeness scan that tells us how much MVP work there is. It is built from the planning artefacts and the code — not from the running UI, because a screen-derived list can only rediscover what exists; a flow specified and never built has no screen to find it by, and that absence is the point.
How to use it. Each rule-able item carries a
☐ Ruling:line. The most valuable section is §1 (triangulation) — a gap that three independent lists agree on is worth more than three separate entries. Nothing here has been built, changed, filed as a ticket, or flipped; this is reconnaissance.
Headline
Section titled “Headline”The two things PDR-0001 singles out as IN for the MVP are the two least-finished surfaces on the web.
PDR-0001 froze MVP scope to “VAS + Clientes are IN; the rest of §4.6 is deferred.” Both are unbuilt or unreachable on the web today:
- VAS / servicios financieros (W-09, Track C) — the special-categories revenue vertical the client “needs to operate their business day one … a core revenue surface, not a nice-to-have” — exists on the web only as legacy TypeORM airtime. The Appwrite POS rebuild (US-VAS-1..6) never landed. This is the single largest hole.
- Clientes / Customers (W-13, Track D) — the other explicit inclusion — exists as routes (
/admin/customers,/admin/customers/[id]) that no UI affordance reaches, and the status board still marks the flow ⚠ “not absorbed.”
No lane reported this, because each lane sees its own initiative and neither VAS nor Clientes has a standing lane. It only surfaces by triangulating the flow inventory against the route inventory against PDR-0001.
Baseline correction — the flag baseline is trustworthy
Section titled “Baseline correction — the flag baseline is trustworthy”The pre-flight noted qa-design’s api container carries the tag :stage-latest rather than stage-f4d8e98. Reconciled by digest, not tag: the running terminales-qa-design-api image digest is sha256:2d24d092de7e9b191f17fc1d14e9c067328acd78304095b4c3ea3ebe89567222, which is byte-identical to the merchants-lane record for f4d8e98 and to the terminales-stage-api (stage-f4d8e98) container. The deploy took; the tag is simply named latest locally (the deploy recipe retags stage-<sha> → stage-latest). No discrepancy — the api plane is current main.
§1 — Triangulated findings (the crown jewels)
Section titled “§1 — Triangulated findings (the crown jewels)”Each of these is asserted by two or three independent inventories (Flow / Route / Flag). Ranked by MVP weight.
T1 — VAS web vertical: IN scope, essentially unbuilt on web
Section titled “T1 — VAS web vertical: IN scope, essentially unbuilt on web”- Flow: US-VAS-1..6 all ⚠ “Built”; screens W41–W46 fully designed; drafts still unbuilt in
backlog/drafts/; US-VAS-4 has no e2e and no Figma frame. - PDR-0001: explicitly IN (Track C), MVP = Phase-1 connectivity only per PDR-0003 (single shared VB webService user; Phases 2/3, bank-statement deposit validation, and wallet payout are post-MVP).
- Consequence: the primary MVP revenue surface has no web build. Every downstream story that rides it (US-BAL-6 wallet-funds gate on VAS, US-VAS-6 reporting) is blocked behind it.
- ☐ Ruling: confirm VAS-web (Phase-1) is the top MVP build item and assign a lane.
T2 — Clientes: routes exist, no one can reach them, board says “not absorbed”
Section titled “T2 — Clientes: routes exist, no one can reach them, board says “not absorbed””- Route:
/admin/customershas 0 external inbound links (grep ofhref=/router.push/redirect/<Link/*_ROUTE); reachable only circularly from its own detail/form./admin/customers/[id]is linked byCustomerList, but the list itself is orphaned. No drawer entry (gap #45). - Flow: board marks US-CLI-1/2 ⚠ and the W-13 v2 screens “propuesta v2 no absorbida” — yet the routes ship. Code and board disagree.
- Flag: web
.env.localsetsCUSTOMERS_WEB=truebut that flag is referenced nowhere insrc/(dead); the api plane carriesCUSTOMERS_MODULE=true. Three planes disagree on whether Clientes is on. - PDR-0001: explicitly IN (Track D); wallet/fiado parked,
customerrole reserved. - ☐ Ruling: Clientes is IN — decide the direction: wire the drawer entry + reconcile the board, and remove the dead
CUSTOMERS_WEBflag.
T3 — /admin/wallet: built and live-verified, but unreachable by its owner
Section titled “T3 — /admin/wallet: built and live-verified, but unreachable by its owner”- Route:
/admin/wallethas 0 inbound links (only mockSession/playground comments). An owner cannot reach their own wallet through the UI. - Flow: US-BAL-1 (wallet balance + movements, owners only, W47) is SHIPPED and live-green (2026-09-09) — the surface exists and even passes live tests via
goto. - PDR-0003: wallet visibility is named in scope.
- Consequence: a shipped, in-scope, live-verified surface with no door. Classic door-vs-room.
- ☐ Ruling: wire a wallet entry point (owner-facing), or confirm wallet visibility is deferred.
T4 — /admin/jobs (Procesos): a drawer door into a room no test enters, and no story describes
Section titled “T4 — /admin/jobs (Procesos): a drawer door into a room no test enters, and no story describes”- Route: Procesos is a real drawer item, but both live nav sweeps share a 15-item list that omits it (along with Reportes, Liquidaciones, Conexiones, Verificación — those four each have their own live spec; Procesos has none). No e2e enters
/admin/jobsthe way a user does. - Flow: the DBOS jobs console is SHIPPED (per async-job primitives) but has no W-screen and no US-xxx story.
- PO ruling (2026-09-12): file it — a drawer item with no e2e entry and no story is exactly the door-vs-room gap. Captured here as a test-coverage + catalog defect.
- ☐ Ruling: add a nav-sweep entry for Procesos (+ the other 4 omitted items) and back-fill a story, or descope the console from MVP.
T5 — Liquidaciones / settlements: fully built, zero user-story rows (reverse-orphan)
Section titled “T5 — Liquidaciones / settlements: fully built, zero user-story rows (reverse-orphan)”- Flow: the entire W-15 finance flow is designed (
flow-15-liquidaciones.md), ticketed (9-itemsettlements.tsv), and built (real owner-gated/admin/settlements,/[reportId],/ops,/requests/[id], #557) — yetuser-stories.mdhas no US-xxx rows for it at all. - Route: all settlements routes resolve and are correctly owner/platform-gated.
- Consequence: the authoritative completeness board undercounts what exists — the opposite failure mode from VAS/Clientes, and evidence the board can’t be trusted as the sole denominator.
- ☐ Ruling: back-fill US-SETTLE rows so the board reflects built reality.
T6 — /admin/giftcards/list: shipped dark, mock-only, no story, contradicts flag-minimalism
Section titled “T6 — /admin/giftcards/list: shipped dark, mock-only, no story, contradicts flag-minimalism”- Route: 0 inbound links.
- Flag:
GIFTCARDS_ADMIN_LIST_WEBdefault OFF →notFound(); the component is explicitly “mock-first,” not wired to real data. - Flow: no W-screen, no US story. (Distinct from
/admin/giftcards, the gift-PIN sale screen, which belongs to VAS US-VAS-2.) - Consequence: a dark, mock-only, story-less screen violates the pre-prod “build only the final path” rule.
- ☐ Ruling: remove the dark screen, or fold it into the VAS gift-PIN scope with a real data seam.
§2 — MVP-gating gaps (PDR-0001 IN, not done)
Section titled “§2 — MVP-gating gaps (PDR-0001 IN, not done)”| # | Gap | PDR-0001 status | State today | What it would take |
|---|---|---|---|---|
| G1 | VAS web vertical (US-VAS-1..6, W41–W46) | IN (Track C, Phase-1) | Legacy TypeORM only; Appwrite rebuild absent; US-VAS-4 no e2e/frame | Full web build of airtime/gift-PIN/service-pay/SIM-chip/deposits-transfers/VAS-reporting on the Appwrite POS model; live behind the VB Phase-1 shared user |
| G2 | Clientes directory + nav (US-CLI-1/2, W-13) | IN (Track D) | Routes exist, unreachable; board drift; dead flag | Drawer entry + board reconcile + dead-flag removal; sale-linkage (sales_orders.customerId) verify |
| G3 | Wallet visibility entry point (US-BAL-1) | IN (PDR-0003 wallet visibility) | Built + live, no UI door | Owner-facing entry point to /admin/wallet |
| G4 | Customer deposits & transfers (US-VAS-5, W44–W46) | IN (VAS financial domains) | ⚠ web rebuild; /admin/transfers unreachable |
Web build + wiring; note deposit bank-statement validation is post-MVP (PDR-0003) |
☐ Ruling (per row): confirm IN / adjust phase / assign lane.
§3 — Flows needing a scope call (not clearly IN or OUT)
Section titled “§3 — Flows needing a scope call (not clearly IN or OUT)”These are specified-but-not-built or materially partial, but PDR-0001 §4.6 does not name them directly. Each needs an explicit PO call.
| # | Flow | State | Proposed reading | Ruling |
|---|---|---|---|---|
| S1 | US-POS-9 offline-blocked sale banner (gap #10, state E6) | Specified, not built | IN per the PO online-only policy (block-all-offline is MVP; offline queue is post-MVP) — build the blocking banner | ☐ |
| S2 | US-STAFF-2 real invitation delivery (/invitacion + POST /pos/invites/accept) |
Emailed-accept path unreachable (server auto-confirms, no email); only auto-join works | Blocked on open decision Q-onboarding-3 — needs the decision before build | ☐ |
| S3 | US-CONX-4 Connections top-up (gap #50) | Ledger-only: records a movement + comprobante, no real funds move | Spec frames it as real top-up; may be acceptable as ledger-only for MVP given VAS Phase-1 — money behaviour, PO call | ☐ |
| S4 | US-VOID-2 card void has no POST …/void HTTP route |
Verified only at the service boundary (#260) | Void is core POS; likely IN — needs the HTTP route + UI leg | ☐ |
| S5 | US-POS-2 line/order discount + supervisor approval (W13) | Deliberately hidden (CartPanel discountsEnabled=false, “pending final design”) |
Blocked on final design, not an accidental gap — confirm MVP or defer | ☐ |
| S6 | US-NOTIF-1 notifications centre + deep-links (W08/W63) | CORE plumbing (#116) only; nav + deep-link gaps, not design-aligned | Shell feature; confirm MVP polish vs defer | ☐ |
| S7 | US-SHIFT-3/4 close variance preview (gap #18) | Denomination close works within tolerance; variance-preview basis mismatches the API snapshot | Blocked on the expected-cash formula PO decision (gap #18) | ☐ |
| S8 | US-BAL-6/7 wallet-funds gate on VAS + serialize engine | Not built; US-BAL-6 rides unbuilt VAS; US-BAL-7 is engine/integrity | Follows the VAS ruling (G1) | ☐ |
§4 — Route findings (dispositions)
Section titled “§4 — Route findings (dispositions)”Full 79-route table and grep methodology retained in the lane’s working notes; the actionable findings:
A — Built + UNREACHABLE (no UI entry; 12). Each verified by zero inbound links. Disposition proposed:
/admin/customers→ MVP-gating (T2/G2): wire nav./admin/wallet→ MVP-gating (T3/G3): wire owner entry./admin/transfers→ MVP (G4, VAS financial): wire nav (flagfinancial_services_web)./admin/pos/held-orders→ defect: unreachable and US-POS-8 “Guardar en espera” silently no-ops live (no POST, cart not cleared). Held orders are W64 (in Wave 2). Wire nav + fix the save./admin/giftcards/list→ remove-dark or fold into VAS (T6)./admin/recent-activities→ see §5 (dev-stub defect); feature not in PDR MVP./admin/tickets→ reconcile/defer: global ticket search, no story, possibly duplicates Ventas./admin/pos/payment-intents→ internal/defer: async-job observability, no story./admin/pos/stock-movements/receive→ remove or wire: orphaned; real receive/transfer/adjust run as dialogs (also finding D)./admin/pos/shifts/close,/terminal/shift/cash-movement→ by-design (rendered as dialogs, not routes); the standalone routes are dead — remove for hygiene./admin/playground→ dev tool, exclude.
B — URL-only by design (not defects): /admin/settlements/ops (platform-admin console), /admin/users (legacy, superseded — candidate for removal), the terminal FAB dialogs (/terminal/voids, /terminal/shift/close, /terminal/shift/cash-movement), /terminal/(locked)/lock (overlay is primary), email-link routes (/verify-email, /reset-password, /login/callback, /invitacion), dev harnesses.
C — Broken drawer targets: none. All 19 drawer + 2 footer + top-bar hrefs resolve; the adminNav.ts invariant holds. (Only ventasNav.ts names a non-shipping /admin/pos/sales-orders/1042/void in a playground intent-map — not user-facing.)
D — Parent never links child: /admin/pos/stock-movements/receive (hard); /admin/pos/shifts/[id] linked only for CLOSED rows (by design).
☐ Ruling: approve the A-row dispositions (wire / defer / remove) as a batch or per-row.
§5 — Flag baseline + PO rulings
Section titled “§5 — Flag baseline + PO rulings”Planes (verified in code). Web = per-launch client bundle (no deployed qa-design web); useFeatureFlag resolves only 20 enumerated flags, default OFF. API plane A = container env (13 FEATURE_FLAG_*, a different namespace; all =true except DEMO). API plane B = feature_flags table = 0 rows (empty → api flags come from container env only). Web .env.local carries 4 dead flags referenced nowhere in src/ (CUSTOMERS_WEB, ADMIN_DASHBOARD_LIVE_DATA, AUTH_SPLIT_LOGIN, OBSERVABILITY_SENTRY_WEB) — hygiene.
Web flags OFF (4) — what’s behind each:
| Flag | Behind it | Status | PO ruling |
|---|---|---|---|
RECENT_ACTIVITIES_WEB (#549) |
Recent-activity feed; ON path built | OFF renders a dev stub — literally “Hola Mundo / Aqui va el contenido de actividades recientes” | DEFECT (2026-09-12): file it — a placeholder that can reach a merchant is shippable-by-accident. Replace the OFF fallback with a real empty/hidden state regardless of the feature decision. |
REFUNDS_BEYOND_SAME_DAY |
The refund engine (#443), a money path shipped ungated | ON is byte-identical to shipped code | NOTE for Alfredo (2026-09-12): a flag whose ON state equals shipped code gates nothing — it only creates the illusion of a switch. Money path; state it plainly and decide to remove the flag or make it actually gate. |
GIFTCARDS_ADMIN_LIST_WEB (#839) |
Gift-cards admin list | Mock-only; OFF → 404 | See T6. |
STORAGE_CANONICAL_BUCKETS |
Storage-bucket canonical cutover (migration flag) | Cutover state unverified | UNKNOWN — ask the storage/infra lane. |
API flag OFF: DEMO=false — demo/seed mode, finished, intentionally off on a real realm.
§6 — Correctly deferred (PDR-0001 OUT) — confirmed, no action
Section titled “§6 — Correctly deferred (PDR-0001 OUT) — confirmed, no action”For completeness, so these are not re-litigated: QR tenders CoDi/SPEI + CFDI receipt QR (BG-9); cobro-rápido + UX v2 absorptions (quick-charge modal, quick-actions FAB, PIN-profile selector, low-stock KPI filters); POS interaction modes (tap-to-add / long-press / fullscreen); Dashboard KPI drill-down (W07). Also post-MVP per PDR-0003: US-BAL-8 wallet withdrawal/payout (seam only, locked #438), VAS Phases 2/3, and deposit bank-statement-import validation.
§7 — Verification-only gaps (built, no live evidence — not build work)
Section titled “§7 — Verification-only gaps (built, no live evidence — not build work)”Flagged so “no live spec” is not mistaken for “not built”: all verification UX (US-VER-1..4, mock-only) and all Connections (US-CONX-1..5, “no live spec, P1 in progress”); several Reports legs (US-REP-2..6) open live but await a re-run or a data dependency (line_items.unitCost, AngelPay commission flow). These belong to the eventual sweep, not the MVP build backlog.
Next step
Section titled “Next step”PO rules the ☐ items above. The go-live sweep — the one this lane was built for — runs after the MVP build items (§1–§3) land, so that it finds defects rather than absences.