[object Object]

← back to Gmc Titlefix

TK-11233: propagation-aware verifier for orphan-delete runs

9084c912a744099315dbc7f8ff33148eacc79c43 · 2026-09-10 10:47:25 -0700 · Steve Abrams

Merchant Center deletes are eventually consistent - an offer stays readable
briefly after a successful delete. An immediate check of the 25-offer canary
read 21/25 and was reported PARTIAL; re-probing moments later returned 404 for
all four and the true result was 25/25.

This verifier never trusts a single fast read: it re-probes survivors after a
delay before calling anything a failure, and writes a SURVIVORS list so a
re-run targets only genuine misses.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

Files touched

Diff

commit 9084c912a744099315dbc7f8ff33148eacc79c43
Author: Steve Abrams <steve@designerwallcoverings.com>
Date:   Thu Sep 10 10:47:25 2026 -0700

    TK-11233: propagation-aware verifier for orphan-delete runs
    
    Merchant Center deletes are eventually consistent - an offer stays readable
    briefly after a successful delete. An immediate check of the 25-offer canary
    read 21/25 and was reported PARTIAL; re-probing moments later returned 404 for
    all four and the true result was 25/25.
    
    This verifier never trusts a single fast read: it re-probes survivors after a
    delay before calling anything a failure, and writes a SURVIVORS list so a
    re-run targets only genuine misses.
    
    Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
---
 tk11233-verify-orphan-deletes.mjs | 65 +++++++++++++++++++++++++++++++++++++++
 1 file changed, 65 insertions(+)

diff --git a/tk11233-verify-orphan-deletes.mjs b/tk11233-verify-orphan-deletes.mjs
new file mode 100644
index 0000000..34b084a
--- /dev/null
+++ b/tk11233-verify-orphan-deletes.mjs
@@ -0,0 +1,65 @@
+#!/usr/bin/env node
+/**
+ * TK-11233 - verify an orphan-delete run actually landed, AFTER propagation.
+ *
+ * Merchant Center deletes are EVENTUALLY CONSISTENT: an offer stays readable for a short window
+ * after a successful delete. On 2026-09-10 an immediate check of the 25-offer canary read 21/25 and
+ * was reported as PARTIAL; re-probing moments later returned 404 for all four and the true result
+ * was 25/25. So this verifier deliberately does NOT trust a single fast read - it re-probes any
+ * survivor after a delay before calling anything a failure.
+ *
+ * Usage: node tk11233-verify-orphan-deletes.mjs <id-list-file> [--wait 90]
+ * Read-only. $0.
+ */
+import { createRequire } from 'module';
+const require = createRequire(import.meta.url);
+const { token, MERCHANT } = require('/Users/macstudio3/Projects/gmc-titlefix/_auth.js');
+import fs from 'fs';
+
+const BASE = 'https://merchantapi.googleapis.com';
+const listPath = process.argv[2];
+const waitS = parseInt((process.argv.find(a => a === '--wait') ? process.argv[process.argv.indexOf('--wait') + 1] : '90'), 10);
+if (!listPath || !fs.existsSync(listPath)) { console.error('usage: node tk11233-verify-orphan-deletes.mjs <id-list-file> [--wait 90]'); process.exit(4); }
+
+const ids = fs.readFileSync(listPath, 'utf8').trim().split('\n').filter(Boolean);
+const name = rid => 'online~' + rid.replace(/^online:/, '').replace(/:/g, '~');
+
+async function probe(H, rid) {
+  const r = await fetch(`${BASE}/products/v1/accounts/${MERCHANT}/products/${name(rid)}`, { headers: H });
+  return r.status;
+}
+
+(async () => {
+  const H = { Authorization: 'Bearer ' + (await token()) };
+  console.log(`checking ${ids.length} offer ids (pass 1)...`);
+  let survivors = [];
+  let gone = 0;
+  for (let i = 0; i < ids.length; i++) {
+    const s = await probe(H, ids[i]);
+    if (s === 404 || s === 410) gone++; else survivors.push(ids[i]);
+    if ((i + 1) % 250 === 0) process.stderr.write(`  ${i + 1}/${ids.length}\n`);
+    await new Promise(z => setTimeout(z, 90));
+  }
+  console.log(`pass 1: ${gone} gone, ${survivors.length} still readable`);
+
+  if (survivors.length) {
+    console.log(`waiting ${waitS}s for propagation, then re-probing the ${survivors.length} survivors...`);
+    await new Promise(z => setTimeout(z, waitS * 1000));
+    const H2 = { Authorization: 'Bearer ' + (await token()) };
+    const stillThere = [];
+    for (const rid of survivors) {
+      const s = await probe(H2, rid);
+      if (s === 404 || s === 410) gone++; else stillThere.push(rid);
+      await new Promise(z => setTimeout(z, 90));
+    }
+    survivors = stillThere;
+    console.log(`after propagation: ${gone} gone, ${survivors.length} genuinely still present`);
+  }
+
+  console.log('');
+  console.log(`RESULT: ${gone}/${ids.length} deleted, ${survivors.length} remaining`);
+  if (!survivors.length) console.log('=> the run SUCCEEDED in full.');
+  else { console.log('=> genuine survivors (safe to re-run the delete on just these):'); survivors.slice(0, 15).forEach(s => console.log('   ' + s));
+    fs.writeFileSync(listPath.replace(/\.txt$/, '') + '-SURVIVORS.txt', survivors.join('\n') + '\n');
+    console.log('   full list -> ' + listPath.replace(/\.txt$/, '') + '-SURVIVORS.txt'); }
+})();

← 0484cb5 TK-11233: surgical rollback - unpublish the 34 sample-only,  ·  back to Gmc Titlefix  ·  TK-11233: canary verified 25/25; add gated expansion mode + 65a1b71 →