[object Object]

← back to Designerwallcoverings

chore: lint, refactor, v0.1.13 (session close) — TK-10895

b319c8f94baea5e20ff961f6d7d59f1035ea743c · 2026-09-10 12:21:18 -0700 · Steve Abrams

Scoped to this session's two files only; other sessions have uncommitted work
in this repo (stroheim-onboard, tk11301/11307/11357, thibaut-wide-width) and
none of it was staged.

lint     - dropped 2 unused catch bindings in fixpos.mjs (no linter configured
           in this repo, so a manual pass); golive.mjs had no style issues.
refactor - extracted the variant-order comparator duplicated 4x in fixpos.mjs
           into a single byPosition const. No logic change. golive.mjs left
           alone: its only duplication is the REST wrapper shared with fixpos,
           and extracting a module for two standalone 'node <path>' one-offs
           risks changing how either resolves imports.

FIX (found by the lint pass, not a style issue) - golive.mjs still carried
    const ue = res?.data?...?.userErrors || []
the exact collapse that made 226 reorders fail silently on 2026-09-10 while
reporting reorderWarnings=0. The type-name fix landed earlier but this guard
never did, so a re-run would have swallowed failures again. Now checks
transport, top-level errors, and userErrors as three separate conditions,
matching fixpos.mjs.

Verified behaviour-identical after all three changes:
  fixpos dry-run  alreadyCorrect=226 fixed=0 stillBad=0 failed=0
  golive dry-run  created=0 skipped=226 failed=0 reorderWarnings=0

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01B18ECN33SGCpS3ntckQMc2

Files touched

Diff

commit b319c8f94baea5e20ff961f6d7d59f1035ea743c
Author: Steve Abrams <steve@designerwallcoverings.com>
Date:   Thu Sep 10 12:21:18 2026 -0700

    chore: lint, refactor, v0.1.13 (session close) — TK-10895
    
    Scoped to this session's two files only; other sessions have uncommitted work
    in this repo (stroheim-onboard, tk11301/11307/11357, thibaut-wide-width) and
    none of it was staged.
    
    lint     - dropped 2 unused catch bindings in fixpos.mjs (no linter configured
               in this repo, so a manual pass); golive.mjs had no style issues.
    refactor - extracted the variant-order comparator duplicated 4x in fixpos.mjs
               into a single byPosition const. No logic change. golive.mjs left
               alone: its only duplication is the REST wrapper shared with fixpos,
               and extracting a module for two standalone 'node <path>' one-offs
               risks changing how either resolves imports.
    
    FIX (found by the lint pass, not a style issue) - golive.mjs still carried
        const ue = res?.data?...?.userErrors || []
    the exact collapse that made 226 reorders fail silently on 2026-09-10 while
    reporting reorderWarnings=0. The type-name fix landed earlier but this guard
    never did, so a re-run would have swallowed failures again. Now checks
    transport, top-level errors, and userErrors as three separate conditions,
    matching fixpos.mjs.
    
    Verified behaviour-identical after all three changes:
      fixpos dry-run  alreadyCorrect=226 fixed=0 stillBad=0 failed=0
      golive dry-run  created=0 skipped=226 failed=0 reorderWarnings=0
    
    Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
    Claude-Session: https://claude.ai/code/session_01B18ECN33SGCpS3ntckQMc2
---
 package.json                        |  2 +-
 scripts/tk10895-fixpos.mjs          | 14 ++++++++------
 scripts/tk10895-trancheB-golive.mjs | 18 ++++++++++++++++--
 3 files changed, 25 insertions(+), 9 deletions(-)

diff --git a/package.json b/package.json
index ae35b5c..3dc512d 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
 {
   "name": "designerwallcoverings-ai",
-  "version": "0.1.12",
+  "version": "0.1.13",
   "private": true,
   "description": "designerwallcoverings.ai — AI room-render landing. Upload a room photo, get matching wallcoverings.",
   "main": "server.js",
diff --git a/scripts/tk10895-fixpos.mjs b/scripts/tk10895-fixpos.mjs
index 3138ae4..353b4af 100644
--- a/scripts/tk10895-fixpos.mjs
+++ b/scripts/tk10895-fixpos.mjs
@@ -23,6 +23,8 @@ const WL = process.argv[process.argv.indexOf('--worklist') + 1];
 const work = JSON.parse(fs.readFileSync(WL, 'utf8'));
 
 const sleep = ms => new Promise(r => setTimeout(r, ms));
+// Shared "oldest-first" comparator used everywhere a variant array needs to be read in on-page order.
+const byPosition = (a, b) => (a.position || 99) - (b.position || 99);
 
 async function rest(path, opts = {}, tries = 5) {
   for (let a = 1; a <= tries; a++) {
@@ -63,9 +65,9 @@ async function reorder(pid, orderedIds, tries = 5) {
         headers: { 'X-Shopify-Access-Token': TOK, 'Content-Type': 'application/json' },
         body: JSON.stringify({ query: q, variables }),
       });
-    } catch (e) { await sleep(1500 * a); continue; }
+    } catch { await sleep(1500 * a); continue; }
     if (r.status === 429 || r.status >= 500) { await sleep(1500 * a); continue; }
-    try { j = await r.json(); } catch (e) { await sleep(1200 * a); continue; }
+    try { j = await r.json(); } catch { await sleep(1200 * a); continue; }
     if (j?.errors?.length) {                      // TOP-LEVEL GraphQL errors — the swallowed case
       const msg = JSON.stringify(j.errors).slice(0, 200);
       if (/throttle/i.test(msg)) { await sleep(2500 * a); continue; }
@@ -93,7 +95,7 @@ async function gqlRead(pid, tries = 3) {
       const j = await r.json();
       const n = j?.data?.product?.variants?.nodes;
       if (Array.isArray(n) && n.length) {
-        return [...n].sort((x, y) => (x.position || 99) - (y.position || 99));
+        return [...n].sort(byPosition);
       }
       return [];
     } catch { await sleep(1000 * a); }
@@ -116,11 +118,11 @@ for (const [i, w] of work.entries()) {
   if (!vs.length) {                                    // fallback only if GraphQL is unavailable
     const g = await rest(`/products/${w.pid}.json`);
     if (g.status !== 200 || !g.body?.product) { console.log(`${tag} FAIL GET ${g.status}`); failed++; continue; }
-    vs = [...(g.body.product.variants || [])].sort((a, b) => (a.position || 99) - (b.position || 99));
+    vs = [...(g.body.product.variants || [])].sort(byPosition);
   }
   if (vs.length < 2) { console.log(`${tag} skip — only ${vs.length} variant`); skipped++; continue; }
 
-  const byPos = [...vs].sort((a, b) => (a.position || 99) - (b.position || 99));
+  const byPos = [...vs].sort(byPosition);
   const target = vs.find(v => String(v.price) === String(w.price)) ||
                  vs.find(v => String(v.price) !== '4.25');
   if (!target) { console.log(`${tag} FAIL — no non-$4.25 variant found`); failed++; continue; }
@@ -141,7 +143,7 @@ for (const [i, w] of work.entries()) {
   const after = vq.length
     ? vq
     : [...(await rest(`/products/${w.pid}.json`)).body?.product?.variants || []]
-        .sort((a, b) => (a.position || 99) - (b.position || 99));
+        .sort(byPosition);
   if (after[0] && String(after[0].id).replace(/\D/g, '') === String(target.id).replace(/\D/g, '')) {
     fixed++; console.log(`${tag} ✓ fixed — $${after[0].price} now position 1`);
   } else {
diff --git a/scripts/tk10895-trancheB-golive.mjs b/scripts/tk10895-trancheB-golive.mjs
index 2bf13f3..b4b7499 100644
--- a/scripts/tk10895-trancheB-golive.mjs
+++ b/scripts/tk10895-trancheB-golive.mjs
@@ -128,8 +128,22 @@ for (const [i, w] of work.entries()) {
     productId: `gid://shopify/Product/${w.pid}`,
     positions: ordered.map((id, idx) => ({ id: `gid://shopify/ProductVariant/${id}`, position: idx + 1 })),
   });
-  const ue = res?.data?.productVariantsBulkReorder?.userErrors || [];
-  if (ue.length) { console.log(`${tag} ⚠ reorder userErrors ${JSON.stringify(ue)}`); reorderWarn++; }
+  // Three SEPARATE conditions — transport, top-level `errors`, then userErrors. The old
+  //     res?.data?.productVariantsBulkReorder?.userErrors || []
+  // collapsed to [] whenever the call returned null OR a top-level `errors` payload with
+  // data:null, so on 2026-09-10 all 226 reorders were rejected server-side
+  // ("VariantPositionInput isn't a defined input type") and this line logged reorderWarnings=0.
+  // A hard failure MUST NOT be indistinguishable from success.
+  if (!res) {
+    console.log(`${tag} ⚠ reorder transport failed (no response)`); reorderWarn++;
+  } else if (res.errors?.length) {
+    console.log(`${tag} ⚠ reorder GraphQL errors ${JSON.stringify(res.errors).slice(0, 200)}`); reorderWarn++;
+  } else if (res.data?.productVariantsBulkReorder?.userErrors === undefined) {
+    console.log(`${tag} ⚠ reorder returned no data ${JSON.stringify(res).slice(0, 200)}`); reorderWarn++;
+  } else {
+    const ue = res.data.productVariantsBulkReorder.userErrors;
+    if (ue.length) { console.log(`${tag} ⚠ reorder userErrors ${JSON.stringify(ue)}`); reorderWarn++; }
+  }
 
   // option name -> "Size" to match live DW convention (Brunschwig/Zoffany)
   if (p.options?.[0]?.name && p.options[0].name !== 'Size') {

← ce697b6 TK-11307: GATED-0 applier — stamp exact ShowroomOnly tag on  ·  back to Designerwallcoverings  ·  auto-data-snapshot: 2026-09-10T12:25:41 (6 data files) — pac 93a10f1 →