[object Object]

← back to Tk10630 Sku Suffix Canary

generator design: Option C uniqueness verified (0 collisions); redirect-prune tool built + dry-run proven

3c841bcea3afa6d8fa95b879fd71125d1ac12fcc · 2026-08-17 16:10:07 -0700 · steve

Files touched

Diff

commit 3c841bcea3afa6d8fa95b879fd71125d1ac12fcc
Author: steve <steve@designerwallcoverings.com>
Date:   Mon Aug 17 16:10:07 2026 -0700

    generator design: Option C uniqueness verified (0 collisions); redirect-prune tool built + dry-run proven
---
 DESIGN-generator-real-code.md |  6 ++++++
 prune-redirects.mjs           | 42 ++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 48 insertions(+)

diff --git a/DESIGN-generator-real-code.md b/DESIGN-generator-real-code.md
index c021be9..db3c109 100644
--- a/DESIGN-generator-real-code.md
+++ b/DESIGN-generator-real-code.md
@@ -84,3 +84,9 @@ never a sequential DW-vendor mint. Correlation, not fabrication.
 4. **Uniqueness is asserted, not proven:** "the `number` is unique" is inferred from a 20k feed
    snapshot, not Momentum's numbering spec. Before Option C, confirm `number` is globally unique
    across ALL Momentum lines (wallcovering + acoustic + textile), else `HW-<number>`/bare collides.
+
+## Uniqueness — RESOLVED (2026-08-17)
+Verified: 20,000 feed records → 20,000 distinct `number`s, **0 cross-category collisions**
+(wallcovering + acoustic + textile share one non-overlapping number space). Option C (bare
+`number` SKU) is collision-safe within the current catalog. Caveat: the feed API caps at 20k
+records; if Momentum's true catalog exceeds 20k, re-verify before go-live. **RECOMMENDATION: Option C.**
diff --git a/prune-redirects.mjs b/prune-redirects.mjs
new file mode 100644
index 0000000..e13f559
--- /dev/null
+++ b/prune-redirects.mjs
@@ -0,0 +1,42 @@
+// Prune DEAD-TARGET redirects (target /products/<handle> no longer exists) to free
+// space under the 100k cap. DRY-RUN by default; --apply deletes. Resumable log.
+// Cody-guarded: strip ?query before the productByHandle test (else a live ?variant
+// target is wrongly flagged dead). Only /products/ targets are ever considered.
+import { readFileSync } from 'node:fs';
+import { gql } from './shopify.mjs';
+import { appendFileSync, existsSync } from 'node:fs';
+const APPLY = process.argv.includes('--apply');
+const LIMIT = Number((process.argv.find(a => a.startsWith('--limit=')) || '').split('=')[1] || Infinity);
+const DONE = 'done-prune.jsonl';
+const done = new Set();
+if (existsSync(DONE)) for (const l of readFileSync(DONE, 'utf8').split('\n')) if (l.trim()) done.add(JSON.parse(l).id);
+const sleep = ms => new Promise(r => setTimeout(r, ms));
+async function q(query, v) { for (let a = 0; ; a++) { try { return (await gql(query, v)).data; } catch (e) { if (/THROTTLED|<html/i.test(e.message) && a < 8) { await sleep(2000 * (a + 1)); continue; } throw e; } } }
+
+const DEL = `mutation($id:ID!){ urlRedirectDelete(id:$id){ deletedUrlRedirectId userErrors{ message } } }`;
+let cursor = null, scanned = 0, dead = 0, deleted = 0, err = 0, checkedAlive = 0;
+const cacheAlive = new Map();
+while (true) {
+  const d = await q(`query($c:String){ urlRedirects(first:250, after:$c){ pageInfo{ hasNextPage endCursor } nodes{ id path target } } }`, { c: cursor });
+  for (const r of d.urlRedirects.nodes) {
+    scanned++;
+    if (!/^\/products\//.test(r.target)) continue;         // only product targets
+    if (done.has(r.id)) continue;
+    const handle = r.target.replace(/^\/products\//, '').split('?')[0].split('#')[0]; // strip ?variant / #
+    let alive = cacheAlive.get(handle);
+    if (alive === undefined) { const pd = await q(`query{ productByHandle(handle:"${handle}"){ id } }`); alive = !!pd.productByHandle; cacheAlive.set(handle, alive); checkedAlive++; }
+    if (alive) continue;
+    dead++;
+    if (APPLY) {
+      const del = await q(DEL, { id: r.id });
+      if (del.urlRedirectDelete.userErrors.length) { err++; }
+      else { deleted++; appendFileSync(DONE, JSON.stringify({ id: r.id, path: r.path }) + '\n'); }
+    }
+    if (dead >= LIMIT) { console.log(`[prune] hit --limit=${LIMIT}`); cursor = null; break; }
+  }
+  if (dead >= LIMIT) break;
+  if (!d.urlRedirects.pageInfo.hasNextPage) break;
+  cursor = d.urlRedirects.pageInfo.endCursor;
+  if (scanned % 2500 === 0) process.stderr.write(`  scanned ${scanned}, dead-targets ${dead}, deleted ${deleted}\n`);
+}
+console.log(JSON.stringify({ mode: APPLY ? 'APPLY' : 'DRY-RUN', scanned, product_targets_checked: checkedAlive, dead_prunable: dead, deleted, errors: err }, null, 2));

← 0ee723d leak-scanner: full-phrase matching (nicolette mayer/lillian  ·  back to Tk10630 Sku Suffix Canary  ·  generator Option C backfill dry-run (782 products, 0 collisi 661478b →