← 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
A data/tk10993-ca-canary-baseline.jsonA tk10993-ca-canary-verify.mjs
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 →