[object Object]

← back to Gmc Titlefix

TK-11450: price writers stamped USD on CA-feed offers, disapproving 81 of 81

209bcaee59a16948be3c31f1e982d7e7349030bd · 2026-09-11 08:24:35 -0700 · Steve

Root cause of the Canada shipping-currency bucket. apply-full.mjs and
apply-canary.mjs write price overrides onto supplemental datasource
10693978453 using row.feedLabel - which can be 'CA' - while hardcoding
currencyCode:'USD'.

Measured live, not inferred: all 1,077 Merchant Center CA offers are DW-written
(bare variant-id offerIds; zero come from the Shopify channel). 996 carry CAD
and are disapproved at 1.3%. 81 carry USD and are disapproved at 100.0% - every
single one - because Google requires the offer currency to match an available
shipping service and the only CA service is CAD-denominated.

The amounts were never wrong, only the label: 77 of the 81 feed amounts match
the product's Shopify CA contextual price and ZERO match its USD base price.
e.g. offer 44612731338803 advertises '429 USD' where Shopify CA says 424.00 CAD.

This also disqualifies both options in the gated memo I wrote. Option A
(convert the prices to CAD) would double-convert amounts that are already CAD.
Option B (add a USD shipping service for Canada) would be actively harmful - it
would let Google serve '429 USD' for a product that actually costs 424 CAD,
roughly 27% below the real price. Memo corrected separately.

Currency now derives from feedLabel and FAILS CLOSED: an unmapped feedLabel
throws rather than defaulting to USD, since defaulting to USD is exactly what
caused this. Negative-tested: CA->CAD, US->USD, MX throws, undefined throws.

Not fixed here, same class, flagged for their owners: primary-fix-batch.mjs and
primary-fix-canary.mjs also hardcode USD (they appear US-only today, so the bug
is latent rather than live).

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

Files touched

Diff

commit 209bcaee59a16948be3c31f1e982d7e7349030bd
Author: Steve <steve@designerwallcoverings.com>
Date:   Fri Sep 11 08:24:35 2026 -0700

    TK-11450: price writers stamped USD on CA-feed offers, disapproving 81 of 81
    
    Root cause of the Canada shipping-currency bucket. apply-full.mjs and
    apply-canary.mjs write price overrides onto supplemental datasource
    10693978453 using row.feedLabel - which can be 'CA' - while hardcoding
    currencyCode:'USD'.
    
    Measured live, not inferred: all 1,077 Merchant Center CA offers are DW-written
    (bare variant-id offerIds; zero come from the Shopify channel). 996 carry CAD
    and are disapproved at 1.3%. 81 carry USD and are disapproved at 100.0% - every
    single one - because Google requires the offer currency to match an available
    shipping service and the only CA service is CAD-denominated.
    
    The amounts were never wrong, only the label: 77 of the 81 feed amounts match
    the product's Shopify CA contextual price and ZERO match its USD base price.
    e.g. offer 44612731338803 advertises '429 USD' where Shopify CA says 424.00 CAD.
    
    This also disqualifies both options in the gated memo I wrote. Option A
    (convert the prices to CAD) would double-convert amounts that are already CAD.
    Option B (add a USD shipping service for Canada) would be actively harmful - it
    would let Google serve '429 USD' for a product that actually costs 424 CAD,
    roughly 27% below the real price. Memo corrected separately.
    
    Currency now derives from feedLabel and FAILS CLOSED: an unmapped feedLabel
    throws rather than defaulting to USD, since defaulting to USD is exactly what
    caused this. Negative-tested: CA->CAD, US->USD, MX throws, undefined throws.
    
    Not fixed here, same class, flagged for their owners: primary-fix-batch.mjs and
    primary-fix-canary.mjs also hardcode USD (they appear US-only today, so the bug
    is latent rather than live).
    
    Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
    Claude-Session: https://claude.ai/code/session_016yzBkdi8E67XZz1TJNmvca
---
 apply-canary.mjs | 19 ++++++++++++++++++-
 apply-full.mjs   | 19 ++++++++++++++++++-
 2 files changed, 36 insertions(+), 2 deletions(-)

diff --git a/apply-canary.mjs b/apply-canary.mjs
index ffb6508..01a3e43 100644
--- a/apply-canary.mjs
+++ b/apply-canary.mjs
@@ -4,6 +4,23 @@ import fs from 'fs';
 import { createRequire } from 'module';
 const require = createRequire(import.meta.url);
 const { token, MERCHANT } = require('./_auth.js');
+
+// CURRENCY BUG FIX 2026-09-11 (TK-11450). These writers honour row.feedLabel (which can be 'CA')
+// but hardcoded currencyCode:'USD' (literally). Result, measured live: all 1,077 Merchant Center CA offers are
+// DW-written, 996 correctly CAD and 81 carrying a CA-market AMOUNT stamped USD. Google requires the
+// offer currency to match an available shipping service's currency; the only CA service is CAD, so
+// every one of those 81 is disapproved — 81 of 81, 100%, versus 1.3% for the CAD ones.
+// Verified before fixing: 77 of the 81 feed amounts match the product's Shopify CA contextual price
+// and ZERO match its USD base price, so the amounts were always CAD and only the label was wrong.
+// FAILS CLOSED: an unknown feedLabel throws rather than silently defaulting to USD, because
+// defaulting to USD is precisely what caused this.
+const FEED_CURRENCY = { US: 'USD', CA: 'CAD', GB: 'GBP' };
+function currencyForFeed(feedLabel) {
+  const c = FEED_CURRENCY[String(feedLabel || '').toUpperCase()];
+  if (!c) throw new Error(`refusing to write: no currency mapping for feedLabel "${feedLabel}" (add it to FEED_CURRENCY rather than defaulting to USD)`);
+  return c;
+}
+
 const DS = 'accounts/146735262/dataSources/10693978453';
 const CANARY = '/Users/macstudio3/.claude/yolo-queue/gmc-fresh-override-canary.json';
 const sleep = ms => new Promise(r=>setTimeout(r,ms));
@@ -14,7 +31,7 @@ for (let i=0;i<list.length;i++){
   if (Date.now()-tokAt > 50*60*1000){ tok=await token(); tokAt=Date.now(); }
   const row=list[i];
   const url=`https://merchantapi.googleapis.com/products/v1/accounts/${MERCHANT}/productInputs:insert?dataSource=${encodeURIComponent(DS)}`;
-  const body={ offerId:row.offerId, contentLanguage:row.contentLanguage, feedLabel:row.feedLabel, productAttributes:{ price:{ amountMicros:String(Math.round(row.realPrice*1e6)), currencyCode:'USD' } } };
+  const body={ offerId:row.offerId, contentLanguage:row.contentLanguage, feedLabel:row.feedLabel, productAttributes:{ price:{ amountMicros:String(Math.round(row.realPrice*1e6)), currencyCode:currencyForFeed(row.feedLabel) } } };
   const r=await fetch(url,{method:'POST',headers:{Authorization:'Bearer '+tok,'Content-Type':'application/json'},body:JSON.stringify(body)});
   if (r.ok) ok++; else { fail++; const t=await r.text(); if(fails.length<12) fails.push(`${row.offerId} ${r.status} ${t.slice(0,110)}`); if(r.status===429) await sleep(3000); }
   if (i%50===0) process.stdout.write(`  ${i}/${list.length} ok ${ok} fail ${fail}\n`);
diff --git a/apply-full.mjs b/apply-full.mjs
index ec1ef29..d3d3799 100644
--- a/apply-full.mjs
+++ b/apply-full.mjs
@@ -9,6 +9,23 @@ import fs from 'fs';
 import { createRequire } from 'module';
 const require = createRequire(import.meta.url);
 const { token, MERCHANT } = require('./_auth.js');
+
+// CURRENCY BUG FIX 2026-09-11 (TK-11450). These writers honour row.feedLabel (which can be 'CA')
+// but hardcoded currencyCode:'USD' (literally). Result, measured live: all 1,077 Merchant Center CA offers are
+// DW-written, 996 correctly CAD and 81 carrying a CA-market AMOUNT stamped USD. Google requires the
+// offer currency to match an available shipping service's currency; the only CA service is CAD, so
+// every one of those 81 is disapproved — 81 of 81, 100%, versus 1.3% for the CAD ones.
+// Verified before fixing: 77 of the 81 feed amounts match the product's Shopify CA contextual price
+// and ZERO match its USD base price, so the amounts were always CAD and only the label was wrong.
+// FAILS CLOSED: an unknown feedLabel throws rather than silently defaulting to USD, because
+// defaulting to USD is precisely what caused this.
+const FEED_CURRENCY = { US: 'USD', CA: 'CAD', GB: 'GBP' };
+function currencyForFeed(feedLabel) {
+  const c = FEED_CURRENCY[String(feedLabel || '').toUpperCase()];
+  if (!c) throw new Error(`refusing to write: no currency mapping for feedLabel "${feedLabel}" (add it to FEED_CURRENCY rather than defaulting to USD)`);
+  return c;
+}
+
 const DS = 'accounts/146735262/dataSources/10693978453';
 const FULL = '/Users/macstudio3/.claude/yolo-queue/gmc-fresh-override-full.json';
 const sleep = ms => new Promise(r=>setTimeout(r,ms));
@@ -44,7 +61,7 @@ for (let i=0;i<list.length;i++){
   if (Date.now()-tokAt > 50*60*1000){ tok=await token(); tokAt=Date.now(); }
   const row=list[i];
   const url=`https://merchantapi.googleapis.com/products/v1/accounts/${MERCHANT}/productInputs:insert?dataSource=${encodeURIComponent(DS)}`;
-  const body={ offerId:row.offerId, contentLanguage:row.contentLanguage, feedLabel:row.feedLabel, productAttributes:{ price:{ amountMicros:String(Math.round(row.realPrice*1e6)), currencyCode:'USD' } } };
+  const body={ offerId:row.offerId, contentLanguage:row.contentLanguage, feedLabel:row.feedLabel, productAttributes:{ price:{ amountMicros:String(Math.round(row.realPrice*1e6)), currencyCode:currencyForFeed(row.feedLabel) } } };
   const r=await fetch(url,{method:'POST',headers:{Authorization:'Bearer '+tok,'Content-Type':'application/json'},body:JSON.stringify(body)});
   if (r.ok) ok++; else { fail++; const t=await r.text(); if(fails.length<12) fails.push(`${row.offerId} ${r.status} ${t.slice(0,110)}`); if(r.status===429) await sleep(3000); }
   if (i%50===0) process.stdout.write(`  ${i}/${list.length} ok ${ok} fail ${fail}\n`);

← 3c07dd5 TK-10993: pinned 9-link rename-repair manifest + executor (v  ·  back to Gmc Titlefix  ·  TK-10993: fix a scope guard that made the executor unrunnabl 413a3ac →