Files
mercadodevida/backlog/features.json
rikrdo 41f144d7bd feat(F-003): HTTP foundation with request context and error envelope
- request_id generated or sanitized-propagated on every request (x-request-id)
- structured JSON logging (pino), one correlated line per request, injectable logger
- error envelope v2 { error: { statusCode, code, message, details? }, requestId }
- 5xx messages always generic; stack traces stay in server logs only
- explicit parseJson (zod) input validation hook at the API layer
- README HTTP contract section; deps justified in spec/tech.md
- all gates approved; verify.sh green
2026-08-14 22:13:28 +02:00

1144 lines
46 KiB
JSON

{
"project": "mercadodevida-vnext",
"description": "Incremental SDD roadmap for MercadoDeVida vNext (modular monolith). Derived from project/design_prompt.md. One feature at a time, spec before code, gates before done.",
"rules": {
"one_feature_at_a_time": true,
"require_review_gate": true,
"require_security_gate": true,
"require_qa_gate": true,
"valid_status": [
"pending",
"in_progress",
"blocked",
"done"
],
"valid_types": [
"feature",
"fix",
"bug",
"chore"
]
},
"features": [
{
"id": "F-001",
"type": "chore",
"title": "Scaffold modular monolith skeleton",
"problem": "No codebase exists. Platform needs a boring, typed, modular home.",
"goal": "TypeScript modular monolith skeleton with strict module boundaries and green toolchain.",
"scope_in": [
"project/ layout: src/modules, src/shared, src/infrastructure, src/app",
"Node + TypeScript strict + Fastify app shell",
"ESLint + Prettier + typecheck + unit test runner",
"GET /health endpoint",
"Boundary lint rule: modules import only own folder or other modules public index"
],
"scope_out": [
"No business logic",
"No database",
"No frontend"
],
"priority": "high",
"risk": "low",
"depends_on": [],
"description": "Problem: No codebase exists. Platform needs a boring, typed, modular home. Goal: TypeScript modular monolith skeleton with strict module boundaries and green toolchain. Scope IN: project/ layout, Fastify + TS strict, toolchain, health endpoint, boundary rule. Scope OUT: no business logic, no DB. Type: chore. Priority: high. Risk: low.",
"acceptance": [
"install, build, lint, typecheck and test commands all green",
"GET /health returns HTTP 200 with status ok",
"src/modules, src/shared, src/infrastructure, src/app exist",
"A module importing another module internal file fails lint",
"verify.sh green"
],
"status": "done",
"created_at": "2026-08-14",
"gates": {
"review": true,
"security": true,
"qa": true
}
},
{
"id": "F-002",
"type": "chore",
"title": "Database foundation with module-owned schemas",
"problem": "Modules need PostgreSQL with clear ownership and safe migrations.",
"goal": "Migrations tooling, table naming convention per module, local dev database.",
"scope_in": [
"SQL migration tool (up/down, ordered, idempotent)",
"Naming convention: <module>_<table> (catalog_products, inventory_stock, ...)",
"docker-compose dev services: PostgreSQL + Redis",
"Migration test: fresh up then down then up",
"Rule: no schema change without migration"
],
"scope_out": [
"No business tables yet",
"No Redis usage beyond availability"
],
"priority": "high",
"risk": "low",
"depends_on": [
"F-001"
],
"description": "Problem: Modules need PostgreSQL with clear ownership and safe migrations. Goal: Migrations tooling, naming convention, local dev database. Scope IN: migration tool, naming convention, docker-compose, migration test. Scope OUT: no business tables. Type: chore. Priority: high. Risk: low.",
"acceptance": [
"Given a fresh database When migrations run Then schema is created and repeat run is a no-op",
"Given applied migrations When down runs Then schema rolls back cleanly",
"Table naming convention documented and enforced",
"Dev PostgreSQL and Redis start with one command",
"verify.sh green"
],
"status": "done",
"created_at": "2026-08-14",
"gates": {
"review": true,
"security": true,
"qa": true
}
},
{
"id": "F-003",
"type": "chore",
"title": "HTTP foundation and request context",
"problem": "Every request needs identity, structured logs and predictable errors.",
"goal": "request_id everywhere, JSON logs, one error envelope, no magic.",
"scope_in": [
"request_id generated or propagated on every request",
"Structured JSON logging with request_id",
"Single error response envelope",
"Input validation hook on API layer"
],
"scope_out": [
"No tracing backend yet",
"No metrics yet"
],
"priority": "high",
"risk": "low",
"depends_on": [
"F-001"
],
"description": "Problem: Every request needs identity, structured logs and predictable errors. Goal: request_id, JSON logs, error envelope. Scope IN: request context, logging, error envelope, validation hook. Scope OUT: no tracing or metrics backend. Type: chore. Priority: high. Risk: low.",
"acceptance": [
"Every response carries request_id",
"Every log line for a request carries the same request_id",
"Given an invalid request When handled Then error envelope shape is stable",
"Internal stack traces never leak to API responses",
"verify.sh green"
],
"status": "done",
"created_at": "2026-08-14",
"gates": {
"review": true,
"security": true,
"qa": true
}
},
{
"id": "F-004",
"type": "chore",
"title": "Typed config and feature flags",
"problem": "Risky features need activation separate from deployment; env access must be typed.",
"goal": "Fail-fast typed config plus simple feature flag module behind an interface.",
"scope_in": [
"Typed env config loader, fail fast on missing required vars",
"FeatureFlag interface with simple store implementation",
"Flags: deployment and activation are separate operations"
],
"scope_out": [
"No external flag service",
"No per-user segmentation yet"
],
"priority": "med",
"risk": "low",
"depends_on": [
"F-001"
],
"description": "Problem: Risky features need activation separate from deployment; env access must be typed. Goal: typed config + feature flag module. Scope IN: config loader, FeatureFlag interface, simple store. Scope OUT: no external flag service. Type: chore. Priority: med. Risk: low.",
"acceptance": [
"Given a missing required env var When app starts Then startup fails with clear message",
"Given flag off When code path guarded by flag runs Then path is skipped",
"Flag state change does not require redeploy",
"verify.sh green"
],
"status": "pending",
"created_at": "2026-08-14",
"gates": {
"review": false,
"security": false,
"qa": false
}
},
{
"id": "F-005",
"type": "feature",
"title": "Identity: register, login, sessions",
"problem": "Customers need accounts; nothing trusts who calls the API.",
"goal": "Registration, login, logout with hashed passwords and secure sessions.",
"scope_in": [
"identity module: domain, application, infrastructure, api",
"Register, login, logout use cases",
"Argon2 password hashing",
"Secure session cookie: HttpOnly, Secure, SameSite",
"Login rate limiting"
],
"scope_out": [
"No MFA yet",
"No OAuth providers",
"No profile editing (users module)"
],
"priority": "high",
"risk": "med",
"depends_on": [
"F-002",
"F-003"
],
"description": "Problem: Customers need accounts; nothing trusts who calls the API. Goal: register/login/logout with hashed passwords and secure sessions. Scope IN: identity module, argon2, secure cookies, rate limit. Scope OUT: no MFA, no OAuth. Type: feature. Priority: high. Risk: med.",
"acceptance": [
"Given valid credentials When login Then HTTP 200 and secure session cookie set",
"Given wrong password When login Then HTTP 401 and no user enumeration hint",
"Passwords stored with argon2, never plaintext or reversible",
"Given 10 failed logins in a row When next login attempted Then HTTP 429",
"Session cookie is HttpOnly, Secure and SameSite",
"verify.sh green"
],
"status": "pending",
"created_at": "2026-08-14",
"gates": {
"review": false,
"security": false,
"qa": false
}
},
{
"id": "F-006",
"type": "feature",
"title": "Users: profile, addresses, RBAC",
"problem": "Authenticated users need profile data, addresses and clear roles.",
"goal": "Users module with profile + address CRUD and customer/admin roles.",
"scope_in": [
"users module owning identity_users profile data and addresses",
"Profile and address CRUD behind use cases",
"Roles: customer, admin; RBAC guard on API layer",
"User can read/update only own data"
],
"scope_out": [
"No fine-grained permissions yet",
"No admin UI"
],
"priority": "med",
"risk": "med",
"depends_on": [
"F-005"
],
"description": "Problem: Authenticated users need profile data, addresses and clear roles. Goal: users module with profile, addresses, RBAC. Scope IN: users module, CRUD, roles, ownership guard. Scope OUT: no admin UI. Type: feature. Priority: med. Risk: med.",
"acceptance": [
"Given user A When A requests user B profile Then HTTP 403",
"Given customer role When admin-only endpoint called Then HTTP 403",
"Given admin role When admin-only endpoint called Then HTTP 200",
"Address CRUD works end to end for own addresses",
"verify.sh green"
],
"status": "pending",
"created_at": "2026-08-14",
"gates": {
"review": false,
"security": false,
"qa": false
}
},
{
"id": "F-007",
"type": "feature",
"title": "Categories module",
"problem": "Products need a navigable taxonomy with stable SEO URLs.",
"goal": "Category tree with unique slugs and SEO metadata.",
"scope_in": [
"categories module with tree structure",
"Unique slug per category, stable URL /categoria/<slug>",
"SEO metadata fields: title, description",
"Category CRUD via use cases"
],
"scope_out": [
"No product assignment yet (catalog core ticket)",
"No storefront rendering yet"
],
"priority": "high",
"risk": "low",
"depends_on": [
"F-002"
],
"description": "Problem: Products need a navigable taxonomy with stable SEO URLs. Goal: category tree with slugs and SEO metadata. Scope IN: categories module, tree, slugs, metadata. Scope OUT: no product assignment, no storefront. Type: feature. Priority: high. Risk: low.",
"acceptance": [
"Given duplicate slug When category created Then HTTP 409",
"Category tree supports parent/child and blocks cycles",
"Public URL is /categoria/<slug>, never internal id",
"verify.sh green"
],
"status": "pending",
"created_at": "2026-08-14",
"gates": {
"review": false,
"security": false,
"qa": false
}
},
{
"id": "F-008",
"type": "feature",
"title": "Catalog core: products domain",
"problem": "There is no product model; everything else depends on it.",
"goal": "Product aggregate with use cases, slugs, states and SEO metadata.",
"scope_in": [
"catalog module: domain, application, infrastructure, api",
"CreateProduct, UpdateProduct, SearchProducts use cases",
"Product states: draft, active, archived",
"Unique slug, stable URL /productos/<slug>",
"SEO metadata per product",
"Product-category assignment"
],
"scope_out": [
"No variants yet",
"No stock, no prices"
],
"priority": "high",
"risk": "med",
"depends_on": [
"F-002",
"F-007"
],
"description": "Problem: There is no product model; everything else depends on it. Goal: product aggregate with use cases, slugs, states, SEO metadata. Scope IN: catalog module, CRUD use cases, states, slugs, category assignment. Scope OUT: no variants, stock, prices. Type: feature. Priority: high. Risk: med.",
"acceptance": [
"Domain layer has zero database or HTTP imports",
"Given duplicate slug When product created Then HTTP 409",
"Only active products appear in public listings",
"Public URL is /productos/<slug>, never internal id",
"verify.sh green"
],
"status": "pending",
"created_at": "2026-08-14",
"gates": {
"review": false,
"security": false,
"qa": false
}
},
{
"id": "F-009",
"type": "feature",
"title": "Brands module",
"problem": "Products need brands for navigation, filtering and SEO pages.",
"goal": "Brand entity with slug and SEO page data.",
"scope_in": [
"brands module or catalog-owned brands with clear ownership",
"Unique slug, stable URL /marca/<slug>",
"Product-brand assignment"
],
"scope_out": [
"No brand storefront page yet"
],
"priority": "med",
"risk": "low",
"depends_on": [
"F-002",
"F-008"
],
"description": "Problem: Products need brands for navigation, filtering and SEO pages. Goal: brand entity with slug and SEO data. Scope IN: brands, slugs, product assignment. Scope OUT: no storefront page yet. Type: feature. Priority: med. Risk: low.",
"acceptance": [
"Given duplicate brand slug When created Then HTTP 409",
"Products list filterable by brand",
"Public URL is /marca/<slug>",
"verify.sh green"
],
"status": "pending",
"created_at": "2026-08-14",
"gates": {
"review": false,
"security": false,
"qa": false
}
},
{
"id": "F-010",
"type": "feature",
"title": "Variants, SKU/EAN and product rich data",
"problem": "One product has many sellable variants; nutrition and allergens must be trusted data.",
"goal": "Variants with SKU/EAN plus attributes, ingredients, allergens, nutrition with provenance.",
"scope_in": [
"Product variants with unique SKU and EAN",
"Attributes per variant",
"Ingredients, allergens, nutrition information",
"Organic/ecological certification fields",
"Provenance tracking: manual, manufacturer, openfoodfacts",
"External source never overwrites trusted internal data without validation"
],
"scope_out": [
"No OpenFoodFacts sync job yet",
"No images yet"
],
"priority": "high",
"risk": "med",
"depends_on": [
"F-008"
],
"description": "Problem: One product has many sellable variants; nutrition and allergens must be trusted data. Goal: variants with SKU/EAN, attributes, ingredients, allergens, nutrition with provenance. Scope IN: variants, rich data, provenance rules. Scope OUT: no OpenFoodFacts sync. Type: feature. Priority: high. Risk: med.",
"acceptance": [
"Given duplicate SKU or EAN When variant created Then HTTP 409",
"Given field with nutrition_source=manual When external source pushes same field Then internal value kept",
"Every nutrition payload stores its provenance",
"verify.sh green"
],
"status": "pending",
"created_at": "2026-08-14",
"gates": {
"review": false,
"security": false,
"qa": false
}
},
{
"id": "F-011",
"type": "feature",
"title": "Product images",
"problem": "Products need images for storefront and SEO.",
"goal": "Image attachment per product/variant behind a storage interface.",
"scope_in": [
"Image entity: url, alt text, ordering, role (main/gallery)",
"Storage adapter behind interface (local first)",
"Attach/detach/reorder via use cases"
],
"scope_out": [
"No CDN or image processing pipeline yet"
],
"priority": "med",
"risk": "low",
"depends_on": [
"F-010"
],
"description": "Problem: Products need images for storefront and SEO. Goal: image attachment per product/variant behind a storage interface. Scope IN: image entity, storage adapter, ordering. Scope OUT: no CDN pipeline. Type: feature. Priority: med. Risk: low.",
"acceptance": [
"Product exposes ordered image list with alt text",
"Swapping storage adapter touches only infrastructure layer",
"verify.sh green"
],
"status": "pending",
"created_at": "2026-08-14",
"gates": {
"review": false,
"security": false,
"qa": false
}
},
{
"id": "F-012",
"type": "feature",
"title": "Search: interface plus PostgreSQL FTS",
"problem": "Customers must find products; search backend must stay replaceable.",
"goal": "ProductSearch interface with PostgreSQL full text search implementation.",
"scope_in": [
"ProductSearch interface",
"PostgreSQL FTS implementation over name, brand, category",
"Basic relevance ordering and pagination",
"Popular searches logged for later cache use"
],
"scope_out": [
"No Elasticsearch/Meilisearch/Algolia yet",
"No typo tolerance beyond FTS defaults"
],
"priority": "high",
"risk": "low",
"depends_on": [
"F-008",
"F-009"
],
"description": "Problem: Customers must find products; search backend must stay replaceable. Goal: ProductSearch interface with PostgreSQL FTS implementation. Scope IN: interface, FTS adapter, pagination. Scope OUT: no external search engine. Type: feature. Priority: high. Risk: low.",
"acceptance": [
"Given product matching query When search Then product returned with stable ordering",
"Swapping implementation requires no API contract change",
"Search latency measured and logged",
"verify.sh green"
],
"status": "pending",
"created_at": "2026-08-14",
"gates": {
"review": false,
"security": false,
"qa": false
}
},
{
"id": "F-013",
"type": "feature",
"title": "Storefront shell (Next.js)",
"problem": "There is no customer-facing site.",
"goal": "Next.js + TypeScript + Tailwind shell consuming backend public API.",
"scope_in": [
"Next.js app with React, TypeScript, Tailwind",
"Layout, navigation, home page",
"Server Components by default; client code only where interaction needs it",
"Typed API client for backend public endpoints"
],
"scope_out": [
"No catalog pages yet",
"No cart UI yet"
],
"priority": "high",
"risk": "low",
"depends_on": [
"F-008"
],
"description": "Problem: There is no customer-facing site. Goal: Next.js + TS + Tailwind shell consuming backend API. Scope IN: app shell, navigation, typed API client. Scope OUT: no catalog pages. Type: feature. Priority: high. Risk: low.",
"acceptance": [
"Home renders server-side with navigation",
"Frontend never imports backend internals, only typed API client",
"Build, lint and typecheck green",
"verify.sh green"
],
"status": "pending",
"created_at": "2026-08-14",
"gates": {
"review": false,
"security": false,
"qa": false
}
},
{
"id": "F-014",
"type": "feature",
"title": "Storefront catalog pages (SSG/ISR)",
"problem": "Products, categories and brands need fast SEO-friendly public pages.",
"goal": "SSG/ISR pages for product, category and brand with stable URLs and metadata.",
"scope_in": [
"Product detail page at /productos/<slug>",
"Category listing at /categoria/<slug>",
"Brand page at /marca/<slug>",
"SSG/ISR rendering with on-demand revalidation",
"Per-page metadata and OpenGraph",
"Search results page"
],
"scope_out": [
"No cart, no checkout UI"
],
"priority": "high",
"risk": "med",
"depends_on": [
"F-010",
"F-012",
"F-013"
],
"description": "Problem: Products, categories and brands need fast SEO-friendly public pages. Goal: SSG/ISR catalog pages with stable URLs and metadata. Scope IN: product/category/brand/search pages, ISR, metadata, OG. Scope OUT: no commerce UI. Type: feature. Priority: high. Risk: med.",
"acceptance": [
"Product page served at /productos/<slug> with no internal id in URL",
"Given catalog update When revalidation triggered Then page reflects new data",
"Each page carries title, description and OpenGraph metadata",
"Lighthouse SEO score >= 90 on product and category pages",
"verify.sh green"
],
"status": "pending",
"created_at": "2026-08-14",
"gates": {
"review": false,
"security": false,
"qa": false
}
},
{
"id": "F-015",
"type": "feature",
"title": "SEO core: structured data, sitemap, redirects",
"problem": "Organic traffic is core business; structured data and crawlability are missing.",
"goal": "JSON-LD schemas, sitemap.xml, robots.txt and redirect management.",
"scope_in": [
"seo module",
"Product, Breadcrumb and Organization JSON-LD",
"sitemap.xml and robots.txt generation",
"Redirect store behind interface with 301 support",
"Canonical URLs on all public pages"
],
"scope_out": [
"No hreflang",
"No external SEO tooling integration"
],
"priority": "high",
"risk": "low",
"depends_on": [
"F-014"
],
"description": "Problem: Organic traffic is core business; structured data and crawlability are missing. Goal: JSON-LD, sitemap, robots, redirects. Scope IN: seo module, schemas, sitemap, robots, redirect store. Scope OUT: no hreflang. Type: feature. Priority: high. Risk: low.",
"acceptance": [
"Product page embeds valid Product JSON-LD (validator clean)",
"sitemap.xml lists active public URLs and excludes draft content",
"Given stored redirect When old URL requested Then HTTP 301 to new URL",
"Every public page has canonical URL",
"verify.sh green"
],
"status": "pending",
"created_at": "2026-08-14",
"gates": {
"review": false,
"security": false,
"qa": false
}
},
{
"id": "F-016",
"type": "feature",
"title": "Inventory module",
"problem": "Catalog says what a product is; nothing says if it can be sold.",
"goal": "Isolated inventory module with atomic stock operations and availability API.",
"scope_in": [
"inventory module, fully isolated from catalog",
"Stock states: available, reserved, sold, incoming",
"InventoryService.checkAvailability() public interface",
"Atomic reserve, release, confirm operations",
"Negative stock impossible at database and domain level"
],
"scope_out": [
"No warehouse/supplier integration",
"No incoming purchase orders UI"
],
"priority": "high",
"risk": "med",
"depends_on": [
"F-002"
],
"description": "Problem: Catalog says what a product is; nothing says if it can be sold. Goal: isolated inventory with atomic stock ops and availability API. Scope IN: inventory module, stock states, atomic reserve/release/confirm, no-negative rule. Scope OUT: no warehouse integration. Type: feature. Priority: high. Risk: med.",
"acceptance": [
"Given 10 concurrent reservations for the last unit When all settle Then exactly 1 succeeds and 9 get unavailable",
"Given zero stock When reservation requested Then rejected and stock never negative",
"Catalog module contains zero references to inventory tables",
"Checkout checks stock only through InventoryService",
"verify.sh green"
],
"status": "pending",
"created_at": "2026-08-14",
"gates": {
"review": false,
"security": false,
"qa": false
}
},
{
"id": "F-017",
"type": "feature",
"title": "Pricing module",
"problem": "Nothing calculates real prices and taxes; frontend must never supply them.",
"goal": "Server-side pricing with VAT behind PricingService.",
"scope_in": [
"pricing module owning price and tax rules",
"Price per variant with VAT (Spain general/reduced rates)",
"PricingService.calculate() public interface",
"Price history table for audit"
],
"scope_out": [
"No promotions yet",
"No multi-currency"
],
"priority": "high",
"risk": "med",
"depends_on": [
"F-002",
"F-010"
],
"description": "Problem: Nothing calculates real prices and taxes; frontend must never supply them. Goal: server-side pricing with VAT behind PricingService. Scope IN: pricing module, VAT, calculate interface, price history. Scope OUT: no promotions, no multi-currency. Type: feature. Priority: high. Risk: med.",
"acceptance": [
"Given variant and quantity When PricingService.calculate Then total with VAT returned",
"Given client-supplied price in any request Then price ignored and recalculated",
"Price change writes history row, never silent update",
"verify.sh green"
],
"status": "pending",
"created_at": "2026-08-14",
"gates": {
"review": false,
"security": false,
"qa": false
}
},
{
"id": "F-018",
"type": "feature",
"title": "Cart module",
"problem": "Customers need a cart that never lies about prices or stock.",
"goal": "Cart with items and server-side recalculation of totals.",
"scope_in": [
"cart module storing product_id, variant_id, quantity only",
"Add, remove, change quantity use cases",
"Totals always recalculated via PricingService and InventoryService",
"Stored prices never trusted"
],
"scope_out": [
"No guest cart persistence across devices yet",
"No promotions applied yet"
],
"priority": "high",
"risk": "low",
"depends_on": [
"F-016",
"F-017"
],
"description": "Problem: Customers need a cart that never lies about prices or stock. Goal: cart with items and server-side recalculation. Scope IN: cart module, item ops, recalculation via Pricing/Inventory. Scope OUT: no promotions yet. Type: feature. Priority: high. Risk: low.",
"acceptance": [
"Given product price changed after add to cart When cart read Then new price shown",
"Given variant out of stock When cart read Then item flagged unavailable",
"Cart payload from client containing price fields is ignored",
"verify.sh green"
],
"status": "pending",
"created_at": "2026-08-14",
"gates": {
"review": false,
"security": false,
"qa": false
}
},
{
"id": "F-019",
"type": "feature",
"title": "Promotions v1",
"problem": "Store needs discounts; discount logic must be validated server-side.",
"goal": "Simple promotions engine behind an interface, validated at recalculation.",
"scope_in": [
"promotions module with percent and fixed-amount rules",
"Promo codes with validity window and usage limits",
"Discount validation inside recalculation, never from frontend",
"No stacking unless rule explicitly allows"
],
"scope_out": [
"No loyalty points",
"No buy-X-get-Y rules yet"
],
"priority": "med",
"risk": "med",
"depends_on": [
"F-017",
"F-018"
],
"description": "Problem: Store needs discounts; discount logic must be validated server-side. Goal: simple promotions engine behind interface, validated at recalculation. Scope IN: percent/fixed rules, promo codes, validity, no frontend trust. Scope OUT: no loyalty. Type: feature. Priority: med. Risk: med.",
"acceptance": [
"Given valid promo code When applied Then discount recalculated server-side",
"Given expired or exhausted code When applied Then HTTP 422 with reason",
"Given client-supplied discount amount Then discount ignored",
"verify.sh green"
],
"status": "pending",
"created_at": "2026-08-14",
"gates": {
"review": false,
"security": false,
"qa": false
}
},
{
"id": "F-020",
"type": "feature",
"title": "Shipping module",
"problem": "Checkout cannot quote delivery cost.",
"goal": "Shipping zones, methods and cost calculation behind ShippingService.",
"scope_in": [
"shipping module with zones and methods",
"ShippingService.calculate(cart, address) interface",
"Free shipping threshold rule",
"Carrier adapter interface (implementation later)"
],
"scope_out": [
"No real carrier API integration yet",
"No tracking"
],
"priority": "med",
"risk": "low",
"depends_on": [
"F-002",
"F-018"
],
"description": "Problem: Checkout cannot quote delivery cost. Goal: zones, methods and cost calculation behind ShippingService. Scope IN: shipping module, zones, methods, threshold, adapter interface. Scope OUT: no carrier API. Type: feature. Priority: med. Risk: low.",
"acceptance": [
"Given address in known zone When calculate Then shipping cost returned",
"Given address outside all zones When checkout Then HTTP 422 with clear reason",
"Given cart above free shipping threshold Then shipping cost is zero",
"verify.sh green"
],
"status": "pending",
"created_at": "2026-08-14",
"gates": {
"review": false,
"security": false,
"qa": false
}
},
{
"id": "F-021",
"type": "feature",
"title": "Orders module with snapshots and state machine",
"problem": "Nothing records purchase truth; history must never depend on live catalog.",
"goal": "Order aggregate with item snapshots and explicit state transitions.",
"scope_in": [
"orders module",
"Order items snapshot: name, SKU, EAN, unit price, discount, tax, quantity",
"States: PENDING, AWAITING_PAYMENT, PAID, PROCESSING, SHIPPED, DELIVERED, CANCELLED, REFUNDED, PARTIALLY_REFUNDED",
"Explicit state machine, illegal transitions rejected",
"Domain events: OrderCreated, OrderPaid, OrderCancelled"
],
"scope_out": [
"No refunds execution yet (payments ticket)",
"No admin order UI"
],
"priority": "high",
"risk": "med",
"depends_on": [
"F-002",
"F-017"
],
"description": "Problem: Nothing records purchase truth; history must never depend on live catalog. Goal: order aggregate with item snapshots and explicit state machine. Scope IN: orders module, snapshots, state machine, domain events. Scope OUT: no refunds execution, no admin UI. Type: feature. Priority: high. Risk: med.",
"acceptance": [
"Given an order When product is later renamed or repriced Then order keeps original snapshot values",
"Given order in SHIPPED When transition to PENDING requested Then transition rejected",
"Every legal state transition is covered by unit test",
"OrderCreated event published on creation",
"verify.sh green"
],
"status": "pending",
"created_at": "2026-08-14",
"gates": {
"review": false,
"security": false,
"qa": false
}
},
{
"id": "F-022",
"type": "feature",
"title": "Checkout orchestrator with idempotency",
"problem": "No flow turns a cart into a payable order safely.",
"goal": "Checkout coordinates validation, pricing, stock, discounts, shipping, taxes, order creation and reservation.",
"scope_in": [
"checkout module as orchestrator only, owning no business data",
"Flow: validate products, prices, stock, discounts, shipping, taxes, create pending order, reserve inventory, create payment intent",
"idempotency_key required on checkout request",
"HTTP 409 on unavailable stock without order or payment",
"checkout_success_total and checkout_failure_total metrics"
],
"scope_out": [
"No payment provider implementation yet (interface stub)",
"No checkout UI yet"
],
"priority": "high",
"risk": "high",
"depends_on": [
"F-018",
"F-019",
"F-020",
"F-021"
],
"description": "Problem: No flow turns a cart into a payable order safely. Goal: checkout orchestrates validation, pricing, stock, discounts, shipping, taxes, pending order, reservation, payment intent. Scope IN: orchestrator flow, idempotency_key, 409 handling, metrics. Scope OUT: no provider implementation, no UI. Type: feature. Priority: high. Risk: high.",
"acceptance": [
"Given a cart containing an unavailable item When checkout requested Then HTTP 409, no order created, no payment initiated",
"Given same idempotency_key sent twice When second request arrives Then same order returned, no duplicate order, no duplicate reservation",
"Given successful checkout Then order exists in AWAITING_PAYMENT and stock is reserved",
"Given checkout failure after reservation When flow aborts Then reservation released",
"verify.sh green"
],
"status": "pending",
"created_at": "2026-08-14",
"gates": {
"review": false,
"security": false,
"qa": false
}
},
{
"id": "F-023",
"type": "feature",
"title": "Payments: provider interface + Stripe + webhooks",
"problem": "Checkout needs real money movement without coupling domain to Stripe.",
"goal": "PaymentProvider interface with Stripe adapter and idempotent webhook processing.",
"scope_in": [
"payments module with PaymentProvider interface",
"StripePaymentProvider adapter; domain never imports Stripe SDK",
"Webhook: validate signature, deduplicate event, process, publish domain event",
"Events: PaymentSucceeded, PaymentFailed, PaymentRefunded, ChargebackCreated",
"payments_transactions table as source of truth"
],
"scope_out": [
"No Redsys/PayPal yet",
"No payout reconciliation"
],
"priority": "high",
"risk": "high",
"depends_on": [
"F-022"
],
"description": "Problem: Checkout needs real money movement without coupling domain to Stripe. Goal: PaymentProvider interface, Stripe adapter, idempotent webhooks. Scope IN: interface, adapter, webhook validation/dedup, domain events. Scope OUT: no Redsys/PayPal. Type: feature. Priority: high. Risk: high.",
"acceptance": [
"Domain code contains zero direct Stripe SDK imports",
"Given webhook with bad signature When received Then rejected with non-2xx and not processed",
"Given same webhook delivered twice When second delivery arrives Then processed exactly once",
"Given PaymentSucceeded When processed Then order moves to PAID and event published",
"Payment status from frontend is never trusted",
"verify.sh green"
],
"status": "pending",
"created_at": "2026-08-14",
"gates": {
"review": false,
"security": false,
"qa": false
}
},
{
"id": "F-024",
"type": "feature",
"title": "Notifications: transactional email",
"problem": "Customers get no feedback after purchase or payment failure.",
"goal": "Transactional emails driven by domain events behind a provider interface.",
"scope_in": [
"notifications module",
"Email provider interface with one implementation",
"Templates: order confirmation, payment failed, order shipped",
"Send triggered by domain events, idempotent per event id"
],
"scope_out": [
"No marketing email",
"No SMS/push"
],
"priority": "med",
"risk": "low",
"depends_on": [
"F-023",
"F-005"
],
"description": "Problem: Customers get no feedback after purchase or payment failure. Goal: transactional emails driven by domain events behind provider interface. Scope IN: notifications module, email adapter, templates, idempotent sends. Scope OUT: no marketing, no SMS. Type: feature. Priority: med. Risk: low.",
"acceptance": [
"Given PaymentSucceeded event When processed Then order confirmation email queued",
"Given same event redelivered When processed Then no duplicate email",
"Swapping email provider touches only infrastructure adapter",
"verify.sh green"
],
"status": "pending",
"created_at": "2026-08-14",
"gates": {
"review": false,
"security": false,
"qa": false
}
},
{
"id": "F-025",
"type": "feature",
"title": "Reviews module",
"problem": "Social proof is missing; reviews must be tied to real purchases.",
"goal": "Verified-purchase reviews with moderation and rating aggregates.",
"scope_in": [
"reviews module",
"One review per order item, verified purchase only",
"Moderation states: pending, published, rejected",
"Rating aggregate per product, cached"
],
"scope_out": [
"No photos in reviews",
"No vendor responses"
],
"priority": "low",
"risk": "low",
"depends_on": [
"F-021",
"F-006"
],
"description": "Problem: Social proof is missing; reviews must be tied to real purchases. Goal: verified-purchase reviews with moderation and aggregates. Scope IN: reviews module, verified purchase rule, moderation, aggregates. Scope OUT: no photos. Type: feature. Priority: low. Risk: low.",
"acceptance": [
"Given user without delivered order item When review submitted Then HTTP 403",
"Given second review for same order item When submitted Then HTTP 409",
"Only published reviews appear on product page",
"verify.sh green"
],
"status": "pending",
"created_at": "2026-08-14",
"gates": {
"review": false,
"security": false,
"qa": false
}
},
{
"id": "F-026",
"type": "feature",
"title": "CMS module",
"problem": "Marketing needs landing and content pages without deploys.",
"goal": "Simple CMS with pages, content blocks, slugs and draft/publish states.",
"scope_in": [
"cms module with page and block model",
"Slug-based public routes",
"Draft/publish states",
"Storefront renders published pages"
],
"scope_out": [
"No WYSIWYG builder",
"No versioning"
],
"priority": "low",
"risk": "low",
"depends_on": [
"F-013"
],
"description": "Problem: Marketing needs landing and content pages without deploys. Goal: simple CMS with pages, blocks, slugs, draft/publish. Scope IN: cms module, slug routes, states, rendering. Scope OUT: no builder. Type: feature. Priority: low. Risk: low.",
"acceptance": [
"Given published page When slug requested Then page renders",
"Given draft page When slug requested Then HTTP 404",
"Given duplicate slug When page created Then HTTP 409",
"verify.sh green"
],
"status": "pending",
"created_at": "2026-08-14",
"gates": {
"review": false,
"security": false,
"qa": false
}
},
{
"id": "F-027",
"type": "feature",
"title": "Caching layer with explicit contracts",
"problem": "Hot catalog pages hit the database on every request.",
"goal": "Redis cache for product detail, category listing and navigation with key, TTL and invalidation.",
"scope_in": [
"Cache wrapper with key, TTL, invalidation strategy and source of truth documented per entry",
"Targets: product detail, category listing, navigation, popular searches",
"Invalidation on catalog update events",
"cache_hit_ratio metric"
],
"scope_out": [
"Redis never source of truth",
"No CDN config yet"
],
"priority": "med",
"risk": "low",
"depends_on": [
"F-014"
],
"description": "Problem: Hot catalog pages hit the database on every request. Goal: Redis cache for hot reads with explicit key/TTL/invalidation contracts. Scope IN: cache wrapper, hot targets, event invalidation, hit ratio metric. Scope OUT: Redis never truth, no CDN. Type: feature. Priority: med. Risk: low.",
"acceptance": [
"Every cache entry documents key pattern, TTL, invalidation and source of truth",
"Given catalog update When event published Then related cache entries invalidated",
"Given Redis down When read path runs Then requests still succeed from PostgreSQL",
"verify.sh green"
],
"status": "pending",
"created_at": "2026-08-14",
"gates": {
"review": false,
"security": false,
"qa": false
}
},
{
"id": "F-028",
"type": "feature",
"title": "Security hardening",
"problem": "Baseline security controls are scattered or missing.",
"goal": "Rate limiting, CSRF, audit log, admin MFA and dependency scanning in one pass.",
"scope_in": [
"Global and per-route rate limiting",
"CSRF protection where applicable",
"Audit log for admin mutations",
"MFA for admin accounts",
"Dependency scanning in CI"
],
"scope_out": [
"No WAF/network-level work",
"No pentest"
],
"priority": "med",
"risk": "med",
"depends_on": [
"F-006"
],
"description": "Problem: Baseline security controls are scattered or missing. Goal: rate limiting, CSRF, audit log, admin MFA, dependency scanning. Scope IN: all listed controls. Scope OUT: no WAF, no pentest. Type: feature. Priority: med. Risk: med.",
"acceptance": [
"Given admin mutation When executed Then audit log row with actor, action and target",
"Given admin login without MFA When attempted Then blocked until MFA enrolled",
"Given request above rate limit Then HTTP 429",
"CI fails on known vulnerable dependency",
"verify.sh green"
],
"status": "pending",
"created_at": "2026-08-14",
"gates": {
"review": false,
"security": false,
"qa": false
}
},
{
"id": "F-029",
"type": "feature",
"title": "Observability: traces and business metrics",
"problem": "Operations cannot see checkout health or latency.",
"goal": "OpenTelemetry traces plus Prometheus business metrics.",
"scope_in": [
"OpenTelemetry tracing across modules",
"Metrics: checkout_success_total, checkout_failure_total, payment_failure_total, order_created_total, inventory_conflict_total, api_latency, database_latency, cache_hit_ratio",
"request_id and trace_id on every request",
"Grafana dashboard baseline"
],
"scope_out": [
"No alerting rules beyond basics",
"No log aggregation infra"
],
"priority": "med",
"risk": "low",
"depends_on": [
"F-023"
],
"description": "Problem: Operations cannot see checkout health or latency. Goal: OTel traces plus Prometheus business metrics. Scope IN: tracing, listed metrics, ids, dashboard. Scope OUT: no full alerting stack. Type: feature. Priority: med. Risk: low.",
"acceptance": [
"Every request carries request_id and trace_id in logs",
"Each listed metric is exposed on /metrics and changes on the corresponding event",
"A checkout flow produces a connected trace across modules",
"verify.sh green"
],
"status": "pending",
"created_at": "2026-08-14",
"gates": {
"review": false,
"security": false,
"qa": false
}
},
{
"id": "F-030",
"type": "feature",
"title": "E2E suite for critical flows",
"problem": "Critical user journeys have no end-to-end protection.",
"goal": "Few but real E2E tests covering the money path.",
"scope_in": [
"E2E runner setup",
"Flows: register, login, search, view product, add to cart, checkout, payment, order confirmation, refund",
"Runs against disposable environment"
],
"scope_out": [
"No visual regression",
"No load testing"
],
"priority": "high",
"risk": "low",
"depends_on": [
"F-022",
"F-023"
],
"description": "Problem: Critical user journeys have no end-to-end protection. Goal: E2E tests covering the money path. Scope IN: runner, critical flows, disposable env. Scope OUT: no visual regression, no load testing. Type: feature. Priority: high. Risk: low.",
"acceptance": [
"Each critical flow has one green E2E test",
"E2E suite runs unattended in CI",
"Given payment webhook simulated in E2E Then order reaches PAID",
"verify.sh green"
],
"status": "pending",
"created_at": "2026-08-14",
"gates": {
"review": false,
"security": false,
"qa": false
}
}
]
}