← back to Tk10630 Sku Suffix Canary
yoloforever cycle3: generator-redesign design doc + Cody-gate revisions (canary gap, Option C, uniqueness)
a3783848675fa25a014ad5d9f9b74f3974581856 · 2026-08-17 15:52:05 -0700 · steve
Files touched
A DESIGN-generator-real-code.mdA leak-scanner-api-handle-alt-scan.mjs
Diff
commit a3783848675fa25a014ad5d9f9b74f3974581856
Author: steve <steve@designerwallcoverings.com>
Date: Mon Aug 17 15:52:05 2026 -0700
yoloforever cycle3: generator-redesign design doc + Cody-gate revisions (canary gap, Option C, uniqueness)
---
DESIGN-generator-real-code.md | 86 ++++++++++++++++++++++++++++++++++++
leak-scanner-api-handle-alt-scan.mjs | 64 +++++++++++++++++++++++++++
2 files changed, 150 insertions(+)
diff --git a/DESIGN-generator-real-code.md b/DESIGN-generator-real-code.md
new file mode 100644
index 0000000..c021be9
--- /dev/null
+++ b/DESIGN-generator-real-code.md
@@ -0,0 +1,86 @@
+# TK-10634 #2 — DESIGN: DWHD→real-code generator redesign (Cycle 3 draft)
+
+## Problem
+`hollywood-import/{assign-sku,hollywood-create,hollywood-add-yard}.mjs` mint a sequential
+`DWHD-<n>` (base 500000) for each Momentum colorway and stamp `pl_brand='Hollywood
+Wallcoverings'`. Because Hollywood is a DW **brand**, not an external vendor, this DW-vendor
+SKU is fabricated — it buries the product's real Momentum identity (the exact bug TK-10630
+cleaned up across ~7k live products). The kill-switch (`~/.dw-fixer-stop`) + the assign-sku
+`--commit` guard currently FREEZE this path; this doc is the redesign that lets it un-freeze.
+
+## Current flow (frozen)
+1. `assign-sku.mjs` — `UPDATE momentum_colorways SET dw_sku='DWHD-'||nextval, pl_brand=... WHERE dw_sku IS NULL`.
+2. `hollywood-create.mjs` — creates the Shopify product + `${dw}-Sample` variant ($4.25).
+3. `hollywood-add-yard.mjs` — adds the "Sold Per Yard" variant `sku: dw+'-Yard'`.
+Result: `DWHD-508xxx-Sample` / `DWHD-508xxx-Yard` + `DWHW2`-style dw_sku metafields.
+
+## Key finding (why this isn't a trivial swap)
+There are TWO code eras:
+- **Legacy real codes** (`HWC-`, `XWH-`, `NOC-`, `FLS-`, `HLW-`, `XJP-`, `XJB-`…) = Momentum's
+ historical BOOK/line codes. `HWC-61244` carries `manufacturer_sku=61244` but does NOT
+ resolve to any record in today's live Momentum Meilisearch feed (discontinued/renamed).
+ These are the real mfr numbers and MUST be PRESERVED, never re-minted.
+- **New colorways from the live feed** = the only place the generator runs. The feed record
+ carries the authoritative identity: `number` (mfr SKU, 8-digit e.g. 09221958),
+ `pattern_name`, `preferred_color_name`, `preferred_color_number`, `product_line.code`,
+ `series`, `collections`, `category_name`.
+
+## Design principle
+A Momentum→Hollywood product's identity must derive from its **real Momentum `number`**,
+never a sequential DW-vendor mint. Correlation, not fabrication.
+
+## Proposed scheme (for Steve's decision)
+- `manufacturer_sku` metafield = the feed **`number`** (authoritative, always).
+- `dw_sku` / variant base = a NON-fabricated code. Options (DECISION NEEDED):
+ - **Option A (recommended):** `HW-<number>` (brand-agnostic-safe prefix `HW` + the real
+ Momentum number). Deterministic, collision-free (number is unique), traceable, never a
+ fake sequential. Variants `HW-<number>-sample` / `-yard`.
+ - **Option B:** reuse the Momentum `product_line.code`/`series` as the prefix if it maps to
+ the legacy book prefixes (HWC/XWH/…). Needs a verified line-code→prefix table first
+ (the feed `product_line.code` seen so far = `mtw`, which does NOT match the legacy
+ prefixes — so this option is blocked until that mapping is confirmed to exist).
+ - **Option C:** bare `number` as the SKU (simplest, but loses the human-readable brand cue).
+- Suffix scheme stays `-sample` / `-yard` (already the standard; TK-10630 normalized it).
+
+## Migration / backfill (existing ~2,538 DWHD-coded products)
+- **2,156 already have a real `manufacturer_sku`** → backfill dw_sku from it (Option A: `HW-<mfr>`).
+- **784 matched the live feed by `number`** (deep-recovered.json) → set from the feed record.
+- **~382 truly orphaned** (no mfr, discontinued) → leave archived / manual (2 active already archived).
+- All backfills are metafield/variant writes = GATED (draft to pending-approval, dry-run first).
+
+## Rollout
+1. Steve picks A/B/C.
+2. Rewrite the 3 scripts to compute the code from `number` (remove the `nextval` mint).
+3. Dry-run on a handful of new colorways; verify against the canary (`dw-hollywood-sku-canary`
+ must stay PASS — the FABRICATED regex already catches any `DW`-prefixed regression).
+4. Backfill existing DWHD products (gated batch, like the TK-10630 passes).
+5. Remove `~/.dw-fixer-stop` only after 3+4 verify clean.
+
+## Open questions for Steve
+- Prefix choice (A `HW-<number>` vs C bare number)?
+- Do legacy `HWC/XWH/NOC` products keep their codes as-is (recommended yes) or get renamed to
+ the new scheme (NOT recommended — churn + redirect cost, and they're already real)?
+- Is `manufacturer_sku` = feed `number` (8-digit) or the legacy 6-digit pattern number when both exist?
+
+---
+## Cody-gate revisions (Cycle 3) — resolve BEFORE any code
+1. **CANARY GAP (blocker):** the guard `FABRICATED=/^DW[A-Z]{1,4}\d?-\d/i` only catches `DW*`
+ codes. A new `HW-`/bare-number scheme would pass clean → the canary goes blind to a future
+ re-mint in the new space. The redesign MUST ship an updated guard: for Option A, add the
+ new prefix to a "sequential-mint" detector (flag `HW-` codes NOT backed by a real feed
+ `number`); for Option C, assert the SKU IS a known Momentum `number`. No code until the
+ canary evolves with it.
+2. **Metafield vs variant (correction to Cody):** `dw_sku` DOES drive the variant SKU
+ (`hollywood-create` sets `${dw}-Sample`). So the 642-active backfill IS a customer-facing
+ variant-SKU change (order-history / PDP), BUT it does NOT create redirects — Shopify
+ redirects fire on HANDLE changes only (TK-10630 changed thousands of variant SKUs with 0
+ redirects). So the redirect cap (TK-10640) is NOT threatened by the backfill; handles are
+ untouched. The doc must state this split explicitly: variant+metafield change, handle unchanged.
+3. **Option C gets a fair hearing (Strategist/Designer):** `HW-` is still a BRAND-derived
+ prefix (Hollywood) — the same brand-as-vendor sin in a thinner disguise, and it violates
+ this doc's own principle ("derive from the real Momentum number, never a DW/brand mint").
+ **Bare `number` (Option C) is the only principle-consistent choice** — the "human-readable
+ brand cue" objection is a UX preference, not a design principle. RECOMMENDATION FLIPPED: C > A.
+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.
diff --git a/leak-scanner-api-handle-alt-scan.mjs b/leak-scanner-api-handle-alt-scan.mjs
new file mode 100644
index 0000000..868c0c4
--- /dev/null
+++ b/leak-scanner-api-handle-alt-scan.mjs
@@ -0,0 +1,64 @@
+// Accurate private-label leak assessment — validates matches as WHOLE TOKENS
+// (handles/titles use hyphen/space separators), eliminating substring false
+// positives like versailles/versace for 'versa'. READ-ONLY.
+import { readFileSync } from 'node:fs';
+import { gql } from './shopify.mjs';
+
+const denylist = JSON.parse(readFileSync(`${process.env.HOME}/.claude/skills/dw-leak-scanner/denylist.json`, 'utf8'));
+const NOISY = new Set(['york', 'yorkwall']);
+const terms = [...new Set(denylist.terms.map(t => t.term.toLowerCase().split(/\s+/)[0]).filter(t => t.length >= 4 && !NOISY.has(t)))];
+const mapsTo = Object.fromEntries(denylist.terms.map(t => [t.term.toLowerCase().split(/\s+/)[0], t.maps_to]));
+
+// whole-token: term bounded by start/end or a non-alphanumeric (hyphen/space)
+const tok = term => new RegExp(`(^|[^a-z0-9])${term}([^a-z0-9]|$)`, 'i');
+
+// VENDOR CORRELATION (2026-08-17, Cody-gated): a term only counts as a LEAK when it
+// appears on a product whose PUBLIC vendor is the label it maps to. Otherwise it's a
+// legit place/pattern name (Chesapeake color, Brewster town, Momentum pattern) on a
+// different vendor — NOT a private-label leak. Without this the scanner false-positives.
+// base public brand for a term, or '' if the term has no vendor mapping (e.g. an
+// ARCHIVED brand like "nicolette mayer" -> "(archived — must stay hidden)").
+const vendorOf = term => { const m = (mapsTo[term] || ''); if (/^\(?archived/i.test(m.trim())) return ''; return m.split('(')[0].trim().toLowerCase(); };
+// Correlation is a FALSE-POSITIVE FILTER, not a global gate (Cody, Cycle 2): only
+// EXCLUDE a match when the product is clearly a DIFFERENT real vendor. Terms with no
+// vendor mapping (archived brands) and blank-vendor products ALWAYS report — never
+// silence an archived-brand or missing-vendor hit.
+const correlates = (vendor, term) => {
+ const b = vendorOf(term);
+ if (!b) return true; // no mapping (archived) -> always report
+ const v = (vendor || '').trim().toLowerCase();
+ if (!v) return true; // blank vendor -> can't rule out -> report
+ return v === b || v.includes(b) || b.includes(v);
+};
+
+const rows = [];
+for (const term of terms) {
+ const re = tok(term);
+ const seen = new Map();
+ for (const field of ['handle', 'title', 'tag']) {
+ let cursor = null, guard = 0;
+ while (guard++ < 20) {
+ const d = await gql(`query($c:String){ products(first:100, query:"${field}:*${term}* status:ACTIVE", after:$c){ pageInfo{ hasNextPage endCursor } nodes{ id handle title vendor tags images(first:2){ nodes{ altText } } } } }`);
+ for (const p of d.data.products.nodes) if (!seen.has(p.id)) seen.set(p.id, p);
+ if (!d.data.products.pageInfo.hasNextPage) break;
+ cursor = d.data.products.pageInfo.endCursor;
+ }
+ }
+ let h = 0, t = 0, g = 0, alt = 0, falsePos = 0;
+ for (const p of seen.values()) {
+ if (!(re.test(p.handle || '') || re.test(p.title || '') || (p.tags || []).some(x => re.test(x)) || p.images.nodes.some(i => re.test(i.altText || '')))) continue;
+ if (!correlates(p.vendor, term)) { falsePos++; continue; } // legit pattern/place name on another vendor
+ if (re.test(p.handle || '')) h++;
+ if (re.test(p.title || '')) t++;
+ if ((p.tags || []).some(x => re.test(x))) g++;
+ if (p.images.nodes.some(i => re.test(i.altText || ''))) alt++;
+ }
+ if (h || t || g || alt) rows.push({ term, maps_to: mapsTo[term], handle: h, title: t, tag: g, alt, false_positives_uncorrelated: falsePos });
+}
+rows.sort((a, b) => (b.handle + b.title + b.tag + b.alt) - (a.handle + a.title + a.tag + a.alt));
+console.log(JSON.stringify({ terms_checked: terms.length, real_leaking_terms: rows.length, rows }, null, 2));
+console.log(`\nREAL active handle leaks (whole-token): ${rows.reduce((a, r) => a + r.handle, 0)}`);
+
+// verdict for run.sh / fleet-health
+const verdict = rows.some(r => r.handle || r.alt || r.title) ? 'FAIL' : (rows.length ? 'WARN' : 'PASS');
+console.log(`\n[api-handle-alt-scan] verdict=${verdict} (customer-facing leaks in handle/title/alt = FAIL; tag-only = WARN). Whole-token validated — no versailles/versace false positives.`);
← 3bf563e TK-10634: chesapeake/brewster leak assessment (whole-token)
·
back to Tk10630 Sku Suffix Canary
·
leak-scanner: full-phrase matching (nicolette mayer/lillian 0ee723d →