[object Object]

← back to Designer Wallcoverings

TK-11337: add reversible bare mfr-SKU tag step + enforced SKU-purity guard (never raw mfr in dw-series sku)

9baa288680e4f04d21032ae2081945d6f8848b26 · 2026-09-09 15:39:32 -0700 · Steve

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VzZkMwx4e9ec829p24cAp3

Files touched

Diff

commit 9baa288680e4f04d21032ae2081945d6f8848b26
Author: Steve <steve@designerwallcoverings.com>
Date:   Wed Sep 9 15:39:32 2026 -0700

    TK-11337: add reversible bare mfr-SKU tag step + enforced SKU-purity guard (never raw mfr in dw-series sku)
    
    Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
    Claude-Session: https://claude.ai/code/session_01VzZkMwx4e9ec829p24cAp3
---
 scripts/versa-20oz-hollywood-fix/apply.mjs | 40 ++++++++++++++++++++++++++++--
 1 file changed, 38 insertions(+), 2 deletions(-)

diff --git a/scripts/versa-20oz-hollywood-fix/apply.mjs b/scripts/versa-20oz-hollywood-fix/apply.mjs
index 3882b734..59453cb8 100644
--- a/scripts/versa-20oz-hollywood-fix/apply.mjs
+++ b/scripts/versa-20oz-hollywood-fix/apply.mjs
@@ -67,7 +67,7 @@ function readManifest() {
 }
 
 const Q_READ = `query($id:ID!){ product(id:$id){
-  id title status bodyHtml
+  id title status bodyHtml tags
   options{ id name position }
   uom: metafield(namespace:"global",key:"unit_of_measure"){id value}
   dwsku: metafield(namespace:"global",key:"dw_sku"){id value}
@@ -104,6 +104,7 @@ function planFor(row, p) {
       status: p.status,
       bodyHtml: p.bodyHtml ?? '',              // BUG-1 FIX: persist the RAW description so rollback can restore it
       bodyHtml_had_specblock: alreadyAppended,
+      tags: Array.isArray(p.tags) ? p.tags.slice() : [],  // FULL old tag set (reversible; append-only)
       unit_of_measure: p.uom?.value ?? null,
       dw_sku_mf: p.dwsku?.value ?? null,
       v_prods_quantity_order_min: p.qmin?.value ?? null,
@@ -150,8 +151,31 @@ const M_BODY = `mutation($p:ProductInput!){ productUpdate(input:$p){ userErrors{
 const M_REORDER = `mutation($pid:ID!,$moves:[ProductVariantPositionInput!]!){
   productVariantsBulkReorder(productId:$pid, positions:$moves){ userErrors{field message} }}`;
 
+// SKU-PURITY INVARIANT (Steve HARD rule 2026-09-09: "create dw series skus. NEVER raw numbers from
+// mfr in the sku"). Asserts every minted identity is a clean DW-series XZW code and contains NEITHER
+// the raw mfr_sku NOR (if present) the internal momentum number. Throws on any violation so a future
+// manifest/code regression can NEVER write a SKU carrying a raw mfr code. Called per-product AND as an
+// apply() preflight over the whole restore-map (fail-fast before the batch starts).
+const XZW_RE = /^XZW-3\d{6}$/;
+function assertSkuPurity(plan) {
+  const dw = plan.new.dw_sku_mf || '';
+  const ySku = plan.new.sellable ? plan.new.sellable.sku : `${dw}-yard`;
+  const sSku = plan.new.sample ? plan.new.sample.sku : `${dw}-sample`;
+  const raws = [plan.mfr_sku, plan.momentum_sku].filter(Boolean).map((s) => String(s).toLowerCase());
+  const bad = [];
+  if (!XZW_RE.test(dw)) bad.push(`dw_sku "${dw}" not a clean XZW-3###### code`);
+  if (!XZW_RE.test((ySku || '').replace(/-yard$/, ''))) bad.push(`sellable sku "${ySku}" not {XZW}-yard`);
+  if (!XZW_RE.test((sSku || '').replace(/-sample$/, ''))) bad.push(`sample sku "${sSku}" not {XZW}-sample`);
+  for (const id of [dw, ySku, sSku]) {
+    const lo = String(id).toLowerCase();
+    for (const raw of raws) if (raw && lo.includes(raw)) bad.push(`identity "${id}" contains raw mfr/momentum "${raw}"`);
+  }
+  if (bad.length) throw new Error(`SKU_PURITY_VIOLATION pid=${plan.pid}: ${bad.join('; ')}`);
+}
+
 async function applyOne(plan) {
   const gid = plan.gid;
+  assertSkuPurity(plan);   // never write a SKU carrying a raw mfr code
   // 1) variant option relabel + SKUs (inventoryItem.sku per standing rule); price passed through unchanged.
   //    BUG-2 FIX: rename the SELLABLE variant's option value "Single Roll"->"Sold Per Yard" via optionValues.
   //    The Sample variant's optionValues are NOT passed, so its value ("Sample") is untouched and the two
@@ -183,6 +207,12 @@ async function applyOne(plan) {
   if (plan.new.sellable && plan.old.sellable.position !== 1) moves.push({ id: plan.new.sellable.id, position: 1 });
   if (plan.new.sample && plan.old.sample.position !== 2) moves.push({ id: plan.new.sample.id, position: 2 });
   if (moves.length) await gql(M_REORDER, { pid: gid, moves });
+  // 5) ADDITIVE bare mfr-SKU tag (Steve 2026-09-09): the raw manufacturer code as a tag, NO prefix.
+  //    Preserve ALL existing tags; append only if absent (dedupe); never clobber the set.
+  const oldTags = plan.old.tags || [];
+  if (plan.mfr_sku && !oldTags.includes(plan.mfr_sku)) {
+    await gql(M_BODY, { p: { id: gid, tags: [...oldTags, plan.mfr_sku] } });
+  }
   ledger(plan);
 }
 
@@ -208,7 +238,11 @@ async function apply() {
   const plans = all.filter((p) => p.old.sellable);
   if (skipped.length) fs.writeFileSync(path.join(DATA, 'skipped-sample-only.json'),
     JSON.stringify(skipped.map((p) => ({ pid: p.pid, handle: p.handle, mfr_sku: p.mfr_sku })), null, 2));
-  process.stderr.write(`applying ${plans.length}; skipped ${skipped.length} sample-only\n`);
+  // PREFLIGHT: assert SKU purity across the WHOLE set before writing a single product (fail-fast).
+  const violations = [];
+  for (const p of plans) { try { assertSkuPurity(p); } catch (e) { violations.push(e.message); } }
+  if (violations.length) throw new Error(`ABORT — ${violations.length} SKU-purity violations:\n` + violations.slice(0, 20).join('\n'));
+  process.stderr.write(`SKU-purity preflight PASS (${plans.length} clean); skipped ${skipped.length} sample-only\n`);
   let n = 0;
   for (const plan of plans) {
     try { await applyOne(plan); }
@@ -238,6 +272,8 @@ async function rollbackOne(plan) {
   if (moves.length) await gql(M_REORDER, { pid: gid, moves });
   // 3) body_html: restore the EXACT captured old body (only if the fix appended the block)
   if (!plan.old.bodyHtml_had_specblock) await gql(M_BODY, { p: { id: gid, bodyHtml: plan.old.bodyHtml || '' } });
+  // 3b) tags: restore the EXACT captured old tag set (reverses the appended bare mfr-SKU tag)
+  await gql(M_BODY, { p: { id: gid, tags: plan.old.tags || [] } });
   // 4) metafields: set back to old value if it existed; DELETE (by identifier) the ones the fix created
   const setBack = [], del = [];
   const spec = [

← 64dc9e71 auto-data-snapshot: 2026-09-09T15:36:04 (2 data files) — scr  ·  back to Designer Wallcoverings  ·  TK-11337: 6-digit XZW series (510985+, next-in-line), dw_sku 51bb5c9b →