Skip to content

PRD: Playwright DEV-Web commerce parity #41

Description

@NanFengCheong

Playwright DEV-Web Commerce Parity PRD

Problem Statement

Nexpo does not yet have one reliable, executable definition of commerce parity across catalog discovery, product selection, cart, checkout, payment, confirmation, and fulfillment. Product-type vocabularies differ across application and API boundaries, category and product registries overlap, product policy flags are not consistently enforced in customer flows, and important cart and payment rules remain distributed across legacy code.

This creates two customer risks. A migrated page can look complete while producing different eligibility, pricing, payload, recovery, or fulfillment outcomes, and a parity check can report green after proving only a URL or heading. Unknown or unregistered product types can also disappear from mixed carts or confirmations instead of failing visibly.

The team needs a commerce authority and evidence model that preserve customer-visible behavior without copying legacy architecture. Host contracts remain server truth, DEV Web remains the deployed behavior comparator, and legacy frontend code remains evidence for hidden compatibility rules. Conflicts between those sources must be classified instead of silently copied.

This PRD is a planning contract. It does not claim runtime parity. Runtime proof starts with Phase 0 safe-baseline evidence and remains incomplete until each applicable implementation and verification gate passes.

Solution

Establish one canonical product-type registry as the runtime authority for product-specific commerce behavior. Keep category descriptors limited to discovery and presentation. Separate pure commerce policy from presentation binding, keep web routes thin, keep Host version changes behind API ports, and render migrated pages through HeroUI-backed adapters.

Deliver parity through customer-journey slices:

  • P0 covers catalog discovery, product selection, cart, personal details, payment details, gateway handoff contract, payment status, and booking confirmation for every registered product family plus a mixed cart.
  • P1 deepens rewards, redemption, add-ons, repay, booking history and detail, document delivery, fulfillment, and entitlement scenarios. P1 data must survive every P0 transformation even when its UI is deferred.
  • Unknown and unregistered product types remain visible, recoverable, and fail closed. They never disappear from cart, totals, payment breakdown, confirmation, or diagnostics.
  • Runtime comparison uses deterministic local fixtures, sanitized Host-shaped contracts, read-only DEV Web checks, and separately approved cart and payment sandbox modes.
  • Evidence proves customer outcomes, request semantics, totals, state transitions, recovery, and safety. A blocked or deferred check never counts as passed.

User Stories

  1. As a customer, I want a supported locale to resolve the correct booking entry, so that language and route state remain consistent.
  2. As a customer, I want malformed, missing, uppercase, and unsupported locale inputs handled predictably, so that direct links do not strand me.
  3. As a guest, I want to browse without signing in, so that authentication is requested only when a protected action requires it.
  4. As a guest, I want device and guest-session identity recovered safely, so that refreshes and duplicate tabs do not create inconsistent carts.
  5. As a member, I want to return to an allowlisted local path after login, so that my booking context is preserved without an open redirect.
  6. As an app customer, I want hosted booking handoff to use opaque one-time state, so that raw sessions and tokens never enter URLs.
  7. As a security owner, I want reused, expired, malformed, or wrong-product handoff state rejected, so that app-to-web entry fails closed.
  8. As a shopper, I want parent categories to show child choices and leaf categories to show products, so that discovery remains understandable.
  9. As a shopper, I want legacy category aliases to resolve to one canonical category while preserving allowed query parameters, so that saved links continue to work.
  10. As a shopper, I want discovery filters to alter the actual product request, so that dates, currency, rooms, promotions, variants, and paging are meaningful.
  11. As a shopper, I want malformed category trees and empty leaves shown as explicit states, so that broken CMS data does not look like a blank page.
  12. As a shopper, I want every catalog result resolved through its product-type adapter, so that mixed product listings preserve product-specific metadata.
  13. As a shopper, I want missing or unknown product types shown explicitly, so that unsupported products never silently disappear.
  14. As a theme-park customer, I want eligible ticket and combo products to expose valid date and selection actions, so that I cannot select closed or sold-out inventory.
  15. As a cable-car customer, I want one-way, return, operating-hour, and timeslot constraints enforced, so that my selection remains bookable.
  16. As a ticket customer, I want nationality and customer eligibility enforced before cart mutation, so that invalid selections do not reach checkout.
  17. As a customer, I want quantity limited by the strictest remaining customer, promotion, variant, inventory, entitlement, backend, and existing-cart limit, so that accepted quantities remain valid.
  18. As a customer, I want blacklist or eligibility service failure shown as a recoverable guard, so that stale or failed checks never permit mutation.
  19. As a hotel customer, I want search results to reflect stay dates, occupancy, currency, price-from, and availability, so that listed rooms match my request.
  20. As a hotel customer, I want room, rate, allotment, stay dates, and cart instance kept as one selection identity, so that distinct stays never merge.
  21. As a hotel customer, I want stale rates and partial availability rejected before mutation, so that checkout does not accept an invalid room.
  22. As a GP or complimentary hotel customer, I want overlapping allocations and stricter promotional limits reconciled, so that entitlement usage remains correct.
  23. As a multi-room customer, I want each room and rate represented independently, so that totals and confirmation lines remain conserved.
  24. As a show customer, I want eligible shows retained after Host and CMS transformation, so that missing optional presentation data does not hide a valid product.
  25. As a show customer, I want session, pass, seat, slot date, and signed price identity preserved, so that two performances never merge.
  26. As a show customer, I want an expired selection to block checkout with a recovery path, so that timer drift or refresh cannot submit stale inventory.
  27. As a show customer, I want duplicate clicks and retries to produce one cart mutation, so that reservation and totals remain stable.
  28. As a dining customer, I want voucher validity, venue, redemption date, price, and fulfillment state shown, so that I understand what can be redeemed.
  29. As a promotion customer, I want bundle constituents, quota, eligibility, and allocated price preserved, so that unsupported constituents remain visible.
  30. As a pass customer, I want validity, activation, expiry, entitlement, and fulfillment rules enforced, so that the pass behaves consistently through confirmation.
  31. As an add-on customer, I want compatibility with a primary item enforced and removal cascaded safely, so that orphan add-ons cannot survive.
  32. As an EPOS customer, I want missing, encoded, unknown, or mismatched product-type entry rejected explicitly, so that dynamic routes fail closed.
  33. As a shopper, I want an intentional empty cart distinguished from expired, restored, and guarded states, so that I know how to recover.
  34. As a shopper, I want cart operations sent through the correct current or compatibility adapter, so that Host payload semantics remain valid.
  35. As a shopper, I want duplicate clicks, timeouts, and retries to result in one cart mutation, so that quantity and reservation state do not drift.
  36. As a mixed-cart customer, I want every line rendered and counted exactly once, so that subtotal and payment breakdown remain complete.
  37. As a mixed-cart customer, I want unsupported product lines shown with a recovery action, so that they never vanish from totals or confirmation.
  38. As a shopper, I want expiry based on the earliest selected item only, so that unselected inventory cannot incorrectly expire my cart.
  39. As a returning shopper, I want cart recovery after refresh, browser back, session change, or pending payment, so that valid work is not lost.
  40. As a checkout customer, I want required name, contact, country, and guest fields validated, so that Host receives usable customer details.
  41. As a checkout customer, I want terms and consent enforced before progression, so that a booking cannot proceed without required agreement.
  42. As an invoice customer, I want individual, company, tax, and address rules applied only when relevant, so that optional invoice data does not block unrelated customers.
  43. As a returning checkout customer, I want summary and cart identity preserved through refresh and browser navigation, so that payment starts from the same cart.
  44. As a privacy owner, I want customer-specific checkout state excluded from public SSR and shared caches, so that private data cannot cross requests.
  45. As a payer, I want available payment methods to reflect payable cash, GP usage, and product compatibility, so that I see only valid options.
  46. As a payer, I want subtotal, discounts, GP, fees, payable cash, and total reconciled, so that the amount sent to the gateway matches the order.
  47. As a payer, I want signed prices and rounding rules applied consistently, so that stale display conversion cannot become payment truth.
  48. As a payer, I want one checkout request and one gateway handoff despite duplicate clicks or timeout, so that duplicate orders are not created.
  49. As a payer, I want handoff limited to approved gateway hosts and fields, so that payment data cannot be redirected or exposed.
  50. As a payer, I want zero-cash, full-GP, partial-GP, and insufficient-GP outcomes handled explicitly, so that each payment path is understandable.
  51. As a payer, I want a pending payment state with safe polling and refresh, so that I do not accidentally resubmit.
  52. As a payer, I want a failed payment state with a valid recovery path, so that retry does not create a duplicate order.
  53. As a payment owner, I want callback signature, reference, amount, status, and replay behavior verified, so that tampered or repeated callbacks fail safely.
  54. As a returning customer, I want repay offered only when the backend and selected method permit it, so that stale bookings cannot be charged again.
  55. As a customer, I want successful payment to transition to the correct confirmation state, so that payment and order status reconcile.
  56. As a customer, I want confirmation to render every purchased product line exactly once, so that mixed orders remain complete.
  57. As a customer, I want non-customer operational rows excluded from confirmation, so that internal special-request data is not presented as a purchase.
  58. As a returning customer, I want booking detail and history to handle pending, confirmed, cancelled, refunded, empty, expired-session, and paged states, so that prior bookings remain understandable.
  59. As a customer, I want document, QR, voucher, ticket, seat, and delivery actions shown only when supported and ready, so that unavailable fulfillment cannot be invoked.
  60. As a keyboard user, I want complete checkout, payment, and recovery flows with visible focus, labels, associated errors, and announced status changes, so that booking is accessible.
  61. As a mobile or narrow-screen customer, I want the same P0 outcomes as desktop, so that layout changes do not remove required actions.
  62. As a customer on a slow or failing connection, I want aborts and HTTP failures classified with recovery, so that retries do not duplicate mutations.
  63. As a privacy owner, I want customer, cart, order, booking, pricing, and payment data redacted from URLs, logs, screenshots, traces, and committed fixtures, so that evidence collection is safe.
  64. As a QA engineer, I want each route family to prove loading, empty, error, success, guarded, and primary-action states, so that visual completeness cannot false-green.
  65. As a QA engineer, I want parity classified as pass, behavior gap, data difference, blocked, deferred, or oracle conflict, so that missing proof is never reported as success.
  66. As a QA engineer, I want route, status, landmark, state, CTA, normalized outcome, request contract, and console safety asserted, so that a heading or URL cannot stand in for parity.
  67. As a commerce engineer, I want one exhaustive product taxonomy and explicit wire mappings, so that catalog, cart, API, and UI select the same behavior.
  68. As a commerce engineer, I want pure product policy separated from UI binding, so that selection, pricing, cart, checkout, payment, and confirmation can be tested without a browser.
  69. As a category author, I want category configuration limited to discovery and presentation, so that category identifiers cannot become a second commerce authority.
  70. As a product-policy author, I want registration completeness and conflict checks, so that missing or duplicated product types fail during verification.
  71. As an API engineer, I want Host version and wire differences isolated behind validated adapters, so that frontend code does not depend on endpoint churn.
  72. As a release owner, I want mutation modes disabled by default and separately approved, so that DEV Web comparison cannot alter carts, payments, redemptions, or entitlements accidentally.
  73. As a release owner, I want a clean source freeze and deployment fingerprint before comparison, so that evidence remains attributable.
  74. As a product owner, I want oracle conflicts reviewed explicitly, so that a deployed defect is not copied merely because DEV Web exhibits it.

Implementation Decisions

  • Parity means equivalent eligibility, state transitions, calculations, request semantics, recovery, and customer outcome. It does not mean pixel-perfect copying.
  • Host contracts are server truth. DEV Web is the deployed customer-behavior comparator. Legacy frontend code is evidence for hidden rules and compatibility behavior.
  • An oracle conflict must be classified and assigned for product review. Payment, inventory, security, and entitlement conflicts must not be copied without approval.
  • One canonical product taxonomy is shared across commerce orchestration. Host literals and legacy aliases are explicit boundary mappings, not additional domain taxonomies.
  • The product-type registry is the runtime authority for product transformation, presentation selection, availability, selection identity, limits, pricing, cart transport, expiry, checkout, payment, confirmation, fulfillment, and analytics projection.
  • Product policy remains pure and independent of React, navigation, frontend stores, query clients, browser globals, and Host endpoint versions.
  • Presentation adapters bind pure product policy to product cards, detail, selection, cart, payment, confirmation, booking detail, and fulfillment UI.
  • Category descriptors own canonical category handles, aliases, discovery-tree structure, navigation, CMS overlays, filters, listing composition, route generation, and query forwarding.
  • Category descriptors never own product selection, availability, limits, cart identity, payloads, pricing, checkout, payment, confirmation, or fulfillment behavior.
  • Product definitions are the canonical source for category compatibility. Category listing membership is derived or validated from that source so two editable relationships cannot drift.
  • Route shells remain thin and own only locale, query, loader, redirect, and web-only binding concerns.
  • Migrated route families render through HeroUI-backed adapters and expose loading, empty, error, success, guarded, and primary-action states.
  • API ports validate Host-shaped data and isolate current and supported compatibility versions.
  • Server commerce state remains backend authoritative. Client state may hold selections, drafts, projections, and process state but never becomes payment, order, redemption, or entitlement truth.
  • Every registered product family must complete P0 depth; listing-only support is incomplete.
  • The initial coverage baseline includes theme park, EPOS combo, EPOS event, TTS show, voucher, hotel, hotel room, pass, and explicit unknown handling.
  • Bundle and TravelPass remain explicit unsupported states until complete policy definitions are approved and registered. They cannot be counted as P0 while unregistered.
  • P1 capabilities may defer customer actions, but their data must survive P0 transformations and remain visible when needed for conservation and recovery.
  • Selection identity must prevent accidental merging across sessions, timeslots, rooms, rates, allotments, stay dates, promotions, signed prices, seats, or cart instances.
  • Purchase quantity uses the strictest applicable remaining limit across customer, category, promotion, variant, inventory, entitlement, backend, and existing-cart constraints.
  • Pricing projects payable cash, GP components, promotions, signed price values, fees, conversion display, and totals without allowing display-only values to become payment truth.
  • Mixed or unsupported currency states must produce an explicit Host-compatible outcome. They must never be silently summed.
  • Cart transport uses the current Host contract plus explicit compatibility adapters only where still supported.
  • Mixed-cart and mixed-order line conservation is an invariant: each source line appears exactly once in the customer projection and contributes exactly once to totals.
  • Unknown products fail closed with visible diagnostics and a recovery path. Silent omission is prohibited.
  • Checkout validates personal, invoice, terms, consent, GP sufficiency, payment compatibility, and summary projection before handoff.
  • Payment orchestration permits one checkout request and one gateway handoff per customer action, validates approved destinations and fields, and treats replay and retry as explicit policies.
  • Local contract proof is necessary but not sufficient for live payment parity. Sandbox callback and status claims remain blocked until approved non-charge runtime evidence exists.
  • Confirmation, booking detail, and fulfillment project product-specific order data while filtering non-customer operational rows.
  • Analytics uses canonical labels but never drives commerce behavior.
  • Phase 0 is the first runtime-proof gate. It freezes sources, restores or validates the harness, proves local render and read-only DEV access, and proves unexpected commerce mutations are aborted.
  • Later runtime claims require their applicable local, contract, DEV read-only, approved mutation, payment, persona, accessibility, privacy, and recovery gates.

Testing Decisions

  • Tests assert external commerce behavior and stable contracts, not internal component structure or private store implementation.
  • Existing route, port, registry, and HeroUI render seams are preferred. New seams are added only when existing ones cannot observe a required outcome.
  • Unit tests cover exhaustive taxonomy and wire mappings, unique registration, category membership derivation, selection identity, availability, strict limits, pricing, GP, cart transport, expiry, checkout, payment, confirmation, fulfillment, and redaction.
  • Dependency-boundary tests prove pure policy does not import presentation, navigation, store, query, browser, or Host-version concerns.
  • Integration tests use sanitized Host-shaped fixtures and cover port validation, runtime registry resolution, line conservation, totals reconciliation, route-to-port-to-registry orchestration, and all HeroUI page states.
  • Every migrated route family receives unit component proof, integration route render proof with realistic providers, and Playwright browser proof.
  • Product-family Playwright coverage includes a happy path, an invalid or guarded path, and a mutation or recovery path.
  • Mixed-cart coverage includes at least one hotel, one timed ticket or show, and one voucher or pass.
  • Persona coverage includes guest, member, GP, complimentary or entitled, and returning-booking customers.
  • Responsive and accessibility coverage includes desktop, mobile, narrow layout, keyboard completion, focus, labels, associated errors, and announced status changes.
  • Resilience coverage classifies slow responses, aborts, offline state, authentication and authorization failure, not found, conflict, validation, throttling, and server errors.
  • Local-fixture mode provides deterministic mutation and exhaustive edge states.
  • Local-contract mode validates orchestration with sanitized Host-shaped responses.
  • DEV Web read-only mode permits navigation and GET, HEAD, or OPTIONS requests only.
  • Safe cart and safe payment modes remain separate, disabled by default, and require approved hosts, identities, data, cleanup ownership, and expiry windows.
  • An unexpected commerce POST, PUT, PATCH, or DELETE aborts the test unless the exact safe mode is enabled.
  • Harmless telemetry is either disabled or explicitly allowlisted; it cannot weaken commerce mutation interception.
  • Deterministic builders cover loading, empty, error, success, guarded, known and unknown product types, price and entitlement variants, inventory limits, expiry, validation, payment status, replay, tampering, confirmation, fulfillment, and repay.
  • Hard assertions include route, HTTP status, landmark, visible state, primary action, normalized outcome, exactly one product definition, line conservation, reconciled totals, enforced guards, request schema, mutation count, and absence of unexpected console errors.
  • Copy, spacing, business-neutral imagery, and non-blocking CMS ordering remain soft comparisons.
  • Evidence results are pass, behavior-gap, data-difference, blocked, deferred, or oracle-conflict. Only pass satisfies a gate.
  • Evidence records target, route, persona, product type, expected and actual outcome, source fingerprint, result, artifacts, redaction status, owner, and follow-up.
  • Screenshots, traces, logs, storage state, and fixtures are scanned for URLs, query data, cookies, authorization, device, cart, order, booking, signed pricing, gateway, and personal fields.
  • Real charges, entitlement or voucher consumption, unapproved callback replay, and production customer fixtures are not used.
  • Runtime execution is phased: safe baseline, canonical registry, local business parity, read-only DEV baseline, approved cart mutation, approved payment and callback, then cross-product and persona sweep.
  • Focused tests run before broad checks. Final verification includes dependency checks, lint, tests, Start build, and Start Playwright E2E.
  • QA must capture the initial signal, try edge cases, document reproduction steps, identify a likely root cause for failures, compare before and after evidence, and reject proof that covers only a shallow seam.

Out of Scope

  • Pixel-perfect visual cloning of DEV Web or legacy pages.
  • Bulk-copying legacy screens, stores, dependencies, API clients, or package manifests.
  • Treating route existence, a heading, a screenshot, or a source label as commerce parity.
  • Replacing Host as the backend authority for cart, checkout, payment, booking, redemption, or entitlement.
  • Replacing the payment gateway.
  • Running real charges or unapproved cart, payment, redemption, callback, or entitlement mutations.
  • Adding profile or account-hub scope that is not required for direct booking history or detail.
  • Completing P1 UI depth before its release slice is approved.
  • Registering Bundle or TravelPass without complete policy, contract, presentation, and test coverage.
  • Using category identifiers as cart, pricing, checkout, payment, confirmation, or fulfillment policy.
  • Moving payment, order, booking, redemption, or entitlement truth into browser state.
  • Shipping production cutover without canary, rollback, monitoring, and payment reconciliation.
  • Accepting mock-only auth or payment proof as final runtime evidence.
  • Changing deployed behavior solely to match a known DEV Web defect without product approval.

Further Notes

  • Tracker issue: PRD: Playwright DEV-Web commerce parity #41. The required ready-for-agent label is pending repository label-management permission.
  • This PRD narrows and extends the accepted Path B legacy-web migration and commerce-state ownership PRDs; it does not replace their hosted-booking, HeroUI, or state-authority decisions.
  • Source planning baseline: docs/testing/2026-07-10-playwright-dev-web-commerce-parity-plan.md.
  • Executable plan: docs/plans/2026-07-10-playwright-dev-web-commerce-parity-implementation-plan.md.
  • Domain terms and ownership follow CONTEXT.md: category is the presentation and discovery axis; product type is the commerce axis.
  • This PRD supersedes the older July 1 customer-journey parity plan for new parity decisions. Git history remains its archive; it is not current runtime proof.
  • Phase 0 must freeze Nexpo, legacy web, legacy app, Host source, and the deployed DEV Web fingerprint. Any Host edit must use a refreshed isolated worktree rather than a stale or dirty checkout.
  • Current repository state and all source fingerprints must be revalidated at Phase 0; planning snapshots are not runtime evidence.
  • Next step: convert this PRD into an executable implementation plan, then run it with the agent-team execution workflow.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions