Files
marketplaces/docs/PRODUCT-PLAN-v3.1-DELIVERY-PLAN.md
sdarbinyan 821fecf5d3
Some checks failed
Architecture Governance / architecture (push) Has been cancelled
docs: record Sprint 0.1 decisions across delivery plan, gap analysis, backend contract
User answered 8 of 9 Sprint 0.1 blocking decisions (2026-08-17); backend
ownership stays open pending a clearer re-ask. Recorded and propagated:

- Payment chain unfrozen -> BACKEND-API-REFERENCE.md §7 and the Phase 1
  contract doc's status banner both updated; Phase 1/6/7 unblocked.
- No fixed external-marketplace list -> Phase 4's connector framework
  respecified as config-driven/generic; Sprint 4.2 retired as "per named
  marketplace," replaced with a generic onboarding runbook.
- FX rate source: ours, in-house, as the default (not just a fallback) ->
  Phase 1 contract's `source` field can read "internal" as the normal case.
- VK ID before OTP -> Phase 8 sprints resequenced (VK ID now 8.2, OTP 8.3).
- Multi-seller orders: unified -> Phase 3.3, Phase 5.2, and Z16 updated to
  the resolved model (one Order, per-seller Fulfillment groups).
- "Fixed 5-second payment" claim: confirmed non-issue, PAYMENT_POLL_INTERVAL_MS
  is already 5000 (real polling cadence, not an artificial delay).
- API namespace: new endpoints only (/api/v2/...), no forced migration of
  legacy endpoints.
- Document version: v3.1 is canonical.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-17 22:08:23 +04:00

463 lines
37 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Product Plan v3.1 — Delivery Plan (Phases → Sprints → Todos)
Companion to [PRODUCT-PLAN-v3.1-GAP-ANALYSIS.md](PRODUCT-PLAN-v3.1-GAP-ANALYSIS.md). Every gap identified there is assigned here exactly once.
**No calendar dates.** The plan itself (§12) refuses invented dates and fixes *sequence + exit criteria* instead. This document does the same. Sprints are ordered units of work, not two-week promises. Sizes are relative: **S** / **M** / **L** / **XL**.
**Ownership tags:** `[FE]` this repo · `[BE]` backend/platform service · `[BOTH]` coordinated contract change · `[DEC]` decision, no code.
**Deviation from the plan's own order, and why:** the plan sequences P0-C (external ingestion) before P0-D (catalog integrity). We swap them. External order ingestion maps `externalSKU → internal offer` (§5.1), and `Offer` does not exist yet — ingestion has nothing to map onto until the Product/Offer split ships. Everything else follows the plan's ordering.
---
## Phase map
| Phase | Name | Plan ref | Gate |
|---|---|---|---|
| **0** | Unblock & seams | — | Decisions answered; every admin domain swappable |
| **1** | Money & payment truth | P0-A, §2.3 §3.3 §3.8 §7 | An order total is explainable from data |
| **2** | Orders canonical + notifications | P0-B, §2.8 §2.10 §3.5 | Paid order appears and notifies without refresh |
| **3** | Catalog integrity + fulfillment | P0-D, §2.1 §2.4 §3.6 | Any published offer is genuinely buyable and fulfillable |
| **4** | External order ingestion | P0-C, §5 §3.7 | External purchase lands in Orders, no duplicates |
| **🚦** | **PRODUCTION LAUNCH GATE** | §3 LAUNCH BLOCKERS, §13.2 | All P0 closed and evidenced |
| **5** | Seller Portal | P1-A, §2.2 | Seller runs own offers and orders in scoped UI |
| **6** | Server cart + checkout session | P1-B, §2.5 §2.6 | Client price never trusted; repeat-safe |
| **7** | Payments hardening + reconciliation | P1-C, §2.7 §7.3 | Internal vs provider matched, mismatches visible |
| **8** | Identity & messaging | §2.9 §3.4 §14 | VK/MAX/Telegram linked; bot collects delivery |
| **9** | Tenant registry, domains, releases | P2-A, §4.3 §8 | New marketplace launched with no hardcode |
| **10** | Tenant content modules (Gorbushka) | P2-B, §11 | Content tenant on same runtime/backoffice |
**Parallel tracks** (start early, run across phases): **A** Analytics pipeline · **S** Security/RBAC/audit · **Q** QA & E2E · **N** API namespace migration · **Z** Pre-existing repo debt.
---
## Phase 0 — Unblock & seams
Nothing downstream can be honestly estimated until this closes. Two sprints: one is other people answering questions, one is work we can do today with no answers.
### Sprint 0.1 — Decisions `[DEC]`
**Answered 2026-08-17.** Kept as a record — the reasoning behind each answer still governs how later phases get built.
- [x] **Backend ownership.** *Still open — user flagged the question itself as unclear on first pass; re-ask in plain terms before Phase 1 implementation starts (not just contract-writing).* Nothing downstream is blocked by this being open — the Phase 1 backend contract doc exists regardless of who builds against it.
- [x] **Unfreeze the payment chain — YES.** `BACKEND-API-REFERENCE.md §7`'s do-not-modify note no longer applies. Phases 1, 6, 7 are unblocked to proceed once backend ownership is confirmed.
- [x] **External marketplaces — no fixed list.** User: connectors must onboard "our new ones, partners, new, etc." as they arrive — i.e. the platform's own future partner integrations, not a fixed enumeration of named third-party marketplaces to build against up front. **Consequence for Phase 4:** build the Sprint 4.1 connector framework generic/config-driven (auth, mapping, retry, dead-letter as pluggable per-connector config) so a new partner is an onboarding, not a code change. Sprint 4.2 ("one sprint per named marketplace") is retired as written — replaced by a generic "add connector" runbook, sized once the framework exists, not per-name up front.
- [x] **FX rate source — build our own, as a safety gate.** User: "not yet, lets handle from our side, if they dont" — no external provider is committed yet. Backend owns FX computation in-house as the authoritative source; the `source` field in the Phase 1 contract stays provider-agnostic and can point at an internal computed rate as legitimately as an external adapter. This *is* the "configured fallback" the contract doc's §3.2 already describes — now the default, not the fallback.
- [x] **§14 vs. email/phone OTP — VK ID first, then everything else.** User: "do all after vk." Delivery-plan Phase 8 sprint order changes: 8.3 (VK ID) now precedes 8.2 (OTP) — see Phase 8 below.
- [x] **Multi-seller orders — unified**, judgment call as instructed. One `Order` per checkout regardless of seller count, split into per-seller `Fulfillment` groups internally (matches §2.8's "canonical Order regardless of source" and §2.5's cart-level seller-grouping requirement without introducing parallel parent orders). Applies to Phase 3's `Offer` model, Phase 5's Seller Portal order view (scoped to that seller's fulfillment groups within the shared order), and closes the three-document disagreement flagged in Z16.
- [x] **"Fixed 5-second payment" claim — resolved as a non-issue.** User: "make polling 5 secs." Checked `config/constants.ts`: `PAYMENT_POLL_INTERVAL_MS` is already `5000`. This is a poll *cadence* against real provider status each tick, not an artificial fixed-delay-then-success — stays compliant with the plan's §3.2 prohibition. No code change needed; confirmed and left as-is.
- [x] **API namespace migration — adopt for new endpoints only, no forced migration.** User: unclear on the question, deferred to "what's recommended," noted "APIs are our domains" (i.e. we control the surface, lower urgency to force a big-bang rename). Recommendation taken: `docs/backend/PHASE-1-MONEY-FX-PAYMENTS-CONTRACT.md` already specifies all-new endpoints under the `/api/v2/...` namespace family. Legacy endpoints (`/cart`, `/orders`, `/items`, etc.) stay as-is until a dedicated migration sprint is scheduled — not blocking Phase 1.
- [x] **Document version — v3.1 is canonical.** The source file's internal "3.0" version block is stale/wrong; all our docs treat v3.1 as authoritative going forward.
**Exit:** all nine answered in writing.
### Sprint 0.2 — Seams and type reconciliation `[FE]` — runs regardless of answers
- [ ] Add DI tokens to the 9 admin domains that have none: Orders, Products, Users, Transactions, Monitoring, Moderation (+ derived Customers, Analytics). **M** — hard prerequisite for every `[BE]` swap in Phases 17.
- [ ] Reconcile `AdminRole` — defined twice with unrelated shapes (auth string-union vs. Users-page display interface). **S**
- [ ] Reconcile the two `Category` types, both fed by the same `/category` response, both in use. **S**
- [ ] Resolve `SellerConfig` (bootstrap) vs. `Seller`/`SellerBranding` (domain) — pick one or document the mapping. Blocks Phase 5. **S**
- [ ] Build the feature-flag / capability-guard service an existing ADR already promises; migrate the hand-rolled `sellerManagement.enabled` check onto it. **S**
- [ ] Build the centralized error-handling layer (`core/error-handling/`, `core/interceptors/` are `.gitkeep`-only today): error-envelope interceptor + 429 handling. **M** `[BOTH]` — envelope shape needs backend agreement.
- [ ] Fix `toAuthErrorShape()` to read a body-level code, not HTTP status alone — the built "session expired" / "invalid signature" screens are currently dead UI. **S**
- [ ] Bind mock implementations to `PRODUCT_DATA_PROVIDER` and `CATEGORY_REPOSITORY`, or delete the dead mock branch. Today both silently ignore `useMockData`. **S**
**Exit:** any admin domain can be pointed at a real backend by swapping one provider.
---
## Phase 1 — Money & payment truth (P0-A)
Closes §3.3 and §3.8, and half of the §13.1 acceptance table. The single highest-value phase: it is what makes totals explainable to a bank.
### Sprint 1.1 — Money model `[BOTH]`
- [ ] `Money = { amountMinor: int, currency }` end to end. Kill float arithmetic in `CurrencyRatesService.convert()`. **L**
- [ ] Currency minor-units + rounding rules table (RUB/USD/EUR/AMD at minimum). **M**
- [ ] Delete browser-owned rates: remove `currencyRates.v1` from `localStorage` and the hardcoded `DEFAULT_RATES` fallbacks (`USD: 0.011`, `AMD: 4.3`). **S**
- [ ] Remove the admin-typed rate editor from Admin Settings once a real source exists. **S**
### Sprint 1.2 — FX quote + rate source `[BE]` + `[FE]`
- [ ] `FxQuote { base, quote, rate, source, observedAt, expiresAt, quoteId }` entity + endpoint. **M**
- [ ] Rate-source adapter behind an interface; concrete provider pluggable (§7.1). **M**
- [ ] Stale/outlier quote rules; checkout **blocks** or uses an explicitly configured fallback. **M**
- [ ] `PriceBook`: offer base currency + allowed display/checkout currencies per tenant. **M**
### Sprint 1.3 — Price snapshot + server-authoritative amount `[BOTH]` — needs the freeze lifted
- [ ] `PriceSnapshot { offerId, amount, currency, fxQuoteId, capturedAt }`, immutable. **L**
- [ ] Server computes and validates the charged amount. Stop trusting `CartPaymentRequest.amount` and the per-item `price[]` array from the browser. **L** — the plan's §2.5 headline requirement.
- [ ] Old orders never recalculated when a rate updates. **S**
- [ ] Backoffice "total formula" panel: lines × qty discounts + delivery + fees, plus the FX quote used (§7.2). **M**
- [ ] `PriceHistory` on offer price and stock, with author/source (§2.1). **M**
### Sprint 1.4 — Payment timeline `[BE]` + `[FE]`
- [ ] Explicit state machines: `PaymentIntent` (created→pending→authorized/paid→failed/cancelled), `Payment` (received→confirmed→captured/settled→refunded), `Order` (pending_payment→paid→processing→fulfilled). **L**
- [ ] Persist `provider event id`, `provider timestamp`, `receivedAt`, `processedAt` per transition. **M**
- [ ] Webhook entrypoint with signature verification + idempotency (§2.7). **L**
- [ ] Idempotency keys on checkout, payment and order creation. Zero `idempot*` exists today. **M**
- [ ] Replace client-polled status signals with server truth; keep polling only as a UI fallback. **M**
- [ ] Keep the current honest behaviour: no artificial delay. Already compliant — protect it with a test. **S**
**Exit criteria (plan's own):** currency converts correctly; payment timeline reconstructable from provider events; every total explainable from `SKU/qty/delivery/discount/FX`.
---
## Phase 2 — Orders canonical + notifications (P0-B)
### Sprint 2.1 — Canonical order model `[BOTH]`
- [ ] `Order` header: `marketplaceId, source, customer, currency, subtotal, discounts, delivery, total, paymentStatus, orderStatus`. **L**
- [ ] `OrderLine` with `offerId, sellerId, skuSnapshot, titleSnapshot, qty, unitPriceMinor, lineTotalMinor, priceSnapshotId`. **M**
- [ ] `OrderEvent` timeline: created, paid, seller notified, accepted, fulfilled, cancelled, refunded (§2.8). Closes our own "Real order audit trail" TODO. **M**
- [ ] Real `AdminOrdersApiGateway` replacing the 24-row static seed with no create path. **L** `[BE]`
- [ ] Admin order actions: assign, resend notification, replay sync, cancel/refund by permission, comment, export. **M**
- [ ] `OrderContactSnapshot` — name/contacts frozen at order time, immune to later profile edits (§2.9). **S**
### Sprint 2.2 — Event bus + Notification Center `[BE]` + `[FE]`
- [ ] Platform event bus emitting `order.created`, `order.paid`, `payment.failed`, `webhook.error`, `stock.low`, `oversell`, `refund.requested/completed`, `external_order.imported`. **L**
- [ ] `Notification` entity: `unread/read`, `severity`, `marketplaceId`, entity type/id, **deep link**. **M**
- [ ] `DeliveryAttempt` log per external channel — a Telegram/email failure must never lose the internal notification (§2.10). **M**
- [ ] Backoffice Notifications section: unread queue, incidents, filter by marketplace and event type. Missing entirely from our nav today. **M**
- [ ] Repoint `AdminOrderWatcherService` from polling to the event stream. Feature is already built and inert — this is what switches it on. **S**
**Exit:** a paid order appears in backoffice without manual refresh, with deep link and seller/source.
---
## Phase 3 — Catalog integrity + fulfillment (P0-D)
Biggest structural change in the whole programme. Everything about multi-seller commerce hangs off it.
### Sprint 3.1 — Product / Offer split `[BOTH]`
- [ ] Introduce `Offer/Listing { id, marketplaceId, sellerId, variantId, sellerSku, priceMinor, currency, stockPolicy, status, publishedAt }`. **XL** — does not exist in any form today.
- [ ] Move price, stock, currency and status off `Product` onto `Offer`. **L**
- [ ] Formalise `Product` / `Variant` / `SKU` / `Category` (with `attributesSchema`, SEO) as content-only. **L**
- [ ] Unify the admin mock product domain with the live storefront `Item` domain — two unrelated shapes today. **L**
- [ ] Offer lookup in backoffice by internal SKU, seller SKU, product ID or external mapping (§2.1 "готово, когда"). **M**
### Sprint 3.2 — Lifecycle, import, inventory `[BOTH]`
- [ ] `draft → moderation → published → paused/archived` for both product and offer; wire the existing mock Moderation module to it. **M**
- [ ] Bulk import CSV/API: required-field validation, **error preview before apply**. Nothing exists (current "bulk" is Admin Categories edit actions only). **L**
- [ ] `InventoryRecord`: `available` / `reserved` / `sold` counted separately. **L**
- [ ] Reservations at checkout or pre-payment per strategy, with TTL. **M**
- [ ] Idempotent upsert for seller feed stock updates; repeat webhook must not double-decrement. **M**
- [ ] Oversell → dedicated incident queue, never silently hidden (§2.4). **M**
### Sprint 3.3 — Fulfillment + executability `[BOTH]`
- [ ] `Fulfillment` entity: manual / warehouse / pickup / digital; `status, assignedTo, issuedAt/shippedAt`, evidence where applicable. One `fulfil*` reference exists in the entire codebase today. **L**
- [ ] Publish-time executability validation — an offer that cannot actually be fulfilled cannot be published (§3.6). **M**
- [ ] Explicit test proving there is **no** inspector-detection branch anywhere: same production flow for every buyer (§3.6, §10.2, §13.2 last item). **S**
- [ ] Multi-seller cart grouping by seller and fulfillment rules — currently undefined behaviour (§2.5). **M****Sprint 0.1 decision (2026-08-17): unified.** One `Order` per checkout regardless of seller count; group lines into per-seller `Fulfillment` entries internally, no parallel parent orders.
**Exit:** any published, available offer really passes order → fulfillment.
---
## Phase 4 — External order ingestion (P0-C)
Zero percent built today. **Sprint 0.1 decision (2026-08-17): no fixed marketplace list** — connectors onboard "our new ones, partners, new, etc." as they arrive, not a pre-named enumeration. Sprint 4.2 is retired as originally written ("one sprint per named marketplace") and replaced with a generic onboarding runbook — Sprint 4.1's framework is now the deliverable that matters, sized to be genuinely config-driven rather than one-off per provider.
### Sprint 4.1 — Connector framework `[BE]`
- [ ] `Connector` + `ConnectorCredentialRef` in secret storage, scoped per marketplace/seller. **M**
- [ ] Inbound: webhook where the provider supports it, polling fallback with cursor/since. **L**
- [ ] `RawExternalEvent` — persist the raw payload before parsing, for traceability. **S**
- [ ] Normalizer: external payload → canonical `ExternalOrderEvent` → internal `Order`. **L**
- [ ] `ExternalOrderMapping`: `externalSellerId / externalProductId / externalSKU → internal seller/offer`. **L**
- [ ] Idempotency on `source + externalOrderId/eventId`; a repeat must not create a duplicate order. **M**
- [ ] Exponential retry, `DeadLetter`, manual replay from backoffice. **M**
- [ ] **Unmatched queue** for events with no SKU mapping. **M**
- [ ] Status/fulfillment push back to the external marketplace where its API allows (§5.2 step 8). **M**
- [ ] **Config-driven adapter contract** — a new partner connector is authored as configuration (auth type, field mapping, rate limits) against the Sprint 4.1 framework, not a bespoke integration each time. **L** — this is what "no fixed list" requires structurally.
### Sprint 4.2 — Connector onboarding runbook `[BE]` — repeats per new partner, no longer named up front
- [ ] Generic onboarding checklist against the Sprint 4.1 framework: auth, endpoint mapping, rate limits, sandbox verification. **M each**, sized down from **L** now that the framework absorbs the bespoke work.
### Sprint 4.3 — Connector observability `[FE]` + `[BE]`
- [ ] Backoffice **Integrations** section (missing from our nav): connectors, payment providers, FX sources, messaging. **M**
- [ ] Per-connector health: last success, lag, errors, rate limit, backlog, unmatched mapping. **M**
- [ ] Trace id on every connector error, visible in backoffice (§5.2 SLA). **S**
- [ ] SLA instrumentation: webhook 99% under 60s; polling ≤ interval + 60s; **0** duplicate orders. **M**
**Exit:** an external purchase creates/updates an order automatically, never duplicates, and notifies the responsible manager.
---
## 🚦 PRODUCTION LAUNCH GATE
Per §3 "LAUNCH BLOCKERS" and the §13.2 checklist. Do not schedule a launch before every line is green **and evidenced by a test, not an assertion**.
- [ ] All P0 closed and confirmed by tests
- [ ] Production analytics collecting real events (Track A)
- [ ] Catalog contains only genuinely available/publishable offers
- [ ] Seller permissions verified (Phase 5 or enforced-empty)
- [ ] cart → checkout → payment → order end-to-end smoke passed
- [ ] Webhook signatures, idempotency, retry verified
- [ ] External connector reconciliation passed
- [ ] FX source live, stale-quote policy verified
- [ ] Notification delivery + fallback verified
- [ ] Refund flow + reconciliation smoke passed
- [ ] Domains/SSL/health checks green (Phase 9)
- [ ] Backup/rollback exists
- [ ] Audit enabled (Track S)
- [ ] **No branch anywhere alters commerce flow based on who the buyer appears to be**
---
## Phase 5 — Seller Portal (P1-A)
A placeholder page with a `false` flag and zero backend bytes today. Note: the enabled code path has **never been exercised even once** — every prior verification ran with the flag at its real value.
### Sprint 5.1 — Seller foundation `[BOTH]`
- [ ] `SellerOrganization`, `SellerUser`, `SellerMarketplaceMembership`, `SellerIntegration`. **L**
- [ ] Onboarding: organisation, credentials/profile, contacts, marketplace applications, moderation status. **L**
- [ ] Backoffice **Sellers** section (missing from nav): organisations, applications, roles, status, listings, integration health. **L**
### Sprint 5.2 — Seller working surfaces `[FE]` + `[BE]`
- [ ] Catalog: create/edit products & offers, media, attributes, submit for moderation, bulk import. **L**
- [ ] Prices & Stock: mass edit, API/feed sync, change history, sync errors. **L**
- [ ] Orders: new, confirm, pick/issue/ship, cancel, return, SLA, comments. Per the unified-orders decision (Sprint 0.1), this view is scoped to *this seller's* `Fulfillment` group within each shared `Order`, not a separate seller-owned order. **L**
- [ ] Finance: accruals, commissions, refunds, settlement/payout register, report export. **L**
- [ ] Team: `SELLER_OWNER`, `SELLER_CATALOG_MANAGER`, `SELLER_ORDER_MANAGER`, `SELLER_FINANCE_VIEWER`, `SELLER_VIEWER`. **M**
- [ ] Integrations: API credentials, webhook/feed status, external SKU mapping, sync logs. **M**
### Sprint 5.3 — Seller isolation `[BE]` + `[Q]`
- [ ] A seller cannot see another seller's products, orders, customers, finance or API keys — enforced backend-side, tested. **M**
- [ ] Bank/payment detail changes: step-up auth + audit event + approval when maker/checker is on. **M**
- [ ] Seller staff permissions verified backend-side regardless of UI visibility. **M**
- [ ] First-ever fixture test of the seller-management enabled state. **S**
---
## Phase 6 — Server cart + checkout session (P1-B)
Partly pulled forward into Sprint 1.3 (server-authoritative amount). This phase completes the move.
### Sprint 6.1 — Server cart `[BOTH]`
- [ ] `Cart` / `CartLine` server-side, keyed on `offerId`. Replaces `localStorage` + Telegram CloudStorage. **L**
- [ ] Idempotent add/update/remove; quantity validated against stock and seller rules. **M**
- [ ] Price-refresh: cart surfaces price changes before checkout and requires explicit confirmation when the total moved. **M**
- [ ] Guest cart via session token; authenticated cart bound to customer account. **M**
- [ ] Expiration: inactive carts cleared, reservations released on TTL. **S**
### Sprint 6.2 — Checkout session `[BOTH]`
- [ ] `CheckoutSession` entity. `features/website/checkout/` is an empty directory today; checkout lives in a 751-line cart popup. **XL**
- [ ] Server re-validates offers and stock at checkout start. **M**
- [ ] Contact requirements enforced by tenant policy: email and/or phone verifiable (§2.6 step 4). **M**
- [ ] Clear total breakdown shown to the customer. **M**
- [ ] `PaymentIntent` via provider adapter; repeat click must not create a second intent. **M**
- [ ] Guest-checkout on/off per tenant policy (§6.2). **S**
- [ ] `DeliveryOption` entity. **M**
---
## Phase 7 — Payments hardening + reconciliation (P1-C)
### Sprint 7.1 — Refunds `[BOTH]`
- [ ] `Refund` as a first-class operation with reason, actor and order-line linkage. `requestRefund(id)` is a mock method today. **L**
- [ ] Partial refunds; `refunded / partially_refunded` states. **M**
### Sprint 7.2 — Reconciliation `[BE]` + `[FE]`
- [ ] `ReconciliationRecord`; match on `providerPaymentId` / merchant reference / amount+currency fallback (§7.3). **L** — zero `reconcil*` in the codebase today.
- [ ] Classify: unmatched, duplicate, amount mismatch, status mismatch. **M**
- [ ] Backoffice **Payments & Finance** section (missing from nav): payments, refunds, reconciliation queue, unmatched events, settlements. **L**
- [ ] Controlled resolution with full audit trail. **M**
- [ ] Settlements / payout register. **L** — zero `settlement*` today.
### Sprint 7.3 — Provider breadth `[DEC]` + `[BOTH]`
- [ ] Decide additional providers beyond the current QR/card flow (wallets, BNPL) — open business question. **DEC**
- [ ] Provider adapter interface so a new provider is a plug-in, not a rewrite. **M**
---
## Phase 8 — Identity & messaging (§2.9, §3.4, §14)
**Sprint 0.1 decision (2026-08-17): VK ID first, then everything else** ("do all after vk"). Order below is resequenced accordingly — VK ID moved ahead of OTP.
### Sprint 8.1 — Customer identity core `[BOTH]`
- [ ] `Customer`, `ExternalIdentity`, `ContactMethod`, `Verification`, `Consent`. **L**
- [ ] Telegram demoted from sole identity to one provider among several. **M**
- [ ] `emailVerifiedAt` / `phoneVerifiedAt` / `telegramLinkedAt`. **S**
- [ ] Backoffice **Customers** on real data: profiles, verified contacts, orders, consent. **M**
- [ ] Sensitive profile changes logged. **S**
### Sprint 8.2 — VK ID `[BOTH]` — new in v3.1, now first per Sprint 0.1
- [ ] OAuth 2.1/PKCE completed **backend-side**; link external identity to `Customer`. **L**
- [ ] VK ID as the primary storefront social login. **M**
- [ ] Repeat login must never create a duplicate customer. **M**
- [ ] Identity-conflict handling → controlled resolution, never overwrite an existing binding (§14.3). **M**
### Sprint 8.3 — Email/phone OTP `[BOTH]` — after VK ID
- [ ] Implement the approved [email/phone login spec](superpowers/specs/2026-08-15-email-phone-login-design.md). **L**
- [ ] Position it as recovery/fallback per v3.1 §14, not as the primary path. **S**
### Sprint 8.4 — MAX + Telegram bot channels `[BOTH]` — new in v3.1
- [ ] `ContactChannel`, `BotConversationBinding`, `MessagingConsent`. **L**
- [ ] MAX bot-assisted linking: one-time code, TTL, single-use, bound to marketplace + browser session. **L**
- [ ] Provider secrets never reach the frontend; all bot updates handled idempotently. **M**
- [ ] Bot adapters (VK / MAX / Telegram) normalised into one `MessagingEvent` keyed to `orderId`. **L**
### Sprint 8.5 — Notification Orchestrator + delivery conversation `[BE]` — new in v3.1
- [ ] Orchestrator routes `order.paid` to the customer's chosen channel; the backoffice notification always fires regardless. **L**
- [ ] Channel choice in checkout ("where should we send confirmation?"), recorded in `OrderContactSnapshot`; linking flow must not lose the cart or checkout session. **M**
- [ ] Delivery Conversation State Machine: `not_started → awaiting_customer → details_received → manager_assigned/auto_confirmed → shipment_planned → completed`. **L**
- [ ] Bot collects city/address/recipient/phone/time window/comment; backend validates and snapshots into the order. **L**
- [ ] **The bot must never change financial statuses** — delivery fields only, via Delivery Service. **M**
- [ ] Follow-up rules per tenant; after N attempts hand off to a manager, no infinite spam. **M**
- [ ] Manager handoff view: message history, current conversation state, accept handoff. **M**
- [ ] Messenger unavailability creates a `DeliveryAttempt` error and triggers fallback — never blocks the order. **M**
---
## Phase 9 — Tenant registry, domains, releases (P2-A)
### Sprint 9.1 — Marketplace Registry `[BOTH]`
- [ ] `Marketplace`, `MarketplaceDomain`, `MarketplaceFeatureSet`, `MarketplaceRevision`. **L**
- [ ] Backoffice **Marketplaces** section (missing from nav): registry, type, status, domains, currencies, feature set, responsible manager. **L**
- [ ] Onboarding wizard, all 8 steps of §4.3 (card → feature set → domains → design → roles → integrations → staging + smoke → production launch). **XL**
- [ ] Lifecycle state machine `draft → configured → content_ready → domains_planned → staging_live → qa_passed → production_ready → live → paused/archived`, **showing which blocker prevents the next transition**. **L**
- [ ] Marketplace dashboard (§4.2): GMV, paid orders, conversion, payment failure rate, orders needing action, seller moderation queue, low stock, unmatched events, integration health, domain/SSL/release status. **L**
- [ ] Re-scope the [super-admin Phase 1 design](superpowers/specs/superuser.md) against this — it overlaps registry and audit. **M**
- [ ] Consolidate `MarketplaceRef` vs. `TenantConfig` if a third marketplace-shaped type appears. **S**
### Sprint 9.2 — Domain automation `[BE]`
- [ ] Hostinger DNS integration, all 7 endpoints from §8.2. Zero references exist today. **L**
- [ ] Read current zone → snapshot/rollback payload → build and validate plan → apply only after production approval. **L**
- [ ] **Never touch MX/SPF/DKIM/DMARC/CAA** without a separate task. **S**
- [ ] Propagation, SSL and health verification; mark domain active only after checks pass. **M**
- [ ] Backoffice **Domains & Releases** section (missing from nav). **M**
### Sprint 9.3 — Publish model `[BOTH]`
- [ ] `draft → validation → preview → publish` with immutable published revisions; rollback creates a new revision (§8.3). **L**
- [ ] Real builder persistence — today `apiEndpoints.builder` is an empty placeholder and "publish" only promotes a `localStorage` signal. **L**
- [ ] CMS/static pages get a real backend write path (currently in-memory bootstrap only). **L**
- [ ] Enforce that orders/payments/inventory ledger are **not** part of a content revision and never roll back with the storefront. **S**
- [ ] Tenant resolution hardening: verified Host server-side, unknown Host → 404 with **no fallback tenant** (§6.1). **M**
---
## Phase 10 — Tenant content modules (P2-B, Gorbushka)
Only after Commerce Core is real. The plan is explicit that Gorbushka does not define the architecture.
### Sprint 10.1 — Directory content entities `[BOTH]`
- [ ] `Shop`, `ShopCategory`, `Service`, `Floor`, `SchemePin`, `RentListing`, `News/Promo`, `StaticPage`, `Lead`, `MallSettings`. Only static pages exist today. **XL**
- [ ] Every entity carries `marketplaceId`, audit, and publish/preview flow. **M**
- [ ] Mall scheme / floors / pins UI. **L**
- [ ] Rent listings + lead capture. **M**
### Sprint 10.2 — Gorbushka tenant config `[FE]`
- [ ] Feature set per §11.1: CMS, shops, services, scheme, rent, news, SEO/media/domains **on**; catalog / seller portal / commerce **platform-ready but off**. **M**
- [ ] Prove commerce can be switched on later without touching backend or storefront code. **M**
---
## Parallel tracks
### Track A — Analytics pipeline (P1-D, §3.1 §6.3)
**Start at Phase 1, not last.** Longest lead time in the programme, and it is a P0 in the plan's own §3. There is no tracking infrastructure at all today — this is not a missing endpoint.
- [ ] **A1** Server-side event logging spine. **XL** `[BE]`
- [ ] **A2** Traffic events: `session_started`, `page_view`, source/utm/referrer, unique users/sessions. **M**
- [ ] **A3** Catalog events: `search`, `category_view`, `product_view`, `seller_view`. **M**
- [ ] **A4** Commerce events: `add_to_cart`, `cart_view`, `checkout_started`, `payment_started`, `payment_success/failed`, `order_created`. **M**
- [ ] **A5** Operations metrics: `order_paid_to_notification` latency, fulfillment time, connector lag, payment webhook lag. **M**
- [ ] **A6** Quality metrics: frontend/backend errors, checkout validation failures, FX stale-rate blocks. **M**
- [ ] **A7** Real funnel dashboard in backoffice, replacing the mock-composed Analytics facade. **L**
- [ ] **A8** **Synthetic traffic technically separated** from production analytics — staging/test only, never presented as real visits (§3.1, §6.3). **M**
- [ ] **A9** Real product view counts — the shipped "Views" column always renders `0`. Either bridge to the live storefront `Item.visits` or serve it from the real Products backend. **S**
- [ ] **A10** Post-launch monitoring set (§13.3): checkout conversion, payment success/failure, webhook lag, order-notification lag, connector lag, FX quote age, unmatched reconciliation, stuck fulfillment. **L**
- [ ] **A11** Trending search terms endpoint — `loadTrending()` is a stub returning `of(null)`. **S**
### Track S — Security, RBAC, audit (§4.4, §10)
**Gate on Phase 5 and on the launch gate.** Today the role model is decorative: types exist, nothing gates any button, page or action. Anyone who authenticates has full access.
- [ ] **S1** Enforce RBAC backend-side with tenant scope on every request. **L**
- [ ] **S2** Implement the 17 roles across 3 scopes (5 platform / 7 marketplace / 5 seller). **L**
- [ ] **S3** Frontend permission guards on routes and actions — currently zero. **M**
- [ ] **S4** Audit log covering permissions, seller changes, catalog moderation, price, payment/refund, manual order actions, integrations, production launch. `audit` appears only as mock display fields today. **L**
- [ ] **S5** Backoffice **Audit & Security** section (missing from nav): role changes, sensitive actions, login/security events, exports. **M**
- [ ] **S6** Step-up authentication for sensitive financial actions. **M**
- [ ] **S7** Rate limits and abuse controls on storefront/auth/provider endpoints; client-side 429 handling (zero today). **M**
- [ ] **S8** Secret storage for provider/connector credentials, scoped per marketplace/seller. **M**
- [ ] **S9** PII minimisation: store only necessary customer data, restrict access and export. **M**
- [ ] **S10** Ed25519 admin auth backend — wired client-side, 404s today. Decide: build it, or drop it for the plan's conventional RBAC. **DEC** + **L**
- [ ] **S11** HttpOnly session cookie (existing frontend-blocked TODO). **M**
### Track Q — QA & E2E (§13)
The plan's entire Definition of Done is end-to-end. We have **zero** E2E tests and ~32% statement / ~19% branch coverage across 11 spec files.
- [ ] **Q1** Stand up an E2E harness (Playwright or equivalent) — none exists. **L**
- [ ] **Q2** Solve automated admin login; several past "verified live" claims were code-inspection only because `/edit` and `/backoffice` need Telegram login. **M**
- [ ] **Q3** E2E: full §13.1 acceptance path — seller → catalog → storefront → cart → checkout → payment → order → notification → fulfillment. **XL**
- [ ] **Q4** E2E: currency switch recalculates by FX quote — explicitly, `160 RUB` must not become `160 USD/AMD`. **M**
- [ ] **Q5** E2E: repeat webhook and double-click create exactly one order. **M**
- [ ] **Q6** E2E: external marketplace purchase imports and notifies. **M**
- [ ] **Q7** Facade tests for cart/checkout, moderation, Orders, Products, Users, Transactions, Monitoring — the domains about to get real backends carry the most regression risk with the least coverage. **L**
- [ ] **Q8** Regression pattern for reactive flag/config reads that must track `bootstrapRevision()` — this bug class already bit us once and was invisible until specifically hunted. **S**
- [ ] **Q9** Set a justified coverage floor and a CI gate. Deliberately unset today. **M**
- [ ] **Q10** One real screen-reader pass (NVDA/VoiceOver). Never performed on this codebase — every accessibility claim to date is automated tree inspection only. **M**
### Track N — API namespace migration (§9.3)
Cheapest now, more expensive every phase. Decision in Sprint 0.1.
- [ ] **N1** Adopt `/api/v2/storefront/*`, `/api/admin/v2/*`, `/api/seller/v1/*`, `/api/identity/v1/*`, `/api/providers/v1/*`, `/api/integrations/v1/*`. **L** `[BOTH]`
- [ ] **N2** Migrate today's flat unversioned endpoints (`/cart`, `/orders`, `/items`, `/category`, `/searchitems`) plus the separate `qrApiUrl` host. **L**
- [ ] **N3** Agree the structured error envelope; today no interceptor reads error bodies at all. **M** (implementation lands in Sprint 0.2)
### Track Z — Pre-existing repo debt
Not in the plan, but real. Fold into whichever phase touches the same surface.
- [ ] **Z1** Dark-mode selector does nothing — nothing reads `data-theme-mode`. **S**
- [ ] **Z2** "Site Layout" selector has no effect — `layout.type` is edited but never read. **S**
- [ ] **Z3** Footer "Contacts" link has no content behind it. **S**
- [ ] **Z4** `SeoService.setItemMeta()` exists but is **never called** — product pages ship only site-wide meta. **S**
- [ ] **Z5** `og:locale` hardcoded to `ru_RU` regardless of active locale. **S**
- [ ] **Z6** No JSON-LD structured data, no sitemap generation. **M**
- [ ] **Z7** Hardcoded Russian payment-description fallback (`'Покупка на Маркетплейсе'`) in a multi-tenant product. **S**
- [ ] **Z8** Brand colours fail WCAG AA — `--border-color` at 1.241.42:1 against a 3:1 requirement; status colours fail 4.5:1 as text. **Needs theme-owner sign-off, not just a code fix.** **M**
- [ ] **Z9** Literal hex `#cdd6d5` in `stars.component.scss:10` with no token behind it. **S**
- [ ] **Z10** Two large lazy chunks unaddressed: `project-editor` (~1.0 MB), `catalog-container` (~330375 kB). Profile under real backend latency, not instant mock responses. **M**
- [ ] **Z11** `navigation.header` is editable in the builder with zero runtime consumer — needs a product decision, not a wiring fix. **DEC**
- [ ] **Z12** `catalog.navigationMode` renders a deliberate placeholder; the mega-menu / carousel / left-nav variants it implies do not exist. **DEC**
- [ ] **Z13** `sellerId` typed as bare `string` instead of the `UUID` alias used elsewhere. **S**
- [ ] **Z14** No shared breadcrumb component; the only breadcrumb logic is a local signal in the catalog container. **S**
- [ ] **Z15** Duplicate search models under two module paths. **S**
- [ ] **Z16** Consolidate the eight cross-linked Seller Management documents onto the now-resolved decision (unified orders, Sprint 0.1, 2026-08-17) — at least three independently restated the question before it was answered. Do this **before** Phase 5 starts. **M**
- [ ] **Z17** Angular 22 upgrade — researched, not started; needs a dependency fix and a Node bump. **Its own dedicated session, never bundled with feature work.** **M**
---
## Critical path
```
Sprint 0.1 (decisions)
└─> Sprint 0.2 (seams)
└─> Phase 1 (money truth) ──────────────┐
└─> Phase 2 (orders + notif) │
└─> Phase 3 (offer split) │
└─> Phase 4 (external ingestion)
└─> 🚦 LAUNCH GATE
Track A (analytics) ── starts at Phase 1, gates the launch ──┘
Track S (RBAC/audit) ── starts at Phase 2, gates the launch ──┘
Track Q (E2E) ── starts at Phase 1, evidences the gate ┘
```
Phases 510 all sit behind the launch gate and can be resequenced by business priority. Phases 14 cannot.
**Single hardest dependency:** Phase 1 Sprint 1.3 needs the payment chain unfrozen. If that answer is "no", the programme stops at Sprint 0.2 and the plan's P0s cannot be delivered — that outcome should go back to them in writing, not be worked around.