[object Object]

← back to Gmc Titlefix

TK-10993: CA/GB recovery verifier + pre-change baseline

74802ae3b0f5d8ec49d4caaa37afc2e74b8d884d · 2026-09-03 20:08:06 -0700 · Steve Abrams

Read-only. Verifies BOTH halves of the Track B console fix (currency AND the
>1 lb weight bracket), plus the UK market enable, then reads the free canary:
whether the ~1,108 surviving CA offers flip disapproved -> active.

Baseline captured 2026-09-04 BEFORE any console edit, so the flip is provable:
UK market disabled, CA/GB both USD, both capped at 1 lb (noShipping above),
CA active=0 disapproved=1,112 (currency-mismatch 1,027, missing_shipping 84).

Distinguishes HALF-FIXED (currency cleared but offers moved to missing_shipping
= the weight bracket was not raised) from a genuine PASS.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011CMTNwhCcyjeem27QgYMJS

Files touched

Diff

commit 74802ae3b0f5d8ec49d4caaa37afc2e74b8d884d
Author: Steve Abrams <steve@designerwallcoverings.com>
Date:   Thu Sep 3 20:08:06 2026 -0700

    TK-10993: CA/GB recovery verifier + pre-change baseline
    
    Read-only. Verifies BOTH halves of the Track B console fix (currency AND the
    >1 lb weight bracket), plus the UK market enable, then reads the free canary:
    whether the ~1,108 surviving CA offers flip disapproved -> active.
    
    Baseline captured 2026-09-04 BEFORE any console edit, so the flip is provable:
    UK market disabled, CA/GB both USD, both capped at 1 lb (noShipping above),
    CA active=0 disapproved=1,112 (currency-mismatch 1,027, missing_shipping 84).
    
    Distinguishes HALF-FIXED (currency cleared but offers moved to missing_shipping
    = the weight bracket was not raised) from a genuine PASS.
    
    Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
    Claude-Session: https://claude.ai/code/session_011CMTNwhCcyjeem27QgYMJS
---
 data/tk10993-ca-canary-baseline.json |  97 ++++++++++++++++++++++++++++++
 tk10993-ca-canary-verify.mjs         | 112 +++++++++++++++++++++++++++++++++++
 2 files changed, 209 insertions(+)

diff --git a/data/tk10993-ca-canary-baseline.json b/data/tk10993-ca-canary-baseline.json
new file mode 100644
index 0000000..133db0a
--- /dev/null
+++ b/data/tk10993-ca-canary-baseline.json
@@ -0,0 +1,97 @@
+{
+  "checked_at": "2026-09-04T03:07:40.489Z",
+  "markets": {
+    "ca": {
+      "enabled": true,
+      "catalogs": 1
+    },
+    "international": {
+      "enabled": true,
+      "catalogs": 1
+    },
+    "gb": {
+      "enabled": false,
+      "catalogs": 0
+    },
+    "us": {
+      "enabled": true,
+      "catalogs": 1
+    }
+  },
+  "shipping": {
+    "CA": [
+      {
+        "name": "weight_based_POUND_54097543219_Custom_rate_weight_based",
+        "currency": "USD",
+        "active": true,
+        "weight_bands": [
+          "1lb",
+          "infinitylb"
+        ],
+        "maxBand": {
+          "value": "infinity",
+          "unit": "lb"
+        },
+        "ships_over_1lb": false
+      }
+    ],
+    "GB": [
+      {
+        "name": "weight_based_POUND_54097543219_Custom_rate_weight_based",
+        "currency": "USD",
+        "active": true,
+        "weight_bands": [
+          "1lb",
+          "infinitylb"
+        ],
+        "maxBand": {
+          "value": "infinity",
+          "unit": "lb"
+        },
+        "ships_over_1lb": false
+      }
+    ]
+  },
+  "status": {
+    "GB": {
+      "active": 0,
+      "disapproved": 4,
+      "codes": {
+        "landing_page_error": 1,
+        "item_missing_required_attribute": 1,
+        "missing_shipping_mismatch_of_shipping_method_and_offer_currency": 2
+      }
+    },
+    "US": {
+      "active": 63508,
+      "disapproved": 1981,
+      "codes": {
+        "image_link_broken": 12,
+        "image_too_generic": 43,
+        "image_link_internal_error": 1,
+        "image_too_small": 28,
+        "image_unwanted_overlays": 1,
+        "image_single_color": 4,
+        "landing_page_error": 1673,
+        "guns_parts_policy_violation": 5,
+        "illegal_drugs_policy_violation": 10,
+        "live_animals_policy_violation": 1,
+        "tobacco_policy_violation": 16,
+        "vehicles_policy_violation": 1,
+        "healthcare_pdt_policy_violation": 1,
+        "item_missing_required_attribute": 9,
+        "missing_shipping_weight": 181,
+        "image_link_pending_crawl": 67
+      }
+    },
+    "CA": {
+      "active": 0,
+      "disapproved": 1112,
+      "codes": {
+        "landing_page_error": 28,
+        "missing_shipping": 84,
+        "missing_shipping_mismatch_of_shipping_method_and_offer_currency": 1027
+      }
+    }
+  }
+}
\ No newline at end of file
diff --git a/tk10993-ca-canary-verify.mjs b/tk10993-ca-canary-verify.mjs
new file mode 100644
index 0000000..b5bfec5
--- /dev/null
+++ b/tk10993-ca-canary-verify.mjs
@@ -0,0 +1,112 @@
+#!/usr/bin/env node
+// TK-10993 — CA/GB recovery verifier. READ-ONLY, $0. Never writes to Google or Shopify.
+//
+// Verifies BOTH halves of the Track B console fix, not just the currency one:
+//   (1) UK Shopify market enabled + catalog present   (sole cause of GB = 0 offers)
+//   (2) CA/GB shipping service currency == CAD/GBP    (the 1,027 mismatch disapprovals)
+//   (3) CA/GB weight bracket extends past 1 lb        (else ~4 lb rolls -> noShipping)
+//   (4) THE CANARY: do the ~1,108 surviving CA offers flip disapproved -> active?
+//
+// Usage:
+//   node tk10993-ca-canary-verify.mjs --baseline   # snapshot pre-change state (run BEFORE Steve's edits)
+//   node tk10993-ca-canary-verify.mjs              # verify + diff against the baseline
+import { createRequire } from 'module';
+import fs from 'fs';
+const require = createRequire(import.meta.url);
+const { token, MERCHANT } = require('./_auth.js');
+
+const BASELINE = new URL('./data/tk10993-ca-canary-baseline.json', import.meta.url).pathname;
+const SAVE = process.argv.includes('--baseline');
+const SHOP = 'designer-laboratory-sandbox';
+
+const shopTok = (fs.readFileSync(process.env.HOME + '/Projects/secrets-manager/.env', 'utf8')
+  .match(/^SHOPIFY_FULL_ACCESS_TOKEN=(.+)$/m) || [])[1];  // narrow admin token lacks read_markets
+
+async function shopify(query) {
+  const r = await fetch(`https://${SHOP}.myshopify.com/admin/api/2024-10/graphql.json`, {
+    method: 'POST', headers: { 'X-Shopify-Access-Token': shopTok, 'Content-Type': 'application/json' },
+    body: JSON.stringify({ query }), signal: AbortSignal.timeout(60000) });
+  return r.json();
+}
+
+const tok = await token();
+const H = { Authorization: 'Bearer ' + tok };
+const snap = { checked_at: new Date().toISOString() };
+
+// (1) Shopify markets
+try {
+  const j = await shopify(`{ markets(first:10){nodes{ name handle enabled catalogs(first:3){nodes{ status }}}}}`);
+  snap.markets = Object.fromEntries((j.data?.markets?.nodes || []).map(m =>
+    [m.handle, { enabled: m.enabled, catalogs: (m.catalogs?.nodes || []).length }]));
+} catch (e) { snap.markets = { error: e.message }; }
+
+// (2)+(3) shipping table for CA/GB
+try {
+  const j = await (await fetch(`https://shoppingcontent.googleapis.com/content/v2.1/${MERCHANT}/shippingsettings/${MERCHANT}`,
+    { headers: H, signal: AbortSignal.timeout(60000) })).json();
+  snap.shipping = {};
+  for (const c of ['CA', 'GB']) {
+    const s = (j.services || []).filter(x => x.deliveryCountry === c);
+    snap.shipping[c] = s.map(x => {
+      const w = x.rateGroups?.[0]?.mainTable?.rowHeaders?.weights || [];
+      const rows = x.rateGroups?.[0]?.mainTable?.rows || [];
+      const maxBand = w.length ? w[w.length - 1] : null;
+      const shipsHeavy = rows.some((r, i) => !r.cells?.[0]?.noShipping && i > 0);
+      return { name: x.name, currency: x.currency, active: x.active,
+               weight_bands: w.map(b => `${b.value}${b.unit || ''}`), maxBand, ships_over_1lb: shipsHeavy };
+    });
+  }
+} catch (e) { snap.shipping = { error: e.message }; }
+
+// (4) THE CANARY — CA aggregate + item-level codes
+try {
+  const j = await (await fetch(`https://merchantapi.googleapis.com/issueresolution/v1/accounts/${MERCHANT}/aggregateProductStatuses?pageSize=100`,
+    { headers: H, signal: AbortSignal.timeout(60000) })).json();
+  snap.status = {};
+  for (const a of (j.aggregateProductStatuses || [])) {
+    if (a.reportingContext !== 'SHOPPING_ADS') continue;
+    const s = a.stats || {};
+    const codes = {};
+    for (const i of (a.itemLevelIssues || [])) if (i.severity === 'DISAPPROVED') codes[i.code] = +i.productCount || 0;
+    snap.status[a.country] = { active: +s.activeCount || 0, disapproved: +s.disapprovedCount || 0, codes };
+  }
+} catch (e) { snap.status = { error: e.message }; }
+
+if (SAVE) {
+  fs.writeFileSync(BASELINE, JSON.stringify(snap, null, 2));
+  console.log('BASELINE SAVED ->', BASELINE);
+}
+
+const base = (!SAVE && fs.existsSync(BASELINE)) ? JSON.parse(fs.readFileSync(BASELINE, 'utf8')) : null;
+const f = n => (n ?? 0).toLocaleString();
+const ca = snap.status?.CA || {}, gb = snap.status?.GB || {};
+const bca = base?.status?.CA || {};
+
+console.log(`\n=== TK-10993 CA/GB verifier — ${snap.checked_at}${base ? `  (baseline ${base.checked_at})` : ''}`);
+console.log(`\n[1] UK market enabled ......... ${snap.markets?.gb?.enabled === true ? 'YES ✅' : 'NO ❌  (GB stays at 0 offers until enabled)'}`);
+for (const c of ['CA', 'GB']) {
+  const svc = snap.shipping?.[c]?.[0];
+  if (!svc) { console.log(`[2/3] ${c} shipping ............ NO SERVICE ❌`); continue; }
+  const want = c === 'CA' ? 'CAD' : 'GBP';
+  console.log(`[2] ${c} currency ............. ${svc.currency}${svc.currency === want ? ' ✅' : ` ❌  (need ${want})`}`);
+  console.log(`[3] ${c} ships over 1 lb ...... ${svc.ships_over_1lb ? 'YES ✅' : 'NO ❌  (~4 lb rolls unshippable)'}   bands=[${svc.weight_bands}]`);
+}
+console.log(`\n[4] THE CANARY — SHOPPING_ADS Canada`);
+console.log(`      active      ${f(bca.active)} -> ${f(ca.active)}`);
+console.log(`      disapproved ${f(bca.disapproved)} -> ${f(ca.disapproved)}`);
+const cur = ca.codes?.missing_shipping_mismatch_of_shipping_method_and_offer_currency || 0;
+const bcur = bca.codes?.missing_shipping_mismatch_of_shipping_method_and_offer_currency || 0;
+console.log(`      currency-mismatch  ${f(bcur)} -> ${f(cur)}`);
+console.log(`      missing_shipping   ${f(bca.codes?.missing_shipping)} -> ${f(ca.codes?.missing_shipping)}`);
+console.log(`      GB active ${f(gb.active)}  disapproved ${f(gb.disapproved)}`);
+
+let verdict;
+if (SAVE) verdict = 'BASELINE — pre-change snapshot only';
+else if (!base) verdict = 'NO BASELINE — run --baseline first';
+else if (ca.active > 0 && cur < bcur * 0.5) verdict = 'PASS ✅ — canary flipped, shipping config PROVEN. Safe to proceed to the batched re-feed.';
+else if (cur < bcur && ca.active === 0) verdict = 'PARTIAL ⚠️ — currency errors falling but nothing serving yet; Google may still be reprocessing, or the weight bracket is still blocking.';
+else if ((ca.codes?.missing_shipping || 0) > (bca.codes?.missing_shipping || 0)) verdict = 'HALF-FIXED ❌ — currency cleared but offers moved to missing_shipping. The >1 lb weight bracket was NOT raised.';
+else if (ca.disapproved >= (bca.disapproved || 0)) verdict = 'NO CHANGE — edits not landed yet, or Google has not reprocessed (allow ~1–3 days).';
+else verdict = 'MOVED — partial change, review the numbers above.';
+console.log(`\nVERDICT: ${verdict}`);
+console.log('cost: $0 (read-only Google + Shopify reads)\n');

← 51a533b TK-10993: track read-only Track-C roll-leak enumerator (prod  ·  back to Gmc Titlefix  ·  TK-10993: gate the canary verdict on the config actually lan a84ae54 →