← back to Ticket System
auto-save: 2026-07-30T10:16:54 (3 files) — tk10002-resku/apply-resku.mjs tk10002-resku/apply-results.json tk10002-resku/check-state.mjs
1eb5274f4a3fad74fb250fcb0794fdfb20dc5e88 · 2026-07-30 10:16:55 -0700 · Steve Abrams
Files touched
A tk10002-resku/apply-resku.mjsA tk10002-resku/apply-results.jsonA tk10002-resku/check-state.mjs
Diff
commit 1eb5274f4a3fad74fb250fcb0794fdfb20dc5e88
Author: Steve Abrams <steve@designerwallcoverings.com>
Date: Thu Jul 30 10:16:55 2026 -0700
auto-save: 2026-07-30T10:16:54 (3 files) — tk10002-resku/apply-resku.mjs tk10002-resku/apply-results.json tk10002-resku/check-state.mjs
---
tk10002-resku/apply-resku.mjs | 246 ++++++++++++++++++
tk10002-resku/apply-results.json | 538 +++++++++++++++++++++++++++++++++++++++
tk10002-resku/check-state.mjs | 26 ++
3 files changed, 810 insertions(+)
diff --git a/tk10002-resku/apply-resku.mjs b/tk10002-resku/apply-resku.mjs
new file mode 100644
index 00000000..ed54150e
--- /dev/null
+++ b/tk10002-resku/apply-resku.mjs
@@ -0,0 +1,246 @@
+#!/usr/bin/env node
+/* TK-10002 APPLY — re-SKU the 64 disjoint confident-STRAY variant fixes.
+ * Steve-approved 2026-07-30. LIVE PROD writes (mirror-first, Shopify-authoritative).
+ *
+ * Scope: final-verdict.json confident[] (68) MINUS 4 EXCLUDED pids
+ * 7664488284211, 7664489529395, 7774188404787 (3-way Dig-240416 conflict)
+ * 7391684657203 (blocked by out-of-sweep DIG-24001 foreigner)
+ * = 64 products.
+ *
+ * Per stray variant:
+ * 0. Read LIVE variants; find the variant whose current sku == stray (case-fold).
+ * - If none carries stray but one already carries `corrected` => DONE, skip (idempotent).
+ * - If neither => SKIP + flag (drifted).
+ * 1. RE-VERIFY guard on `corrected` IMMEDIATELY (case-folded, base + -Sample,
+ * exclude ARCHIVED, exclude THIS product via bareId normalization). BLOCKED => SKIP + DRAFT-tag.
+ * 2. Mirror write: UPDATE the row matching this pid+stray to corrected (variant_sku,sku), synced_at=now().
+ * 3. Shopify write: productVariantsBulkUpdate(productId, [{id, inventoryItem:{sku:corrected}}]).
+ * Verify userErrors=[]. On failure => re-queue (don't advance), mirror already led but
+ * Shopify is authoritative + re-run guard next pass; record failure.
+ *
+ * Coordinated sweep: iterate passes; each pass writes whichever strays are guard-CLEAR now.
+ * A blocker fixed this pass frees its dependent next pass. Stop when a pass makes no progress.
+ * Residual BLOCKED after convergence => DRAFT + tag Needs-ReSKU-Review.
+ *
+ * Idempotent/resumable: reads apply-audit.jsonl; a stray already at corrected (live) is skipped.
+ */
+import fs from 'fs';
+import { createRequire } from 'module';
+const require = createRequire('/Users/macstudio3/Projects/Designer-Wallcoverings/');
+const pg = require('pg');
+const { Pool } = pg;
+
+const DIR = '/Users/macstudio3/Projects/ticket-system/tk10002-resku';
+const SHOP = 'designer-laboratory-sandbox.myshopify.com';
+const API = '2024-10';
+const TOKEN = fs.readFileSync('/Users/macstudio3/Projects/secrets-manager/.env','utf8')
+ .split('\n').find(l=>l.startsWith('SHOPIFY_ADMIN_TOKEN=')).split('=').slice(1).join('=').trim();
+const GQL = `https://${SHOP}/admin/api/${API}/graphql.json`;
+const AUDIT = `${DIR}/apply-audit.jsonl`;
+const DRY = process.argv.includes('--dry');
+const BATCH_GAP_MS = 800; // per-write courtesy gap (well under bulk-push 90s rule; this is single-variant writes)
+
+const EXCLUDED = new Set(['7664488284211','7664489529395','7774188404787','7391684657203']);
+
+const pool = new Pool({ connectionString: 'postgresql://dw_admin@127.0.0.1:5432/dw_unified' });
+const bareId = x => String(x||'').replace(/^gid:\/\/shopify\/Product\//,'');
+const cf = x => String(x||'').trim().toUpperCase(); // case-fold
+const sleep = ms => new Promise(r=>setTimeout(r,ms));
+function audit(rec){ fs.appendFileSync(AUDIT, JSON.stringify({ts:new Date().toISOString(),...rec})+'\n'); }
+
+async function gql(query, variables){
+ const res = await fetch(GQL, { method:'POST',
+ headers:{'X-Shopify-Access-Token':TOKEN,'Content-Type':'application/json'},
+ body: JSON.stringify({query, variables}) });
+ const j = await res.json();
+ if(j.errors) throw new Error('GQL '+JSON.stringify(j.errors));
+ return j.data;
+}
+
+async function readVariants(pid){
+ const d = await gql(`query($id:ID!){ product(id:$id){ id status variants(first:50){ nodes{ id sku title } } } }`,
+ { id:`gid://shopify/Product/${pid}` });
+ return d.product;
+}
+
+// Case-folded guard on corrected SKU: taken if any NON-archived product OTHER than selfPid
+// carries corrected as an exact SKU or as base + '-%' across variant_sku/sku/dw_sku.
+async function guard(corrected, selfPid){
+ const { rows } = await pool.query(
+ `SELECT shopify_id, status, variant_sku, sku, dw_sku FROM shopify_products
+ WHERE (UPPER(variant_sku)=UPPER($1) OR UPPER(variant_sku) LIKE UPPER($2)
+ OR UPPER(sku) =UPPER($1) OR UPPER(sku) LIKE UPPER($2)
+ OR UPPER(dw_sku) =UPPER($1) OR UPPER(dw_sku) LIKE UPPER($2))
+ AND UPPER(COALESCE(status,''))<>'ARCHIVED'`,
+ [corrected, corrected+'-%']);
+ const foreign = rows.filter(r => bareId(r.shopify_id) !== bareId(selfPid));
+ return foreign.length
+ ? { clear:false, by: foreign.map(r=>`${bareId(r.shopify_id)}/${r.status}/${r.variant_sku||r.sku||r.dw_sku}`) }
+ : { clear:true };
+}
+
+async function mirrorUpdate(pid, stray, corrected){
+ // Update rows for THIS product whose stored sku matches the stray (case-fold), to corrected.
+ const q = await pool.query(
+ `UPDATE shopify_products SET variant_sku=$1, sku=$1, synced_at=now()
+ WHERE shopify_id=$2 AND (UPPER(variant_sku)=UPPER($3) OR UPPER(sku)=UPPER($3))`,
+ [corrected, `gid://shopify/Product/${pid}`, stray]);
+ return q.rowCount;
+}
+
+async function shopifyUpdate(pid, variantGid, corrected){
+ const d = await gql(
+ `mutation($pid:ID!,$v:[ProductVariantsBulkInput!]!){
+ productVariantsBulkUpdate(productId:$pid, variants:$v){
+ userErrors{ field message }
+ productVariants{ id sku }
+ } }`,
+ { pid:`gid://shopify/Product/${pid}`, v:[{ id:variantGid, inventoryItem:{ sku:corrected } }] });
+ return d.productVariantsBulkUpdate;
+}
+
+async function tag(pid, tagName){
+ const d = await gql(`mutation($id:ID!,$tags:[String!]!){ tagsAdd(id:$id, tags:$tags){ userErrors{message} } }`,
+ { id:`gid://shopify/Product/${pid}`, tags:[tagName] });
+ return d.tagsAdd;
+}
+
+// ---- Build the work set: (pid, stray, corrected, variantTitle) rows for the 64 ----
+const fv = JSON.parse(fs.readFileSync(`${DIR}/final-verdict.json`,'utf8'));
+const work = [];
+for(const p of fv.confident){
+ if(EXCLUDED.has(String(p.pid))) continue;
+ for(const s of p.strays){
+ work.push({ pid:String(p.pid), title:p.title, variantTitle:s.variantTitle,
+ stray:s.stray, corrected:s.corrected });
+ }
+}
+const products = new Set(work.map(w=>w.pid));
+console.log(`Work set: ${products.size} products / ${work.length} stray variants (excluded ${EXCLUDED.size} pids).`);
+
+const done = new Set(); // key pid|stray fully applied
+const skipped = []; // {key, reason}
+const draftTagged = new Set();
+const results = []; // audit-ready rows
+
+function key(w){ return `${w.pid}|${cf(w.stray)}`; }
+
+// ---- Coordinated multi-pass sweep ----
+let pass = 0;
+let remaining = work.slice();
+while(remaining.length){
+ pass++;
+ let progress = 0;
+ const next = [];
+ console.log(`\n=== PASS ${pass} — ${remaining.length} strays remaining ===`);
+ for(const w of remaining){
+ if(done.has(key(w))) continue;
+ // 0. read live variants
+ let prod;
+ try { prod = await readVariants(w.pid); }
+ catch(e){ console.log(` READ-FAIL ${w.pid}: ${e.message}`); next.push(w); continue; }
+ const vsByStray = prod.variants.nodes.find(v=>cf(v.sku)===cf(w.stray));
+ const vsByCorrected = prod.variants.nodes.find(v=>cf(v.sku)===cf(w.corrected));
+ if(!vsByStray && vsByCorrected){
+ // already corrected (idempotent)
+ done.add(key(w)); progress++;
+ results.push({pid:w.pid, variantTitle:w.variantTitle, from:w.stray, to:w.corrected, action:'ALREADY-DONE'});
+ console.log(` ALREADY-DONE ${w.pid} ${w.corrected}`);
+ continue;
+ }
+ if(!vsByStray){
+ // stray not present and corrected not present => drift
+ skipped.push({key:key(w), pid:w.pid, reason:`stray SKU ${w.stray} not found on product (drift); no corrected variant either`});
+ results.push({pid:w.pid, variantTitle:w.variantTitle, from:w.stray, to:w.corrected, action:'SKIP', reason:'stray-not-found-drift'});
+ console.log(` SKIP-DRIFT ${w.pid} stray ${w.stray} not on product`);
+ done.add(key(w)); // don't retry; it's terminal
+ continue;
+ }
+ // 1. guard re-verify immediately before write
+ const g = await guard(w.corrected, w.pid);
+ if(!g.clear){
+ // may self-clear next pass if blocker is in our work set; keep for a later pass
+ next.push(w);
+ continue;
+ }
+ // 2. mirror write
+ if(DRY){
+ console.log(` DRY would write ${w.pid} ${w.stray} -> ${w.corrected} (variant ${vsByStray.id.split('/').pop()})`);
+ done.add(key(w)); progress++;
+ results.push({pid:w.pid, variantTitle:w.variantTitle, from:w.stray, to:w.corrected, action:'DRY'});
+ continue;
+ }
+ const mrows = await mirrorUpdate(w.pid, w.stray, w.corrected);
+ // 3. shopify write
+ let sres;
+ try { sres = await shopifyUpdate(w.pid, vsByStray.id, w.corrected); }
+ catch(e){
+ audit({phase:'shopify-write-fail', pid:w.pid, stray:w.stray, corrected:w.corrected, err:e.message, mirrorRows:mrows});
+ console.log(` SHOPIFY-FAIL ${w.pid} ${w.corrected}: ${e.message} — re-queue`);
+ next.push(w); continue;
+ }
+ if(sres.userErrors && sres.userErrors.length){
+ audit({phase:'shopify-usererrors', pid:w.pid, stray:w.stray, corrected:w.corrected, userErrors:sres.userErrors, mirrorRows:mrows});
+ console.log(` USERERRORS ${w.pid} ${w.corrected}: ${JSON.stringify(sres.userErrors)}`);
+ skipped.push({key:key(w), pid:w.pid, reason:`userErrors ${JSON.stringify(sres.userErrors)}`});
+ results.push({pid:w.pid, variantTitle:w.variantTitle, from:w.stray, to:w.corrected, action:'SKIP', reason:'shopify-userErrors'});
+ done.add(key(w));
+ continue;
+ }
+ const wroteSku = sres.productVariants && sres.productVariants[0] && sres.productVariants[0].sku;
+ audit({phase:'applied', pid:w.pid, stray:w.stray, corrected:w.corrected, variantGid:vsByStray.id, mirrorRows:mrows, wroteSku});
+ results.push({pid:w.pid, variantTitle:w.variantTitle, from:w.stray, to:w.corrected, action:'APPLIED', mirrorRows:mrows, wroteSku});
+ console.log(` APPLIED ${w.pid} ${w.stray} -> ${wroteSku} (mirror rows=${mrows})`);
+ done.add(key(w)); progress++;
+ await sleep(BATCH_GAP_MS);
+ }
+ remaining = next.filter(w=>!done.has(key(w)));
+ console.log(` pass ${pass}: progress=${progress}, still-blocked=${remaining.length}`);
+ if(progress===0) break; // no forward progress => residue
+}
+
+// ---- Residue: still-blocked after convergence => DRAFT + tag ----
+for(const w of remaining){
+ if(done.has(key(w))) continue;
+ const g = await guard(w.corrected, w.pid);
+ const reason = `guard BLOCKED at convergence by ${g.by ? g.by.join(', ') : '?'}`;
+ skipped.push({key:key(w), pid:w.pid, reason});
+ results.push({pid:w.pid, variantTitle:w.variantTitle, from:w.stray, to:w.corrected, action:'SKIP-BLOCKED', reason});
+ if(!DRY && !draftTagged.has(w.pid)){
+ // DRAFT + tag Needs-ReSKU-Review
+ try {
+ await gql(`mutation($id:ID!){ productUpdate(input:{id:$id, status:DRAFT}){ userErrors{message} } }`,
+ { id:`gid://shopify/Product/${w.pid}` });
+ await tag(w.pid, 'Needs-ReSKU-Review');
+ draftTagged.add(w.pid);
+ audit({phase:'draft-tagged', pid:w.pid, reason});
+ console.log(` DRAFT+TAG ${w.pid}: ${reason}`);
+ } catch(e){ console.log(` DRAFT-TAG-FAIL ${w.pid}: ${e.message}`); }
+ } else {
+ console.log(` ${DRY?'DRY ':''}RESIDUE-BLOCKED ${w.pid}: ${reason}`);
+ }
+}
+
+// ---- Summary ----
+const applied = results.filter(r=>r.action==='APPLIED');
+const already = results.filter(r=>r.action==='ALREADY-DONE');
+const dryRows = results.filter(r=>r.action==='DRY');
+const skips = results.filter(r=>r.action.startsWith('SKIP'));
+const productsFixed = new Set(applied.concat(already).map(r=>r.pid));
+console.log(`\n=== SUMMARY ===`);
+console.log(`products with >=1 variant fixed/already: ${productsFixed.size}`);
+console.log(`variants APPLIED: ${applied.length}`);
+console.log(`variants ALREADY-DONE: ${already.length}`);
+if(DRY) console.log(`variants DRY (would-apply): ${dryRows.length}`);
+console.log(`variants SKIPPED: ${skips.length}`);
+console.log(`products DRAFT-tagged: ${draftTagged.size}`);
+
+fs.writeFileSync(`${DIR}/apply-results.json`, JSON.stringify({
+ generated_at:new Date().toISOString(), dry:DRY,
+ products_in_scope:products.size, variants_in_scope:work.length,
+ variants_applied:applied.length, variants_already_done:already.length,
+ variants_dry:dryRows.length, variants_skipped:skips.length,
+ products_draft_tagged:[...draftTagged], passes:pass, results
+},null,2));
+console.log(`\nwrote apply-results.json`);
+await pool.end();
diff --git a/tk10002-resku/apply-results.json b/tk10002-resku/apply-results.json
new file mode 100644
index 00000000..cdfc6112
--- /dev/null
+++ b/tk10002-resku/apply-results.json
@@ -0,0 +1,538 @@
+{
+ "generated_at": "2026-07-30T17:02:57.742Z",
+ "dry": true,
+ "products_in_scope": 64,
+ "variants_in_scope": 71,
+ "variants_applied": 0,
+ "variants_already_done": 0,
+ "variants_dry": 44,
+ "variants_skipped": 27,
+ "products_draft_tagged": [],
+ "passes": 2,
+ "results": [
+ {
+ "pid": "6649656049715",
+ "variantTitle": "Priced per single roll (2'x12)'- Packaged in Doubles",
+ "from": "DIG-513061 - 04 Black White",
+ "to": "DIG-5130614 - 04 Black White",
+ "action": "DRY"
+ },
+ {
+ "pid": "6836337868851",
+ "variantTitle": "Priced per single roll (2'x12)'- Packaged in Doubles",
+ "from": "DIGGM-7400755-ROLL",
+ "to": "DIG-7400755-ROLL",
+ "action": "DRY"
+ },
+ {
+ "pid": "7535350022195",
+ "variantTitle": "Sold as Complete Mural 12' x 10",
+ "from": "Dig-352501-Set",
+ "to": "Dig-352500-Set",
+ "action": "DRY"
+ },
+ {
+ "pid": "7548957392947",
+ "variantTitle": "Mural 144\" x 120\"",
+ "from": "DIG-320255-mural",
+ "to": "DIG-320256-mural",
+ "action": "DRY"
+ },
+ {
+ "pid": "7582548623411",
+ "variantTitle": "Priced per single roll (2'x12)'- Packaged in Doubles",
+ "from": "Dig-510130-Roll",
+ "to": "DIG-510132-Roll",
+ "action": "DRY"
+ },
+ {
+ "pid": "7664462364723",
+ "variantTitle": "Type 2 Textured Vinyl / 2ft x 1ft Sample",
+ "from": "Dig-2400089-25-type-2-vinyl-Sample",
+ "to": "DIG-2400085-25-type-2-vinyl-Sample",
+ "action": "DRY"
+ },
+ {
+ "pid": "7664487890995",
+ "variantTitle": "Type 2 Textured Vinyl / 2ft x 1ft Sample",
+ "from": "Dig-2400083-25-type-2-vinyl-Sample",
+ "to": "DIG-27173-25-type-2-vinyl-Sample",
+ "action": "DRY"
+ },
+ {
+ "pid": "7664488218675",
+ "variantTitle": "Type 2 Textured Vinyl / 2ft x 1ft Sample",
+ "from": "Dig-7700743-25-type-2-vinyl-Sample",
+ "to": "Dig-240426-25-type-2-vinyl-Sample",
+ "action": "DRY"
+ },
+ {
+ "pid": "7664488251443",
+ "variantTitle": "Prepasted / 2ft x 1ft Sample",
+ "from": "Dig-2400089-25-pre-Sample",
+ "to": "Dig-240425-25-pre-Sample",
+ "action": "DRY"
+ },
+ {
+ "pid": "7664488251443",
+ "variantTitle": "Type 2 Textured Vinyl / 2ft x 1ft Sample",
+ "from": "Dig-20009-25-type-2-vinyl-Sample",
+ "to": "Dig-240425-25-type-2-vinyl-Sample",
+ "action": "DRY"
+ },
+ {
+ "pid": "7664490643507",
+ "variantTitle": "Type 2 Textured Vinyl / 2ft x 1ft Sample",
+ "from": "Dig-435098-25-type-2-vinyl-Sample",
+ "to": "Dig-240413-25-type-2-vinyl-Sample",
+ "action": "DRY"
+ },
+ {
+ "pid": "7664493002803",
+ "variantTitle": "Type 2 Textured Vinyl / 2ft x 1ft Sample",
+ "from": "Dig-240407-25-type-2-vinyl-Sample",
+ "to": "DIG-2400065-25-type-2-vinyl-Sample",
+ "action": "DRY"
+ },
+ {
+ "pid": "7664511418419",
+ "variantTitle": "Prepasted / 2ft x 1ft Sample",
+ "from": "Dig-25-25-pre-Sample",
+ "to": "DIG_198404-25-pre-Sample",
+ "action": "DRY"
+ },
+ {
+ "pid": "7664511451187",
+ "variantTitle": "Prepasted / 2ft x 1ft Sample",
+ "from": "Dig-25-25-pre-Sample",
+ "to": "DIG_198405-25-pre-Sample",
+ "action": "DRY"
+ },
+ {
+ "pid": "7664511451187",
+ "variantTitle": "Type 2 Textured Vinyl / 2ft x 1ft Sample",
+ "from": "Dig-198401-25-type-2-vinyl-Sample",
+ "to": "DIG_198405-25-type-2-vinyl-Sample",
+ "action": "DRY"
+ },
+ {
+ "pid": "7664511778867",
+ "variantTitle": "Type 2 Textured Vinyl / 2ft x 1ft Sample",
+ "from": "Dig-3002-25-type-2-vinyl-Sample",
+ "to": "DIG-9100-25-type-2-vinyl-Sample",
+ "action": "DRY"
+ },
+ {
+ "pid": "7664511877171",
+ "variantTitle": "Type 2 Textured Vinyl / 2ft x 1ft Sample",
+ "from": "Dig-60015-25-type-2-vinyl-Sample",
+ "to": "DIG-60014-25-type-2-vinyl-Sample",
+ "action": "DRY"
+ },
+ {
+ "pid": "7664512106547",
+ "variantTitle": "Type 2 Textured Vinyl / 2ft x 1ft Sample",
+ "from": "Dig-40051-25-type-2-vinyl-Sample",
+ "to": "DIG-40041-25-type-2-vinyl-Sample",
+ "action": "DRY"
+ },
+ {
+ "pid": "7664512237619",
+ "variantTitle": "Type 2 Textured Vinyl / 2ft x 1ft Sample",
+ "from": "Dig-240416-25-type-2-vinyl-Sample",
+ "to": "DIG-68000-25-type-2-vinyl-Sample",
+ "action": "DRY"
+ },
+ {
+ "pid": "7664512303155",
+ "variantTitle": "Type 2 Textured Vinyl / 2ft x 1ft Sample",
+ "from": "Dig-5101406-25-type-2-vinyl-Sample",
+ "to": "DIG-5101407-25-type-2-vinyl-Sample",
+ "action": "DRY"
+ },
+ {
+ "pid": "7664512466995",
+ "variantTitle": "Type 2 Textured Vinyl / 2ft x 1ft Sample",
+ "from": "Dig-85337-25-type-2-vinyl-Sample",
+ "to": "DIG-85336-25-type-2-vinyl-Sample",
+ "action": "DRY"
+ },
+ {
+ "pid": "7664512696371",
+ "variantTitle": "Type 2 Textured Vinyl / 2ft x 1ft Sample",
+ "from": "Dig-4350959-25-type-2-vinyl-Sample",
+ "to": "DIG-1985-25-type-2-vinyl-Sample",
+ "action": "DRY"
+ },
+ {
+ "pid": "7664512761907",
+ "variantTitle": "Prepasted / 2ft x 1ft Sample",
+ "from": "Dig-3001-25-pre-Sample",
+ "to": "Book-3001-25-pre-Sample",
+ "action": "DRY"
+ },
+ {
+ "pid": "7664512761907",
+ "variantTitle": "Type 2 Textured Vinyl / 2ft x 1ft Sample",
+ "from": "Dig-1952-25-type-2-vinyl-Sample",
+ "to": "Book-3001-25-type-2-vinyl-Sample",
+ "action": "DRY"
+ },
+ {
+ "pid": "7664512827443",
+ "variantTitle": "Prepasted / 2ft x 1ft Sample",
+ "from": "Dig-1100-25-pre-Sample",
+ "to": "Vin-1100-25-pre-Sample",
+ "action": "DRY"
+ },
+ {
+ "pid": "7664512827443",
+ "variantTitle": "Type 2 Textured Vinyl / 2ft x 1ft Sample",
+ "from": "Dig-1100-25-type-2-vinyl-Sample",
+ "to": "Vin-1100-25-type-2-vinyl-Sample",
+ "action": "DRY"
+ },
+ {
+ "pid": "7664513646643",
+ "variantTitle": "Prepasted / 2ft x 1ft Sample",
+ "from": "Dig-25-25-pre-sample",
+ "to": "DIG_10082002-25-pre-sample",
+ "action": "DRY"
+ },
+ {
+ "pid": "7664513646643",
+ "variantTitle": "Type 2 Textured Vinyl / 2ft x 1ft Sample",
+ "from": "Dig-25-25-type-2-vinyl-Sample",
+ "to": "DIG_10082002-25-type-2-vinyl-Sample",
+ "action": "DRY"
+ },
+ {
+ "pid": "7664515285043",
+ "variantTitle": "Type 2 Textured Vinyl / 2ft x 1ft Sample",
+ "from": "Dig-4350100-25-type-2-vinyl-Sample",
+ "to": "DIG-435003-25-type-2-vinyl-Sample",
+ "action": "DRY"
+ },
+ {
+ "pid": "7664583704627",
+ "variantTitle": "Prepasted / 2ft x 1ft Sample",
+ "from": "Dig-1010-25-pre-Sample",
+ "to": "Vin-1010-25-pre-Sample",
+ "action": "DRY"
+ },
+ {
+ "pid": "7664584196147",
+ "variantTitle": "Type 2 Textured Vinyl / 2ft x 1ft Sample",
+ "from": "Dig-770351-25-type-2-vinyl-Sample",
+ "to": "Dig-540002-25-type-2-vinyl-Sample",
+ "action": "DRY"
+ },
+ {
+ "pid": "7664584851507",
+ "variantTitle": "Type 2 Textured Vinyl / 2ft x 1ft Sample",
+ "from": "Dig-770089-25-type-2-vinyl-Sample",
+ "to": "DIG-740108-25-type-2-vinyl-Sample",
+ "action": "DRY"
+ },
+ {
+ "pid": "7664584884275",
+ "variantTitle": "Type 2 Textured Vinyl / 2ft x 1ft Sample",
+ "from": "Dig-240407-25-type-2-vinyl-Sample",
+ "to": "DIG-74118-25-type-2-vinyl-Sample",
+ "action": "DRY"
+ },
+ {
+ "pid": "7664585015347",
+ "variantTitle": "Type 2 Textured Vinyl / 2ft x 1ft Sample",
+ "from": "Dig-553342-25-type-2-vinyl-Sample",
+ "to": "DIG-553341-25-type-2-vinyl-Sample",
+ "action": "DRY"
+ },
+ {
+ "pid": "7664585965619",
+ "variantTitle": "Type 2 Textured Vinyl / 2ft x 1ft Sample",
+ "from": "Dig-762174-25-type-2-vinyl-Sample",
+ "to": "DIG-762168-25-type-2-vinyl-Sample",
+ "action": "DRY"
+ },
+ {
+ "pid": "7664586326067",
+ "variantTitle": "Type 2 Textured Vinyl / 2ft x 1ft Sample",
+ "from": "Dig-553794-25-type-2-vinyl-Sample",
+ "to": "DIG-553793-25-type-2-vinyl-Sample",
+ "action": "DRY"
+ },
+ {
+ "pid": "7664586686515",
+ "variantTitle": "Type 2 Textured Vinyl / 2ft x 1ft Sample",
+ "from": "Dig-25-25-type-2-vinyl-Sample",
+ "to": "DIG-439241-25-type-2-vinyl-Sample",
+ "action": "DRY"
+ },
+ {
+ "pid": "7664631349299",
+ "variantTitle": "Type 2 Textured Vinyl / 2ft x 1ft Sample",
+ "from": "Dig-51235-25-type-2-vinyl-Sample",
+ "to": "DIG-51288-25-type-2-vinyl-Sample",
+ "action": "DRY"
+ },
+ {
+ "pid": "7664636067891",
+ "variantTitle": "Type 2 Textured Vinyl / 2ft x 1ft Sample",
+ "from": "Dig-435086-25-type-2-vinyl-Sample",
+ "to": "DIG-435095-25-type-2-vinyl-Sample",
+ "action": "DRY"
+ },
+ {
+ "pid": "7664636100659",
+ "variantTitle": "Type 2 Textured Vinyl / 2ft x 1ft Sample",
+ "from": "Dig-770089-25-type-2-vinyl-Sample",
+ "to": "DIG-435086-25-type-2-vinyl-Sample",
+ "action": "DRY"
+ },
+ {
+ "pid": "7664640917555",
+ "variantTitle": "Type 2 Textured Vinyl / 2ft x 1ft Sample",
+ "from": "Dig-700005-25-type-2-vinyl-Sample",
+ "to": "WPEX-700005-25-type-2-vinyl-Sample",
+ "action": "DRY"
+ },
+ {
+ "pid": "7664640917555",
+ "variantTitle": "Prepasted / 2ft x 1ft Sample",
+ "from": "Dig-700005-25-pre-sample",
+ "to": "WPEX-700005-25-pre-sample",
+ "action": "DRY"
+ },
+ {
+ "pid": "7664640983091",
+ "variantTitle": "Type 2 Textured Vinyl / 2ft x 1ft Sample",
+ "from": "Dig-700007-25-type-2-vinyl-Sample",
+ "to": "WPEX-700007-25-type-2-vinyl-Sample",
+ "action": "DRY"
+ },
+ {
+ "pid": "7664640983091",
+ "variantTitle": "Prepasted / 2ft x 1ft Sample",
+ "from": "Dig-700007-25-pre-sample",
+ "to": "WPEX-700007-25-pre-sample",
+ "action": "DRY"
+ },
+ {
+ "pid": "7664462168115",
+ "variantTitle": "Type 2 Textured Vinyl / 2ft x 1ft Sample",
+ "from": "Dig-5101409-25-type-2-vinyl-Sample",
+ "to": "DIG-2400091-25-type-2-vinyl-Sample",
+ "action": "SKIP-BLOCKED",
+ "reason": "guard BLOCKED at convergence by 7664512270387/ACTIVE/Dig-2400091-25-type-2-vinyl-Sample"
+ },
+ {
+ "pid": "7664489496627",
+ "variantTitle": "Type 2 Textured Vinyl / 2ft x 1ft Sample",
+ "from": "Dig-240412-25-type-2-vinyl-Sample",
+ "to": "Dig-240417-25-type-2-vinyl-Sample",
+ "action": "SKIP-BLOCKED",
+ "reason": "guard BLOCKED at convergence by 7664489529395/ACTIVE/Dig-240417-25-type-2-vinyl-Sample, 7774188404787/ACTIVE/Dig-240417-25-type-2-vinyl-Sample"
+ },
+ {
+ "pid": "7664490545203",
+ "variantTitle": "Type 2 Textured Vinyl / 2ft x 1ft Sample",
+ "from": "Dig-171053-25-type-2-vinyl-Sample",
+ "to": "DIG-171052-25-type-2-vinyl-Sample",
+ "action": "SKIP-BLOCKED",
+ "reason": "guard BLOCKED at convergence by 7664490577971/ACTIVE/Dig-171052-25-type-2-vinyl-Sample"
+ },
+ {
+ "pid": "7664490577971",
+ "variantTitle": "Type 2 Textured Vinyl / 2ft x 1ft Sample",
+ "from": "Dig-171052-25-type-2-vinyl-Sample",
+ "to": "DIG-171051-25-type-2-vinyl-Sample",
+ "action": "SKIP-BLOCKED",
+ "reason": "guard BLOCKED at convergence by 7664585867315/ACTIVE/Dig-171051-25-type-2-vinyl-Sample"
+ },
+ {
+ "pid": "7664511549491",
+ "variantTitle": "Type 2 Textured Vinyl / 2ft x 1ft Sample",
+ "from": "Dig-198402-25-type-2-vinyl-Sample",
+ "to": "DIG-198401-25-type-2-vinyl-Sample",
+ "action": "SKIP-BLOCKED",
+ "reason": "guard BLOCKED at convergence by 7664511451187/ACTIVE/Dig-198401-25-type-2-vinyl-Sample"
+ },
+ {
+ "pid": "7664511582259",
+ "variantTitle": "Type 2 Textured Vinyl / 2ft x 1ft Sample",
+ "from": "Dig-98527-25-type-2-vinyl-Sample",
+ "to": "DIG-198402-25-type-2-vinyl-Sample",
+ "action": "SKIP-BLOCKED",
+ "reason": "guard BLOCKED at convergence by 7664511549491/ACTIVE/Dig-198402-25-type-2-vinyl-Sample"
+ },
+ {
+ "pid": "7664511647795",
+ "variantTitle": "Type 2 Textured Vinyl / 2ft x 1ft Sample",
+ "from": "Dig-98521-25-type-2-vinyl-Sample",
+ "to": "DIG-98527-25-type-2-vinyl-Sample",
+ "action": "SKIP-BLOCKED",
+ "reason": "guard BLOCKED at convergence by 7664511582259/ACTIVE/Dig-98527-25-type-2-vinyl-Sample"
+ },
+ {
+ "pid": "7664511680563",
+ "variantTitle": "Type 2 Textured Vinyl / 2ft x 1ft Sample",
+ "from": "Dig-5531532-25-type-2-vinyl-Sample",
+ "to": "DIG-98521-25-type-2-vinyl-Sample",
+ "action": "SKIP-BLOCKED",
+ "reason": "guard BLOCKED at convergence by 7664511647795/ACTIVE/Dig-98521-25-type-2-vinyl-Sample"
+ },
+ {
+ "pid": "7664511909939",
+ "variantTitle": "Type 2 Textured Vinyl / 2ft x 1ft Sample",
+ "from": "Dig-60016-25-type-2-vinyl-Sample",
+ "to": "DIG-60015-25-type-2-vinyl-Sample",
+ "action": "SKIP-BLOCKED",
+ "reason": "guard BLOCKED at convergence by 7664511877171/ACTIVE/Dig-60015-25-type-2-vinyl-Sample"
+ },
+ {
+ "pid": "7664511942707",
+ "variantTitle": "Type 2 Textured Vinyl / 2ft x 1ft Sample",
+ "from": "Dig-60018-25-type-2-vinyl-Sample",
+ "to": "DIG-60016-25-type-2-vinyl-Sample",
+ "action": "SKIP-BLOCKED",
+ "reason": "guard BLOCKED at convergence by 7664511909939/ACTIVE/Dig-60016-25-type-2-vinyl-Sample"
+ },
+ {
+ "pid": "7664512008243",
+ "variantTitle": "Type 2 Textured Vinyl / 2ft x 1ft Sample",
+ "from": "Dig-40011-25-type-2-vinyl-Sample",
+ "to": "DIG-60018-25-type-2-vinyl-Sample",
+ "action": "SKIP-BLOCKED",
+ "reason": "guard BLOCKED at convergence by 7664511942707/ACTIVE/Dig-60018-25-type-2-vinyl-Sample"
+ },
+ {
+ "pid": "7664512270387",
+ "variantTitle": "Type 2 Textured Vinyl / 2ft x 1ft Sample",
+ "from": "Dig-2400091-25-type-2-vinyl-Sample",
+ "to": "DIG-435098-25-type-2-vinyl-Sample",
+ "action": "SKIP-BLOCKED",
+ "reason": "guard BLOCKED at convergence by 7664490643507/ACTIVE/Dig-435098-25-type-2-vinyl-Sample"
+ },
+ {
+ "pid": "7664512335923",
+ "variantTitle": "Type 2 Textured Vinyl / 2ft x 1ft Sample",
+ "from": "Dig-85330-25-type-2-vinyl-Sample",
+ "to": "DIG-5101406-25-type-2-vinyl-Sample",
+ "action": "SKIP-BLOCKED",
+ "reason": "guard BLOCKED at convergence by 7664512303155/ACTIVE/Dig-5101406-25-type-2-vinyl-Sample"
+ },
+ {
+ "pid": "7664512401459",
+ "variantTitle": "Type 2 Textured Vinyl / 2ft x 1ft Sample",
+ "from": "Dig-85335-25-type-2-vinyl-Sample",
+ "to": "DIG-85330-25-type-2-vinyl-Sample",
+ "action": "SKIP-BLOCKED",
+ "reason": "guard BLOCKED at convergence by 7664512335923/ACTIVE/Dig-85330-25-type-2-vinyl-Sample"
+ },
+ {
+ "pid": "7664512434227",
+ "variantTitle": "Type 2 Textured Vinyl / 2ft x 1ft Sample",
+ "from": "Dig-40021-25-type-2-vinyl-Sample",
+ "to": "DIG-85335-25-type-2-vinyl-Sample",
+ "action": "SKIP-BLOCKED",
+ "reason": "guard BLOCKED at convergence by 7664512401459/ACTIVE/Dig-85335-25-type-2-vinyl-Sample"
+ },
+ {
+ "pid": "7664512499763",
+ "variantTitle": "Type 2 Textured Vinyl / 2ft x 1ft Sample",
+ "from": "Dig-85331-25-type-2-vinyl-Sample",
+ "to": "DIG-85337-25-type-2-vinyl-Sample",
+ "action": "SKIP-BLOCKED",
+ "reason": "guard BLOCKED at convergence by 7664512466995/ACTIVE/Dig-85337-25-type-2-vinyl-Sample"
+ },
+ {
+ "pid": "7664512532531",
+ "variantTitle": "Type 2 Textured Vinyl / 2ft x 1ft Sample",
+ "from": "Dig-85333-25-type-2-vinyl-Sample",
+ "to": "DIG-85331-25-type-2-vinyl-Sample",
+ "action": "SKIP-BLOCKED",
+ "reason": "guard BLOCKED at convergence by 7664512499763/ACTIVE/Dig-85331-25-type-2-vinyl-Sample"
+ },
+ {
+ "pid": "7664512565299",
+ "variantTitle": "Type 2 Textured Vinyl / 2ft x 1ft Sample",
+ "from": "Dig-85334-25-type-2-vinyl-Sample",
+ "to": "DIG-85333-25-type-2-vinyl-Sample",
+ "action": "SKIP-BLOCKED",
+ "reason": "guard BLOCKED at convergence by 7664512532531/ACTIVE/Dig-85333-25-type-2-vinyl-Sample"
+ },
+ {
+ "pid": "7664512598067",
+ "variantTitle": "Type 2 Textured Vinyl / 2ft x 1ft Sample",
+ "from": "Dig-85332-25-type-2-vinyl-Sample",
+ "to": "DIG-85334-25-type-2-vinyl-Sample",
+ "action": "SKIP-BLOCKED",
+ "reason": "guard BLOCKED at convergence by 7664512565299/ACTIVE/Dig-85334-25-type-2-vinyl-Sample"
+ },
+ {
+ "pid": "7664512630835",
+ "variantTitle": "Type 2 Textured Vinyl / 2ft x 1ft Sample",
+ "from": "Dig-98621-25-type-2-vinyl-Sample",
+ "to": "DIG-85332-25-type-2-vinyl-Sample",
+ "action": "SKIP-BLOCKED",
+ "reason": "guard BLOCKED at convergence by 7664512598067/ACTIVE/Dig-85332-25-type-2-vinyl-Sample"
+ },
+ {
+ "pid": "7664512663603",
+ "variantTitle": "Type 2 Textured Vinyl / 2ft x 1ft Sample",
+ "from": "Dig-240416-25-type-2-vinyl-Sample",
+ "to": "DIG-98621-25-type-2-vinyl-Sample",
+ "action": "SKIP-BLOCKED",
+ "reason": "guard BLOCKED at convergence by 7664512630835/ACTIVE/Dig-98621-25-type-2-vinyl-Sample"
+ },
+ {
+ "pid": "7664512794675",
+ "variantTitle": "Type 2 Textured Vinyl / 2ft x 1ft Sample",
+ "from": "Dig-1100-25-type-2-vinyl-Sample",
+ "to": "DIG-1952-25-type-2-vinyl-Sample",
+ "action": "SKIP-BLOCKED",
+ "reason": "guard BLOCKED at convergence by 7664512761907/ACTIVE/Dig-1952-25-type-2-vinyl-Sample"
+ },
+ {
+ "pid": "7664584949811",
+ "variantTitle": "Type 2 Textured Vinyl / 2ft x 1ft Sample",
+ "from": "Dig-5531533-25-type-2-vinyl-Sample",
+ "to": "DIG-5531532-25-type-2-vinyl-Sample",
+ "action": "SKIP-BLOCKED",
+ "reason": "guard BLOCKED at convergence by 7664511680563/ACTIVE/Dig-5531532-25-type-2-vinyl-Sample"
+ },
+ {
+ "pid": "7664585048115",
+ "variantTitle": "Type 2 Textured Vinyl / 2ft x 1ft Sample",
+ "from": "Dig-2010-25-type-2-vinyl-Sample",
+ "to": "DIG-553342-25-type-2-vinyl-Sample",
+ "action": "SKIP-BLOCKED",
+ "reason": "guard BLOCKED at convergence by 7664585015347/ACTIVE/Dig-553342-25-type-2-vinyl-Sample"
+ },
+ {
+ "pid": "7664585867315",
+ "variantTitle": "Type 2 Textured Vinyl / 2ft x 1ft Sample",
+ "from": "Dig-171051-25-type-2-vinyl-Sample",
+ "to": "DIG-747481-25-type-2-vinyl-Sample",
+ "action": "SKIP-BLOCKED",
+ "reason": "guard BLOCKED at convergence by 7664488284211/ACTIVE/Dig-747481-25-type-2-vinyl-Sample"
+ },
+ {
+ "pid": "7664585998387",
+ "variantTitle": "Type 2 Textured Vinyl / 2ft x 1ft Sample",
+ "from": "Dig-762179-25-type-2-vinyl-Sample",
+ "to": "DIG-762174-25-type-2-vinyl-Sample",
+ "action": "SKIP-BLOCKED",
+ "reason": "guard BLOCKED at convergence by 7664585965619/ACTIVE/Dig-762174-25-type-2-vinyl-Sample"
+ },
+ {
+ "pid": "7664586031155",
+ "variantTitle": "Type 2 Textured Vinyl / 2ft x 1ft Sample",
+ "from": "Dig-762165-25-type-2-vinyl-Sample",
+ "to": "DIG-762179-25-type-2-vinyl-Sample",
+ "action": "SKIP-BLOCKED",
+ "reason": "guard BLOCKED at convergence by 7664585998387/ACTIVE/Dig-762179-25-type-2-vinyl-Sample"
+ }
+ ]
+}
\ No newline at end of file
diff --git a/tk10002-resku/check-state.mjs b/tk10002-resku/check-state.mjs
new file mode 100644
index 00000000..66a93296
--- /dev/null
+++ b/tk10002-resku/check-state.mjs
@@ -0,0 +1,26 @@
+#!/usr/bin/env node
+// Read-only: for the 64 clean confident-STRAY products, check LIVE Shopify whether each
+// stray variant now carries its `corrected` SKU (DONE) or still the `stray` SKU (PENDING).
+import fs from 'node:fs';
+const TOKEN=(process.env.SHOPIFY_ADMIN_TOKEN||'').trim();
+const STORE='designer-laboratory-sandbox.myshopify.com', API='2024-10';
+const EXCLUDE=new Set(['7664488284211','7664489529395','7774188404787','7391684657203']);
+const fv=JSON.parse(fs.readFileSync(new URL('./final-verdict.json',import.meta.url)));
+const products=fv.confident.filter(p=>!EXCLUDE.has(String(p.pid)));
+const gql=async(q)=>{const r=await fetch(`https://${STORE}/admin/api/${API}/graphql.json`,{method:'POST',headers:{'X-Shopify-Access-Token':TOKEN,'Content-Type':'application/json'},body:JSON.stringify({query:q})});return (await r.json());};
+let done=0,pending=0,other=0,errs=0; const pend=[];
+for(const p of products){
+ const q=`{product(id:"gid://shopify/Product/${p.pid}"){status variants(first:40){nodes{sku}}}}`;
+ let d; try{d=(await gql(q)).data.product;}catch(e){errs++;continue;}
+ if(!d){other++;continue;}
+ const skus=new Set(d.variants.nodes.map(v=>(v.sku||'').trim()));
+ for(const s of p.strays){
+ const corr=s.corrected.trim(), stray=s.stray.trim();
+ if(skus.has(corr)) done++;
+ else if(skus.has(stray)){ pending++; pend.push({pid:p.pid,stray,corrected:corr}); }
+ else other++;
+ }
+}
+console.log(`products checked=${products.length} strayVariants: DONE=${done} PENDING=${pending} OTHER=${other} fetchErrs=${errs}`);
+if(pend.length){console.log('\nPENDING (still to write):'); pend.slice(0,80).forEach(x=>console.log(` ${x.pid}: ${x.stray} -> ${x.corrected}`));}
+fs.writeFileSync(new URL('./state-check.json',import.meta.url),JSON.stringify({done,pending,other,errs,pend},null,2));
← dda63dba TK-10002 PLAN v2: bound full stray family (71 products/81 st
·
back to Ticket System
·
TK-10002 re-SKU: 65 stray variants fixed live (6 skipped, he 467d9321 →