[object Object]

← back to Gmc Titlefix

weight-override FIX-FIRST (contrarian): +verify-link/list-ds modes, stratified canary (both offerId formats), body-error check, kill undefined WEIGHT_LB; discovered DS 10683334493 NOT linked → must link to primary 180695450 first

766c06363ca7a2fd5c63abbb32c30543d9b139fb · 2026-07-09 08:56:31 -0700 · steve

Files touched

Diff

commit 766c06363ca7a2fd5c63abbb32c30543d9b139fb
Author: steve <steve@designerwallcoverings.com>
Date:   Thu Jul 9 08:56:31 2026 -0700

    weight-override FIX-FIRST (contrarian): +verify-link/list-ds modes, stratified canary (both offerId formats), body-error check, kill undefined WEIGHT_LB; discovered DS 10683334493 NOT linked → must link to primary 180695450 first
---
 gmc-weight-override.js | 37 +++++++++++++++++++++++++++++++++----
 1 file changed, 33 insertions(+), 4 deletions(-)

diff --git a/gmc-weight-override.js b/gmc-weight-override.js
index fbb2803..07a34cc 100644
--- a/gmc-weight-override.js
+++ b/gmc-weight-override.js
@@ -24,6 +24,9 @@ const INCLUDE_SAMPLES = args.includes('--include-samples');
 const CREATE_DS = args.includes('--create-ds');
 const APPLY = args.includes('--apply');
 const DS = APPLY ? args[args.indexOf('--apply') + 1] : null;
+const LIST_DS = args.includes('--list-ds');
+const VERIFY_LINK = args.includes('--verify-link');
+const VL_DS = VERIFY_LINK ? args[args.indexOf('--verify-link') + 1] : null;
 const CANARY = args.includes('--canary') ? parseInt(args[args.indexOf('--canary') + 1] || '10', 10) : 0;
 const sleep = ms => new Promise(r => setTimeout(r, ms));
 
@@ -52,25 +55,51 @@ async function createDataSource() {
   console.error('all datasource shapes failed');
 }
 
+// Verify the supplemental datasource is LINKED to a primary feed — else inserts 200 but do nothing.
+async function verifyLink(ds) {
+  const tok = await token();
+  const name = ds.startsWith('accounts/') ? ds : `accounts/${MERCHANT}/dataSources/${ds}`;
+  const r = await (await fetch(`https://merchantapi.googleapis.com/datasources/v1/${name}`, { headers: { Authorization: 'Bearer ' + tok } })).json();
+  console.log('datasource:', JSON.stringify(r, null, 2).slice(0, 800));
+  const ref = r.supplementalProductDataSource?.referencingPrimaryDataSources || r.referencedPrimaryDataSourceId || null;
+  console.log(ref ? `✓ LINKED to primary: ${JSON.stringify(ref)}` : '⚠️ NOT LINKED — inserts will 200 but NOT propagate. Link it (MC UI: Feeds → supplemental → Link to primary) before pushing.');
+}
+async function listDs() {
+  const tok = await token();
+  const r = await (await fetch(`https://merchantapi.googleapis.com/datasources/v1/accounts/${MERCHANT}/dataSources?pageSize=100`, { headers: { Authorization: 'Bearer ' + tok } })).json();
+  for (const d of (r.dataSources || [])) console.log(`${d.name} | ${d.displayName || ''} | ${d.primaryProductDataSource ? 'PRIMARY' : d.supplementalProductDataSource ? 'supplemental' : '?'}`);
+}
+// Stratified canary: pull N/2 from each offerId format so BOTH shapes are tested.
+function stratify(list, n) {
+  const shopifyFmt = list.filter(t => /^shopify_/.test(t.offerId));
+  const bareFmt = list.filter(t => !/^shopify_/.test(t.offerId));
+  const half = Math.ceil(n / 2);
+  return [...shopifyFmt.slice(0, half), ...bareFmt.slice(0, n - Math.min(half, shopifyFmt.length))];
+}
 async function apply(ds) {
   const list = JSON.parse(fs.readFileSync(OUT, 'utf8')).targets;
-  const slice = CANARY ? list.slice(0, CANARY) : list;
+  const slice = CANARY ? stratify(list, CANARY) : list;
   let tok = await token(), tokAt = Date.now(), ok = 0, fail = 0;
-  console.log(`Pushing shipping_weight=${WEIGHT_LB}lb for ${slice.length} offers → ${ds}`);
+  console.log(`Pushing tiered shipping_weight (real ${WEIGHT_REAL}lb / sample ${WEIGHT_SAMPLE}lb) for ${slice.length} offers → ${ds}`);
+  if (CANARY) console.log(`  canary formats: ${slice.map(t => /^shopify_/.test(t.offerId) ? 'S' : 'B').join('')} (S=shopify_ B=bare)`);
   for (let i = 0; i < slice.length; i++) {
     if (Date.now() - tokAt > 50 * 60 * 1000) { tok = await token(); tokAt = Date.now(); }
     const row = slice[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: { shipping: [], shippingWeight: { value: row.weight, unit: 'lb' } } };
+    const body = { offerId: row.offerId, contentLanguage: row.contentLanguage, feedLabel: row.feedLabel, productAttributes: { shippingWeight: { value: row.weight, unit: 'lb' } } };
     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++; if (fail <= 10) console.error('FAIL', row.offerId, r.status, (await r.text()).slice(0, 160)); }
+    const txt = await r.text(); let j = {}; try { j = JSON.parse(txt); } catch (e) {}
+    if (r.ok && !j.error) ok++; else { fail++; if (fail <= 10) console.error('FAIL', row.offerId, r.status, txt.slice(0, 160)); }
     if (i % 500 === 0) console.log(`  ${i}/${slice.length} | ok ${ok} fail ${fail}`);
     if (r.status === 429) await sleep(3000);
   }
   console.log(`DONE: ok ${ok} fail ${fail} of ${slice.length}`);
+  if (CANARY) console.log(`\n⚠️ Canary HTTP-200 only proves delivery, NOT propagation. Confirm the DS is LINKED (node gmc-weight-override.js --verify-link "${ds}") AND check GMC in 24h that these cleared before scaling.`);
 }
 
 (async () => {
+  if (LIST_DS) return listDs();
+  if (VERIFY_LINK) { if (!VL_DS) { console.error('need datasource: --verify-link <DS>'); process.exit(1); } return verifyLink(VL_DS); }
   if (CREATE_DS) return createDataSource();
   if (APPLY) { if (!DS) { console.error('need datasource: --apply <DS>'); process.exit(1); } return apply(DS); }
   const out = buildList();

← e01bb10 weight override: tiered feed weights — real 1lb / samples 0.  ·  back to Gmc Titlefix  ·  add --link mode: append weight supplemental to primary 18069 1e92087 →