← 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
A proof-designer.mjsA publish-rollback.mjs
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 →