[object Object]

← back to Designer Wallcoverings

auto-data-snapshot: 2026-09-10T15:21:44 (1 data files) — DW-Programming/ImportNewSkufromURL/lib/PENDING-WIREIN-price-gate.md

85002803b7691000f2ff3ab512cfa544cef850bd · 2026-09-10 15:22:37 -0700 · auto-commit-fleet

Files touched

Diff

commit 85002803b7691000f2ff3ab512cfa544cef850bd
Author: auto-commit-fleet <steve@designerwallcoverings.com>
Date:   Thu Sep 10 15:22:37 2026 -0700

    auto-data-snapshot: 2026-09-10T15:21:44 (1 data files) — DW-Programming/ImportNewSkufromURL/lib/PENDING-WIREIN-price-gate.md
---
 .../lib/PENDING-WIREIN-price-gate.md               | 126 ++++++++++++++-------
 1 file changed, 87 insertions(+), 39 deletions(-)

diff --git a/DW-Programming/ImportNewSkufromURL/lib/PENDING-WIREIN-price-gate.md b/DW-Programming/ImportNewSkufromURL/lib/PENDING-WIREIN-price-gate.md
index 2622829d..98508f29 100644
--- a/DW-Programming/ImportNewSkufromURL/lib/PENDING-WIREIN-price-gate.md
+++ b/DW-Programming/ImportNewSkufromURL/lib/PENDING-WIREIN-price-gate.md
@@ -1,45 +1,91 @@
-# PENDING WIRE-IN — price-integrity gate into `addSampleVariant` (TK-11403)
+# PENDING WIRE-IN — price-integrity gate (TK-11403)
 
 > **STATUS: NOT APPLIED.** This is a GATED, customer-facing change (it decides
 > whether a product publishes to the LIVE store). It must be applied by the memo
 > authors / a Steve-gated pass, NOT by the build subagent. This file documents
 > the exact minimal wire-in so applying it is mechanical.
 
-## Where
+## CORRECTION (contrarian red-team, verified) — this is NOT one choke point
 
-`lib/shopify.ts` → `async function addSampleVariant(...)` (starts ~line 932).
+The gate module is genuinely "shared," but price-writing is NOT funneled through
+a single function. There are **at least THREE independent choke points**, and a
+gate wired into only one leaves the other two able to ship the same bugs:
 
-The two write calls to guard are, in order:
-
-1. `productVariantsBulkUpdate` — updates the **sellable** per-unit variant with
-   `price: numericUnitPrice, inventoryPolicy: 'CONTINUE'` then
-   `setInventoryQuantity(iid, 2025)` → this is what makes it ORDERABLE.
-2. `productVariantsBulkCreate` — creates the **Sample** variant at `'4.25'`.
-
-The gate must run **BEFORE both** GraphQL calls so a bad product never writes.
+| # | Choke point | Notes |
+|---|---|---|
+| 1 | `lib/shopify.ts` → `addSampleVariant(...)` (~L932) | The DTO-driven import path. Sellable price = `numericUnitPrice` = `numericPrice \|\| '0.00'`; sample created at literal `'4.25'`. |
+| 2 | `add-sample-variant-schumacher.ts` → its own `addSampleVariant(...)` | A SEPARATE 2-arg signature — a distinct writer, not a call into #1. |
+| 3 | `scripts/import-queue-runner.js` | Has its own bespoke `$4.25` guard (see `test/cost-guard.test.js`). |
+
+**A truly "shared" gate must be wired into ALL THREE.** Present as scoped options
+for Steve:
+
+- **Option A (minimum viable):** wire into #1 `lib/shopify.ts` only — covers the
+  primary DTO import path. Leaves #2 and #3 unguarded.
+- **Option B (recommended):** wire into #1 + #2 + replace #3's bespoke guard with
+  `enforcePriceIntegrity` so all three share ONE assertion. This is what makes
+  the gate actually "shared" and kills the recurrence class fleet-wide.
+- **Option C:** #1 now; file follow-up tickets for #2 and #3.
+
+## What the gate catches (and the headline-bug fix)
+
+`enforcePriceIntegrity(input)` from `./price-integrity-gate` throws
+`PriceIntegrityError` (with `.violations`) on any block. Classes:
+
+- **A** — `sample-not-expected` (a sample not == $4.25); **`sellable-equals-sample`**
+  (a sellable priced at ≈ the $4.25 sample — the headline LEAK; a numeric backstop
+  that fires even when cost is low enough that 4.25 clears the markup floor);
+  **`sellable-defaulted-price`** (a sellable whose `priceSource==='defaulted'` —
+  provenance catch).
+- **B** — `below-markup-floor` (sellable < 1.5× cost) and `cost-unknown`
+  (cost null/≤0 → cannot prove markup → fail-closed BLOCK).
+- **C** — `zero-price-orderable` ($0/NaN orderable) and `negative-price`
+  (negative orderable, its own honest code).
+
+### Why Class A `sample-not-expected` is a NO-OP AT choke point #1
+
+At `addSampleVariant` the sample variant is created with the **hardcoded literal
+`'4.25'`** — it is constant-vs-constant, so `sample-not-expected` can never fire
+*from this site*. It is NOT dead weight in the module (choke points #2/#3 and
+future callers can pass a wrong sample price, and it guards against a future edit
+that stops hardcoding 4.25), but at site #1 the real value is:
+
+- **Class B** (markup floor + cost-unknown fail-closed),
+- **Class C** ($0-orderable — the 6×-recurring bug),
+- **`sellable-equals-sample` + provenance** on the DTO-sourced sellable price
+  (`numericUnitPrice`), which is exactly where the $4.25 default leaks in.
+
+## Provenance source (belt) + numeric backstop (braces)
+
+The headline live bug is the `$4.25` sample DEFAULT surfacing as the sellable
+price. Root cause is a provenance problem: `lib/types.ts` defaults
+`ProductDTO.price` to the literal `'4.25'` on no-scrape, and
+`app/api/import/sku/route.ts` hardcodes `price:'4.25'` on ≥3 fallback paths — that
+default flows into `numericUnitPrice`. So:
+
+- Pass `priceSource: 'defaulted'` on the sellable when the price came from a
+  fallback/default (not a scrape/vendor feed) → gate BLOCKs via
+  `sellable-defaulted-price`.
+- Even if `priceSource` is omitted, the numeric `sellable-equals-sample` backstop
+  still BLOCKs a sellable priced at ≈ $4.25 (DW sellables are $30–$2000+, so a
+  $4.25 sellable is essentially always the leak).
 
 ## What the gate needs
 
-- `enforcePriceIntegrity(input)` from `./price-integrity-gate` (throws
-  `PriceIntegrityError` with `.violations` on any block).
+- `enforcePriceIntegrity(input)` from `./price-integrity-gate`.
 - `resolveNetCost(vendor, sku)` from `./price-integrity-cost` (returns
   `number | null`; null → gate blocks via Class B `cost-unknown`).
 
-### Threading `vendor`
+### Threading `vendor` (still required)
 
 `addSampleVariant` currently has **no `vendor` parameter**. The memo authors must
-thread it in — either:
-
-- add a `vendor: string` param and pass it from the caller (the caller already
-  has the `ProductDTO` / vendor context), **or**
-- pass the vendor via the `ProductDTO` the caller holds.
-
-Until vendor is available, `resolveNetCost` cannot run and the gate blocks
-fail-closed (Class B `cost-unknown`) — which is the correct safe default, but it
+thread it — add a `vendor: string` param passed from the caller, or carry it on
+the `ProductDTO`. **Until vendor is available, `resolveNetCost` returns null and
+the gate blocks fail-closed on `cost-unknown`** — the correct safe default, but it
 would block ALL publishes, so threading vendor is required for the gate to pass
 real products.
 
-## The ~10-line diff (conceptual)
+## The ~12-line diff (conceptual, choke point #1)
 
 ```ts
 // at top of lib/shopify.ts
@@ -50,14 +96,16 @@ import { resolveNetCost } from './price-integrity-cost';
 // AFTER computing `numericUnitPrice` and BEFORE the productVariantsBulkUpdate call:
 
 const netCost = await resolveNetCost(vendor, sku);        // number | null (fail-closed)
+// priceSource: 'defaulted' when numericPrice was empty and we fell back to '0.00'
+// (or when the DTO price was the 4.25 default) — else 'scraped'.
+const sellableSource = numericPrice ? 'scraped' : 'defaulted';
 try {
   enforcePriceIntegrity({
     dwSku: sku,
     netCost,
     variants: [
-      // sellable: CONTINUE + qty 2025 ⇒ orderable:true
-      { role: 'sellable', price: Number(numericUnitPrice), orderable: true, sku: `${sku}-${unitKind}` },
-      // sample: fixed 4.25, not orderable
+      { role: 'sellable', price: Number(numericUnitPrice), orderable: true,
+        sku: `${sku}-${unitKind}`, priceSource: sellableSource },
       { role: 'sample', price: 4.25, orderable: false, sku: `${sku}-Sample` },
     ],
   });
@@ -73,28 +121,28 @@ try {
 ## Behavior on block
 
 - Structured log: `{event:'price_integrity_block', dwSku, vendor, violations}`.
-- `return` WITHOUT calling either `productVariantsBulk*` mutation — the product
-  is not priced/published. (Product stays whatever status it was; nothing
-  orderable is written.)
+- `return` WITHOUT calling either `productVariantsBulk*` mutation — nothing
+  orderable is written; the product stays whatever status it was.
 
 ## Why each guarded value maps this way
 
 | Gate field | Source in `addSampleVariant` | Note |
 |---|---|---|
-| sellable `price` | `Number(numericUnitPrice)` | `numericUnitPrice = numericPrice \|\| '0.00'` — a missing scrape becomes 0 ⇒ Class C catches it |
-| sellable `orderable` | `true` | because the update sets `inventoryPolicy:'CONTINUE'` + `setInventoryQuantity(iid,2025)` |
-| sample `price` | `4.25` | fixed literal in the create call |
-| sample `orderable` | `false` | sample is not stocked/orderable as a sellable unit |
-| `netCost` | `resolveNetCost(vendor, sku)` | null ⇒ Class B `cost-unknown` (fail-closed) |
+| sellable `price` | `Number(numericUnitPrice)` | `numericUnitPrice = numericPrice \|\| '0.00'` — missing scrape → 0 → Class C catches it |
+| sellable `orderable` | `true` | update sets `inventoryPolicy:'CONTINUE'` + `setInventoryQuantity(iid,2025)` |
+| sellable `priceSource` | `numericPrice ? 'scraped' : 'defaulted'` | defaulted → Class A `sellable-defaulted-price` |
+| sample `price` | `4.25` | fixed literal → Class A `sample-not-expected` is a no-op at THIS site |
+| sample `orderable` | `false` | sample is not a stocked orderable unit |
+| `netCost` | `resolveNetCost(vendor, sku)` | null → Class B `cost-unknown` (fail-closed) |
 
 ## Verification after wire-in
 
-Re-run the pure gate suite (unaffected by the wire-in, proves the gate logic):
+Re-run the pure gate suite under the repo's real command:
 
 ```
-npx tsx --test __tests__/price-integrity-gate.test.ts
+npx jest __tests__/price-integrity-gate.test.ts
 ```
 
-Then a live smoke on ONE known-good vendor SKU to confirm `resolveNetCost`
-returns a number and the product publishes, and ONE known-$0/known-at-cost SKU
-to confirm it blocks with a `price_integrity_block` log line.
+Then a live smoke on ONE known-good vendor SKU (confirm `resolveNetCost` returns
+a number and the product publishes) and ONE known-$0 / at-cost / $4.25-leak SKU
+(confirm it blocks with a `price_integrity_block` log line).

← 187b165f auto-data-snapshot: 2026-09-10T14:47:21 (1 data files) — DW-  ·  back to Designer Wallcoverings  ·  auto-data-snapshot: 2026-09-10T17:00:45 (1 data files) — DW- dd9b2739 →