[object Object]

← back to Designer Wallcoverings

guard TK-11357 Fix D: 3 kravet activators found by a widened writer enumeration + harden the hourly newest-1000 guard against a price-LESS variant

bafa2f6022d66fa7a6352a63c90c7729a0eb63a5 · 2026-09-10 09:37:23 -0700 · Steve

My first enumeration grepped only the GraphQL inventory mutations, which is the same blind spot
this lineage has hit before (a .js/GraphQL-only grep misses a REST inventory_levels writer such
as fentucci-naturals/scripts/go-live.py). Re-ran across *.mjs *.js *.cjs *.py including the REST
inventory_levels/{set,adjust,connect} endpoints. That surfaced 10 writers outside the brief's
list. Seven are structurally safe and deliberately untouched (active-gate-add-samples,
add-missing-samples-v2, create-missing-sample-variants, fix-missing-sample-variants,
fix-missing-samples, resume-sample-variant-creation — all create and stock only the $4.25 SAMPLE
variant at a hardcoded price; and fentucci go-live.py, which already drops the $0/yard variant).
Three were genuinely unguarded:

  kravet-3b-activate-validated.js  had a PRODUCT-level gate only ("a real roll variant priced
    > $5"), then setInvAll() stamped a flat TARGET_QTY on EVERY variant with an inventoryItem —
    the same variant-level hole as activate-1838.
  kravet25-reactivate.js          NO price check on the stamp at all.
  kravet-full-monty-single.js     NO price check, flat qty=1000 on every variant. (The guard's
    `desired` argument carries 1000 here, so the file's own quantity is preserved.)

CORRECTNESS CATCH in kravet25-reactivate: step 2 repricies the main variant to MAP and step 5
stocks from the PRE-reprice snapshot, so a naive guard would refuse to stock a roll that had just
been legitimately priced — breaking Steve's 2026-06-20 rule. The guard is fed the POST-reprice
price; `map > 0` is already asserted upstream, so this cannot launder a real $0. (Also fixed an
undefined `prod` reference I introduced in the same hunk — caught by reading the call site, not
by node --check.)

Separately, WIDENED the existing TK-10965 guard in inventory-set-2026-newest.mjs (the hourly
cadence sibling): `Number(v.price) === 0` let a variant with an ABSENT/unparseable price through,
because Number(undefined) is NaN and NaN === 0 is false — so a price-less variant was still
stamped 2026. Now `!(Number(v.price) > 0)`, which skips 0, '', null, undefined and NaN. Left
price-ONLY on purpose (it is a LIVE hourly job and has no product tags in scope); the harness
declares fixture R6 N/A for that file rather than pretending it passes.

Harness extended with a PREDICATE mode that extracts a real `if (<expr>) continue;` skip-filter —
the exact shape of the TK-11299 reference commit 23987f86 — and evaluates it. All 22 writers now
covered: 4/4 required each; 3/3 regression (block mode), 2/2 + R6 N/A (predicate mode).
RESULT: PASS.

SOURCE-ONLY: nothing run with --apply, no Shopify write. Reversible: git revert.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

Files touched

Diff

commit bafa2f6022d66fa7a6352a63c90c7729a0eb63a5
Author: Steve <steve@designerwallcoverings.com>
Date:   Thu Sep 10 09:37:23 2026 -0700

    guard TK-11357 Fix D: 3 kravet activators found by a widened writer enumeration + harden the
    hourly newest-1000 guard against a price-LESS variant
    
    My first enumeration grepped only the GraphQL inventory mutations, which is the same blind spot
    this lineage has hit before (a .js/GraphQL-only grep misses a REST inventory_levels writer such
    as fentucci-naturals/scripts/go-live.py). Re-ran across *.mjs *.js *.cjs *.py including the REST
    inventory_levels/{set,adjust,connect} endpoints. That surfaced 10 writers outside the brief's
    list. Seven are structurally safe and deliberately untouched (active-gate-add-samples,
    add-missing-samples-v2, create-missing-sample-variants, fix-missing-sample-variants,
    fix-missing-samples, resume-sample-variant-creation — all create and stock only the $4.25 SAMPLE
    variant at a hardcoded price; and fentucci go-live.py, which already drops the $0/yard variant).
    Three were genuinely unguarded:
    
      kravet-3b-activate-validated.js  had a PRODUCT-level gate only ("a real roll variant priced
        > $5"), then setInvAll() stamped a flat TARGET_QTY on EVERY variant with an inventoryItem —
        the same variant-level hole as activate-1838.
      kravet25-reactivate.js          NO price check on the stamp at all.
      kravet-full-monty-single.js     NO price check, flat qty=1000 on every variant. (The guard's
        `desired` argument carries 1000 here, so the file's own quantity is preserved.)
    
    CORRECTNESS CATCH in kravet25-reactivate: step 2 repricies the main variant to MAP and step 5
    stocks from the PRE-reprice snapshot, so a naive guard would refuse to stock a roll that had just
    been legitimately priced — breaking Steve's 2026-06-20 rule. The guard is fed the POST-reprice
    price; `map > 0` is already asserted upstream, so this cannot launder a real $0. (Also fixed an
    undefined `prod` reference I introduced in the same hunk — caught by reading the call site, not
    by node --check.)
    
    Separately, WIDENED the existing TK-10965 guard in inventory-set-2026-newest.mjs (the hourly
    cadence sibling): `Number(v.price) === 0` let a variant with an ABSENT/unparseable price through,
    because Number(undefined) is NaN and NaN === 0 is false — so a price-less variant was still
    stamped 2026. Now `!(Number(v.price) > 0)`, which skips 0, '', null, undefined and NaN. Left
    price-ONLY on purpose (it is a LIVE hourly job and has no product tags in scope); the harness
    declares fixture R6 N/A for that file rather than pretending it passes.
    
    Harness extended with a PREDICATE mode that extracts a real `if (<expr>) continue;` skip-filter —
    the exact shape of the TK-11299 reference commit 23987f86 — and evaluates it. All 22 writers now
    covered: 4/4 required each; 3/3 regression (block mode), 2/2 + R6 N/A (predicate mode).
    RESULT: PASS.
    
    SOURCE-ONLY: nothing run with --apply, no Shopify write. Reversible: git revert.
    
    Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
---
 .../kravet-3b-activate-validated.js                | 34 +++++++++++++++---
 .../data/kravet-map-pricing/kravet25-reactivate.js | 42 ++++++++++++++++++----
 shopify/scripts/inventory-set-2026-newest.mjs      |  5 ++-
 shopify/scripts/kravet-full-monty-single.js        | 37 +++++++++++++++++--
 .../tk11357-source-fix-proof/predicate-proof.mjs   | 41 +++++++++++++++++++--
 5 files changed, 141 insertions(+), 18 deletions(-)

diff --git a/shopify/scripts/data/kravet-map-pricing/kravet-3b-activate-validated.js b/shopify/scripts/data/kravet-map-pricing/kravet-3b-activate-validated.js
index 71fe0fce..31499b8c 100644
--- a/shopify/scripts/data/kravet-map-pricing/kravet-3b-activate-validated.js
+++ b/shopify/scripts/data/kravet-map-pricing/kravet-3b-activate-validated.js
@@ -86,6 +86,29 @@ const Q_DETAIL = `query($id:ID!){product(id:$id){
 const M_STATUS = `mutation($input:ProductInput!){productUpdate(input:$input){product{id status} userErrors{field message}}}`;
 const M_PUBLISH = `mutation($id:ID!,$input:[PublicationInput!]!){publishablePublish(id:$id,input:$input){userErrors{field message}}}`;
 const M_TAGS = `mutation($id:ID!,$tags:[String!]!){tagsAdd(id:$id,tags:$tags){userErrors{field message}}}`;
+const { safeStampQuantity } = require('../.././lib/inventory-stamp-guard.mjs');  // GUARD TK-11357 (shared guard)
+// ── GUARD TK-11357 BEGIN ─ do not edit without re-running the fixture proof ──────────
+// A $0 / quote-only sellable variant must NEVER receive positive stock: positive stock is what
+// flips availableForSale=true, making it checkout-orderable at $0 (lineage TK-10825 -> 10965 ->
+// 11140 -> 11299 -> 11301 -> 11357). $0 is the LIVE theme's deliberate quote-only SENTINEL
+// (snippets/product-form-content.liquid renders the "Contact Us" button iff variant.price == 0),
+// so the remedy is NEVER to write a placeholder price - it is "do not stock it".
+// Steve's 2026-06-20 "active products are never out of stock" rule is PRESERVED for PRICED goods:
+// a priced variant still gets `desired`. The quote-only tag/vendor decision is delegated to the
+// shared guard (lib/inventory-stamp-guard.mjs); this adds one strictly-safer rule of its own -
+// price <= 0 / NaN is ALWAYS 0, even on a variant labelled "Sample" (a $0 "sample" is the same
+// $0-orderable defect). The real $4.25 memo sample is unaffected and keeps its existing quantity.
+function safeQuantities(product, variants, locationId, desired) {
+  return (variants || []).map(v => ({
+    inventoryItemId: v.inventoryItem.id,
+    locationId,
+    quantity: Number(v.price) > 0
+      ? safeStampQuantity({ title: v.title, price: v.price }, product, desired)
+      : 0,
+  }));
+}
+// ── GUARD TK-11357 END ────────────────────────────────────────────
+
 const M_SET = `mutation($input:InventorySetQuantitiesInput!){inventorySetQuantities(input:$input){userErrors{field message}}}`;
 const M_ACTIVATE = `mutation($itemId:ID!,$locId:ID!,$qty:Int!){inventoryActivate(inventoryItemId:$itemId,locationId:$locId,available:$qty){userErrors{field message}}}`;
 
@@ -111,9 +134,10 @@ function mf(metafields, ns, key) {
   return m ? m.value : '';
 }
 
-async function setInvAll(variants) {
-  const qs = variants.filter(v => v.inventoryItem?.id)
-    .map(v => ({ inventoryItemId: v.inventoryItem.id, locationId: LOCATION_ID, quantity: TARGET_QTY }));
+async function setInvAll(product, variants) {
+  // GUARD TK-11357: the sellableRollOK check upstream is PRODUCT-level (a roll priced > $5), so a
+  // $0 variant riding alongside the priced roll still reached this flat TARGET_QTY stamp. Per variant.
+  const qs = safeQuantities(product, variants.filter(v => v.inventoryItem?.id), LOCATION_ID, TARGET_QTY);
   if (!qs.length) return { ok: false, err: 1 };
   let r = await gqlR(M_SET, { input: { reason: 'correction', name: 'available', ignoreCompareQuantity: true, quantities: qs } });
   if (!(r.data?.inventorySetQuantities?.userErrors || []).length) return { ok: true, err: 0 };
@@ -121,7 +145,7 @@ async function setInvAll(variants) {
   for (const q of qs) {
     const sr = await gqlR(M_SET, { input: { reason: 'correction', name: 'available', ignoreCompareQuantity: true, quantities: [q] } });
     if (!(sr.data?.inventorySetQuantities?.userErrors || []).length) continue;
-    const ar = await gqlR(M_ACTIVATE, { itemId: q.inventoryItemId, locId: LOCATION_ID, qty: TARGET_QTY });
+    const ar = await gqlR(M_ACTIVATE, { itemId: q.inventoryItemId, locId: LOCATION_ID, qty: q.quantity }); // GUARD TK-11357
     if ((ar.data?.inventoryActivate?.userErrors || []).length) err++;
     await sleep(60);
   }
@@ -223,7 +247,7 @@ async function setInvAll(variants) {
         console.log(`   ⚠ publish ${JSON.stringify(ppr.data.publishablePublish.userErrors)}`);
     }
     // 3. inventory = 2026 every variant
-    const inv = await setInvAll(variants);
+    const inv = await setInvAll(p, variants);
     if (!inv.ok) console.log(`   ⚠ inv errors ${inv.err}`);
     activated.push(baseSku);
     await sleep(150);
diff --git a/shopify/scripts/data/kravet-map-pricing/kravet25-reactivate.js b/shopify/scripts/data/kravet-map-pricing/kravet25-reactivate.js
index cea1ed6a..dae085f3 100644
--- a/shopify/scripts/data/kravet-map-pricing/kravet25-reactivate.js
+++ b/shopify/scripts/data/kravet-map-pricing/kravet25-reactivate.js
@@ -63,8 +63,31 @@ async function gqlR(q, v = {}, tries = 5) {
   return gql(q, v);
 }
 
-const Q = `query($id:ID!){product(id:$id){id title status ${PUB_ALIASES}
+const Q = `query($id:ID!){product(id:$id){id title status vendor tags ${PUB_ALIASES}
   variants(first:30){nodes{id title sku price inventoryItem{id unitCost{amount}}}}}}`;
+const { safeStampQuantity } = require('../.././lib/inventory-stamp-guard.mjs');  // GUARD TK-11357 (shared guard)
+// ── GUARD TK-11357 BEGIN ─ do not edit without re-running the fixture proof ──────────
+// A $0 / quote-only sellable variant must NEVER receive positive stock: positive stock is what
+// flips availableForSale=true, making it checkout-orderable at $0 (lineage TK-10825 -> 10965 ->
+// 11140 -> 11299 -> 11301 -> 11357). $0 is the LIVE theme's deliberate quote-only SENTINEL
+// (snippets/product-form-content.liquid renders the "Contact Us" button iff variant.price == 0),
+// so the remedy is NEVER to write a placeholder price - it is "do not stock it".
+// Steve's 2026-06-20 "active products are never out of stock" rule is PRESERVED for PRICED goods:
+// a priced variant still gets `desired`. The quote-only tag/vendor decision is delegated to the
+// shared guard (lib/inventory-stamp-guard.mjs); this adds one strictly-safer rule of its own -
+// price <= 0 / NaN is ALWAYS 0, even on a variant labelled "Sample" (a $0 "sample" is the same
+// $0-orderable defect). The real $4.25 memo sample is unaffected and keeps its existing quantity.
+function safeQuantities(product, variants, locationId, desired) {
+  return (variants || []).map(v => ({
+    inventoryItemId: v.inventoryItem.id,
+    locationId,
+    quantity: Number(v.price) > 0
+      ? safeStampQuantity({ title: v.title, price: v.price }, product, desired)
+      : 0,
+  }));
+}
+// ── GUARD TK-11357 END ────────────────────────────────────────────
+
 const M_STATUS = `mutation($input:ProductInput!){productUpdate(input:$input){product{id status} userErrors{field message}}}`;
 const M_VAR = `mutation($pid:ID!,$variants:[ProductVariantsBulkInput!]!){productVariantsBulkUpdate(productId:$pid,variants:$variants){productVariants{id price} userErrors{field message}}}`;
 const M_MF = `mutation($mf:[MetafieldsSetInput!]!){metafieldsSet(metafields:$mf){userErrors{field message}}}`;
@@ -89,9 +112,9 @@ function parseCsv(p) {
   return lines.filter(Boolean).map(l => { const v = splitCsvLine(l); const o = {}; cols.forEach((c, i) => o[c] = v[i]); return o; });
 }
 
-async function setInvAll(variants) {
-  const qs = variants.filter(v => v.inventoryItem?.id)
-    .map(v => ({ inventoryItemId: v.inventoryItem.id, locationId: LOCATION_ID, quantity: TARGET_QTY }));
+async function setInvAll(product, variants) {
+  // GUARD TK-11357: this stamped a flat TARGET_QTY on EVERY variant with no price check at all.
+  const qs = safeQuantities(product, variants.filter(v => v.inventoryItem?.id), LOCATION_ID, TARGET_QTY);
   if (!qs.length) return { set: 0, act: 0, err: 0 };
   let r = await gqlR(M_SET, { input: { reason: 'correction', name: 'available', ignoreCompareQuantity: true, quantities: qs } });
   if (!(r.data?.inventorySetQuantities?.userErrors || []).length) return { set: qs.length, act: 0, err: 0 };
@@ -99,7 +122,7 @@ async function setInvAll(variants) {
   for (const q of qs) {
     const sr = await gqlR(M_SET, { input: { reason: 'correction', name: 'available', ignoreCompareQuantity: true, quantities: [q] } });
     if (!(sr.data?.inventorySetQuantities?.userErrors || []).length) { set++; continue; }
-    const ar = await gqlR(M_ACTIVATE, { itemId: q.inventoryItemId, locId: LOCATION_ID, qty: TARGET_QTY });
+    const ar = await gqlR(M_ACTIVATE, { itemId: q.inventoryItemId, locId: LOCATION_ID, qty: q.quantity }); // GUARD TK-11357
     if ((ar.data?.inventoryActivate?.userErrors || []).length) err++; else act++;
     await sleep(60);
   }
@@ -148,8 +171,13 @@ async function setInvAll(variants) {
       const pe = ppr.data?.publishablePublish?.userErrors || [];
       if (pe.length) { console.log(`   ⚠ publish ${JSON.stringify(pe)}`); } else published++;
     } else published++;
-    // 5. inventory 2026 every variant
-    const inv = await setInvAll(vs);
+    // 5. inventory 2026 every variant.
+    // GUARD TK-11357 CORRECTNESS NOTE: `vs` is the PRE-reprice snapshot, so the main variant may
+    // still read $0 there even though step 2 just set it to MAP. Pass the POST-reprice price or the
+    // guard would refuse to stock a legitimately-priced roll (breaking Steve's 2026-06-20 rule).
+    // `map > 0` is already asserted above, so this substitution can never launder a real $0.
+    const vsForGuard = vs.map(v => (v.id === main.id ? { ...v, price: map.toFixed(2) } : v));
+    const inv = await setInvAll(p, vsForGuard);
     if (inv.err) console.log(`   ⚠ inv errors ${inv.err}`); else invOK++;
     await sleep(150);
   }
diff --git a/shopify/scripts/inventory-set-2026-newest.mjs b/shopify/scripts/inventory-set-2026-newest.mjs
index 8a21d20a..b83a9778 100644
--- a/shopify/scripts/inventory-set-2026-newest.mjs
+++ b/shopify/scripts/inventory-set-2026-newest.mjs
@@ -71,7 +71,10 @@ async function scan() {
   let totV = 0, correct = 0; const fixes = [];
   for (const p of prods) for (const v of p.variants.nodes) {
     totV++;
-    if (Number(v.price) === 0) continue; // GUARD TK-10965: never re-inflate $0 quote-only variants
+    // GUARD TK-10965, widened by TK-11357: `=== 0` let a variant with an ABSENT/unparseable price
+    // through (Number(undefined) is NaN, and NaN === 0 is false), so a price-less variant was still
+    // stamped 2026 = orderable at $0. `!(> 0)` is fail-safe: 0, '', null, undefined and NaN all skip.
+    if (!(Number(v.price) > 0)) continue; // GUARD TK-11357-PREDICATE: never re-inflate a $0/price-less variant
     const ii = v.inventoryItem;
     const tracked = ii?.tracked, lvl = ii?.inventoryLevel;
     const onh = lvl?.quantities?.find(x => x.name === 'on_hand')?.quantity;
diff --git a/shopify/scripts/kravet-full-monty-single.js b/shopify/scripts/kravet-full-monty-single.js
index c6b4d7bc..bcb12ff0 100644
--- a/shopify/scripts/kravet-full-monty-single.js
+++ b/shopify/scripts/kravet-full-monty-single.js
@@ -104,6 +104,29 @@ async function loadDefinitions() {
 }
 
 const TEXT_TYPES = new Set(['single_line_text_field', 'multi_line_text_field']);
+const { safeStampQuantity } = require('./lib/inventory-stamp-guard.mjs');  // GUARD TK-11357 (shared guard)
+// ── GUARD TK-11357 BEGIN ─ do not edit without re-running the fixture proof ──────────
+// A $0 / quote-only sellable variant must NEVER receive positive stock: positive stock is what
+// flips availableForSale=true, making it checkout-orderable at $0 (lineage TK-10825 -> 10965 ->
+// 11140 -> 11299 -> 11301 -> 11357). $0 is the LIVE theme's deliberate quote-only SENTINEL
+// (snippets/product-form-content.liquid renders the "Contact Us" button iff variant.price == 0),
+// so the remedy is NEVER to write a placeholder price - it is "do not stock it".
+// Steve's 2026-06-20 "active products are never out of stock" rule is PRESERVED for PRICED goods:
+// a priced variant still gets `desired`. The quote-only tag/vendor decision is delegated to the
+// shared guard (lib/inventory-stamp-guard.mjs); this adds one strictly-safer rule of its own -
+// price <= 0 / NaN is ALWAYS 0, even on a variant labelled "Sample" (a $0 "sample" is the same
+// $0-orderable defect). The real $4.25 memo sample is unaffected and keeps its existing quantity.
+function safeQuantities(product, variants, locationId, desired) {
+  return (variants || []).map(v => ({
+    inventoryItemId: v.inventoryItem.id,
+    locationId,
+    quantity: Number(v.price) > 0
+      ? safeStampQuantity({ title: v.title, price: v.price }, product, desired)
+      : 0,
+  }));
+}
+// ── GUARD TK-11357 END ────────────────────────────────────────────
+
 
 const QUERY = `query($sku: String!) {
   products(first: 1, query: $sku) {
@@ -111,7 +134,8 @@ const QUERY = `query($sku: String!) {
       id title handle descriptionHtml status tags
       metafields(first: 100) { edges { node { id namespace key value type } } }
       images(first: 30) { edges { node { url altText } } }
-      variants(first: 10) { edges { node { id sku inventoryItem { id tracked } inventoryPolicy inventoryQuantity } } }
+      vendor
+      variants(first: 10) { edges { node { id sku title price inventoryItem { id tracked } inventoryPolicy inventoryQuantity } } }
     } }
   }
 }`;
@@ -380,7 +404,14 @@ function buildTags(mf, existingTags) {
   const locationId = locJ.data.locations.edges[0]?.node.id;
   if (!locationId) console.log('  ⚠ no location — skipping inventory set');
   else {
-    for (const { node: v } of p.variants.edges) {
+    // GUARD TK-11357: this stamped a flat qty=1000 on EVERY variant with NO price check at all —
+    // any $0 variant became availableForSale=true, i.e. orderable at $0. Decide per variant.
+    // (qty 1000 here, not 2026 — the guard's `desired` argument carries whatever the file uses.)
+    const _rows = safeQuantities({ vendor: p.vendor, tags: p.tags },
+      p.variants.edges.map(e => e.node).filter(v => v.inventoryItem?.id), locationId, 1000);
+    for (const row of _rows) {
+      const v = p.variants.edges.map(e => e.node).find(n => n.inventoryItem.id === row.inventoryItemId);
+      if (row.quantity === 0) { console.log(`  ⛔ ${v?.sku || row.inventoryItemId}: $${v?.price} — NOT stocked (TK-11357 $0-orderable guard)`); continue; }
       if (!v.inventoryItem.tracked) {
         await gql(INV_ITEM_UPDATE, { id: v.inventoryItem.id, input: { tracked: true } });
       }
@@ -388,7 +419,7 @@ function buildTags(mf, existingTags) {
         name: 'available',
         reason: 'correction',
         ignoreCompareQuantity: true,
-        quantities: [{ inventoryItemId: v.inventoryItem.id, locationId, quantity: 1000 }],
+        quantities: [row],
       } });
     }
     console.log(`  ✓ inventory set (qty=1000, tracked) on ${p.variants.edges.length} variants`);
diff --git a/shopify/scripts/tk11357-source-fix-proof/predicate-proof.mjs b/shopify/scripts/tk11357-source-fix-proof/predicate-proof.mjs
index 3dc601fb..f155b7af 100644
--- a/shopify/scripts/tk11357-source-fix-proof/predicate-proof.mjs
+++ b/shopify/scripts/tk11357-source-fix-proof/predicate-proof.mjs
@@ -55,8 +55,17 @@ const TARGETS = [
   [DWM, 'shopify/scripts/inventory-set-2026.js'],
   [DWM, 'shopify/scripts/set-active-zero-to-2026.js'],
   [DWM, 'shopify/scripts/activate-null-level-to-2026.js'],
+  [DWM, 'shopify/scripts/data/kravet-map-pricing/kravet-3b-activate-validated.js'],
+  [DWM, 'shopify/scripts/data/kravet-map-pricing/kravet25-reactivate.js'],
+  [DWM, 'shopify/scripts/kravet-full-monty-single.js'],
   [SDG, 'scripts/create_sdg.mjs'],
 ];
+// PREDICATE-mode targets: writers guarded by a `if (<expr>) continue;` skip-filter rather than a
+// safeQuantities() block (the exact shape of the 23987f86 reference diff). The harness extracts the
+// REAL expression from the marked line and evaluates it — expected: skip iff the safe quantity is 0.
+const PREDICATE_TARGETS = [
+  [DWM, 'shopify/scripts/inventory-set-2026-newest.mjs'],
+];
 
 const LOC = 'gid://shopify/Location/5795643504';
 const DESIRED = 2026;
@@ -89,7 +98,7 @@ const FIXTURES = [
     product: { vendor: 'Phillipe Romano', tags: ['quote-only'] },
     variants: [{ title: 'Sample', price: '4.25', inventoryQuantity: 0, inventoryItem: iv(6) }],
     expect: [2026] },
-  { id: 'R6', required: false,
+  { id: 'R6', required: false, needsProduct: true,
     name: 'quote-only line whose sellable IS priced -> 0 (shared-guard tag/vendor rule)',
     product: { vendor: 'Fentucci Naturals', tags: ['quotes', 'Needs-Price'] },
     variants: [{ title: 'Full Roll', price: '58.00', inventoryQuantity: 0, inventoryItem: iv(7) }],
@@ -171,6 +180,32 @@ for (const [repo, rel] of TARGETS) {
   results.push({ rel, state, fails, bytes: block.length });
 }
 
+// ── predicate-mode files ────────────────────────────────────────────────────────────────
+for (const [repo, rel] of PREDICATE_TARGETS) {
+  const abs = `${repo}/${rel}`;
+  const src = fs.existsSync(abs) ? fs.readFileSync(abs, 'utf8') : null;
+  if (!src) { results.push({ rel, state: 'MISSING FILE' }); allOk = false; continue; }
+  const m = src.match(/^\s*if \((.+?)\) continue; \/\/ GUARD TK-11357-PREDICATE/m);
+  if (!m) { results.push({ rel, state: 'NO GUARD PREDICATE' }); allOk = false; continue; }
+  // eslint-disable-next-line no-new-func
+  const skips = new Function('v', `return !!(${m[1]});`);
+  let req = 0, reqTotal = 0, reg = 0, regTotal = 0; const fails = []; const na = [];
+  for (const f of FIXTURES) {
+    // A price-only skip-filter cannot see product tags/vendor, so the shared guard's
+    // price-suppressed rule is STRUCTURALLY out of reach here. Declared N/A, not silently passed.
+    // This is the same semantics as the TK-11299 reference commit 23987f86 (`Number(v.price) > 0`).
+    if (f.needsProduct) { na.push(f.id); continue; }
+    // expectation: the variant is SKIPPED exactly when its required safe quantity is 0
+    const gotSkip = f.variants.map(v => skips(v));
+    const wantSkip = f.expect.map(q => q === 0);
+    const pass = JSON.stringify(gotSkip) === JSON.stringify(wantSkip);
+    if (f.required) { reqTotal++; if (pass) req++; else fails.push(`${f.id} want skip ${JSON.stringify(wantSkip)} got ${JSON.stringify(gotSkip)}`); }
+    else { regTotal++; if (pass) reg++; else fails.push(`${f.id} want skip ${JSON.stringify(wantSkip)} got ${JSON.stringify(gotSkip)}`); }
+  }
+  if (req !== reqTotal || reg !== regTotal) allOk = false;
+  results.push({ rel: rel + '  [predicate]', state: `${req}/${reqTotal} required · ${reg}/${regTotal} regression` + (na.length ? ` · N/A ${na.join(',')} (price-only guard: no product tags in scope)` : ''), fails });
+}
+
 console.log('\nper-file extracted-predicate results');
 console.log('-'.repeat(78));
 for (const r of results) {
@@ -181,5 +216,7 @@ for (const r of results) {
 console.log('-'.repeat(78));
 console.log(FIXTURES.map(f => `${f.id} ${f.name}`).join('\n'));
 console.log('-'.repeat(78));
-console.log(allOk ? 'RESULT: PASS — every patched writer is 4/4 required + 3/3 regression' : 'RESULT: FAIL');
+console.log(allOk
+  ? 'RESULT: PASS — every patched writer is 4/4 required; 3/3 regression (block mode) / 2/2 + R6 N/A (predicate mode)'
+  : 'RESULT: FAIL');
 process.exit(allOk ? 0 : 1);

← 39156e23 guard TK-11357 Fix D: $0 gate in the 5 catalog-wide inventor  ·  back to Designer Wallcoverings  ·  TK-11387: add gated, reversible chronic-bouncer suppression 950f19d5 →