[object Object]

← back to Shopify Sample Shipping

Track publish-rollback.mjs and proof-designer.mjs (git-hygiene: gitify the documented rollback target)

9ca4cf564da82ebc348c1def8adee23b1b0ab438 · 2026-09-15 19:04:01 -0700 · Steve

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

Files touched

Diff

commit 9ca4cf564da82ebc348c1def8adee23b1b0ab438
Author: Steve <steve@designerwallcoverings.com>
Date:   Tue Sep 15 19:04:01 2026 -0700

    Track publish-rollback.mjs and proof-designer.mjs (git-hygiene: gitify the documented rollback target)
    
    Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
---
 proof-designer.mjs   | 55 ++++++++++++++++++++++++++++++++++++++++++++++++++++
 publish-rollback.mjs | 15 ++++++++++++++
 2 files changed, 70 insertions(+)

diff --git a/proof-designer.mjs b/proof-designer.mjs
new file mode 100644
index 0000000..f2cf2c0
--- /dev/null
+++ b/proof-designer.mjs
@@ -0,0 +1,55 @@
+// READ-ONLY proof: does TRADESHIP give a designer $0 shipping on a 12-sample ($51) cart?
+// Uses draftOrderCalculate (creates NOTHING) with a real in-segment customer + the code.
+import { query } from './query.mjs';
+const SAMPLE = 'gid://shopify/ProductVariant/44090076954675';
+const lineItems = [{ variantId: SAMPLE, quantity: 12 }]; // 12 samples = $51 (over the $45 band)
+const addr = { address1: '15442 Ventura Blvd', city: 'Sherman Oaks', provinceCode: 'CA', countryCode: 'US', zip: '91403' };
+
+async function pickCustomer(q, label) {
+  const r = await query(`query($q:String!){customers(first:1,query:$q){nodes{id email tags}}}`, { q });
+  const c = r.customers.nodes[0];
+  console.log(`${label}: ${c ? c.email + '  tags=[' + c.tags.join(', ') + ']' : 'NONE FOUND'}`);
+  return c?.id;
+}
+const CALC = `mutation($input:DraftOrderInput!){draftOrderCalculate(input:$input){calculatedDraftOrder{
+  totalShippingPriceSet{shopMoney{amount}} shippingLine{title price}
+  totalDiscountsSet{shopMoney{amount}}
+  availableShippingRates{title price{amount}} discountCodes
+  appliedDiscount{title} } userErrors{field message}}}`;
+
+async function scenario(label, customerId, codes, ship) {
+  const input = { lineItems, shippingAddress: addr };
+  if (customerId) input.purchasingEntity = { customerId };
+  if (codes) input.discountCodes = codes;
+  if (ship) input.shippingLine = ship;
+  const d = (await query(CALC, { input })).draftOrderCalculate;
+  if (d.userErrors?.length) { return { err: JSON.stringify(d.userErrors) }; }
+  const c = d.calculatedDraftOrder;
+  const rates = c.availableShippingRates || [];
+  const free = rates.some(r => parseFloat(r.price.amount) === 0);
+  if (label) {
+    console.log(`  ${label}:`);
+    console.log(`     applied discountCodes: ${JSON.stringify(c.discountCodes)} | selected shippingLine: ${c.shippingLine?.title || '—'} $${c.shippingLine?.price ?? '—'}`);
+    console.log(`     totalShipping: $${c.totalShippingPriceSet?.shopMoney?.amount} | totalDiscounts: $${c.totalDiscountsSet?.shopMoney?.amount}`);
+  }
+  return { free, codes: c.discountCodes, ship: c.totalShippingPriceSet?.shopMoney?.amount };
+}
+
+console.log('=== PROOF: TRADESHIP for a designer on 12 samples ($51, over the $45 band) ===\n');
+// try in-segment designers until one passes Shopify's calc email-domain validation
+const cands = (await query(`query($q:String!){customers(first:50,query:$q){nodes{id email tags}}}`,
+  { q: "tag:'sample-freeship'" })).customers.nodes;
+let designer = null, designerEmail = null;
+for (const c of cands) {
+  const t = await scenario('', c.id, ['TRADESHIP']);
+  if (!t.err) { designer = c.id; designerEmail = c.email; break; }
+}
+console.log('in-segment DESIGNER used:', designerEmail || 'NONE of first 50 validated');
+const retail = await pickCustomer("tag:'Home Owner'", 'RETAIL (Home Owner)');
+console.log();
+const UPS = { title: 'UPS® Ground', price: '20.53' }; // select the real carrier rate, then see if the code zeroes it
+if (designer) {
+  await scenario('A) DESIGNER + TRADESHIP + UPS rate  (expect shipping discounted to $0)', designer, ['TRADESHIP'], UPS);
+  await scenario('C) DESIGNER + no code + UPS rate    (expect shipping stays $20.53)', designer, null, UPS);
+} else console.log('  (could not find an in-segment customer whose email passes calc validation)');
+await scenario('B) RETAIL   + TRADESHIP + UPS rate  (expect code rejected, shipping stays $20.53)', retail, ['TRADESHIP'], UPS);
diff --git a/publish-rollback.mjs b/publish-rollback.mjs
new file mode 100644
index 0000000..49e24c8
--- /dev/null
+++ b/publish-rollback.mjs
@@ -0,0 +1,15 @@
+#!/usr/bin/env node
+// TK-11333 — ROLLBACK: re-publish the theme that was live before ~/publish. Steve runs (--apply).
+import fs from 'node:fs';
+import { query } from './query.mjs';
+const APPLY = process.argv.includes('--apply');
+const REC = new URL('./verification/theme-publish.json', import.meta.url);
+if (!fs.existsSync(REC)) { console.error('no theme-publish.json — nothing to roll back'); process.exit(1); }
+const rec = JSON.parse(fs.readFileSync(REC, 'utf8'));
+console.log('will re-publish previous LIVE:', rec.previousMainName, rec.previousMainId);
+if (!rec.previousMainId) { console.error('no previousMainId recorded'); process.exit(1); }
+if (!APPLY) { console.log('\nDRY-RUN. --apply re-publishes it LIVE.'); process.exit(0); }
+const r = (await query(`mutation($id:ID!){themePublish(id:$id){userErrors{field message}}}`, { id: rec.previousMainId })).themePublish;
+if (r.userErrors?.length) { console.error('rollback errors:', JSON.stringify(r.userErrors)); process.exit(1); }
+const after = (await query(`{themes(first:30){nodes{id name role}}}`)).themes.nodes.find(t => t.role === 'MAIN');
+console.log('\n✅ ROLLED BACK. LIVE theme is now:', after ? `${after.name} ${after.id}` : '(?)');

← 7373d39 Fix ledger undo_cmd to real rollback script (publish-rollbac  ·  back to Shopify Sample Shipping  ·  install-theme-engine: read themeDuplicate.newTheme (payload 8432862 →