# 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 1–7.
- [ ] 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.24–1.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` (~330–375 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 5–10 all sit behind the launch gate and can be resequenced by business priority. Phases 1–4 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.
