← back to Tk 10965 Zero Price Analysis

REMEDIATION.md

43 lines

# TK-10965 — Remediation draft (GATED — Steve APPROVE/REVISE/BLOCK)

> Supersedes the mechanism/fix in `pending-approval/zero-price-orderable-1281-quoteonly-REMEDIATION.md`,
> which misdiagnosed the cause as `inventory_policy=CONTINUE` (it is DENY; see FINDINGS.md §3).
> **This is a DRAFT. No Shopify or DB writes have been performed. Nothing auto-executes.**

## Fix A — Data remediation (make the $0 variants non-orderable)

**What:** For the sellable **non-Sample** variant of each affected product, set inventory **`on_hand = 0`** at the Ventura Blvd location (`gid://shopify/Location/5795643504`), keeping `inventoryPolicy = DENY` and `tracked = true`. Result: `availableForSale → false` → not purchasable. Do **not** touch the $4.25 Sample variant.

This is the exact inverse of the importer's `setInventory2026()`; it lands each product in the same non-orderable state its own Sample variant already has.

**Scope:** **1,743 products** (NOT 1,281) — Phillipe Romano 1,281 + **Fentucci Naturals 462**. The Fentucci 462 are the canary's blind spot and MUST be included or the fix is only 73% complete. Exact live id/variant list is enumerable via `verify.mjs` (extend it to emit the variant GIDs for the apply script).

**Mechanism:** one `inventorySetQuantities` mutation per batch (`name:'on_hand', reason:'correction', quantities:[{inventoryItemId, locationId, quantity:0}]`) — the same API `setInventory2026()` uses, value `0` instead of `2026`.

**Reversibility:** trivially reversible. Capture a before-map `{variantId → 2026}` (all are uniformly 2026) and restore via the identical mutation. Recommend batches of 50 with a saved restore-map per batch and a `verify.mjs` re-run after each.

**Acceptance test:** `node verify.mjs` returns **exit 0 / RESULT: CLEAN** (0 $0-orderable across all scoped vendors), and a canary re-run reports **PASS**.

> **Multi-location caveat (second-model confirmed, Kimi k2.5):** `availableForSale` aggregates stock across **all** online-fulfillment locations. `setInventory2026()` only ever wrote the Ventura Blvd location, so single-location zeroing is expected to suffice — but the apply script MUST verify no *other* location holds positive stock for the variant (and zero those too) before declaring clean. `verify.mjs` inherently catches this: it reads the aggregated `availableForSale`, so a residual location keeps the variant DIRTY.

**Gate:** canonical, customer-facing Shopify write across 1,743 products → **HARD GATE** (>500 items + customer-facing). Draft only; needs Steve's explicit APPROVE.

## Fix B — Prevention (stop recurrence at the source) — RECOMMENDED, still gated for deploy

The data fix alone is temporary: the next activation run re-stamps `2026`. Root-cause fix in the importer:

- In **`setInventory2026()`** (and the product-create `inventory_quantity: 2026` literal) in every affected script — canonical `shopify/scripts/cadence/cadence-import.js` + `.../templates/new-product-import-template.js` + the ~18 vendor push scripts (`command54-shopify-push.js`, `justindavid-shopify-push.js`, `hollywood-shopify-push.js`, `as-creation-shopify-push.js`, Fentucci's importer, etc.) — **guard on price**: **skip the positive-stock stamp when the sellable variant's price is `0` (or the product is quote-only / `contact-for-price` / `Needs-Price`).** An unpriced sellable variant must never be given buyable stock.
- Rationale: a $0 offer should not be advertised as in-stock anyway (GMC disapproves $0 offers), so the "keep it in stock for channels" intent does not apply to quote-only lines.

## Fix C — Close the canary blind spot — SAFE, low-risk (own follow-up ticket)

`~/.claude/skills/zero-price-orderable-canary/check.mjs` searches only `tag:'quote-only'`. Broaden the `SEARCH` to the full quote/price-suppressed tag family (`quote-only`, `quotes`, `contact-for-price`, `Needs-Price`) **or** switch to the vendor-scoped scan in `verify.mjs`, so Fentucci (and future untagged lines) are no longer invisible. This is a read-only skill edit (per-machine, `~/.claude/skills` is gitignored) — not a Shopify write — but is logged here so it isn't lost.

## Recommended sequence

1. **B** (fix the importer so re-stamping stops) →
2. **A** (zero the 1,743 existing variants, batched + restore-map + `verify.mjs` gate) →
3. **C** (broaden the canary) → re-run canary → expect PASS.

All three are drafted; **A and B are customer-facing/prod and stay Steve-gated.**