[object Object]

← back to Gmc Titlefix

TK-11844/11847: resolve bare-offer product status; guard destinationStatuses; harden vid fallback

7657c83bad3b05b7b2215443546282497d8e7385 · 2026-09-16 15:55:56 -0700 · Steve

- tk11844: build variantId->productId map so bare-numeric offers (offerId IS the
  variant id) resolve their product; shopify_product_status was
  "(bare-offer:unknown-pid)" for 7/27 rows. Re-run: all 27 now ACTIVE, and 2 of the
  formerly-blind bare offers are SERVING on ACTIVE products (Sakka, Haiku). Removed
  dead `sp` line that needlessly materialized pjProducts.entries() per hit.
- tk11847: guard p.destinationStatuses?.[0]?.status (empty array no longer throws
  and silently drops the GMC read into parity_unknown); harden vid to fall back to
  the numeric tail so prefixed offers without ?variant= still resolve.

Both scripts remain strictly read-only ($0, no Shopify/GMC writes).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TAwgQZWgKL5R6d5uLG3QUt

Files touched

Diff

commit 7657c83bad3b05b7b2215443546282497d8e7385
Author: Steve <steve@designerwallcoverings.com>
Date:   Wed Sep 16 15:55:56 2026 -0700

    TK-11844/11847: resolve bare-offer product status; guard destinationStatuses; harden vid fallback
    
    - tk11844: build variantId->productId map so bare-numeric offers (offerId IS the
      variant id) resolve their product; shopify_product_status was
      "(bare-offer:unknown-pid)" for 7/27 rows. Re-run: all 27 now ACTIVE, and 2 of the
      formerly-blind bare offers are SERVING on ACTIVE products (Sakka, Haiku). Removed
      dead `sp` line that needlessly materialized pjProducts.entries() per hit.
    - tk11847: guard p.destinationStatuses?.[0]?.status (empty array no longer throws
      and silently drops the GMC read into parity_unknown); harden vid to fall back to
      the numeric tail so prefixed offers without ?variant= still resolve.
    
    Both scripts remain strictly read-only ($0, no Shopify/GMC writes).
    
    Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
    Claude-Session: https://claude.ai/code/session_01TAwgQZWgKL5R6d5uLG3QUt
---
 data/tk11844-pj-authoritative-join.json | 30 ++++++------
 tk11844-pj-authoritative-join.mjs       | 11 +++--
 tk11847-verify-parity.mjs               | 82 +++++++++++++++++++++++++++++++++
 3 files changed, 104 insertions(+), 19 deletions(-)

diff --git a/data/tk11844-pj-authoritative-join.json b/data/tk11844-pj-authoritative-join.json
index af0ce9f..2c31625 100644
--- a/data/tk11844-pj-authoritative-join.json
+++ b/data/tk11844-pj-authoritative-join.json
@@ -1,7 +1,7 @@
 {
  "ticket": "TK-11844",
  "read_only": true,
- "fetched_at": "2026-09-16T21:41:39.126Z",
+ "fetched_at": "2026-09-16T22:55:22.976Z",
  "method": "authoritative Shopify vendor->id-set join on live GMC offers (both offerId forms)",
  "walk_complete": true,
  "total_offers_walked": 61992,
@@ -26,9 +26,9 @@
    "title": "Sakka - White on Marshmallow Manila Hemp 11' Panel Height - Mural Repeat: 264\" (",
    "brand": null,
    "price": 5212.67,
-   "shopify_productId": null,
+   "shopify_productId": "7375889825843",
    "shopify_variantId": "44455859126323",
-   "shopify_product_status": "(bare-offer:unknown-pid)",
+   "shopify_product_status": "ACTIVE",
    "issues": [],
    "link": "https://www.designerwallcoverings.com/products/dwjp-13182-philip-jeffries-sakka-white-on-marshmallow-manila-hemp-11-panel-height-mural-repeat-264-670-6-cm-h-9022"
   },
@@ -42,9 +42,9 @@
    "title": "Haiku - Red Rejuvination on Venetian Glass Glam Grass 11' Panel Height - Mural R",
    "brand": null,
    "price": 5212.67,
-   "shopify_productId": null,
+   "shopify_productId": "7375889137715",
    "shopify_variantId": "44455856701491",
-   "shopify_product_status": "(bare-offer:unknown-pid)",
+   "shopify_product_status": "ACTIVE",
    "issues": [],
    "link": "https://www.designerwallcoverings.com/products/dwjp-13178-philip-jeffries-haiku-red-rejuvination-on-venetian-glass-glam-grass-11-panel-height-mural-repeat-264-670-6-cm-h-9020?variant=44455856701491"
   },
@@ -390,9 +390,9 @@
    "title": "",
    "brand": null,
    "price": 7,
-   "shopify_productId": null,
+   "shopify_productId": "7375633416243",
    "shopify_variantId": "42511636201523",
-   "shopify_product_status": "(bare-offer:unknown-pid)",
+   "shopify_product_status": "ACTIVE",
    "issues": [
     "DISAPPROVED|item_missing_required_attribute"
    ],
@@ -408,9 +408,9 @@
    "title": "",
    "brand": null,
    "price": 7,
-   "shopify_productId": null,
+   "shopify_productId": "7375654158387",
    "shopify_variantId": "42511667757107",
-   "shopify_product_status": "(bare-offer:unknown-pid)",
+   "shopify_product_status": "ACTIVE",
    "issues": [
     "DISAPPROVED|item_missing_required_attribute"
    ],
@@ -426,9 +426,9 @@
    "title": "",
    "brand": null,
    "price": 7,
-   "shopify_productId": null,
+   "shopify_productId": "7375739027507",
    "shopify_variantId": "42511905947699",
-   "shopify_product_status": "(bare-offer:unknown-pid)",
+   "shopify_product_status": "ACTIVE",
    "issues": [
     "DISAPPROVED|item_missing_required_attribute"
    ],
@@ -444,9 +444,9 @@
    "title": "",
    "brand": null,
    "price": 7,
-   "shopify_productId": null,
+   "shopify_productId": "7375630401587",
    "shopify_variantId": "42511624405043",
-   "shopify_product_status": "(bare-offer:unknown-pid)",
+   "shopify_product_status": "ACTIVE",
    "issues": [
     "DISAPPROVED|item_missing_required_attribute"
    ],
@@ -462,9 +462,9 @@
    "title": "Saint Germain Hemp II - Silver On White",
    "brand": null,
    "price": 4.25,
-   "shopify_productId": null,
+   "shopify_productId": "7375727231027",
    "shopify_variantId": "42511885860915",
-   "shopify_product_status": "(bare-offer:unknown-pid)",
+   "shopify_product_status": "ACTIVE",
    "issues": [
     "DISAPPROVED|tobacco_policy_violation"
    ],
diff --git a/tk11844-pj-authoritative-join.mjs b/tk11844-pj-authoritative-join.mjs
index 5798486..32cde50 100644
--- a/tk11844-pj-authoritative-join.mjs
+++ b/tk11844-pj-authoritative-join.mjs
@@ -35,13 +35,14 @@ async function sgql(q,v){for(let a=0;a<6;a++){const r=await fetch(SURL,{method:'
 console.error('[1/3] Shopify: enumerating vendor=Phillip Jeffries products+variants...');
 const pjProductIds=new Set(), pjVariantIds=new Set();
 const pjProducts=new Map(); // productId -> {title,handle,status}
+const pjVariantToProduct=new Map(); // variantId -> productId  (so bare offers can resolve their product)
 let after=null, ppages=0;
 do{
   const d=await sgql(`query($a:String){products(first:100,after:$a,query:"vendor:'Phillip Jeffries'"){pageInfo{hasNextPage endCursor} nodes{id title handle status variants(first:100){nodes{id}}}}}`,{a:after});
   for(const p of d.products.nodes){
     const pid=numTail(p.id); pjProductIds.add(pid);
     pjProducts.set(pid,{title:p.title,handle:p.handle,status:p.status});
-    for(const v of p.variants.nodes) pjVariantIds.add(numTail(v.id));
+    for(const v of p.variants.nodes){ const vid=numTail(v.id); pjVariantIds.add(vid); pjVariantToProduct.set(vid,pid); }
   }
   after=d.products.pageInfo.hasNextPage?d.products.pageInfo.endCursor:null; ppages++;
   if(ppages%5===0) process.stderr.write(` p${ppages}(${pjProductIds.size})`);
@@ -79,7 +80,9 @@ try{
         const approved=ds.filter(d=>(d.approvedCountries||[]).length>0).length;
         const disapproved=ds.filter(d=>(d.disapprovedCountries||[]).length>0).length;
         const textWouldMatch=isPJtext(a.brand)||isPJtext(a.title)||isPJtext((a.productTypes||[]).join(' '));
-        const sp=pjProducts.get(productId)|| (variantId? [...pjProducts.entries()].length&&null : null);
+        // resolve the product even for bare offers, whose offerId is the variant id
+        const resolvedPid = productId || (variantId && pjVariantToProduct.get(variantId)) || null;
+        const sp = resolvedPid ? pjProducts.get(resolvedPid) : null;
         hits.push({
           offerId:p.offerId,
           form: /^\d+$/.test(String(p.offerId))?'bare':'prefixed',
@@ -90,9 +93,9 @@ try{
           title:(a.title||'').slice(0,80),
           brand:a.brand||null,
           price:money(a.price),
-          shopify_productId:productId||null,
+          shopify_productId:resolvedPid,
           shopify_variantId:variantId||null,
-          shopify_product_status: productId&&pjProducts.get(productId)?pjProducts.get(productId).status:'(bare-offer:unknown-pid)',
+          shopify_product_status: sp?sp.status:'(unknown-pid)',
           issues:[...new Set((ps.itemLevelIssues||[]).map(i=>`${i.severity}|${i.code}`))],
           link:a.link||null,
         });
diff --git a/tk11847-verify-parity.mjs b/tk11847-verify-parity.mjs
new file mode 100644
index 0000000..819860c
--- /dev/null
+++ b/tk11847-verify-parity.mjs
@@ -0,0 +1,82 @@
+// TK-11847 READ-ONLY re-verify + price-parity + second-cause probe for the 29
+// legal_restrictions (alcohol-classifier) false-positive offers. $0. No writes anywhere.
+// GMC reads via mc-read v1 (token API). Shopify landing via public products.json, paced 2.5s.
+import fs from 'fs';
+import { createRequire } from 'module';
+const require = createRequire(import.meta.url);
+const mc = require('./_mc-read-v1.js');
+
+const EV = JSON.parse(fs.readFileSync('/Users/macstudio3/Projects/gmc-titlefix/data/tk11449-policy-locate.json','utf8'));
+const rows = EV.rows.filter(r => r.issues.some(i => i.includes('legal_restrictions')));
+const UA = 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/125 Safari/537.36';
+
+// colorway-drink false-positive lexicon (name-based)
+const DRINK = /\b(champagne|chardonnay|zinfandel|merlot|cabernet|prosecco|riesling|sauvignon|pinot|rosé|rose wine|sherry|brandy|whiskey|whisky|bourbon|tequila|vodka|absinthe|chianti|burgundy|bordeaux|sangria|mimosa|bellini|cognac)\b/i;
+
+const out = [];
+for (const r of rows) {
+  const u = new URL(r.link);
+  const handle = u.pathname.split('/').pop();
+  const vid = u.searchParams.get('variant') || String(r.offerId).match(/(\d+)$/)?.[1] || r.offerId;
+  const rec = { offerId: r.offerId, title: r.title, handle, vid,
+    ev_ctx_approved: r.ctx_approved, ev_ctx_disapproved: r.ctx_disapproved };
+
+  // --- GMC current status (READ-ONLY) ---
+  try {
+    const p = await mc.getProduct('online:en:US:' + r.offerId);
+    rec.advertised = p.price ? Number(p.price.value) : null;
+    rec.gmc_dest = p.destinationStatuses?.[0]?.status ?? null;
+    const dis = [...new Set(p.itemLevelIssues.filter(i=>i.servability==='disapproved').map(i=>i.code))];
+    const noimp = [...new Set(p.itemLevelIssues.filter(i=>i.servability==='not_impacted').map(i=>i.code))];
+    rec.disapproved_codes = dis;
+    rec.notimpacted_codes = noimp;
+    rec.still_legal_restrictions = dis.includes('legal_restrictions_policy_violation');
+    rec.other_disapprovals = dis.filter(c => c !== 'legal_restrictions_policy_violation');
+    // second-cause probe: title has no drink word => capture attributes that a classifier might read
+    rec.title_has_drink_word = DRINK.test(r.title);
+    if (!rec.title_has_drink_word) {
+      const a = (p._v1 && p._v1.productAttributes) || {};
+      rec.probe = {
+        productTypes: a.productTypes || null,
+        googleProductCategory: a.googleProductCategory || null,
+        description: (a.description||'').slice(0,240),
+        productDetails: a.productDetails ? a.productDetails.map(d=>`${d.attributeName}=${d.attributeValue}`).slice(0,12) : null,
+        customLabels: [a.customLabel0,a.customLabel1,a.customLabel2,a.customLabel3,a.customLabel4].filter(Boolean),
+      };
+    }
+  } catch(e) { rec.gmc_err = String(e).slice(0,120); }
+
+  // --- Shopify landing parity (READ-ONLY public feed) ---
+  try {
+    const res = await fetch(`https://www.designerwallcoverings.com/products/${handle}.json`,
+      { headers:{'User-Agent':UA}, signal:AbortSignal.timeout(30000) });
+    rec.http = res.status;
+    if (res.ok) {
+      const p = (await res.json()).product;
+      rec.published = !!p.published_at;
+      rec.variants = p.variants.length;
+      const v = p.variants.find(v=>String(v.id)===String(vid));
+      rec.landing_variant_found = !!v;
+      rec.landing_price = v ? Number(v.price) : null;
+      rec.parity = (v && rec.advertised!=null) ? (Math.abs(Number(v.price)-rec.advertised)<0.005) : null;
+    }
+  } catch(e) { rec.http = 'ERR '+String(e).slice(0,60); }
+
+  out.push(rec);
+  const drk = rec.title_has_drink_word ? 'DRINK' : 'NO-WORD';
+  console.log(`${rec.parity===true?'PAR-OK ':rec.parity===false?'PAR-BAD':'PAR-??'} ${rec.still_legal_restrictions?'LR':'  '} ${drk.padEnd(7)} adv=${String(rec.advertised).padEnd(7)} land=${String(rec.landing_price).padEnd(7)} other=${(rec.other_disapprovals||[]).join(',')||'-'}  ${rec.title}`);
+  await new Promise(z=>setTimeout(z, 2500));
+}
+
+const summary = {
+  total: out.length,
+  still_legal_restrictions: out.filter(r=>r.still_legal_restrictions).length,
+  parity_ok: out.filter(r=>r.parity===true).length,
+  parity_bad: out.filter(r=>r.parity===false).length,
+  parity_unknown: out.filter(r=>r.parity==null).length,
+  with_other_disapprovals: out.filter(r=>(r.other_disapprovals||[]).length).length,
+  no_drink_word: out.filter(r=>r.title_has_drink_word===false).length,
+};
+fs.writeFileSync('/Users/macstudio3/Projects/gmc-titlefix/data/tk11847-verify-parity.json',
+  JSON.stringify({ ticket:'TK-11847', read_only:true, fetched_at:new Date().toISOString(), merchant:mc.MERCHANT, summary, rows:out }, null, 1));
+console.log('\nSUMMARY', JSON.stringify(summary));

← e92df0c TK-11844: authoritative Shopify->GMC identity join for Phill  ·  back to Gmc Titlefix  ·  auto-data-snapshot: 2026-09-17T09:53:49 (2 data files) — dat 2378dcd →