Epic 003 traceability

Contract

Traceability is bidirectional:

acceptance scenario → one or more executable/manual evidence references
test declaring scenario ID → one existing acceptance scenario

Every executable test that proves an Epic 003 scenario includes the exact stable scenario ID in its title or native metadata. Playwright follows the repository's title convention:

test("[MVP-KDS-002-S02] preparing order advances to ready", async ({ page }) => {});

Vitest/integration tests use the same searchable bracketed ID. One test may prove multiple scenarios when the title or enclosing describe metadata keeps every ID machine-searchable. Incidental test coverage is not claimed as acceptance evidence unless the ID is declared.

Evidence classes:

Not every scenario requires browser E2E. Headline journeys and acceptance scenarios may both use Playwright, but journey tests demonstrate a coherent long flow while scenario evidence proves the smaller contracts.

Status model

Task status remains separately governed by the task index. This document starts at Specified because this change implements no application behavior or acceptance tests.

Headline journey mapping

Journey ID Evidence reference Task Status
MVP-JRN-001 tests/e2e/journeys.spec.ts headline Playwright journey MVP-TASK-035 Implemented
MVP-JRN-002 tests/e2e/journeys.spec.ts commercial-loop Playwright journey plus order-history.integration.test.ts durable-history evidence MVP-TASK-036 Implemented
MVP-JRN-003 tests/e2e/journeys.spec.ts no-checkout Playwright journey plus postgres-order-repository.integration.test.ts classification/sales-exclusion evidence MVP-TASK-037 Implemented
MVP-JRN-004 tests/e2e/journeys.spec.ts checkout-failure-safety Playwright journeys plus postgres-order-repository.integration.test.ts outcome/concurrency evidence MVP-TASK-038 Implemented

Scenario-to-evidence index

Ranges below are only compact references to individually defined stable IDs; implementations must name each exact scenario ID they prove. Replace Planned with concrete repository test path/title references as tasks land.

Scenario ID(s) Expected evidence Task(s) Test/evidence reference Status
MVP-PROD-001-S01 E2E + API/component 001 products.spec.ts; auth-http.test.ts; postgres-product-repository.integration.test.ts; ProductsPage.test.tsx Implemented
MVP-PROD-001-S02 Component/API integration 001 auth-http.test.ts; postgres-product-repository.integration.test.ts; ProductsPage.test.tsx Implemented
MVP-PROD-001-S03 E2E + API 001 products.spec.ts; auth-http.test.ts Implemented
MVP-PROD-002-S01–S02 E2E + API/database integration 002 products.spec.ts; auth-http.test.ts; product.integration.test.ts; postgres-product-repository.integration.test.ts; ProductsPage.test.tsx Implemented
MVP-PROD-002-S03 Component/API + representative E2E 003 product-policy.test.ts; product-service.test.ts; auth-http.test.ts; product.integration.test.ts; ProductsPage.test.tsx; products.spec.ts Implemented
MVP-PROD-003-S01 E2E + API/database integration 004 products.spec.ts; auth-http.test.ts; product.integration.test.ts; postgres-product-repository.integration.test.ts; product-update-api.test.ts; ProductsPageEdit.test.tsx; product-service.test.ts Implemented
MVP-PROD-003-S02 Database integration 023 order-history.integration.test.ts "[MVP-PROD-003-S02] leaves a finalized snapshot untouched when the catalog is edited" proves a rename, relabel, reprice, and VAT reassignment leave the finalized item snapshot and finalization fact byte-identical while a later order uses the updated data Implemented
MVP-PROD-004-S01 E2E + API/database integration 005 product-service.test.ts; postgres-product-repository.test.ts; postgres-product-repository.integration.test.ts; auth-http.test.ts; product.integration.test.ts; product-api.test.ts; ProductsPageDelete.test.tsx; products.spec.ts Implemented
MVP-PROD-004-S02 Database integration 023 order-history.integration.test.ts "[MVP-PROD-004-S02] keeps a finalized snapshot and its history valid when the product is deleted" proves deleting the catalog row leaves the item snapshot, finalization fact, and fulfillment history readable and unchanged Implemented
MVP-ORD-001-S01 Component + E2E 006 KdsPage.test.tsx; kds.spec.ts; accessibility.spec.ts Implemented
MVP-ORD-001-S02–S04 Component + E2E 007 KdsPage.test.tsx; kds.spec.ts Implemented
MVP-ORD-001-S05–S06 Unit/component + journey 008 KdsPage.test.tsx; kds.spec.ts Implemented
MVP-ORD-001-S07–S08 Component/E2E + persistence absence 009 KdsPage.test.tsx; kds.spec.ts; architecture/static absence of an order persistence boundary Implemented
MVP-ORD-001-S09 E2E 012–013 KdsPage.test.tsx; kds.spec.ts (non-commercial path; commercial continuation remains task 013) Implemented
MVP-ORD-002-S01–S06 E2E + integration 010 KdsPage.test.tsx; kds.spec.ts (capture/review boundary; durable PostgreSQL finalization continues in task 012) Implemented
MVP-ORD-003-S01–S05 E2E + application/database integration 012, 027 order-service.test.ts; postgres-order-repository.integration.test.ts; KdsPage.test.tsx; kds.spec.ts (finalization; later history/sales presentation remains task 027) Implemented
MVP-PAY-001-S01, S05–S06 E2E + application/database integration 013, 023 order-service.test.ts; order-http.test.ts; auth-http.test.ts; postgres-order-repository.integration.test.ts; order-api.test.ts; KdsPage.test.tsx; kds.spec.ts (commercial finalization; later history remains task 023) Implemented
MVP-PAY-001-S02 E2E + integration 028 tests/e2e/kds.spec.ts "[MVP-PAY-001-S02] [MVP-SALES-001-S07] a definitively declined payment is unpaid, unqueued, uncounted, and safely recoverable" drives both definitive classes through the real stack; postgres-order-repository.integration.test.ts "[MVP-PAY-001-S02] [MVP-SALES-001-S07] persists nothing for a definitive refusal and leaves the day's sales untouched"; order-service.test.ts, order-http.test.ts, fake-checkout-gateway.test.ts, checkout-composition.test.ts, config.test.ts, KdsPage.test.tsx cover the application, API, adapter, configuration, and UI layers Implemented
MVP-PAY-001-S03 E2E + durable-outcome integration 029 postgres-order-repository.integration.test.ts "[MVP-PAY-001-S03] [MVP-REL-001-S02] [MVP-SALES-001-S07] durably records an unconfirmed attempt without an order, a queue entry, or a sale" runs the real checkout boundary, service, and PostgreSQL write, then reconstructs the identical attempt through a fresh repository/pool, proves the row is append-only, unresolvable, and unique, and that a later confirmed sale neither resolves nor counts it; kds.spec.ts "[MVP-PAY-001-S03] [MVP-REL-001-S02] [MVP-SALES-001-S07] an unconfirmed payment stays visible across a reload, blocks a blind retry, and is never counted as a sale" drives the disabled retry control, the durable banner after page.reload(), and the unchanged ticker in desktop/mobile; order-service.test.ts, order-http.test.ts, checkout-composition.test.ts, fake-checkout-gateway.test.ts, and config.test.ts prove the distinct error, the CHECKOUT_UNRESOLVED contract, and the amount-keyed unconfirmed outcome; order-api.test.ts and KdsPage.test.tsx prove the read boundary and that the copy claims neither outcome Implemented
MVP-PAY-001-S04 Concurrency integration + E2E 030 postgres-order-repository.integration.test.ts "[MVP-PAY-001-S04] admits one provider operation under concurrent and repeated checkout initiation" proves one fake-provider call across two repositories and safe replay; KdsPage.test.tsx and kds.spec.ts carry the scenario ID for disabled/progress behavior and one browser request; checkout-operation-id.test.ts proves the client identity works without the secure-context-only randomUUID Implemented
MVP-KDS-001-S01, S04 Integration + E2E/component 014, 015 postgres-order-repository.integration.test.ts; order-service.test.ts; order-http.test.ts; auth-http.test.ts; order-api.test.ts; KdsPage.test.tsx; kds.spec.ts (tablet case now also asserts S02/S03) Implemented
MVP-KDS-001-S02–S03 Component + tablet E2E 015 KdsPage.test.tsx; kds.spec.ts (768×1024 tablet queue; next-three window, kitchen-label snapshots, explicit quantities, no drill-down) Implemented
MVP-KDS-001-S05–S06 Unit/component + tablet E2E 016 order-fingerprint.test.ts; KdsPage.test.tsx; kds.spec.ts (768×1024 tablet; per-order compact overflow, queue number preferred, item fingerprint with safe truncation, no internal IDs) Implemented
MVP-KDS-001-S07–S08 Component + tablet E2E 017 KdsPage.test.tsx; kds.spec.ts (768×1024 tablet; 15 active fixtures plus a polled 16th order, one representation per active order, visible accurate count, last entry scrolled into view) Implemented
MVP-KDS-002-S01 E2E + integration 018 kds.spec.ts "[MVP-KDS-002-S01] … advances a queued order to preparing"; postgres-order-repository.integration.test.ts "Queued to Preparing transition in PostgreSQL"; KdsPage.test.tsx; order-service.test.ts; order-http.test.ts Implemented
MVP-KDS-002-S02 E2E + integration 019 kds.spec.ts "[MVP-KDS-002-S02] … queued through preparing to ready"; postgres-order-repository.integration.test.ts "[MVP-KDS-002-S02] [MVP-HIST-001-S02] … Preparing to Ready"; KdsPage.test.tsx; order-service.test.ts; order-http.test.ts Implemented
MVP-KDS-002-S03 E2E + integration 020 kds.spec.ts "[MVP-KDS-002-S03] … delivers the order out of the queue"; postgres-order-repository.integration.test.ts "[MVP-KDS-002-S03] [MVP-HIST-001-S07] … while its record and history stay durable"; KdsPage.test.tsx; kitchen-queue-card.test.tsx; order-service.test.ts; order-http.test.ts Implemented
MVP-KDS-002-S04–S05 E2E + transition-history integration 021 kds.spec.ts tagged transition sequence; postgres-order-repository.integration.test.ts tagged full chronology; KdsPage.test.tsx; order-service.test.ts; order-http.test.ts; order-api.test.ts Implemented
MVP-KDS-002-S06 Component + E2E 022 kitchen-state-selector.spec.ts "[MVP-KDS-002-S06] … offers an explicit safely constrained state selector"; kitchen-queue-card.test.tsx explicit selector cases; KdsPage.test.tsx "[MVP-KDS-002-S06] … rejects unavailable states" Specified
MVP-KDS-003-S01–S04 E2E + database integration 024 kds.spec.ts tagged edit/clear display case; order-history.integration.test.ts tagged immutable commercial-facts case Implemented
MVP-HIST-001-S02 Database integration 018–020, 023 postgres-order-repository.integration.test.ts tagged cases prove exactly-once timestamped Queued→Preparing, Preparing→Ready, and Ready→Delivered history; order-history.integration.test.ts "[MVP-HIST-001-S02] [MVP-HIST-001-S03] appends every forward and backward transition in trustworthy order" adds the mixed forward/backward sequence with bracketed, non-decreasing database timestamps and a fresh-connection readback Implemented
MVP-HIST-001-S03 Database integration 021, 023 postgres-order-repository.integration.test.ts tagged full backward-correction chronology; kds.spec.ts proves the same sequence through the browser; order-history.integration.test.ts re-proves the four-fact chronology with database-time ordering through a new connection Implemented
MVP-HIST-001-S07 Database integration 020, 023 postgres-order-repository.integration.test.ts "[MVP-KDS-002-S03] [MVP-HIST-001-S07] … while its record and history stay durable" proves the delivered order row, item snapshots, and full history survive leaving the active queue; kds.spec.ts proves the same through the browser; order-history.integration.test.ts "[MVP-HIST-001-S07] keeps every fact durable when an order leaves the active queue" adds the finalization fact, checkout facts, and complete history after delivery Implemented
MVP-HIST-001-S01, S06 Database integration 023, 032 order-history.integration.test.ts "[MVP-HIST-001-S01] keeps the finalized snapshot durable across a catalog change and a fresh connection" proves the internal ID, classification, fulfillment metadata, quantities, and product snapshots read back unchanged through a new repository and pool after the catalog was edited; "[MVP-HIST-001-S06] keeps the checkout attempt, outcome, timestamps, and reference associated with the order" proves attempt, outcome, charged gross, provider reference, and the bracketed database recorded time stay associated with the internal order identity and survive every transition and delivery; MVP-TASK-032 adds the whole-stack process restart: tests/docker/backend-lifecycle.mjs reads the same snapshot back through the packaged gateway after really replacing the backend process, and order-restart-durability.integration.test.ts rebuilds it through a fresh repository, pool, and service graph with every fact-table row count unchanged Implemented
MVP-HIST-001-S04–S05 Database integration 024 order-history.integration.test.ts tagged metadata edit/clear case proves formatted old/new facts and database timestamps Implemented
MVP-SALES-001-S01 Integration + tablet E2E 025 backend/src/repository/postgres-order-repository.integration.test.ts; tests/e2e/kds.spec.ts; supporting API/component evidence in backend/src/order-http.test.ts, frontend/src/app/api/order-api.test.ts, and frontend/src/pages/KdsPage.test.tsx Implemented
MVP-SALES-001-S02–S04, S08 Integration/component/tablet E2E 026 backend/src/repository/postgres-order-repository.integration.test.ts "[MVP-SALES-001-S02] [MVP-SALES-001-S04] [MVP-SALES-001-S08] groups today's quantities by stable product identity and keeps one counter through a rename" proves the real application path aggregating by product identity through a rename; tests/e2e/kds.spec.ts "[MVP-SALES-001-S02] [MVP-SALES-001-S03] [MVP-SALES-001-S04] shows today's per-product counts beside the gross in the tablet's persistent top region"; frontend/src/pages/KdsPage.test.tsx mixed-label breakdown and empty-day cases; backend/src/order-http.test.ts and frontend/src/app/api/order-api.test.ts pass-through evidence Implemented
MVP-SALES-001-S05–S07, S09, S10 Integration + E2E/restart evidence 027–029, 032 postgres-order-repository.integration.test.ts "[MVP-SALES-001-S05] [MVP-SALES-001-S06] [MVP-SALES-001-S07] [MVP-SALES-001-S09] excludes non-sales, ignores fulfillment, and reconstructs the same figures through a fresh repository" proves non-commercial plus rejected/unknown checkout exclusion, every forward/backward fulfillment change through delivery, repeated reads, and reconstruction through a new repository/pool; kds.spec.ts tags the non-commercial unchanged-sales journey, the confirmed-sale browser reload, the "[MVP-PAY-001-S02] [MVP-SALES-001-S07]" definitive-decline journey, and the "[MVP-PAY-001-S03] [MVP-REL-001-S02] [MVP-SALES-001-S07]" unconfirmed-attempt journey (unchanged ticker beside a real sale) in desktop/mobile; MVP-TASK-029 additionally proves the exclusion is by construction — an unresolved attempt has no order, item snapshot, or finalization fact to join — through a fresh repository/pool; MVP-TASK-032 adds the data-bearing whole-process restart: tests/docker/backend-lifecycle.mjs replaces the packaged backend process and re-reads an identical active queue, identical today's figures, and identical durable fact rows with unchanged row counts, while order-restart-durability.integration.test.ts reproduces the same claim at the storage boundary Implemented
MVP-REL-001-S02 Integration + E2E using the deterministic fake 029 postgres-order-repository.integration.test.ts and kds.spec.ts "[MVP-PAY-001-S03] [MVP-REL-001-S02] [MVP-SALES-001-S07] durably records an unconfirmed attempt without an order, a queue entry, or a sale" / "an unconfirmed payment stays visible across a reload, blocks a blind retry, and is never counted as a sale" prove that a checkout which cannot obtain a definitive provider result produces explicit unknown semantics, a durable visible record, and no paid or sales state whatsoever Implemented
MVP-REL-001-S01, S03, S05 Frontend/application integration + E2E 031 KdsPage.test.tsx "[MVP-REL-001-S01] [MVP-REL-001-S03] [MVP-REL-001-S05] keeps loaded operational data visible and marks it stale during a backend outage" forces queue, sales, and unresolved-payment refreshes offline after frontend state has loaded; postgres-order-repository.integration.test.ts "[MVP-REL-001-S02] [MVP-REL-001-S03] isolates a provider failure from unrelated active orders and sales" proves a throwing checkout boundary creates no paid/order facts and leaves the real PostgreSQL queue and sales byte-identical; kds.spec.ts "[MVP-REL-001-S01] [MVP-REL-001-S03] [MVP-REL-001-S05] a lost backend request is visible, keeps the draft, and leaves the existing queue intact" aborts the browser finalization request and proves visible failure, no offline/sync/success claim, retained draft, and intact durable queue Implemented
MVP-REL-001-S04 Docker/process restart + integration 032 tests/docker/backend-lifecycle.mjs creates two real finalized orders through the packaged gateway, drives one of them out of the active queue while leaving the other mid-fulfillment, then really replaces the backend process (Compose stop, clean SIGTERM exit 0, start again, and a different container PID and start time) and re-reads through the same gateway. The active queue, today's commercial figures, and every durable order, item, finalization, transition, checkout, and metadata row come back identical, and the row counts of all six fact tables are unchanged, so nothing was reset and nothing was duplicated. Compose generates the local signing secret per start, so the run signs in again after the restart and no surviving record can come from process memory. order-restart-durability.integration.test.ts carries the same claim at the storage boundary: after every connection is closed, a fresh repository, pool, and service graph rebuilds all four facets and appends nothing. No host crash, high availability, or backup/restore is simulated Implemented
MVP-TECH-001-S01 Unit/integration 011 checkout-service.test.ts; fake-checkout-gateway.test.ts; checkout-composition.test.ts; config.test.ts Implemented
MVP-TECH-001-S02–S03 Integration + architecture/static review 011, 034 checkout-service.test.ts; fake-checkout-gateway.test.ts; checkout-composition.test.ts; checkout-boundary.test.mjs; continued by MVP-TASK-034 Implemented
MVP-TECH-002-S01 Discovery/manual 033 docs/decisions/004-surfboard-integration-discovery.md "Answers to the discovery questions (MVP-TECH-002-S01)" — twenty sourced Yes/No/Unknown answers dated 2026-10-09, explicit unknowns register, Demo and human-owned account/ECR/Skatteverket prerequisites Implemented
MVP-TECH-002-S02 Architecture/static review + discovery/manual 033 docs/decisions/004-surfboard-integration-discovery.md environment-topology section keeps local/test on the deterministic fake independently of Surfboard; existing checkout-boundary.test.mjs MVP-TECH-001 executable evidence is unchanged by this documentation-only task Implemented
MVP-TECH-002-S03 Review of discovery record and adapter task 033, 034 ADR 004 defines the boundary; surfboard-checkout-gateway.test.ts, surfboard-webhook.test.ts, checkout-composition.test.ts, and config.test.ts cover the implemented payment/configuration/webhook portion. Fiscal and real Demo evidence remain explicitly blocked in MVP-TASK-034. Blocked

Maintenance and automation

When a task lands:

  1. Keep the scenario ID and story text stable; clarify without renumbering.
  2. Add exact test file and title (or decision/manual record) to this index.
  3. Change only scenarios whose required evidence actually exists and passed.
  4. Update affected journey references after the long-flow evidence exists.
  5. Record gaps honestly in current state/handover.

MVP-TASK-039 implements pnpm traceability:check (scripts/traceability-check.mjs), the mechanical half of this contract, inside pnpm quality. It rejects duplicate scenario/journey IDs, tests declaring nonexistent IDs, scenarios without a required **Evidence:** declaration, scenarios missing from this index, and index rows that are duplicated, reference undefined scenarios, or carry empty/Planned references; node scripts/traceability-check.mjs --report prints a generated per-scenario index. Fixtures in tests/ci/traceability.test.mjs cover the valid and failure cases; that fixture file is the one documented exclusion from the tracked test-file scan, because it intentionally declares synthetic IDs no story defines. Semantic adequacy of the evidence and the choice of evidence level remain review responsibilities; the checker requires nothing beyond a declared ID and an index row.