[object Object]

← back to Dw Yolo Loop

Kravet-family cost drive: MAP resolver + parallel per-brand currency-check

f145eb5d28cc3b1da28aaa7c98094e3941a11a8f · 2026-06-15 08:48:24 -0700 · Steve Abrams

- Resolved MAP for 2,439 active Kravet-family from kravet_master_price (whls x1.5, verified)
- 4,618 need cost sourcing (Kravet 2043, Clarke&Clarke 1157, Brunschwig 917, ...)
- currency-check.js: per-brand exact-sku Algolia verifier (reuses kravet-disco-algolia-verify
  logic); CURRENT / DISCO_COLOR / DISCO_PATTERN / AMW_SKIP; read-only, per-brand CSV
- run-parallel.sh: fans all 14 brands concurrently (needs AKEY)
- blocked: AKEY (Algolia key) empty in env

Files touched

Diff

commit f145eb5d28cc3b1da28aaa7c98094e3941a11a8f
Author: Steve Abrams <steve@designerwallcoverings.com>
Date:   Mon Jun 15 08:48:24 2026 -0700

    Kravet-family cost drive: MAP resolver + parallel per-brand currency-check
    
    - Resolved MAP for 2,439 active Kravet-family from kravet_master_price (whls x1.5, verified)
    - 4,618 need cost sourcing (Kravet 2043, Clarke&Clarke 1157, Brunschwig 917, ...)
    - currency-check.js: per-brand exact-sku Algolia verifier (reuses kravet-disco-algolia-verify
      logic); CURRENT / DISCO_COLOR / DISCO_PATTERN / AMW_SKIP; read-only, per-brand CSV
    - run-parallel.sh: fans all 14 brands concurrently (needs AKEY)
    - blocked: AKEY (Algolia key) empty in env
---
 .gitignore                            |    1 +
 gmc-425-count.js                      |  135 ++++
 gmc-425-result.json                   | 1227 +++++++++++++++++++++++++++++++++
 gmc-gate-probe.js                     |   14 +
 scripts/kravet-cost/currency-check.js |   71 ++
 scripts/kravet-cost/run-parallel.sh   |   32 +
 6 files changed, 1480 insertions(+)

diff --git a/.gitignore b/.gitignore
index 4e2ab7e..c5c86d0 100644
--- a/.gitignore
+++ b/.gitignore
@@ -18,3 +18,4 @@ data/google-feed/run-full.*
 data/google-feed/*.csv
 data/google-feed/*.md
 data/google-feed/report.json
+data/kravet-cost/
diff --git a/gmc-425-count.js b/gmc-425-count.js
new file mode 100644
index 0000000..f66140f
--- /dev/null
+++ b/gmc-425-count.js
@@ -0,0 +1,135 @@
+#!/usr/bin/env node
+// READ-ONLY GMC $4.25 feed-price blast-radius count. No writes anywhere except local result json.
+const fs = require('fs');
+
+const SHOP = 'designer-laboratory-sandbox.myshopify.com';
+const VERSION = '2024-10';
+const ENV = fs.readFileSync('/Users/stevestudio2/Projects/secrets-manager/.env', 'utf8');
+const TOKEN = (ENV.match(/^SHOPIFY_ADMIN_TOKEN=(.+)$/m) || [])[1];
+if (!TOKEN) { console.error('no token'); process.exit(1); }
+
+const URL = `https://${SHOP}/admin/api/${VERSION}/graphql.json`;
+const SAMPLE_PRICE = 4.25;
+
+const sleep = (ms) => new Promise(r => setTimeout(r, ms));
+
+async function gql(query, variables) {
+  for (let attempt = 0; attempt < 7; attempt++) {
+    let res;
+    try {
+      res = await fetch(URL, {
+        method: 'POST',
+        headers: { 'X-Shopify-Access-Token': TOKEN, 'Content-Type': 'application/json' },
+        body: JSON.stringify({ query, variables }),
+      });
+    } catch (e) { await sleep(2000 * (attempt + 1)); continue; }
+    if (res.status === 429) { await sleep(2000 * (attempt + 1)); continue; }
+    const json = await res.json();
+    if (json.errors) {
+      const throttled = JSON.stringify(json.errors).includes('THROTTLED');
+      if (throttled) { await sleep(2000 * (attempt + 1)); continue; }
+      throw new Error(JSON.stringify(json.errors));
+    }
+    const avail = json.extensions?.cost?.throttleStatus?.currentlyAvailable ?? 4000;
+    if (avail < 400) await sleep(1500);
+    return json.data;
+  }
+  throw new Error('exhausted retries');
+}
+
+const QUERY = `
+query($cursor: String) {
+  products(first: 100, after: $cursor, query: "status:active") {
+    pageInfo { hasNextPage endCursor }
+    nodes {
+      title
+      vendor
+      hasOnlyDefaultVariant
+      variants(first: 60) {
+        nodes { title price }
+      }
+    }
+  }
+}`;
+
+(async () => {
+  let cursor = null;
+  let total = 0, pages = 0;
+
+  let bucketA = 0; // sample-only
+  let bucketB = 0; // multi-variant, min/feed is 4.25 but a real roll exists
+  let bucketC = 0; // real sellable feed price
+  let bucketD = 0; // no price / $0 only
+
+  let feedMin425 = 0;
+  let feedFirst425 = 0;
+
+  const vendorCount = {};       // a+b by MIN model
+  const vendorCountFirst = {};  // first-variant == 4.25
+
+  const is425 = (x) => Math.abs(x - SAMPLE_PRICE) < 0.005;
+  const startedAt = Date.now();
+
+  while (true) {
+    const data = await gql(QUERY, { cursor });
+    const conn = data.products;
+    pages++;
+    for (const p of conn.nodes) {
+      total++;
+      const variants = p.variants.nodes || [];
+      const prices = variants.map(v => parseFloat(v.price)).filter(x => !isNaN(x));
+      if (prices.length === 0) { bucketD++; continue; }
+
+      const firstPrice = prices[0];
+      const minPrice = Math.min(...prices);
+      const maxPrice = Math.max(...prices);
+      const hasRealRoll = prices.some(x => x > SAMPLE_PRICE + 0.005);
+      const minIs425 = is425(minPrice);
+      const firstIs425 = is425(firstPrice);
+
+      if (minIs425) feedMin425++;
+      if (firstIs425) feedFirst425++;
+
+      if (prices.every(x => x === 0)) {
+        bucketD++;
+      } else if (is425(maxPrice) && !hasRealRoll) {
+        bucketA++;
+        vendorCount[p.vendor] = (vendorCount[p.vendor] || 0) + 1;
+      } else if (minIs425 && hasRealRoll) {
+        bucketB++;
+        vendorCount[p.vendor] = (vendorCount[p.vendor] || 0) + 1;
+      } else if (minPrice === 0 && hasRealRoll) {
+        bucketD++;
+      } else {
+        bucketC++;
+      }
+
+      if (firstIs425) vendorCountFirst[p.vendor] = (vendorCountFirst[p.vendor] || 0) + 1;
+    }
+    if (!conn.pageInfo.hasNextPage) break;
+    cursor = conn.pageInfo.endCursor;
+    if (pages % 10 === 0) {
+      process.stderr.write(`  ...${total} products, ${pages} pages, ${((Date.now()-startedAt)/1000).toFixed(0)}s\n`);
+    }
+  }
+
+  const result = {
+    generated_at: new Date().toISOString(),
+    total_active: total,
+    pages,
+    elapsed_s: ((Date.now() - startedAt) / 1000).toFixed(1),
+    buckets_min_model: {
+      a_sample_only: bucketA,
+      b_has_real_roll_but_feeds_425: bucketB,
+      c_real_feed_price_ok: bucketC,
+      d_no_price_or_zero: bucketD,
+    },
+    feed_425_min_model: feedMin425,
+    feed_425_first_model: feedFirst425,
+    would_feed_425_a_plus_b: bucketA + bucketB,
+    vendor_breakdown_a_plus_b: Object.entries(vendorCount).sort((x,y)=>y[1]-x[1]),
+    vendor_breakdown_first_model: Object.entries(vendorCountFirst).sort((x,y)=>y[1]-x[1]),
+  };
+  console.log(JSON.stringify(result, null, 2));
+  fs.writeFileSync('/Users/stevestudio2/Projects/designerwallcoverings/gmc-425-result.json', JSON.stringify(result, null, 2));
+})().catch(e => { console.error('FATAL', e.message); process.exit(1); });
diff --git a/gmc-425-result.json b/gmc-425-result.json
new file mode 100644
index 0000000..5a6bb1f
--- /dev/null
+++ b/gmc-425-result.json
@@ -0,0 +1,1227 @@
+{
+  "generated_at": "2026-06-15T15:03:48.894Z",
+  "total_active": 74656,
+  "pages": 747,
+  "elapsed_s": "242.1",
+  "buckets_min_model": {
+    "a_sample_only": 35835,
+    "b_has_real_roll_but_feeds_425": 26077,
+    "c_real_feed_price_ok": 12632,
+    "d_no_price_or_zero": 112
+  },
+  "feed_425_min_model": 61879,
+  "feed_425_first_model": 59119,
+  "would_feed_425_a_plus_b": 61912,
+  "vendor_breakdown_a_plus_b": [
+    [
+      "Phillipe Romano",
+      9183
+    ],
+    [
+      "Rebel Walls",
+      3618
+    ],
+    [
+      "Hollywood Wallcoverings",
+      3156
+    ],
+    [
+      "Malibu Wallpaper",
+      2832
+    ],
+    [
+      "Phillip Jeffries",
+      2473
+    ],
+    [
+      "Kravet",
+      2464
+    ],
+    [
+      "Koroseal",
+      2353
+    ],
+    [
+      "Schumacher",
+      2118
+    ],
+    [
+      "China Seas",
+      1788
+    ],
+    [
+      "Los Angeles Fabrics",
+      1634
+    ],
+    [
+      "Brunschwig & Fils",
+      1600
+    ],
+    [
+      "Thibaut",
+      1595
+    ],
+    [
+      "Jeffrey Stevens",
+      1501
+    ],
+    [
+      "Designer Wallcoverings",
+      1481
+    ],
+    [
+      "Coordonné",
+      1319
+    ],
+    [
+      "Mind the Gap",
+      1133
+    ],
+    [
+      "Osborne & Little",
+      1010
+    ],
+    [
+      "Scalamandre Wallpaper",
+      962
+    ],
+    [
+      "DW Bespoke Studio",
+      957
+    ],
+    [
+      "Lee Jofa",
+      957
+    ],
+    [
+      "Wolf Gordon",
+      935
+    ],
+    [
+      "Anna French",
+      924
+    ],
+    [
+      "Cole & Son",
+      885
+    ],
+    [
+      "Designers Guild",
+      850
+    ],
+    [
+      "Versa Designed Surfaces",
+      842
+    ],
+    [
+      "Sandberg",
+      764
+    ],
+    [
+      "Surface Stick",
+      740
+    ],
+    [
+      "Maya Romanoff",
+      624
+    ],
+    [
+      "Harlequin",
+      583
+    ],
+    [
+      "Arte International",
+      558
+    ],
+    [
+      "Nina Campbell",
+      551
+    ],
+    [
+      "LA Walls",
+      529
+    ],
+    [
+      "William Morris",
+      526
+    ],
+    [
+      "Newmor Wallcoverings",
+      515
+    ],
+    [
+      "Versace",
+      513
+    ],
+    [
+      "Romo",
+      503
+    ],
+    [
+      "British Walls",
+      449
+    ],
+    [
+      "Graham & Brown",
+      439
+    ],
+    [
+      "AS Creation",
+      431
+    ],
+    [
+      "Glitter Walls",
+      402
+    ],
+    [
+      "Clarke And Clarke",
+      399
+    ],
+    [
+      "Architectural Fabrics",
+      328
+    ],
+    [
+      "Gaston Y Daniela",
+      273
+    ],
+    [
+      "GP & J Baker",
+      263
+    ],
+    [
+      "Fentucci",
+      254
+    ],
+    [
+      "Elitis",
+      244
+    ],
+    [
+      "Innovations USA",
+      203
+    ],
+    [
+      "Christian Lacroix Europe",
+      170
+    ],
+    [
+      "Donghia",
+      159
+    ],
+    [
+      "Brand McKenzie",
+      158
+    ],
+    [
+      "Novasuede",
+      157
+    ],
+    [
+      "Roberto Cavalli Wallpaper",
+      149
+    ],
+    [
+      "Ralph Lauren",
+      141
+    ],
+    [
+      "Mulberry",
+      128
+    ],
+    [
+      "Marburg",
+      119
+    ],
+    [
+      "Laura Ashley Wallpaper",
+      110
+    ],
+    [
+      "Primo Leathers",
+      107
+    ],
+    [
+      "Ultrasuede",
+      107
+    ],
+    [
+      "Mid Century Modern",
+      106
+    ],
+    [
+      "Hospitality Wallcoverings",
+      96
+    ],
+    [
+      "Traditional Whimsy",
+      89
+    ],
+    [
+      "Nobilis",
+      77
+    ],
+    [
+      "LEED Walls",
+      73
+    ],
+    [
+      "Daisy Bennett",
+      69
+    ],
+    [
+      "Threads",
+      63
+    ],
+    [
+      "Missoni Wallpaper",
+      58
+    ],
+    [
+      "Glambeads by DW",
+      58
+    ],
+    [
+      "Luxury Murals",
+      56
+    ],
+    [
+      "MC Escher Wallpaper",
+      50
+    ],
+    [
+      "Madagascar Walls",
+      49
+    ],
+    [
+      "Graduate Collection",
+      49
+    ],
+    [
+      "Lincrusta",
+      47
+    ],
+    [
+      "Retro Walls",
+      46
+    ],
+    [
+      "Hollywood Acoustical",
+      46
+    ],
+    [
+      "Sancar",
+      42
+    ],
+    [
+      "Hand Crafted",
+      40
+    ],
+    [
+      "Apartment Wallpaper",
+      39
+    ],
+    [
+      "Glass Beaded",
+      37
+    ],
+    [
+      "Tres Tintas",
+      36
+    ],
+    [
+      "Marimekko Exclusive",
+      35
+    ],
+    [
+      "DW Home",
+      35
+    ],
+    [
+      "Pixels",
+      35
+    ],
+    [
+      "Graduate Collection UK",
+      34
+    ],
+    [
+      "Boråstapeter",
+      31
+    ],
+    [
+      "Phyllis Morris",
+      28
+    ],
+    [
+      "Dolce & Gabbana",
+      27
+    ],
+    [
+      "Natural Paint",
+      26
+    ],
+    [
+      "Franquemont London",
+      26
+    ],
+    [
+      "Zeuxis Parrhasius Europe",
+      21
+    ],
+    [
+      "Steve Abrams Studios",
+      16
+    ],
+    [
+      "Emiliana Parati",
+      15
+    ],
+    [
+      "Wallpaper NYC",
+      12
+    ],
+    [
+      "Showroom Line",
+      11
+    ],
+    [
+      "Witch & Watchman",
+      10
+    ],
+    [
+      "Hygge & West",
+      10
+    ],
+    [
+      "SUGARBOO",
+      10
+    ],
+    [
+      "NLXL",
+      9
+    ],
+    [
+      "Backdrop",
+      9
+    ],
+    [
+      "Designtex",
+      9
+    ],
+    [
+      "Contrado",
+      6
+    ],
+    [
+      "Zeuxis Parrhasius at DW",
+      5
+    ],
+    [
+      "Andrew Martin",
+      5
+    ],
+    [
+      "Vahallan",
+      4
+    ],
+    [
+      "Zoffany",
+      4
+    ],
+    [
+      "Architectural Wallcoverings",
+      4
+    ],
+    [
+      "Baker Lifestyle",
+      4
+    ],
+    [
+      "Pierre Frey",
+      3
+    ],
+    [
+      "Holland and Sherry",
+      3
+    ],
+    [
+      "Peg Norriss",
+      3
+    ],
+    [
+      "Sacha Walckhoff",
+      3
+    ],
+    [
+      "Fabricut",
+      3
+    ],
+    [
+      "WallQuest",
+      3
+    ],
+    [
+      "Zinc Textile",
+      3
+    ],
+    [
+      "Villa Nova",
+      3
+    ],
+    [
+      "Black Edition",
+      3
+    ],
+    [
+      "Caroline Cecil Textiles",
+      3
+    ],
+    [
+      "Clarke and Clarke",
+      3
+    ],
+    [
+      "Erika Wakerly",
+      2
+    ],
+    [
+      "Clarence House",
+      2
+    ],
+    [
+      "Winfield Thybony",
+      2
+    ],
+    [
+      "Patty Madden",
+      2
+    ],
+    [
+      "Atomic 50 Ceilings",
+      2
+    ],
+    [
+      "Kirkby Design",
+      2
+    ],
+    [
+      "Kravet Couture",
+      2
+    ],
+    [
+      "Kravet Design",
+      2
+    ],
+    [
+      "PR Faux Leather",
+      1
+    ],
+    [
+      "Malibu Walls",
+      1
+    ],
+    [
+      "Printy6",
+      1
+    ],
+    [
+      "French Market",
+      1
+    ],
+    [
+      "OLIVIA AND POPP",
+      1
+    ],
+    [
+      "Edge Wallcovering",
+      1
+    ],
+    [
+      "Morris and Company",
+      1
+    ],
+    [
+      "Steven Abrams Photography",
+      1
+    ],
+    [
+      "DW Exclusive Wallpaper",
+      1
+    ],
+    [
+      "Catchii Netherlands Europe",
+      1
+    ],
+    [
+      "Celerie Kemble",
+      1
+    ],
+    [
+      "Happy Menocal",
+      1
+    ],
+    [
+      "Sara Bergqvist",
+      1
+    ],
+    [
+      "Rebel Studio",
+      1
+    ],
+    [
+      "Milton & King",
+      1
+    ],
+    [
+      "Kelly Wearstler",
+      1
+    ],
+    [
+      "Linherr Hollingsworth",
+      1
+    ],
+    [
+      "Sarah Bartholomew",
+      1
+    ],
+    [
+      "Farrow & Ball",
+      1
+    ],
+    [
+      "Rebecca Moses",
+      1
+    ],
+    [
+      "Ananbo",
+      1
+    ],
+    [
+      "Studio Ditte",
+      1
+    ],
+    [
+      "A.S. Création",
+      1
+    ],
+    [
+      "Dedar",
+      1
+    ],
+    [
+      "Mark Alexander",
+      1
+    ],
+    [
+      "Dupenny",
+      1
+    ],
+    [
+      "DWHD",
+      1
+    ],
+    [
+      "G P & J Baker",
+      1
+    ],
+    [
+      "Lee Jofa Modern",
+      1
+    ],
+    [
+      "Scion",
+      1
+    ]
+  ],
+  "vendor_breakdown_first_model": [
+    [
+      "Phillipe Romano",
+      9130
+    ],
+    [
+      "Hollywood Wallcoverings",
+      3156
+    ],
+    [
+      "Malibu Wallpaper",
+      2832
+    ],
+    [
+      "Rebel Walls",
+      2499
+    ],
+    [
+      "Phillip Jeffries",
+      2472
+    ],
+    [
+      "Kravet",
+      2464
+    ],
+    [
+      "Koroseal",
+      2353
+    ],
+    [
+      "China Seas",
+      1788
+    ],
+    [
+      "Los Angeles Fabrics",
+      1634
+    ],
+    [
+      "Brunschwig & Fils",
+      1597
+    ],
+    [
+      "Thibaut",
+      1566
+    ],
+    [
+      "Jeffrey Stevens",
+      1482
+    ],
+    [
+      "Designer Wallcoverings",
+      1451
+    ],
+    [
+      "Schumacher",
+      1393
+    ],
+    [
+      "Coordonné",
+      1319
+    ],
+    [
+      "Mind the Gap",
+      1133
+    ],
+    [
+      "Osborne & Little",
+      1010
+    ],
+    [
+      "Scalamandre Wallpaper",
+      962
+    ],
+    [
+      "Lee Jofa",
+      956
+    ],
+    [
+      "Wolf Gordon",
+      935
+    ],
+    [
+      "Anna French",
+      924
+    ],
+    [
+      "Cole & Son",
+      853
+    ],
+    [
+      "Designers Guild",
+      850
+    ],
+    [
+      "Versa Designed Surfaces",
+      842
+    ],
+    [
+      "DW Bespoke Studio",
+      768
+    ],
+    [
+      "Sandberg",
+      764
+    ],
+    [
+      "Surface Stick",
+      740
+    ],
+    [
+      "Maya Romanoff",
+      624
+    ],
+    [
+      "Harlequin",
+      583
+    ],
+    [
+      "Arte International",
+      558
+    ],
+    [
+      "Nina Campbell",
+      551
+    ],
+    [
+      "LA Walls",
+      529
+    ],
+    [
+      "William Morris",
+      526
+    ],
+    [
+      "Newmor Wallcoverings",
+      515
+    ],
+    [
+      "Versace",
+      513
+    ],
+    [
+      "British Walls",
+      449
+    ],
+    [
+      "Graham & Brown",
+      439
+    ],
+    [
+      "AS Creation",
+      431
+    ],
+    [
+      "Glitter Walls",
+      402
+    ],
+    [
+      "Clarke And Clarke",
+      399
+    ],
+    [
+      "Architectural Fabrics",
+      328
+    ],
+    [
+      "Gaston Y Daniela",
+      272
+    ],
+    [
+      "GP & J Baker",
+      263
+    ],
+    [
+      "Fentucci",
+      253
+    ],
+    [
+      "Elitis",
+      244
+    ],
+    [
+      "Innovations USA",
+      203
+    ],
+    [
+      "Christian Lacroix Europe",
+      170
+    ],
+    [
+      "Donghia",
+      159
+    ],
+    [
+      "Brand McKenzie",
+      158
+    ],
+    [
+      "Novasuede",
+      157
+    ],
+    [
+      "Roberto Cavalli Wallpaper",
+      149
+    ],
+    [
+      "Ralph Lauren",
+      141
+    ],
+    [
+      "Mulberry",
+      127
+    ],
+    [
+      "Marburg",
+      119
+    ],
+    [
+      "Laura Ashley Wallpaper",
+      110
+    ],
+    [
+      "Primo Leathers",
+      107
+    ],
+    [
+      "Ultrasuede",
+      107
+    ],
+    [
+      "Mid Century Modern",
+      106
+    ],
+    [
+      "Hospitality Wallcoverings",
+      96
+    ],
+    [
+      "Traditional Whimsy",
+      89
+    ],
+    [
+      "Nobilis",
+      77
+    ],
+    [
+      "LEED Walls",
+      73
+    ],
+    [
+      "Daisy Bennett",
+      69
+    ],
+    [
+      "Threads",
+      62
+    ],
+    [
+      "Missoni Wallpaper",
+      58
+    ],
+    [
+      "Glambeads by DW",
+      58
+    ],
+    [
+      "Luxury Murals",
+      56
+    ],
+    [
+      "MC Escher Wallpaper",
+      50
+    ],
+    [
+      "Madagascar Walls",
+      49
+    ],
+    [
+      "Graduate Collection",
+      49
+    ],
+    [
+      "Lincrusta",
+      47
+    ],
+    [
+      "Hollywood Acoustical",
+      46
+    ],
+    [
+      "Retro Walls",
+      43
+    ],
+    [
+      "Sancar",
+      42
+    ],
+    [
+      "Hand Crafted",
+      40
+    ],
+    [
+      "Apartment Wallpaper",
+      39
+    ],
+    [
+      "Glass Beaded",
+      37
+    ],
+    [
+      "Tres Tintas",
+      36
+    ],
+    [
+      "Marimekko Exclusive",
+      35
+    ],
+    [
+      "Graduate Collection UK",
+      34
+    ],
+    [
+      "Boråstapeter",
+      31
+    ],
+    [
+      "Phyllis Morris",
+      28
+    ],
+    [
+      "Dolce & Gabbana",
+      27
+    ],
+    [
+      "Natural Paint",
+      26
+    ],
+    [
+      "Franquemont London",
+      26
+    ],
+    [
+      "Zeuxis Parrhasius Europe",
+      21
+    ],
+    [
+      "Emiliana Parati",
+      15
+    ],
+    [
+      "Romo",
+      13
+    ],
+    [
+      "Steve Abrams Studios",
+      13
+    ],
+    [
+      "Wallpaper NYC",
+      12
+    ],
+    [
+      "Showroom Line",
+      11
+    ],
+    [
+      "Witch & Watchman",
+      10
+    ],
+    [
+      "Hygge & West",
+      10
+    ],
+    [
+      "SUGARBOO",
+      10
+    ],
+    [
+      "NLXL",
+      9
+    ],
+    [
+      "Backdrop",
+      9
+    ],
+    [
+      "Pixels",
+      6
+    ],
+    [
+      "Zeuxis Parrhasius at DW",
+      5
+    ],
+    [
+      "Andrew Martin",
+      5
+    ],
+    [
+      "Vahallan",
+      4
+    ],
+    [
+      "Zoffany",
+      4
+    ],
+    [
+      "Architectural Wallcoverings",
+      4
+    ],
+    [
+      "DW Home",
+      3
+    ],
+    [
+      "Pierre Frey",
+      3
+    ],
+    [
+      "Holland and Sherry",
+      3
+    ],
+    [
+      "Peg Norriss",
+      3
+    ],
+    [
+      "Sacha Walckhoff",
+      3
+    ],
+    [
+      "Fabricut",
+      3
+    ],
+    [
+      "WallQuest",
+      3
+    ],
+    [
+      "Zinc Textile",
+      3
+    ],
+    [
+      "Villa Nova",
+      3
+    ],
+    [
+      "Black Edition",
+      3
+    ],
+    [
+      "Erika Wakerly",
+      2
+    ],
+    [
+      "Clarence House",
+      2
+    ],
+    [
+      "Patty Madden",
+      2
+    ],
+    [
+      "Atomic 50 Ceilings",
+      2
+    ],
+    [
+      "Kirkby Design",
+      2
+    ],
+    [
+      "PR Faux Leather",
+      1
+    ],
+    [
+      "Malibu Walls",
+      1
+    ],
+    [
+      "French Market",
+      1
+    ],
+    [
+      "OLIVIA AND POPP",
+      1
+    ],
+    [
+      "Edge Wallcovering",
+      1
+    ],
+    [
+      "Steven Abrams Photography",
+      1
+    ],
+    [
+      "Contrado",
+      1
+    ],
+    [
+      "DW Exclusive Wallpaper",
+      1
+    ],
+    [
+      "Winfield Thybony",
+      1
+    ],
+    [
+      "Catchii Netherlands Europe",
+      1
+    ],
+    [
+      "Celerie Kemble",
+      1
+    ],
+    [
+      "Happy Menocal",
+      1
+    ],
+    [
+      "Sara Bergqvist",
+      1
+    ],
+    [
+      "Rebel Studio",
+      1
+    ],
+    [
+      "Milton & King",
+      1
+    ],
+    [
+      "Kelly Wearstler",
+      1
+    ],
+    [
+      "Linherr Hollingsworth",
+      1
+    ],
+    [
+      "Sarah Bartholomew",
+      1
+    ],
+    [
+      "Farrow & Ball",
+      1
+    ],
+    [
+      "Rebecca Moses",
+      1
+    ],
+    [
+      "Ananbo",
+      1
+    ],
+    [
+      "Studio Ditte",
+      1
+    ],
+    [
+      "A.S. Création",
+      1
+    ],
+    [
+      "Dedar",
+      1
+    ],
+    [
+      "Mark Alexander",
+      1
+    ],
+    [
+      "Dupenny",
+      1
+    ],
+    [
+      "DWHD",
+      1
+    ],
+    [
+      "Baker Lifestyle",
+      1
+    ],
+    [
+      "Kravet Couture",
+      1
+    ],
+    [
+      "Kravet Design",
+      1
+    ]
+  ]
+}
\ No newline at end of file
diff --git a/gmc-gate-probe.js b/gmc-gate-probe.js
new file mode 100644
index 0000000..b6de185
--- /dev/null
+++ b/gmc-gate-probe.js
@@ -0,0 +1,14 @@
+const fs=require('fs');
+const SHOP='designer-laboratory-sandbox.myshopify.com',VERSION='2024-10';
+const ENV=fs.readFileSync('/Users/stevestudio2/Projects/secrets-manager/.env','utf8');
+const TOKEN=(ENV.match(/^SHOPIFY_ADMIN_TOKEN=(.+)$/m)||[])[1];
+const URL=`https://${SHOP}/admin/api/${VERSION}/graphql.json`;
+const sleep=ms=>new Promise(r=>setTimeout(r,ms));
+async function gql(q,v){for(let a=0;a<7;a++){let res;try{res=await fetch(URL,{method:'POST',headers:{'X-Shopify-Access-Token':TOKEN,'Content-Type':'application/json'},body:JSON.stringify({query:q,variables:v})});}catch(e){await sleep(2000*(a+1));continue;}if(res.status===429){await sleep(2000*(a+1));continue;}const j=await res.json();if(j.errors){if(JSON.stringify(j.errors).includes('THROTTLED')){await sleep(2000*(a+1));continue;}throw new Error(JSON.stringify(j.errors));}const av=j.extensions?.cost?.throttleStatus?.currentlyAvailable??4000;if(av<400)await sleep(1500);return j.data;}throw new Error('retries');}
+const Q=`query($c:String){products(first:120,after:$c,query:"status:active"){pageInfo{hasNextPage endCursor}nodes{featuredImage{url} widthMeta:metafield(namespace:"custom",key:"width"){value} widthMeta2:metafield(namespace:"specs",key:"width"){value}}}}`;
+(async()=>{let cursor=null,total=0,pages=0,noImg=0,hasImg=0,noWidth=0,hasWidth=0,imgAndWidth=0;
+while(true){const d=await gql(Q,{cursor});const c=d.products;pages++;
+for(const p of c.nodes){total++;const img=!!p.featuredImage?.url;const w=!!(p.widthMeta?.value||p.widthMeta2?.value);if(img)hasImg++;else noImg++;if(w)hasWidth++;else noWidth++;if(img&&w)imgAndWidth++;}
+if(!c.pageInfo.hasNextPage)break;cursor=c.pageInfo.endCursor;if(pages%15===0)process.stderr.write(`  ...${total} ${pages}p\n`);}
+const out={total_active:total,pages,has_image:hasImg,no_image:noImg,has_width_metafield:hasWidth,no_width_metafield:noWidth,image_AND_width:imgAndWidth};
+console.log(JSON.stringify(out,null,2));fs.writeFileSync('/Users/stevestudio2/Projects/designerwallcoverings/gmc-gate-probe.json',JSON.stringify(out,null,2));})().catch(e=>{console.error('FATAL',e.message);process.exit(1);});
diff --git a/scripts/kravet-cost/currency-check.js b/scripts/kravet-cost/currency-check.js
new file mode 100644
index 0000000..a7de589
--- /dev/null
+++ b/scripts/kravet-cost/currency-check.js
@@ -0,0 +1,71 @@
+#!/usr/bin/env node
+/**
+ * currency-check.js — per-brand "is this mfr_sku still CURRENT at Kravet?" verifier.
+ * Reuses the proven exact-sku Algolia logic from kravet-disco-algolia-verify.js.
+ * READ-ONLY (Algolia index queries + a DB read). Writes a per-brand result CSV only —
+ * never archives/updates Shopify or the DB (archiving disco SKUs is a separate gated step).
+ *
+ * USAGE:  AKEY=<algolia-key> node currency-check.js "<vendor brand>"
+ * Designed to run one brand per process so the whole family fans out in parallel
+ * (see run-parallel.sh). Output: data/kravet-cost/currency-<brand-slug>.csv
+ *
+ * A SKU is:
+ *   CURRENT       — exact sku present in the live Kravet US index
+ *   DISCO_COLOR   — pattern still indexed, this colorway gone
+ *   DISCO_PATTERN — whole pattern absent (line itself is indexed elsewhere)
+ *   AMW_SKIP      — Andrew Martin (AMW*/AM1*) — not in this index, needs its own source
+ */
+const https = require('https');
+const fs = require('fs');
+const { execSync } = require('child_process');
+
+const APP = 'M9TBUM1WAE', IDX = 'kravet_production_kravet_us_products', KEY = process.env.AKEY;
+const BRAND = process.argv[2];
+if (!BRAND) { console.error('usage: AKEY=… node currency-check.js "<brand>"'); process.exit(2); }
+if (!KEY) { console.error('FATAL: AKEY (Algolia key) not set in env — cannot currency-check'); process.exit(3); }
+
+const slug = BRAND.toLowerCase().replace(/[^a-z0-9]+/g, '-').replace(/^-|-$/g, '');
+const OUT = `data/kravet-cost/currency-${slug}.csv`;
+const sleep = ms => new Promise(r => setTimeout(r, ms));
+
+// pull this brand's ACTIVE mfr_skus from the mirror (read-only)
+const sql = `select distinct on (shopify_id) mfr_sku from shopify_products
+  where status='ACTIVE' and vendor='${BRAND.replace(/'/g, "''")}' and mfr_sku is not null and mfr_sku<>'';`;
+const rows = execSync(`psql -d dw_unified -tA -c "${sql}"`, { encoding: 'utf8' })
+  .trim().split('\n').map(s => s.trim()).filter(Boolean);
+
+function query(q) {
+  return new Promise(res => {
+    const body = JSON.stringify({ query: q, restrictSearchableAttributes: ['sku'], hitsPerPage: 200, attributesToRetrieve: ['sku'] });
+    const req = https.request(`https://${APP}-dsn.algolia.net/1/indexes/${IDX}/query`,
+      { method: 'POST', headers: { 'X-Algolia-Application-Id': APP, 'X-Algolia-API-Key': KEY, 'Content-Type': 'application/json' } },
+      r => { let b = ''; r.on('data', d => b += d); r.on('end', () => { try { res(JSON.parse(b)); } catch (e) { res({ hits: [], _err: 1 }); } }); });
+    req.on('error', () => res({ hits: [], _err: 1 }));
+    req.setTimeout(20000, () => { req.destroy(); res({ hits: [], _err: 1 }); });
+    req.write(body); req.end();
+  });
+}
+
+(async () => {
+  const patCache = {};
+  const out = [];
+  let current = 0, dColor = 0, dPat = 0, amw = 0, err = 0, i = 0;
+  for (const sku of rows) {
+    i++;
+    const pat = sku.split('.')[0];
+    if (/^AMW|^AM1/i.test(pat)) { out.push([sku, 'AMW_SKIP']); amw++; continue; }
+    if (!(pat in patCache)) {
+      const r = await query(pat);
+      if (r._err) { out.push([sku, 'ERROR']); err++; continue; }
+      patCache[pat] = new Set((r.hits || []).map(h => (h.sku || '').toUpperCase()).filter(s => s.startsWith(pat.toUpperCase())));
+      await sleep(120);
+    }
+    const set = patCache[pat];
+    if (set.has(sku.toUpperCase())) { out.push([sku, 'CURRENT']); current++; }
+    else if (set.size > 0) { out.push([sku, 'DISCO_COLOR']); dColor++; }
+    else { out.push([sku, 'DISCO_PATTERN']); dPat++; }
+    if (i % 100 === 0) process.stderr.write(`  [${BRAND}] ${i}/${rows.length} cur:${current} discoC:${dColor} discoP:${dPat}\n`);
+  }
+  fs.writeFileSync(OUT, 'mfr_sku,status\n' + out.map(r => r.join(',')).join('\n') + '\n');
+  console.log(`[${BRAND}] total:${rows.length} CURRENT:${current} DISCO_COLOR:${dColor} DISCO_PATTERN:${dPat} AMW:${amw} ERR:${err} -> ${OUT}`);
+})();
diff --git a/scripts/kravet-cost/run-parallel.sh b/scripts/kravet-cost/run-parallel.sh
new file mode 100644
index 0000000..6439228
--- /dev/null
+++ b/scripts/kravet-cost/run-parallel.sh
@@ -0,0 +1,32 @@
+#!/usr/bin/env bash
+# run-parallel.sh — fan the Kravet-family currency-check out, one process per brand,
+# all brands concurrent. Needs AKEY (Algolia key) in the environment.
+#   AKEY=<key> bash scripts/kravet-cost/run-parallel.sh
+# Read-only: each worker only queries Algolia + reads the mirror, writes a per-brand CSV.
+set -uo pipefail
+cd "$(dirname "$0")/../.."
+[ -z "${AKEY:-}" ] && { echo "FATAL: export AKEY=<algolia-key> first"; exit 1; }
+mkdir -p data/kravet-cost
+
+BRANDS=(
+  "Kravet" "Brunschwig & Fils" "Clarke And Clarke" "Cole & Son" "Lee Jofa"
+  "GP & J Baker" "Mulberry" "Threads" "Andrew Martin" "Baker Lifestyle"
+  "Kravet Design" "Kravet Couture" "Lee Jofa Modern" "G P & J Baker"
+)
+echo "fanning ${#BRANDS[@]} brands in parallel…"
+pids=()
+for b in "${BRANDS[@]}"; do
+  AKEY="$AKEY" node scripts/kravet-cost/currency-check.js "$b" >>"data/kravet-cost/run.log" 2>&1 &
+  pids+=($!)
+done
+# wait for all, report
+fail=0
+for p in "${pids[@]}"; do wait "$p" || fail=$((fail+1)); done
+echo "done — $fail worker(s) errored. per-brand CSVs in data/kravet-cost/, log: data/kravet-cost/run.log"
+echo "=== summary ==="
+for f in data/kravet-cost/currency-*.csv; do
+  [ -f "$f" ] || continue
+  tot=$(($(wc -l < "$f")-1))
+  cur=$(grep -c ',CURRENT$' "$f" 2>/dev/null || echo 0)
+  echo "  $(basename "$f"): $tot skus, $cur current"
+done
\ No newline at end of file

← 90c7e9a Google feed: prep-actions (punch-list + unpublish-list) + ga  ·  back to Dw Yolo Loop  ·  Kravet cost campaign: 6,373/8,317 (77%) MAP-resolved from fr 2dae150 →