BACKEND · v186

Live Dutchie data layer — cut the cache

2026-06-09 · backend architecture update · no frontend change (v185 remains the latest UI) · promos untouched

The problem

The site read product data from a D1 scrape cache — a daily copy of Dutchie kept in our own database. A copy goes stale the instant Dutchie changes, which caused wrong prices (From $Infinity), search that ranked rolling papers above flower, an unreliable "available at N stores" widget, and a fragile hand-patch workflow. Root cause: the cache was a second source of truth that drifts.

The decision (Solution A)

Built + live-verified this update

Isolated preview workersessions-availability-edge-demo on Darryl's account, own empty D1. Proven it cannot see prod data, so nothing it does affects the live promos campaign.

What V1–V4 confirmed

Search & filters — status

Search ranking — FIXED. The v2 ranker replaced the broken substring match: "blueberry" → flower (not rolling papers), "510" → cartridges, "gummies" → gummies, "pink kush" → Pink Kush flower #1.

Filter pass-through — partially wired. Dutchie's MenuFilter surface, mapped this session:

FilterNative in Dutchie?Live in /api/v2/products?
search
category
brand (brandId)
strain type (indica/sativa/hybrid)pending
subcategorypending
potency (THC / CBD)pending
effectspending
price range (min/max)❌ not nativeworker-side, pending
sort (price / potency)❌ not nativeworker-side, pending
weight / on-sale❌ not nativeworker-side, pending

The first three pass straight through to Dutchie and are live; strain/subcategory/potency/effects are native to Dutchie and wire the same way (pending); price-range/sort/weight/on-sale aren't native — they get applied in the worker after fetch, the same place the ranker runs. The mobile filter UI (the v185 bottom-sheet) is a separate frontend fix already shipped in v185 — untouched by this backend update.

Test plan — 50 QA questions (Opus)

The use-case checklist to run before this goes live. Each maps to a pass/fail test against the live data layer; status filled in as tests run.

A. Store entry & scoping

  1. Age-gate to mandatory store-pick blocks all browsing until a store is chosen (no skip, no default-to-all)?
  2. The chosen retailerId persists across refresh, deep-link, and back-button so every page stays store-scoped?
  3. Deep-linking to a PDP/shop URL with no store selected routes to the picker, not a blank/empty page?
  4. Switching store mid-session clears/re-validates the cart and refreshes prices to the new store?
  5. The store list matches the canonical dispensary CSV (UUID + dispensaryId), with closed/unmapped stores excluded?

B. Search & ranking

  1. Search returns the right #1 for name queries (blueberry to flower, not rolling papers)?
  2. Multi-word queries work regardless of word order (blue dream / dream blue)?
  3. Accessories demoted unless intended (510 to cartridges; 510 battery to batteries)?
  4. A brand-name query surfaces that brand's items even when the brand isn't in the product name?
  5. Dutchie's fuzzy zero-token-match items are suppressed from the top?
  6. Empty/whitespace query gives a sensible browse fallback, not an error or empty list?
  7. Search latency stays acceptable (under ~700 ms p95) on the live path?

C. Filters

  1. Category filter returns only that category and combines correctly with search?
  2. Strain-type / potency / subcategory / effects return correct subsets (once wired)?
  3. Worker-side price-range bounds min/max correctly across a product's variants?
  4. Sort (price/potency, asc/desc) orders correctly and stays stable across pagination?
  5. Multiple filters AND together correctly (FLOWER + INDICA + under $40)?
  6. Removing/clearing a filter restores the full set?
  7. Zero-match filter combos show a clean empty state, not an error?

D. Browse & pagination

  1. Category browse paginates past Dutchie's ~20/page with no dupes, no gaps, correct total?
  2. Cards show correct image, name, brand, min price, in-stock state?
  3. Out-of-stock products handled consistently per policy (hidden vs sold-out)?

E. Product detail (PDP)

  1. PDP shows live price + the exact variants/options Dutchie has at that store?
  2. Multi-variant (3.5g/7g/28g) selection updates price/stock correctly?
  3. PDP price equals what Dutchie charges at checkout (no drift)?
  4. Potency/strain/brand/description render live, with graceful blanks when missing?

F. Cross-store Available at N stores

  1. Lists exactly the stores that stock the product (by enterpriseProductId, not name)?
  2. Each store's price + qty is live and equals that store's own menu?
  3. N-of-M counts accurate and sorted by distance/price as intended?
  4. Behavior verified at 0 / 1 / 50 stocking stores (latency, subrequest budget)?
  5. A store that errors (404/timeout) is skipped gracefully, not crashing the widget?
  6. With the rebuilt index live, the cart re-validates the chosen store's price before adding?

G. Cart & Cannabis Act

  1. Add-to-cart carries product_id + variant_id (not name) through to checkout?
  2. 30g dried-equivalent cap correct across flower / pre-rolls / vapes / edibles / beverages?
  3. Gram-math bug fixed (a 355 ml beverage must not count as 355 g)?
  4. Cart blocks checkout above 30g with a clear message?
  5. Steppers / remove / subtotal update correctly and match Dutchie pricing?
  6. Cart is store-scoped (adding a different store's item behaves per policy)?

H. Checkout

  1. Checkout creates the Dutchie checkout, adds items by ID, lands on the dispensary subdomain with the right items?
  2. Our cart total equals the Dutchie checkout total at handoff?
  3. For a Cova-only / not-in-Dutchie SKU, it fails safe (clear message), not silently empty/substitute?
  4. Checkout works across several different stores, not just the one tested?

I. Live correctness & freshness

  1. All live endpoints no-cache (or correct short-TTL) so a Dutchie price change reflects within the window (no stale edge cache)?
  2. No remaining user-facing read path to the old D1 cache?
  3. A Dutchie stock/price change appears on refresh within the expected TTL?

J. Performance & resilience

  1. Cross-store fan-out stays within the Worker's 50-subrequest + CPU limits at 50 stores?
  2. Under Dutchie 429 rate-limiting: backoff/partial results, not a hard fail?
  3. If seshweed-api (the Dutchie proxy) is down, the site degrades with a clear message, not a blank page?

K. Isolation, security, compliance

  1. Proven the demo worker cannot read/write the prod/promos D1 under every endpoint?
  2. The hardcoded Dutchie token is rotated into a secret, and new surfaces meet the floors (Cannabis Act purchase-on-Dutchie, AODA/WCAG 2.1 AA)?

What's next

Companion records in Studio consciousness: SADD-001 deep-dive · SADD-001b ranker · SADD-002 schema · SADD-003 Solution A · SADD-004 summary. Demo worker: sessions-availability-edge-demo.darryl-ef5.workers.dev