← back to Gmc Titlefix
TK-10635: add gated feed-linkage fix (root cause of $4.25 leak: price-override DS never linked to primary 180695450) + rollback
436960aee0b3637b613d41cc5bcd22fe3dec05c7 · 2026-08-27 10:30:30 -0700 · Steve Abrams
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Files touched
A link-price-override-ds-ROLLBACK.mjsA link-price-override-ds.mjs
Diff
commit 436960aee0b3637b613d41cc5bcd22fe3dec05c7
Author: Steve Abrams <steve@designerwallcoverings.com>
Date: Thu Aug 27 10:30:30 2026 -0700
TK-10635: add gated feed-linkage fix (root cause of $4.25 leak: price-override DS never linked to primary 180695450) + rollback
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
---
link-price-override-ds-ROLLBACK.mjs | 27 +++++++++++++++
link-price-override-ds.mjs | 67 +++++++++++++++++++++++++++++++++++++
2 files changed, 94 insertions(+)
diff --git a/link-price-override-ds-ROLLBACK.mjs b/link-price-override-ds-ROLLBACK.mjs
new file mode 100644
index 0000000..9af0ed6
--- /dev/null
+++ b/link-price-override-ds-ROLLBACK.mjs
@@ -0,0 +1,27 @@
+// ROLLBACK for link-price-override-ds.mjs — restores primary feed 180695450's
+// defaultRule to the EXACT pre-change snapshot saved at /tmp/gmc-primary-180695450-CURRENT.json.
+// Dry-run by default; --apply --yes-i-am-steve to fire.
+import fs from 'fs';
+import { createRequire } from 'module';
+const require = createRequire(import.meta.url);
+const { token, MERCHANT } = require('./_auth.js');
+
+const PRIMARY = '180695450';
+const SNAP = '/tmp/gmc-primary-180695450-CURRENT.json';
+const args = new Set(process.argv.slice(2));
+const ARMED = args.has('--apply') && args.has('--yes-i-am-steve');
+
+if (!fs.existsSync(SNAP)) { console.error('No snapshot at', SNAP, '— cannot roll back safely. Abort.'); process.exit(1); }
+const snap = JSON.parse(fs.readFileSync(SNAP, 'utf8'));
+const rule = snap.primaryProductDataSource?.defaultRule?.takeFromDataSources || [];
+console.log('Will RESTORE takeFromDataSources to:', rule.map(x => x.self ? 'SELF' : x.supplementalDataSourceName.split('/').pop()).join(' > '));
+
+if (!ARMED) { console.log('\nDRY-RUN. To apply: node ~/Projects/gmc-titlefix/link-price-override-ds-ROLLBACK.mjs --apply --yes-i-am-steve'); process.exit(0); }
+
+const tok = await token(); const H = { Authorization: 'Bearer ' + tok, 'Content-Type': 'application/json' };
+const base = `https://merchantapi.googleapis.com/datasources/v1/accounts/${MERCHANT}/dataSources/${PRIMARY}`;
+const body = { primaryProductDataSource: { ...snap.primaryProductDataSource, defaultRule: { takeFromDataSources: rule } } };
+const r = await fetch(`${base}?updateMask=primaryProductDataSource.defaultRule`, { method: 'PATCH', headers: H, body: JSON.stringify(body) });
+const j = await r.json();
+if (!r.ok) { console.error('ROLLBACK FAILED', r.status, JSON.stringify(j).slice(0, 400)); process.exit(1); }
+console.log('ROLLED BACK OK.');
diff --git a/link-price-override-ds.mjs b/link-price-override-ds.mjs
new file mode 100644
index 0000000..252489a
--- /dev/null
+++ b/link-price-override-ds.mjs
@@ -0,0 +1,67 @@
+// GATED: link the price-override supplemental DS into primary feed 180695450's
+// defaultRule.takeFromDataSources, positioned BEFORE self so the real-price
+// override WINS over the Shopify $4.25 sample price on the live GMC feed.
+//
+// WHY: verify-canary (2026-08-27) proved all 400 canary overrides stayed at $4.25
+// (price_flipped=0). ROOT CAUSE: none of the 7 "DW Real-Price Overrides" datasources
+// are in ANY primary feed's takeFromDataSources rule, so every price override pushed
+// since Aug-17 landed in a datasource the feed never reads. Firing apply-full.mjs
+// without this link = zero remediation effect.
+//
+// This is a CUSTOMER-FACING GMC FEED STRUCTURE CHANGE (changes what price wins for the
+// whole catalog on the live Google feed) => GATED. Dry-run by default; requires
+// --apply --yes-i-am-steve to fire. Reversible via link-price-override-ds-ROLLBACK.mjs
+// (restores the exact pre-change rule saved to /tmp/gmc-primary-180695450-CURRENT.json).
+import fs from 'fs';
+import { createRequire } from 'module';
+const require = createRequire(import.meta.url);
+const { token, MERCHANT } = require('./_auth.js');
+
+const PRIMARY = '180695450';
+const PRICE_DS = `accounts/${MERCHANT}/dataSources/10693978453`; // canary-verified target
+const args = new Set(process.argv.slice(2));
+const ARMED = args.has('--apply') && args.has('--yes-i-am-steve');
+
+const tok = await token(); const H = { Authorization: 'Bearer ' + tok, 'Content-Type': 'application/json' };
+const base = `https://merchantapi.googleapis.com/datasources/v1/accounts/${MERCHANT}/dataSources/${PRIMARY}`;
+
+const cur = await (await fetch(base, { headers: H })).json();
+const rule = cur.primaryProductDataSource?.defaultRule?.takeFromDataSources || [];
+const ids = rule.map(x => x.self ? 'SELF' : x.supplementalDataSourceName.split('/').pop());
+console.log('CURRENT takeFromDataSources:', ids.join(' > '));
+
+// Save an exact rollback snapshot every run.
+fs.writeFileSync('/tmp/gmc-primary-180695450-CURRENT.json', JSON.stringify(cur, null, 2));
+
+if (rule.some(x => x.supplementalDataSourceName === PRICE_DS)) {
+ console.log('ALREADY LINKED — price-override DS 10693978453 is present. No change needed.');
+ process.exit(0);
+}
+
+// Build new rule: [existing supplementals..., PRICE_DS, self] (price override before self)
+const selfIdx = rule.findIndex(x => x.self);
+const newRule = rule.filter(x => !x.self);
+newRule.push({ supplementalDataSourceName: PRICE_DS });
+if (selfIdx !== -1) newRule.push({ self: true });
+console.log('NEW takeFromDataSources:', newRule.map(x => x.self ? 'SELF' : x.supplementalDataSourceName.split('/').pop()).join(' > '));
+
+const body = {
+ primaryProductDataSource: {
+ ...cur.primaryProductDataSource,
+ defaultRule: { takeFromDataSources: newRule }
+ }
+};
+
+if (!ARMED) {
+ console.log('\nDRY-RUN (not armed). To apply the live feed-link change:');
+ console.log(' node ~/Projects/gmc-titlefix/link-price-override-ds.mjs --apply --yes-i-am-steve');
+ console.log('Rollback after apply: node ~/Projects/gmc-titlefix/link-price-override-ds-ROLLBACK.mjs --apply --yes-i-am-steve');
+ process.exit(0);
+}
+
+const url = `${base}?updateMask=primaryProductDataSource.defaultRule`;
+const r = await fetch(url, { method: 'PATCH', headers: H, body: JSON.stringify(body) });
+const j = await r.json();
+if (!r.ok) { console.error('FAILED', r.status, JSON.stringify(j).slice(0, 400)); process.exit(1); }
+console.log('\nLINKED OK. New rule:', (j.primaryProductDataSource?.defaultRule?.takeFromDataSources || []).map(x => x.self ? 'SELF' : x.supplementalDataSourceName.split('/').pop()).join(' > '));
+console.log('Next: wait for feed reprocess (~15-60 min), then re-run verify-canary.mjs to confirm price_flipped rises before firing apply-full.mjs.');
← 137e34a Add gated apply-full.mjs for the 10,352-row GMC $4.25 remedi
·
back to Gmc Titlefix
·
track canary tooling (apply-canary/build-fresh-canary/verify 463ca3d →