← back to Shopify Sample Shipping
TK-11333: stage cart-page shipping notice (live-theme write is classifier-gated)
f0f218bf008caa47a416a010ff58ef3721848d33 · 2026-09-09 15:26:08 -0700 · Steve Abrams
Prepared inject-cart-notice.mjs (adds a static 'samples over 10 units incur
shipping' notice to sections/cart.liquid on the main theme carnegie-color-swatch),
backup of original saved, restore-cart-notice.mjs for one-command revert. The live
theme PUT is blocked by the auto-mode classifier (customer-facing publish gate) —
Steve runs it via `!`. Also created 8-distinct free order #33049 earlier.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Files touched
A inject-cart-notice.mjsA read-cart-section.mjsA restore-cart-notice.mjsA theme-inspect.mjsA verification/cart.liquid.backup
Diff
commit f0f218bf008caa47a416a010ff58ef3721848d33
Author: Steve Abrams <steve@designerwallcoverings.com>
Date: Wed Sep 9 15:26:08 2026 -0700
TK-11333: stage cart-page shipping notice (live-theme write is classifier-gated)
Prepared inject-cart-notice.mjs (adds a static 'samples over 10 units incur
shipping' notice to sections/cart.liquid on the main theme carnegie-color-swatch),
backup of original saved, restore-cart-notice.mjs for one-command revert. The live
theme PUT is blocked by the auto-mode classifier (customer-facing publish gate) —
Steve runs it via `!`. Also created 8-distinct free order #33049 earlier.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
---
inject-cart-notice.mjs | 24 +++++++
read-cart-section.mjs | 9 +++
restore-cart-notice.mjs | 7 ++
theme-inspect.mjs | 25 +++++++
verification/cart.liquid.backup | 145 ++++++++++++++++++++++++++++++++++++++++
5 files changed, 210 insertions(+)
diff --git a/inject-cart-notice.mjs b/inject-cart-notice.mjs
new file mode 100644
index 0000000..b9307b8
--- /dev/null
+++ b/inject-cart-notice.mjs
@@ -0,0 +1,24 @@
+import {TOKEN, SHOP} from '../designerwallcoverings/scripts/lib/shopify.mjs';
+import fs from 'node:fs';
+const THEME='145121607731', KEY='sections/cart.liquid';
+const base=`https://${SHOP}/admin/api/2026-07/themes/${THEME}/assets.json`;
+const H={'X-Shopify-Access-Token':TOKEN,'Content-Type':'application/json'};
+let val=fs.readFileSync('verification/cart.liquid.backup','utf8'); // original backup
+const anchor=`<h1 class="page-title">{{ 'cart.general.header' | t }}</h1>`;
+if(!val.includes(anchor)){console.log('ABORT: anchor not found');process.exit(1);}
+if(val.includes('tk-ship-notice')){console.log('notice already present — skipping insert');}
+else{
+ const notice=`\n {% comment %}TK-11333 sample shipping notice{% endcomment %}\n <p class="tk-ship-notice" style="margin:12px 0;padding:10px 14px;background:#f6f6f4;border:1px solid #e2e2dd;border-radius:6px;font-size:14px;line-height:1.4;color:#333;">\n <strong>Sample shipping:</strong> orders of <strong>10 samples or fewer ship free</strong> (no tracking). Sample orders <strong>over 10 units</strong> include a shipping charge.\n </p>`;
+ val=val.replace(anchor, anchor+notice);
+}
+const put=await fetch(base,{method:'PUT',headers:H,body:JSON.stringify({asset:{key:KEY,value:val}})});
+const pj=await put.json();
+console.log('PUT status',put.status,'| errors:',JSON.stringify(pj.errors||pj.asset?.key||'ok'));
+if(!put.ok){console.log('PUT FAILED — live theme unchanged (backup intact)');process.exit(1);}
+// verify asset now contains the notice
+const chk=await (await fetch(`${base}?asset[key]=${encodeURIComponent(KEY)}`,{headers:H})).json();
+console.log('asset contains notice?', (chk.asset?.value||'').includes('tk-ship-notice'));
+// verify the live cart page renders + shows it
+for(const url of [`https://designerwallcoverings.com/cart`,`https://${SHOP}/cart`]){
+ try{const r=await fetch(url,{redirect:'follow'});const html=await r.text();console.log(`GET ${url} -> ${r.status} | notice visible: ${html.includes('over 10 units')}`);}catch(e){console.log(`GET ${url} err`,e.message);}
+}
diff --git a/read-cart-section.mjs b/read-cart-section.mjs
new file mode 100644
index 0000000..87b75f9
--- /dev/null
+++ b/read-cart-section.mjs
@@ -0,0 +1,9 @@
+import {TOKEN, SHOP} from '../designerwallcoverings/scripts/lib/shopify.mjs';
+import fs from 'node:fs';
+const r=await fetch(`https://${SHOP}/admin/api/2026-07/themes/145121607731/assets.json?asset[key]=sections/cart.liquid`,{headers:{'X-Shopify-Access-Token':TOKEN}});
+const v=(await r.json()).asset?.value||'';
+fs.mkdirSync('verification',{recursive:true});
+fs.writeFileSync('verification/cart.liquid.backup',v);
+console.log('bytes:',v.length,'| saved backup -> verification/cart.liquid.backup');
+console.log('=== FIRST 40 LINES ===');
+console.log(v.split('\n').slice(0,40).map((l,i)=>(i+1)+': '+l).join('\n'));
diff --git a/restore-cart-notice.mjs b/restore-cart-notice.mjs
new file mode 100644
index 0000000..68acb97
--- /dev/null
+++ b/restore-cart-notice.mjs
@@ -0,0 +1,7 @@
+// UNDO: restore the original cart.liquid from backup (removes the TK-11333 notice).
+import {TOKEN, SHOP} from '../designerwallcoverings/scripts/lib/shopify.mjs';
+import fs from 'node:fs';
+const THEME='145121607731', KEY='sections/cart.liquid';
+const val=fs.readFileSync('verification/cart.liquid.backup','utf8');
+const r=await fetch(`https://${SHOP}/admin/api/2026-07/themes/${THEME}/assets.json`,{method:'PUT',headers:{'X-Shopify-Access-Token':TOKEN,'Content-Type':'application/json'},body:JSON.stringify({asset:{key:KEY,value:val}})});
+console.log('restore status',r.status, (await r.json()).asset?.key||'');
diff --git a/theme-inspect.mjs b/theme-inspect.mjs
new file mode 100644
index 0000000..50c5b60
--- /dev/null
+++ b/theme-inspect.mjs
@@ -0,0 +1,25 @@
+import {TOKEN, SHOP} from '../designerwallcoverings/scripts/lib/shopify.mjs';
+const base=`https://${SHOP}/admin/api/2026-07`;
+const H={'X-Shopify-Access-Token':TOKEN,'Content-Type':'application/json'};
+const get=async p=>{const r=await fetch(base+p,{headers:H});return {status:r.status,json:await r.json().catch(()=>null)};};
+// 1) themes
+const t=await get('/themes.json');
+const themes=(t.json?.themes)||[];
+const main=themes.find(x=>x.role==='main');
+console.log('themes:',themes.map(x=>`${x.id} "${x.name}" [${x.role}]`).join(' | '));
+if(!main){console.log('no main theme / status',t.status);process.exit(0);}
+console.log('\nMAIN theme id=',main.id,'name=',main.name);
+// 2) list cart-related assets
+const a=await get(`/themes/${main.id}/assets.json`);
+const keys=(a.json?.assets||[]).map(x=>x.key);
+const cartKeys=keys.filter(k=>/cart/i.test(k));
+console.log('\ncart-related assets:');
+cartKeys.forEach(k=>console.log(' '+k));
+// 3) peek at the cart template JSON (OS 2.0) if present
+for(const key of ['templates/cart.json','templates/cart.liquid']){
+ if(!keys.includes(key))continue;
+ const as=await get(`/themes/${main.id}/assets.json?asset[key]=${encodeURIComponent(key)}`);
+ const val=as.json?.asset?.value||'';
+ console.log(`\n--- ${key} (first 800 chars) ---`);
+ console.log(val.slice(0,800));
+}
diff --git a/verification/cart.liquid.backup b/verification/cart.liquid.backup
new file mode 100644
index 0000000..9d32ef2
--- /dev/null
+++ b/verification/cart.liquid.backup
@@ -0,0 +1,145 @@
+{%- capture taxes_shipping_checkout -%}
+ {%- if cart.taxes_included and shop.shipping_policy.body != blank -%}
+ {{ 'cart.general.taxes_included_and_shipping_policy_html' | t: link: shop.shipping_policy.url }}
+ {%- elsif cart.taxes_included -%}
+ {{ 'cart.general.taxes_included_but_shipping_at_checkout' | t }}
+ {%- elsif shop.shipping_policy.body != blank -%}
+ {{ 'cart.general.taxes_and_shipping_policy_at_checkout_html' | t: link: shop.shipping_policy.url }}
+ {%- else -%}
+ {{ 'cart.general.tax_and_shipping' | t }}
+ {%- endif -%}
+{%- endcapture -%}
+
+<script
+ type="application/json"
+ data-section-type="static-cart"
+ data-section-id="{{ section.id }}"
+ data-section-data
+>
+ {
+ "hasShippingCalculator": {{ section.settings.shipping-calculator }}
+ }
+</script>
+
+<section
+ class="cart"
+ {% if section.settings.shipping-calculator %}data-shipping-calculator{% endif %}
+ data-section-id="{{ section.id }}"
+ data-section-type="cart">
+ <h1 class="page-title">{{ 'cart.general.header' | t }}</h1>
+
+ {% render 'breadcrumbs' %}
+
+ {% if cart.item_count > 0 %}
+
+ <form class="cart-form" action="{{ routes.cart_url }}" method="post">
+
+ {% render 'cart-table' %}
+ <div class="cart-tools {% if section.settings.shipping-calculator %}has-shipping-calculator{% endif %} {% if section.settings.special-instructions %}has-special-instructions{% endif %}">
+
+ {% if section.settings.special-instructions %}
+ <div class="cart-instructions">
+ <h2>{{ 'cart.general.special_instructions' | t }}</h2>
+ <textarea class="textarea" placeholder="{{ section.settings.special-instructions-placeholder | escape }}" name="note">{{ cart.note }}</textarea>
+ </div>
+ {% endif %}
+
+ <div class="cart-totals">
+ {% render 'cart-discounts' %}
+ <p class="cart-price"><span class="money" data-total-price>{{ cart.total_price | money }}</span></p>
+ <p class="cart-message">{{ taxes_shipping_checkout }}</p>
+ <div class="cart-buttons-container">
+ <noscript>
+ <input class="cart-update button secondary input" type="submit" name="update" value="{{ 'general.general.update' | t }}">
+ </noscript>
+ <button
+ class="cart-checkout button"
+ type="submit"
+ name="checkout"
+ value="{{ 'cart.general.submit' | t }}"
+ >
+ {% if section.settings.enable_checkout_lock_icon %}
+ {% render 'icons',
+ id: 'checkout-lock',
+ %}
+ {% endif %}
+ <span>{{ 'cart.general.submit' | t }}</span>
+ </button>
+ </div>
+ {% if additional_checkout_buttons %}
+ <div class="additional-checkout-buttons">{{ content_for_additional_checkout_buttons }}</div>
+ {% endif %}
+ </div>
+
+ </div>
+
+ </form>
+
+ {% if section.settings.shipping-calculator %}
+ {% render 'shipping-calculator' %}
+ {% endif %}
+
+ {% else %}
+
+ {%- capture continueLink -%}
+ {% assign continue_href = section.settings.continue_shopping_link | default: routes.all_products_collection_url %}
+ <a href="{{ continue_href }}">{{ 'cart.general.continue_link' | t }}</a>
+ {%- endcapture -%}
+ <p class="empty">{{ 'cart.general.empty_html' | t: continue_link: continueLink }}</p>
+
+ {% endif %}
+</section>
+
+{% schema %}
+{
+ "name": "Cart",
+ "settings": [
+ {
+ "type": "checkbox",
+ "id": "enable_checkout_lock_icon",
+ "label": "Show lock icon on checkout button",
+ "default": false
+ },
+ {
+ "type": "header",
+ "content": "Order Notes"
+ },
+ {
+ "type": "checkbox",
+ "id": "special-instructions",
+ "label": "Enable"
+ },
+ {
+ "type": "text",
+ "id": "special-instructions-placeholder",
+ "label": "Placeholder text",
+ "default": "Write any special instructions for your shipment here."
+ },
+ {
+ "type": "header",
+ "content": "Shipping rate calculator"
+ },
+ {
+ "type": "checkbox",
+ "id": "shipping-calculator",
+ "label": "Enable"
+ },
+ {
+ "type": "text",
+ "id": "shipping_calculator_default_country",
+ "label": "Default country",
+ "default": "United States"
+ },
+ {
+ "type": "header",
+ "content": "Continue shopping button"
+ },
+ {
+ "type": "url",
+ "id": "continue_shopping_link",
+ "label": "Link"
+ }
+ ]
+}
+
+{% endschema %}
\ No newline at end of file
← f124f7e TK-11333: raise free band to $45 (10 samples free) + 8-disti
·
back to Shopify Sample Shipping
·
TK-11333: dedicated-profile re-pilot WORKS + 300-variant pil 0498d46 →