← 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
M package.jsonM scripts/tk10895-fixpos.mjsM scripts/tk10895-trancheB-golive.mjs
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 →