[object Object]

← back to Designer Wallcoverings

chore: image-gate sweep hardening per code review — check DEMOTE userErrors before counting, include Video/ExternalVideo media, media(first:50), summary count reflects demotions (session close)

b05ee05535911da0d5dd32f62a8b50b86657c37a · 2026-07-29 20:18:18 -0700 · Steve

Files touched

Diff

commit b05ee05535911da0d5dd32f62a8b50b86657c37a
Author: Steve <steve@designerwallcoverings.com>
Date:   Wed Jul 29 20:18:18 2026 -0700

    chore: image-gate sweep hardening per code review — check DEMOTE userErrors before counting, include Video/ExternalVideo media, media(first:50), summary count reflects demotions (session close)
---
 shopify/scripts/cadence/cadence-import.js | 9 ++++++---
 1 file changed, 6 insertions(+), 3 deletions(-)

diff --git a/shopify/scripts/cadence/cadence-import.js b/shopify/scripts/cadence/cadence-import.js
index f3ebaba0..55a518ce 100644
--- a/shopify/scripts/cadence/cadence-import.js
+++ b/shopify/scripts/cadence/cadence-import.js
@@ -929,7 +929,7 @@ async function verifyActivatedMedia(pids) {
   if (!pids.length) return out;
   console.log(`\nverifying image uploads reached READY on ${pids.length} activated product(s)…`);
   await sleep(30000);                                   // let async media settle
-  const Q = `query($ids:[ID!]!){nodes(ids:$ids){... on Product{id handle status media(first:10){nodes{... on MediaImage{status}}}}}}`;
+  const Q = `query($ids:[ID!]!){nodes(ids:$ids){... on Product{id handle status media(first:50){nodes{... on MediaImage{status} ... on Video{status} ... on ExternalVideo{status}}}}}}`;
   const DEMOTE = `mutation($input:ProductInput!){productUpdate(input:$input){product{id} userErrors{message}}}`;
   const TAGS = `mutation($id:ID!,$tags:[String!]!){tagsAdd(id:$id,tags:$tags){userErrors{message}}}`;
   let pending = pids.slice();
@@ -944,7 +944,9 @@ async function verifyActivatedMedia(pids) {
         if (st.includes('READY')) continue;             // an image landed — gate satisfied
         if (st.includes('PROCESSING')) { next.push(n.id); continue; }
         // no READY, nothing PROCESSING (all FAILED or zero media): no image is coming
-        await gqlRetry(DEMOTE, { input: { id: n.id, status: 'DRAFT' } });
+        const dr = await gqlRetry(DEMOTE, { input: { id: n.id, status: 'DRAFT' } });
+        const dErr = dr.json?.data?.productUpdate?.userErrors;
+        if (dErr && dErr.length) { console.warn(`  ✗ ${n.handle}: DEMOTE failed — ${JSON.stringify(dErr).slice(0,120)} (still ACTIVE+imageless — five-field canary will flag)`); continue; }
         await gqlRetry(TAGS, { id: n.id, tags: ['Needs-Image'] });
         out.demoted++;
         console.log(`  ⚠ ${n.handle}: image upload FAILED/absent → demoted to DRAFT (Needs-Image)`);
@@ -1072,8 +1074,9 @@ if (require.main === module) (async () => {
     catch (e) { console.warn('  inventory-set failed (products are ACTIVE but inventory not 2026 — re-run inventory-set-2026.js):', e.message); }
   }
   // TK-10040 image-gate sweep: demote any activated product whose async image upload never landed.
+  let imageDemoted = 0;
   if (COMMIT && ACTIVATE && activatedPids.length) {
-    try { await verifyActivatedMedia(activatedPids); }
+    try { const sweep = await verifyActivatedMedia(activatedPids); imageDemoted = sweep.demoted; activatedCount -= imageDemoted; }
     catch (e) { console.warn('  image-verify sweep failed (products stay as-is; five-field canary is the backstop):', e.message); }
   }
   // auto-membership into "Trending Wallcovering Collection 2026" (the New-Arrivals auto-flow fix).

← f321a5a0 cadence-import: delete dead <li> spec-list blocks (TK-10039  ·  back to Designer Wallcoverings  ·  chore: v1.2.8 (session close) 71ee4e4f →