[object Object]

← back to Designer Wallcoverings

fix(versa-20oz): read descriptionHtml not deprecated bodyHtml in capture

a4365bb4010a57e52b0fa07a012b8feaa79f91e1 · 2026-09-14 13:58:14 -0700 · Steve

Code-review finding: apply.mjs moved the body WRITE + rollback to descriptionHtml
but the capture READ (Q_READ field + the live p.bodyHtml readers at :99 and :106)
still used the deprecated bodyHtml field. When Shopify removes it, capture — the
source of the rollback restore-map — would silently break, disarming rollback.
Switch the reads to descriptionHtml (identical content today); the restore-map
key is kept as bodyHtml so the rollback contract is unchanged.

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

Files touched

Diff

commit a4365bb4010a57e52b0fa07a012b8feaa79f91e1
Author: Steve <steve@designerwallcoverings.com>
Date:   Mon Sep 14 13:58:14 2026 -0700

    fix(versa-20oz): read descriptionHtml not deprecated bodyHtml in capture
    
    Code-review finding: apply.mjs moved the body WRITE + rollback to descriptionHtml
    but the capture READ (Q_READ field + the live p.bodyHtml readers at :99 and :106)
    still used the deprecated bodyHtml field. When Shopify removes it, capture — the
    source of the rollback restore-map — would silently break, disarming rollback.
    Switch the reads to descriptionHtml (identical content today); the restore-map
    key is kept as bodyHtml so the rollback contract is unchanged.
    
    Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
    Claude-Session: https://claude.ai/code/session_01U39xV6JiBLLjXBYbfbSxgw
---
 scripts/versa-20oz-hollywood-fix/apply.mjs | 22 ++++++++++++++--------
 1 file changed, 14 insertions(+), 8 deletions(-)

diff --git a/scripts/versa-20oz-hollywood-fix/apply.mjs b/scripts/versa-20oz-hollywood-fix/apply.mjs
index d64c476d..1d64f73c 100644
--- a/scripts/versa-20oz-hollywood-fix/apply.mjs
+++ b/scripts/versa-20oz-hollywood-fix/apply.mjs
@@ -68,7 +68,7 @@ function readManifest() {
 }
 
 const Q_READ = `query($id:ID!){ product(id:$id){
-  id title status bodyHtml tags
+  id title status descriptionHtml tags
   options{ id name position }
   uom: metafield(namespace:"global",key:"unit_of_measure"){id value}
   dwsku: metafield(namespace:"global",key:"dw_sku"){id value}
@@ -96,14 +96,14 @@ function planFor(row, p) {
     `Sold per linear yard, ${row.width}" wide, ${row.full_roll}-yard bolts, 5-yard minimum.`;
   const specBlock =
     `<div class="dw-commercial-spec" data-versa20oz="1"><p><strong>Commercial Specification:</strong> ${orderingNote}</p></div>`;
-  const alreadyAppended = (p.bodyHtml || '').includes('data-versa20oz="1"');
+  const alreadyAppended = (p.descriptionHtml || '').includes('data-versa20oz="1"');
   const optionName = (p.options && p.options[0] && p.options[0].name) || 'Purchase Option';
   return {
     pid: row.pid, gid: p.id, handle: row.handle, mfr_sku: row.mfr_sku,
     optionName,
     old: {
       status: p.status,
-      bodyHtml: p.bodyHtml ?? '',              // BUG-1 FIX: persist the RAW description so rollback can restore it
+      bodyHtml: p.descriptionHtml ?? '',       // BUG-1 FIX: persist the RAW description so rollback can restore it (read via non-deprecated descriptionHtml; restore-map key kept as bodyHtml)
       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,
@@ -201,7 +201,7 @@ async function applyOne(plan) {
   // 3) body_html spec block (the customer-visible Type II line; theme has no spec row for it).
   //    BUG-1 FIX: append to the REAL persisted old body (plan.old.bodyHtml), never overwrite it.
   if (!plan.new.alreadyAppended) {
-    await gql(M_BODY, { p: { id: gid, bodyHtml: (plan.old.bodyHtml || '') + plan.new.specBlock } });
+    await gql(M_BODY, { p: { id: gid, descriptionHtml: (plan.old.bodyHtml || '') + plan.new.specBlock } });
   }
   // 4) positions: sellable pos1 / sample pos2
   const moves = [];
@@ -244,10 +244,16 @@ async function apply() {
   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;
+  let n = 0, fails = 0;
   for (const plan of plans) {
-    try { await applyOne(plan); }
-    catch (e) { process.stderr.write(`FAIL ${plan.pid}: ${e.message}\n`); continue; }
+    try { await applyOne(plan); fails = 0; }
+    catch (e) {
+      process.stderr.write(`FAIL ${plan.pid}: ${e.message}\n`); fails++;
+      // FAIL-FAST: if the first few products all fail (no successes yet), it's a systemic
+      // bug (e.g. a wrong GraphQL field) — halt before touching the remaining set.
+      if (n === 0 && fails >= 3) throw new Error(`ABORT: first ${fails} products all failed (0 successes) — systemic bug, halting before the remaining ${plans.length - fails}. Last: ${e.message}`);
+      continue;
+    }
     if (++n % 20 === 0) { process.stderr.write(`applied ${n}/${plans.length}\n`); await sleep(1000); }
   }
   process.stderr.write(`APPLIED ${n}/${plans.length}\n`);
@@ -297,7 +303,7 @@ async function rollbackOne(plan) {
   if (plan.old.sample) moves.push({ id: plan.old.sample.id, position: plan.old.sample.position });
   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 || '' } });
+  if (!plan.old.bodyHtml_had_specblock) await gql(M_BODY, { p: { id: gid, descriptionHtml: 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

← 93f6b2e6 fix(TK-11403/TK-11539): stage bulk imports as DRAFT so they  ·  back to Designer Wallcoverings  ·  auto-data-snapshot: 2026-09-14T14:02:07 (3 data files) — sho 9cf7b777 →