← 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
M DW-Programming/ImportNewSkufromURL/lib/PENDING-WIREIN-price-gate.md
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 →