# F-138 — Implementer evidence ## What Closed the `GET /pricing/variants/:id` 404 race: `PricingService.seedVariantPrice` (pricing owns its table) is injected into `CreateProductVariant` and called best-effort right after variant creation. The single constructor covers all 3 call sites (product autovariant, lazy variant GET, explicit POST variant). ## Design recap (architect-approved) - `PricingRepository` + `PricingService` gain `seedVariantPrice(variantId): Promise`. - `PgPricingRepository.seedVariantPrice`: `INSERT pricing_variant_prices(variant_id, net_unit_amount_cents, offer_cents, cost_cents, vat_rate) VALUES ($1, 0, NULL, NULL, 'general') ON CONFLICT (variant_id) DO NOTHING` — same column set as `setVariantPrice` (`currency`/`created_at`/`updated_at` via DDL defaults). Idempotent + parameterized. - `CreateProductVariant` ctor += `pricing: PricingServicePort` (type-only import of pricing **public index** → R1-clean); after `variants.create`, best-effort `await this.pricing.seedVariantPrice(variant.id)` (try/catch: variant already persisted; seed failure must not fail/roll back variant creation). - `catalog.routes.ts`: `CatalogRoutesDeps += pricing`; `new CreateProductVariant(repository, variants, deps.pricing)`. - `build-app.ts`: hoist `const pricing = createPricingService(deps.pool)` above `registerCatalogRoutes` so it can be passed in; pricing routes + cart keep using it (unchanged). ## Files changed (source) - `pricing/domain/ports.ts` — +`seedVariantPrice` on `PricingService` + `PricingRepository`. - `pricing/application/pricing-service.ts` — +`seedVariantPrice` delegate. - `pricing/infrastructure/pg-pricing-repository.ts` — +`seedVariantPrice` impl. - `catalog/application/variant-use-cases.ts` — import `PricingServicePort`; ctor + execute seed (best-effort). - `catalog/api/catalog.routes.ts` — `CatalogRoutesDeps += pricing: PricingServicePort`; ctor passes `deps.pricing`. - `app/build-app.ts` — hoist `pricing` const; pass to `registerCatalogRoutes` deps. ## Tests - **NEW** `catalog/tests/variant-use-cases.test.ts` (3 tests, runnable no DB): FakeProductRepository + FakeProductVariantRepository + FakePricingService — asserts seed-called-with-variant.id, no-seed-when-product-missing, seed-failure-doesn't-break-creation. - `catalog.itest.ts` AC itest (skipIf !hasDb): POST `/products/:id/variants` → `pricing_variant_prices` row exists with `net=0/vat=general/currency=EUR`. - Collateral (fake fakes updated to satisfy new port method): `pricing/tests/pricing-service.test.ts` repo() factory, `cart/tests/cart-service.test.ts` + `checkout/tests/checkout-service.test.ts` PricingServicePort fakes — added `seedVariantPrice: async () => undefined` no-op. ## Verification (fresh run) - `npx tsc --noEmit` → **0 errors** (strict, noUncheckedIndexedAccess). - `npx vitest run` (targeted: variant/pricing/cart/checkout/product/build-app) → **16/16 pass**. - `npm test` → **209 passed | 57 skipped** (was 206 → +3 new; no regression in cart/checkout/pricing/build-app). - `scripts/check-module-boundaries.mjs src` → **0 NEW R1 violations**. (Pre-existing R1 on `security.routes.ts:14`→`log-broadcaster` is out of F-138 scope; git diff proves untouched.) - `./scripts/verify.sh` → **VERIFY_OK** (pre-close).