# Solution Design: ecomm MVP — sign up and create a single storefront | | | | --- | --- | | **Anchor** | [wiggleverse/wiggleverse-ecomm#1](https://git.wiggleverse.org/wiggleverse/wiggleverse-ecomm/issues/1) (`type/feature`, `priority/P1`) | | **Author(s)** | Claude (session ecomm-0020), for Ben Stull | | **Reviewers / approvers** | Ben Stull | | **Status** | `draft` | | **Version** | v0.1.0 | | **Source artifacts** | BDD corpus: [research/shopify](https://git.wiggleverse.org/wiggleverse/wiggleverse-ecomm-prototype-content/src/branch/main/research/shopify) (esp. [flows/first-run-experience.md](https://git.wiggleverse.org/wiggleverse/wiggleverse-ecomm-prototype-content/src/branch/main/research/shopify/flows/first-run-experience.md)) · Prototype: [wiggleverse-ecomm-prototype](https://git.wiggleverse.org/wiggleverse/wiggleverse-ecomm-prototype) (R01–R07) · Reference: Shopify first-run experience · Supersedes: — | **Change log** | Date | Version | Change | By | | --- | --- | --- | --- | | 2026-06-10 | v0.1.0 | Initial draft (session ecomm-0020) | Claude | --- ## 1. Business Context *The business lens — solution-agnostic throughout. No mechanism is proposed until §2.* ### 1.1 Executive Summary Independent merchants who want to sell online need a commerce platform that treats them honestly — and today Wiggleverse has nothing to offer them: no way to exist on the platform, no storefront to own, no surface to manage. This design delivers the first real value of ecomm: a person can become a merchant — establish their identity, claim their one storefront, and stand on a stable management surface — in every environment the platform runs in, from day one. The beneficiaries are the first merchants (who can finally exist on the platform) and the ecomm line itself (every subsequent capability — catalog, orders, checkout — gains a real account-and-storefront spine to attach to, instead of mocks). Just as importantly, it proves the platform can be brought to life anywhere from nothing: a fresh, empty environment reaches "first merchant, first storefront" through the product alone, with no hand-seeded data — so launching the real service is an ordinary, rehearsed act rather than a one-off ritual. ### 1.2 Background ecomm is Wiggleverse's multi-tenant, OHM-grounded alternative to Shopify — a commerce platform whose mechanics are accountable to the [OHM concept corpus](https://ohm.wiggleverse.org) (no dark patterns, data minimization, durable addressability, honest pricing). The product was explored in a first-pass prototype (`wiggleverse-ecomm-prototype`, releases R01–R07, 189 scenario-bound tests) built against a harvested corpus of ~1,238 BDD scenarios describing Shopify's behavior. That prototype is read-only prior art; `wiggleverse-ecomm` is the clean greenfield rebuild, stood up 2026-06-10 with its architecture intentionally undecided until this design. The strategic moment: the rebuild has repos, an issue tracker, and a charter — but no app code and no users. Everything in the product line queues behind one question: *how does a person come to exist on the platform and own a storefront?* Until that is answered for real — against real persistence, in real environments — no other capability can ship honestly. ### 1.3 Business Actors / Roles | Role | Responsible for (in the business) | | --- | --- | | **Merchant** | A person running a (small) commerce business who wants to sell under their own storefront: they decide to join a platform, establish their business presence on it, and manage that presence over time. | | **Platform operator** | The person/org running the ecomm service itself: stands up and maintains the environments the platform runs in, and is accountable for the service being available to merchants. | | **ecomm team** | Builds the platform; needs each capability to rest on real, shippable foundations rather than mocks. | (Shoppers — the merchants' customers — are deliberately *not* an actor in this design; nothing here is shopper-facing yet. See §1.7.) ### 1.4 Problem Statement A person cannot become a merchant on ecomm. There is no way to establish an identity on the platform, no way to claim a storefront, and no surface from which to manage one — the platform has no users because it cannot have users. And even if it could, there is today no defined path from "fresh, empty environment" to "working platform serving its first merchant": local development, pre-production, and production bring-up are all undefined, so nothing built on top can ever actually launch. ### 1.5 Pain Points | # | Pain | Who feels it | Cost / frequency today | | --- | --- | --- | --- | | PP-1 | No way to establish an identity on the platform — a would-be merchant simply cannot exist on ecomm. | Merchant | Total exclusion; blocks every merchant, always. | | PP-2 | No way to own a storefront or reach a management surface — even an identity would have nothing to hold. | Merchant | Total; there is no unit of "my business" on the platform. | | PP-3 | Every subsequent capability (catalog, orders, checkout, policies) has nowhere real to attach — work would have to build on mocks and be redone. | ecomm team | Blocks the entire product line; rework risk on everything shipped against fakes. | | PP-4 | No defined, repeatable path from an empty environment to a working platform — bring-up of dev, pre-prod, and prod is folklore. | Platform operator | The service cannot launch anywhere; every environment question is open-ended toil. | ### 1.6 Targeted Business Outcomes | Outcome | Success metric | Baseline → Target | Guardrail (must not regress) | How / when measured | | --- | --- | --- | --- | --- | | Merchants can exist on the platform | A real person can go from stranger to merchant-with-storefront, unaided | impossible → possible in every environment | No dark patterns at the door: no trial clock, no payment demand, no plan wall (OHM: Agency & Anti-Manipulation) | Walk the flow end-to-end in each environment at release | | The product line is unblocked | Subsequent Features attach to a real account + storefront spine | everything mocked → nothing mocked | The spine doesn't need rework to host the next Feature (no throwaway auth/tenancy) | First post-MVP Feature builds on it without spine changes | | Launching anywhere is routine | A fresh, empty environment reaches "first merchant signs up and creates the first storefront" through the product flows alone | folklore → documented, rehearsed gesture | Zero hand-seeded data in any environment | Bootstrap rehearsal executed on pre-prod, then prod, at release | ### 1.7 Scope (business) - **In scope:** a person establishing an identity on the platform (joining and returning); a merchant establishing their one storefront; the merchant standing on a stable management surface for it; the platform being bring-up-able from empty in every environment it runs in (local development, pre-production, production). - **Out of scope (later designs):** anything sold or bought — catalog, orders, checkout, payments; the shopper-facing public storefront; teams/staff (anyone besides the one merchant touching the storefront); plans, billing, or any commercial relationship between merchant and platform; storefront settings/management beyond simply *having* the surface. - **Non-goals:** gating who may join (the prototype admitted users by invitation; ecomm is an open product — anyone may become a merchant, and re-introducing an admission gate is explicitly not pursued); supporting more than one storefront per merchant *in this design* (the business wants the door left open, not the capability now). ### 1.8 Assumptions · Constraints · Dependencies - **Assumptions:** - One storefront per merchant is the right *experience* bar for the MVP; multiple-storefront merchants are a future need, not a current one. Risk if wrong: an early merchant genuinely needs a second storefront — accepted; the door is designed to stay open (§1.7, #1 acceptance). - Merchants are reachable by email and will complete an email-based step to join. Risk if wrong: an entry channel rethink — accepted at MVP scale. - **Constraints:** - OHM-groundedness is a charter constraint: entry must be honest — no manufactured urgency, no data collected beyond need, no lock-in mechanics. - The three environments are a constraint, not a stretch goal: Feature #1's acceptance says no environment is "later". - Wiggleverse engineering standards apply (handbook): provisioning and deploy via flotilla (§8), secrets never in repos or transcripts (§6.3), private repos by default (§5.5). - **Dependencies:** - **flotilla / launch-app** for environment provisioning and deploy (the operator-run front door, handbook §8.5) — needed when the pre-prod and prod environments are stood up. - An **email delivery channel** for the entry step in real environments — needed by the production-readiness slice (§7). ### 1.9 Business Use Cases **BUC-1 — As a merchant, I can establish my identity on the platform, so that I can begin doing business there.** ```gherkin Scenario: BUC-1 — A stranger becomes known to the platform Given a person who has never dealt with the platform When they present themselves and prove who they are Then the platform knows them as a returning individual from then on And they were asked for nothing beyond what proving who they are required ``` ```gherkin Scenario: BUC-1a — A person who cannot prove who they are is not admitted as someone else Given a person presenting an identity they cannot prove When their proof fails Then the platform does not mistake them for the claimed identity And the genuine owner of that identity is unharmed ``` - **BUC-1 acceptance criteria:** the same person, returning later, is recognized as the same merchant; two different people are never conflated; no personal data beyond the minimum was demanded (OHM: Privacy & Data Minimization). **BUC-2 — As a returning merchant, I can resume my standing on the platform, so that my business presence persists beyond a single visit.** ```gherkin Scenario: BUC-2 — A known merchant returns Given a merchant the platform already knows When they return and prove who they are Then they resume exactly the standing they had — same identity, same storefront ``` - **BUC-2 acceptance criteria:** returning costs only the proof-of-identity step; nothing about their standing is lost or reset. **BUC-3 — As a merchant, I can establish my storefront, so that my business has a presence of its own on the platform.** ```gherkin Scenario: BUC-3 — A merchant claims their storefront Given a merchant known to the platform who has no storefront yet When they establish their storefront Then the platform holds exactly one storefront that is theirs And establishing it cost them nothing and committed them to nothing ``` ```gherkin Scenario: BUC-3a — One storefront is the bar Given a merchant who already has their storefront When they deal with the platform Then they are never led toward acquiring a second one ``` - **BUC-3 acceptance criteria:** every merchant who completes the step has exactly one storefront; no payment, plan, or commitment was demanded (corpus: 14.01.0015, 14.01.0016); the platform's books would still be correct if a merchant were someday allowed several (the 1-1 bar is an experience rule, not a law of the data — Feature #1 constraint). **BUC-4 — As a merchant, I can stand on a management surface for my storefront, so that everything I will later do for my business has a home.** ```gherkin Scenario: BUC-4 — The merchant arrives at their storefront's home Given a merchant with a storefront When they return to the platform Then they arrive directly at their storefront's management surface And it is honestly empty — it claims no capability that does not exist yet ``` - **BUC-4 acceptance criteria:** arrival is direct (no dead ends, no limbo — a merchant *without* a storefront is guided to establish one instead, never stranded); the surface carries the storefront's identity and nothing fabricated. **BUC-5 — As a platform operator, I can bring the platform to life in a fresh environment, so that merchants can be served there without any hand-built state.** ```gherkin Scenario: BUC-5 — An empty environment reaches its first merchant Given a brand-new environment with empty persistence When the operator performs the documented bring-up gesture And the first merchant walks the ordinary product flows Then the environment serves that merchant a working storefront And no data was placed by hand at any step ``` ```gherkin Scenario: BUC-5a — Bring-up is rehearsable Given the bring-up gesture documented for one environment When it is performed in the next environment (dev → pre-prod → prod) Then it is the same gesture, and day-one production is simply the bootstrap state ``` - **BUC-5 acceptance criteria:** bring-up is a repeatable, documented gesture (not folklore); it holds identically in local development, pre-production, and production; the first merchant arrives through the product flows alone. --- ## 2. Solution Proposal **Build the first vertical slice of the ecomm web application** — a multi-tenant modular monolith that carries forward the prototype's *proven* shape and inverts its *disqualifying* assumption: - **Carry forward** (validated by R01–R07 of the prototype): a four-layer Python backend (`entrypoint → api → domains → platform`) with the layer contract mechanically enforced; SQLite (WAL) with forward-only numbered migrations and no ORM; a React/Vite SPA admin talking to a screen-shaped REST BFF; scenario-bound end-to-end tests (one test per BDD scenario ID); OHM concepts cited inline next to the rules they ground. - **Invert** (the prototype's bootstrap was the wrong way round for a real product): **no seeded tenant and no admission gate.** The prototype was born with one hard-coded store and one invited owner; ecomm is born *empty*. Identity is open sign-up via **email + one-time code** (passwordless; corpus 14.01.0003–0004), the storefront is created *through the product* by its merchant, and an empty database is a first-class, fully working state — which is precisely what makes BUC-5's bootstrap story true by construction rather than by tooling. - **Defer** (YAGNI for four screens): the prototype's second API surface (GraphQL) is not built in this MVP; the layering keeps the seam so it can be added later as a projection over the same domain core. Google/Apple OAuth and sign-in-by-emailed-link (corpus 14.01.0005–0007) are deferred the same way — the email-canonical identity rule (14.01.0010) keeps them addable without account migration. **Why this approach** over the alternatives: - *Do nothing / concierge* (hand-create merchants on request): fails BUC-5 outright (every merchant is hand-seeded state) and validates nothing — the prototype already validated the need; the open question is productionizing. - *Re-architect on a heavier stack* (Postgres, separate services, SSR framework): buys scale and rendering properties the MVP measurably does not need, at the cost of slower iteration and a bigger bootstrap surface — directly against the "launching anywhere is routine" outcome (§1.6). The single-VM flotilla deployment standard (handbook §8) favors the lighter stack; revisit at real scale (§6.7, §7.4). - *Resume the prototype codebase*: it bakes in the seeded-tenant and invite-gate assumptions at the foundation, and the rebuild exists precisely to lay clean foundations. Its *patterns* are kept; its code is reference only. **Solution-specific scope:** the landing page, sign-up/log-in (email + one-time code), create-storefront (both entry points: post-sign-up and returning-without-storefront), the empty admin shell, sign-out, and the bring-up story for localhost/PPE/Prod including real email delivery in deployed environments. **Solution-specific non-goals (this release):** GraphQL surface; OAuth providers and magic-link completion; the onboarding questionnaire and POS opt-in from the corpus first-run flow (14.01.0017–0024) — the merchant lands directly on their admin; storefront rename/settings (14.01.0025 makes naming optional at creation; *changing* it later is the Settings Feature's job); account-lifecycle polish beyond what passwordless entry gives for free (password reset does not exist because passwords do not exist; the one-time code *is* email verification; account deletion is deferred and logged in §9). --- ## 3. Product Personas | Product persona | In ecomm | Maps to business role(s) | | --- | --- | --- | | **Visitor** | An unauthenticated person on the entry surfaces: landing, sign-up/log-in. | Merchant (before they are known) | | **Merchant** | An authenticated account holder; creates and owns the storefront; lands on its admin. The MVP's only authenticated user type. | Merchant | | **Operator** | Brings up and deploys environments; interacts through flotilla and the bring-up runbook, not the web UI. | Platform operator; ecomm team | ## 4. Product Use Cases Corpus grounding: scenario IDs cite the harvested Shopify first-run experience ([flows/first-run-experience.md](https://git.wiggleverse.org/wiggleverse/wiggleverse-ecomm-prototype-content/src/branch/main/research/shopify/flows/first-run-experience.md), 14.01.\*) and the zero-state dashboard (01.01.0008). Divergences are marked. **PUC-1 — Landing offers the two doors** *(realizes BUC-1, BUC-2; corpus 14.01.0001)* ```gherkin Scenario: PUC-1 — The landing page offers signing up or logging in Given a Visitor on the ecomm landing page Then they see what ecomm is, a primary action to get started, and a link to log in And no trial, pricing, or fee is presented (corpus 14.01.0011) ``` **PUC-2 — Sign up with email + one-time code** *(realizes BUC-1; corpus 14.01.0002–0004)* ```gherkin Scenario: PUC-2 — A Visitor signs up Given a Visitor who chose to get started When they enter their email address and request a code Then a one-time code is sent to that address And the screen moves to code entry, telling them where the code went When they enter the code within its validity window Then they are signed in as a new Merchant And they are taken directly into creating their storefront (PUC-4) ``` ```gherkin Scenario: PUC-2a — Wrong code Given a Visitor on the code-entry screen When they enter an incorrect code Then the screen says the code didn't match and lets them retry or request a new one And after repeated failures the code is invalidated and a fresh request is required ``` ```gherkin Scenario: PUC-2b — Expired code When they enter a code past its validity window Then the screen says the code expired and offers to send a fresh one ``` ```gherkin Scenario: PUC-2c — Resend is rate-limited When they ask for another code immediately after one was sent Then the screen asks them to wait briefly before resending ``` **PUC-3 — Log in as a returning Merchant** *(realizes BUC-2; corpus 14.01.0009, 14.01.0010)* ```gherkin Scenario: PUC-3 — A returning Merchant logs in Given a Visitor whose email already belongs to a Merchant account When they complete the same email + code flow (from either door) Then they are signed in to that same account — the email is the canonical key And no duplicate account is created (corpus 14.01.0010) ``` *(Sign-up and log-in are deliberately the same mechanism; the two doors differ only in framing. A "sign up" with a known email is a log-in; a "log in" with an unknown email creates the account. Honest copy on the code-entry screen states which happened.)* **PUC-4 — Create the storefront after sign-up** *(realizes BUC-3; corpus 14.01.0013, 14.01.0015, 14.01.0016, 14.01.0025, 14.01.0026)* ```gherkin Scenario: PUC-4 — A new Merchant creates their storefront Given a Merchant signed in with no storefront Then they are on the create-storefront screen — there is nothing else to do yet When they optionally enter a storefront name and confirm Then the storefront is created — named as entered, or with a generated default name And no payment method, plan, or commitment was requested (14.01.0015/0016) And they land on the storefront's admin page (PUC-6) ``` **PUC-5 — Returning without a storefront → prompted to create** *(realizes BUC-3, BUC-4)* ```gherkin Scenario: PUC-5 — A Merchant who never finished creating one Given a returning Merchant who has no storefront When they log in Then they arrive at the create-storefront screen (PUC-4), not an empty admin or an error ``` **PUC-6 — Returning with a storefront → straight to its admin** *(realizes BUC-4; corpus 14.01.0027)* ```gherkin Scenario: PUC-6 — A Merchant returns to their storefront Given a returning Merchant who has a storefront When they log in Then they land directly on that storefront's admin page And nothing further is mandatory (corpus 14.01.0027) ``` **PUC-7 — One storefront: no second-storefront affordance** *(realizes BUC-3a; **diverges** from corpus 14.01.0012/0014 — multi-store per account is deliberately not offered; see §9 D-2)* ```gherkin Scenario: PUC-7 — No "create another storefront" exists Given a Merchant with a storefront Then no surface offers creating another one And directly invoking storefront creation anyway is refused with an honest explanation that ecomm is one storefront per account today ``` **PUC-8 — The admin shell is honestly empty** *(realizes BUC-4; zero-state per corpus 01.01.0008)* ```gherkin Scenario: PUC-8 — Empty admin Given a Merchant on their storefront's admin page Then the page carries the storefront's name and the signed-in account's email And states plainly that there is nothing to manage yet And shows no fabricated metrics, fake modules, or teaser upsells ``` **PUC-9 — Sign out** *(product-only)* ```gherkin Scenario: PUC-9 — A Merchant signs out Given a signed-in Merchant anywhere in the admin When they sign out Then their session ends and they are back on the landing page as a Visitor ``` **PUC-10 — A developer bootstraps localhost** *(realizes BUC-5; Operator-facing — the surface is a terminal, not a screen)* ```gherkin Scenario: PUC-10 — Clean checkout to first storefront, locally Given a clean checkout of wiggleverse-ecomm and the documented prerequisites When the developer runs the documented dev bring-up command Then the app starts against an empty local store, applying schema migrations itself And the developer completes sign-up → create storefront → admin in a browser And the one-time code reaches them through the local dev channel (no real mail needed) ``` **PUC-11 — The Operator bootstraps a deployed environment (PPE, then Prod)** *(realizes BUC-5, BUC-5a)* ```gherkin Scenario: PUC-11 — Fresh deployed environment to first merchant Given an environment provisioned and deployed through flotilla, with empty persistence When the app starts, applying schema migrations itself Then the first real merchant can complete sign-up → storefront → admin through the public flows, with the code arriving by real email And the rehearsal on PPE and the day-one Prod state are the same gesture (BUC-5a) ``` ## 5. UX Layout Text is the source of truth; wireframes are generated from these descriptions on demand and not committed. One SPA, four surfaces. Visual language: calm, text-first, honest — no countdowns, no badges, no fake density. ### 5.1 Screen: Landing (serves PUC-1) - **Purpose:** a Visitor learns what ecomm is and picks a door. - **Layout (top → bottom):** - **Header:** wordmark "ecomm" (left); "Log in" link (right). - **Primary content:** one short value statement (what ecomm is, in OHM voice — honest commerce, your storefront is yours); primary button **"Create your storefront"**. - **Footer:** minimal (project identity). - **States:** happy only — static page. A signed-in Merchant hitting `/` is redirected per entry routing (§6.5, PUC-6/PUC-5). - **Notifications:** none. ### 5.2 Screen: Sign in (serves PUC-2, PUC-3; two steps) - **Purpose:** the single email + one-time-code flow behind both doors. - **Step 1 — email entry:** - **Header:** wordmark; back-to-landing link. - **Primary content:** heading framed by the door used ("Create your storefront" / "Log in" — same form); email field (label "Email"; autofocus); primary button **"Send code"**; one line of honest copy: "We'll email you a one-time code. That's all we need." - **States:** happy → step 2 · invalid email format: inline field error · sending: button busy state · resend cooldown active: button disabled with "you can resend in ~Ns" (PUC-2c) · delivery failure: banner "We couldn't send the code — try again" (no fake success; §6.9). - **Step 2 — code entry:** - **Primary content:** "We sent a code to ⟨email⟩" (with a "wrong address?" link back to step 1); code field (one-time-code input semantics); primary button **"Continue"**; secondary "Resend code" (cooldown-aware). - **States:** happy: session starts → entry routing (§6.5) · wrong code: inline "That code didn't match" with attempts remaining (PUC-2a) · expired: inline "That code expired" + resend offer (PUC-2b) · attempts exhausted: code invalidated, prompt to request a fresh one · loading: button busy. - **Honest-copy rule (PUC-3):** after verification the screen states which happened — "Welcome to ecomm" (new account) vs "Welcome back" (existing). - **Notifications:** the one-time-code **email** (subject "Your ecomm code: ⟨code⟩"; body: the code, its validity window, and "if you didn't request this, ignore it"). In localhost/dev the code reaches the developer via the dev channel instead (§6.5 PUC-10). ### 5.3 Screen: Create storefront (serves PUC-4, PUC-5, PUC-7) - **Purpose:** a storefront-less Merchant establishes their one storefront. - **Layout:** - **Header:** wordmark; signed-in email (right) with a "Sign out" action (PUC-9). - **Primary content:** heading "Create your storefront"; name field (label "Storefront name", explicitly optional — helper text "You can leave this blank; we'll pick a placeholder name you can change later"); primary button **"Create storefront"**. - **States:** happy: → admin (PUC-6) · blank name: allowed — a default name is generated (corpus 14.01.0026) · creating: button busy · already-owns (defense in depth, PUC-7): banner "Your account already has its storefront" + link to the admin · error: banner with retry. - **Notifications:** none (no confirmation email — minimal channel use). ### 5.4 Screen: Admin shell (serves PUC-8, PUC-9) - **Purpose:** the storefront's stable home; honestly empty in this release. - **Layout:** - **Header:** storefront name (left — the admin's anchor identity); signed-in email + "Sign out" (right). - **Primary content:** empty state — heading with the storefront's name, one line: "There's nothing to manage yet. Catalog, orders, and settings will appear here as ecomm grows." Nothing else: no zeroed metric tiles, no locked-feature teasers (OHM: Agency & Anti-Manipulation — no manufactured aspiration). - **States:** happy/empty are the same state by design · loading: minimal skeleton while `/me` resolves · session expired: redirect to sign-in. - **Notifications:** none. ## 6. Technical Design ### 6.1 Invariants ### 6.2 High-level architecture ### 6.3 Data model & ownership ### 6.4 Interfaces & contracts ### 6.5 Per–Product-Use-Case design ### 6.6 Non-functional requirements & cross-cutting concerns ### 6.7 Key decisions & alternatives considered ### 6.8 Testing strategy ### 6.9 Failure modes, rollback & flags ## 7. Delivery Plan ### 7.1 Approach / strategy ### 7.2 Slicing plan ### 7.3 Rollout / launch plan ### 7.4 Risks & mitigations ## 8. Traceability matrix ## 9. Open Questions & Decisions log ## 10. Glossary & References