← 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
A tk11233-verify-orphan-deletes.mjs
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 →