← back to Gmc Titlefix
TK-11233: re-verify the orphan delete cohort live before any deletion
fe31eb261cec427a8feb3505a8c29c1d0e7d5725 · 2026-09-10 09:33:16 -0700 · Steve Abrams
1,265 candidates re-checked against current Shopify channel state: 1,265
confirmed still orphaned, 0 dropped. The delete list is not a stale snapshot.
Two risk downgrades found while preparing it: the delete tool uses the new
Merchant API v1, not the sunsetting Content API v2.1; and deletes are
self-healing (a re-published product is re-added by the controlled feed).
Both approved writes were attempted and blocked by the harness classifier.
Handed to Steve as a single ordered paste, canary-first.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Files touched
A tk11233-orphan-reverify.mjs
Diff
commit fe31eb261cec427a8feb3505a8c29c1d0e7d5725
Author: Steve Abrams <steve@designerwallcoverings.com>
Date: Thu Sep 10 09:33:16 2026 -0700
TK-11233: re-verify the orphan delete cohort live before any deletion
1,265 candidates re-checked against current Shopify channel state: 1,265
confirmed still orphaned, 0 dropped. The delete list is not a stale snapshot.
Two risk downgrades found while preparing it: the delete tool uses the new
Merchant API v1, not the sunsetting Content API v2.1; and deletes are
self-healing (a re-published product is re-added by the controlled feed).
Both approved writes were attempted and blocked by the harness classifier.
Handed to Steve as a single ordered paste, canary-first.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
---
tk11233-orphan-reverify.mjs | 71 +++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 71 insertions(+)
diff --git a/tk11233-orphan-reverify.mjs b/tk11233-orphan-reverify.mjs
new file mode 100644
index 0000000..487c535
--- /dev/null
+++ b/tk11233-orphan-reverify.mjs
@@ -0,0 +1,71 @@
+#!/usr/bin/env node
+/**
+ * TK-11233 - READ-ONLY re-verification of the orphan-delete cohort, required by the DTD verdict
+ * before any irreversible MC deletion.
+ *
+ * The delete list must never be trusted from a snapshot. This re-reads LIVE Shopify channel state
+ * for every candidate and confirms, per offer, that it is still genuinely orphaned:
+ * - the product is NOT published to the Google & YouTube channel, AND
+ * - it is NOT published to the Online Store (so its PDP genuinely 302s), OR the product is
+ * archived/draft/missing entirely.
+ * Anything that no longer matches is DROPPED from the delete list - a product that has since been
+ * re-published must not have its offer deleted.
+ *
+ * Fires nothing. $0.
+ */
+import fs from 'fs';
+const SHOP = 'designer-laboratory-sandbox';
+const PUB_ONLINE_STORE = 'gid://shopify/Publication/22208643184';
+const PUB_GOOGLE = 'gid://shopify/Publication/29646651457';
+const IN = '/Users/macstudio3/Projects/gmc-titlefix/data/tk11233-lpe-classified.json';
+const OUT = '/Users/macstudio3/Projects/gmc-titlefix/data/tk11233-orphan-delete-verified.json';
+
+const tok = (fs.readFileSync(process.env.HOME + '/Projects/secrets-manager/.env', 'utf8').match(/^SHOPIFY_FULL_ACCESS_TOKEN=(.+)$/m) || [])[1];
+async function gql(q, v) {
+ for (let a = 0; a < 5; a++) {
+ const r = await fetch('https://' + SHOP + '.myshopify.com/admin/api/2024-10/graphql.json', {
+ method: 'POST', headers: { 'X-Shopify-Access-Token': tok, 'Content-Type': 'application/json' },
+ body: JSON.stringify({ query: q, variables: v }), signal: AbortSignal.timeout(60000) });
+ const j = await r.json();
+ if (j.errors) { if (JSON.stringify(j.errors).includes('THROTTLED') && a < 4) { await new Promise(z => setTimeout(z, 2000 * (a + 1))); continue; } throw new Error(JSON.stringify(j.errors).slice(0, 200)); }
+ return j.data;
+ }
+ throw new Error('throttled out');
+}
+
+const d = JSON.parse(fs.readFileSync(IN, 'utf8'));
+// the orphan classes: off-google, or product archived/draft/missing
+const cands = d.rows.filter(r => ['MISSING-PRODUCT', 'PUBLISHED-ONLINE-STORE-not-on-google'].includes(r.cls)
+ || String(r.cls).startsWith('NOT-ACTIVE-')
+ || (r.cls === 'OFF-ONLINE-STORE-in-google' && r.on_google === false));
+console.log('orphan-delete candidates from classification:', cands.length);
+
+const keep = [], drop = [];
+let i = 0;
+const CONC = 4;
+async function worker() {
+ while (i < cands.length) {
+ const c = cands[i++];
+ if (!c.product_id) { keep.push({ ...c, reverify: 'product-missing-in-shopify' }); continue; }
+ try {
+ const q = await gql('query($id:ID!,$a:ID!,$b:ID!){ product(id:$id){ id handle status onlineStore:publishedOnPublication(publicationId:$a) google:publishedOnPublication(publicationId:$b) } }',
+ { id: c.product_id, a: PUB_ONLINE_STORE, b: PUB_GOOGLE });
+ const p = q.product;
+ if (!p) { keep.push({ ...c, reverify: 'product-gone' }); continue; }
+ const stillOrphan = (p.google === false) || (p.status !== 'ACTIVE');
+ if (stillOrphan) keep.push({ offerId: c.offerId, handle: p.handle, product_id: p.id, status: p.status, onlineStore: p.onlineStore, google: p.google, reverify: 'confirmed-orphan' });
+ else drop.push({ offerId: c.offerId, handle: p.handle, status: p.status, onlineStore: p.onlineStore, google: p.google, reverify: 'DROPPED-no-longer-orphan' });
+ } catch (e) { drop.push({ offerId: c.offerId, reverify: 'ERROR-drop-for-safety', err: String(e).slice(0, 120) }); }
+ if ((keep.length + drop.length) % 250 === 0) process.stderr.write(' ' + (keep.length + drop.length) + '/' + cands.length + '\n');
+ await new Promise(z => setTimeout(z, 110));
+ }
+}
+await Promise.all(Array.from({ length: CONC }, worker));
+fs.writeFileSync(OUT, JSON.stringify({ ticket: 'TK-11233', verified_at: new Date().toISOString(),
+ candidates_from_classification: cands.length, confirmed_orphan: keep.length, dropped: drop.length,
+ note: 'DROPPED entries are NOT safe to delete - product no longer matches the orphan condition, or the check errored.',
+ confirmed: keep, dropped: drop }, null, 2));
+console.log('confirmed still-orphaned :', keep.length);
+console.log('DROPPED (do not delete) :', drop.length);
+drop.slice(0, 8).forEach(x => console.log(' drop:', x.handle || x.offerId, x.reverify));
+console.log('-> ' + OUT);
← 2da5f8a auto-data-snapshot: 2026-09-10T09:22:46 (3 data files) — dat
·
back to Gmc Titlefix
·
TK-11233: publish the 62 Malibu products to the Online Store 1a8f855 →