[object Object]

← back to Gmc Titlefix

Add gated apply-full.mjs for the 10,352-row GMC $4.25 remediation (TK-00066)

137e34a961ba684e4791c35263be757c777fb842 · 2026-08-26 12:18:05 -0700 · steve@designerwallcoverings.com

Mirrors apply-canary.mjs push mechanics but reads the full override file,
requires --apply --yes-i-am-steve to fire, and re-validates the cleanliness
invariant (aborts if any row could re-leak $4.25). Held for Steve's confirm
after the 400-row canary passes the 24-48h verify gate.

Files touched

Diff

commit 137e34a961ba684e4791c35263be757c777fb842
Author: steve@designerwallcoverings.com <steve@designerwallcoverings.com>
Date:   Wed Aug 26 12:18:05 2026 -0700

    Add gated apply-full.mjs for the 10,352-row GMC $4.25 remediation (TK-00066)
    
    Mirrors apply-canary.mjs push mechanics but reads the full override file,
    requires --apply --yes-i-am-steve to fire, and re-validates the cleanliness
    invariant (aborts if any row could re-leak $4.25). Held for Steve's confirm
    after the 400-row canary passes the 24-48h verify gate.
---
 apply-full.mjs | 54 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 54 insertions(+)

diff --git a/apply-full.mjs b/apply-full.mjs
new file mode 100644
index 0000000..ec1ef29
--- /dev/null
+++ b/apply-full.mjs
@@ -0,0 +1,54 @@
+// Push the FULL 10,352-row FRESH price overrides onto the supplemental datasource.
+// Mirrors apply-canary.mjs exactly, but: (1) reads the FULL file, (2) has a REAL
+// guard (refuses to fire without --apply --yes-i-am-steve; bare run = dry-run summary),
+// (3) re-validates the cleanliness invariant at runtime and ABORTS if any row could
+// re-leak <=$4.26 or is missing a field.
+// Reversible: same offerId override; removing the supplement reverts to $4.25.
+// Run ONLY after the 400-row canary passes the 24-48h verify gate (approved>=95%).
+import fs from 'fs';
+import { createRequire } from 'module';
+const require = createRequire(import.meta.url);
+const { token, MERCHANT } = require('./_auth.js');
+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));
+
+const args = new Set(process.argv.slice(2));
+const ARMED = args.has('--apply') && args.has('--yes-i-am-steve');
+
+const list = JSON.parse(fs.readFileSync(FULL,'utf8')).overrides;
+
+// --- Pre-flight: re-validate the cleanliness invariant. Abort rather than re-leak. ---
+const leak    = list.filter(r => !(r.realPrice > 4.26));
+const nonUS   = list.filter(r => r.feedLabel !== 'US');
+const missing = list.filter(r => !r.offerId || !r.contentLanguage || !r.feedLabel);
+const prices  = list.map(r => r.realPrice);
+console.log(`FULL override file: ${list.length} rows | realPrice $${Math.min(...prices)}..$${Math.max(...prices)}`);
+console.log(`  rows <=$4.26 (LEAK): ${leak.length} | non-US: ${nonUS.length} | missing-field: ${missing.length}`);
+if (leak.length || nonUS.length || missing.length) {
+  console.error('ABORT: file failed the cleanliness invariant — refusing to push (would re-leak / malformed).');
+  process.exit(1);
+}
+
+if (!ARMED) {
+  console.log('\nDRY-RUN (not armed). File is clean and ready.');
+  console.log('To fire the live push of all ' + list.length + ' overrides, run:');
+  console.log('  node ~/Projects/gmc-titlefix/apply-full.mjs --apply --yes-i-am-steve');
+  process.exit(0);
+}
+
+// --- Armed: fire the live push (identical mechanics to apply-canary.mjs) ---
+let tok = await token(), tokAt = Date.now(), ok=0, fail=0; const fails=[];
+console.log(`\nARMED — pushing ${list.length} FULL overrides -> ${DS}`);
+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 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`);
+}
+console.log(`\nFULL DONE: ok ${ok} / fail ${fail} of ${list.length}`);
+if (fails.length){ console.log('--- first failures ---'); fails.forEach(f=>console.log('  '+f)); }
+fs.writeFileSync('/Users/macstudio3/.claude/yolo-queue/gmc-full-apply-result.json', JSON.stringify({ when:'today', ds:DS, pushed:list.length, ok, fail },null,2));

← 7a7e72b auto-save: 2026-07-28T17:01:56 (2 files) — data/ lever2-add-  ·  back to Gmc Titlefix  ·  TK-10635: add gated feed-linkage fix (root cause of $4.25 le 436960a →