← back to Designer Wallcoverings
Coastal Mist live fix APPLIED + verified: real image, oil filter removed, Red/Needs-Image stripped; script hardened (no-eval, idempotent, token-masked)
d436f9a2764309d7764b305d3322b2af091d66dc · 2026-06-24 08:04:58 -0700 · Steve
Files touched
M pending-approval/coastal-mist-oil-filter-image-fix.mdM pending-approval/fix-coastal-mist-BA469.sh
Diff
commit d436f9a2764309d7764b305d3322b2af091d66dc
Author: Steve <steve@designerwallcoverings.com>
Date: Wed Jun 24 08:04:58 2026 -0700
Coastal Mist live fix APPLIED + verified: real image, oil filter removed, Red/Needs-Image stripped; script hardened (no-eval, idempotent, token-masked)
---
.../coastal-mist-oil-filter-image-fix.md | 12 +++-
pending-approval/fix-coastal-mist-BA469.sh | 81 +++++++++++++---------
2 files changed, 61 insertions(+), 32 deletions(-)
diff --git a/pending-approval/coastal-mist-oil-filter-image-fix.md b/pending-approval/coastal-mist-oil-filter-image-fix.md
index 8bdd5cf0..be354e92 100644
--- a/pending-approval/coastal-mist-oil-filter-image-fix.md
+++ b/pending-approval/coastal-mist-oil-filter-image-fix.md
@@ -1,9 +1,19 @@
# PENDING APPROVAL — Live Shopify fix: "Portofino Bamboo - Coastal Mist" wrong image + bad color tag
-**Status:** awaiting Steve sign-off (customer-facing prod write — NOT executed by agent)
+**Status:** ✅ DONE — applied live 2026-06-24 (Steve-authorized; "use old tokens" / "run the live fix")
**Drafted:** 2026-06-24
**Decision basis:** DTD verdict A (3/3 unanimous — Claude/Codex/Qwen)
+## ✅ APPLIED 2026-06-24 — verified live
+- New image `37570967371827` (960×1200, alt = "Portofino Bamboo - Coastal Mist Wallcovering | Phillipe Romano") at position 1.
+- Oil-filter image `37091234611251` deleted.
+- Tags `Red` + `Needs-Image` stripped; `color:Smoke` + `display_variant` + all else kept.
+- Confirmed on the public storefront (designerwallcoverings.com): single correct image, clean tags.
+- Canonical CDN image now: `https://cdn.shopify.com/s/files/1/0015/4117/7456/files/portofino-bamboo-coastal-mist.jpg?v=1782313353`
+- philipperomano local data repointed to that canonical CDN URL.
+- ⚠ Token rotation declined by Steve ("use old tokens") — `shpat_…75b9` was printed once to a local terminal; not externally transmitted. Script now masks it.
+- ℹ️ dw_unified `shopify_products` mirror still holds the old image/tags; it self-heals from Shopify on the daily sync (sync-shopify-mirror.js).
+
## UPDATE 2026-06-24 — genuine image FOUND + ready-to-run script
- **Real SKU:** Washington Wallcoverings **BA469** "Smoke Gray Sisal" (Best of Asia IV). "Phillipe Romano Portofino Bamboo – Coastal Mist" is the DW private-label rebrand. `color:Smoke` was correct all along.
- **Root cause (confirmed):** importer fuzzy-matched real SKU **BA469 → B7469** (a Baldwin oil filter) — a 2-char string collision.
diff --git a/pending-approval/fix-coastal-mist-BA469.sh b/pending-approval/fix-coastal-mist-BA469.sh
index a9cb6986..ae8d2922 100755
--- a/pending-approval/fix-coastal-mist-BA469.sh
+++ b/pending-approval/fix-coastal-mist-BA469.sh
@@ -6,13 +6,14 @@
# (designerwallcoverings.com), which currently shows a Baldwin B7469 automotive
# oil filter. Root cause: importer fuzzy-matched real SKU BA469 -> file B7469.
#
-# Real product : Washington Wallcoverings BA469 "Smoke Gray Sisal" (Best of Asia IV)
-# Verified image: https://www.interiordecorating.com/images/cat/wall/washington/BA469.jpg
-# (confirmed grey sisal grasscloth; matches color:Smoke tag)
-# ⚠ low-res 200x250 — see HI-RES note at bottom before going live.
+# Real product : Washington Wallcoverings BA469 "Smoke Gray Sisal" (Best of Asia IV).
+# Image : coastal-mist-hires.jpg (full-res 960x1200), uploaded as a base64
+# attachment so Shopify rehosts on the DW CDN.
+# Alt text : OUR info only ("Portofino Bamboo - Coastal Mist Wallcovering |
+# Phillipe Romano") — source vendor never exposed.
#
-# Three steps: (1) add the real BA469 image, (2) delete the oil-filter image,
-# (3) strip the wrong `Red` tag and (image now present) `Needs-Image`.
+# Three steps: (1) add the real image, (2) delete the oil-filter image,
+# (3) strip the wrong `Red` tag and `Needs-Image` (keep color:Smoke + all else).
#
# To execute for real: CONFIRM=1 bash fix-coastal-mist-BA469.sh
# ─────────────────────────────────────────────────────────────────────────────
@@ -22,9 +23,8 @@ SHOP="designer-laboratory-sandbox.myshopify.com"
API="2024-10"
PRODUCT_ID="7808267157555"
OIL_FILTER_IMAGE_ID="37091234611251"
-NEW_IMAGE_FILE="$(dirname "$0")/coastal-mist-hires.jpg" # full-res 960x1200 (in this dir)
-NEW_IMAGE_ALT="Portofino Bamboo - Coastal Mist Wallcovering | Phillipe Romano" # OUR info only — no source-vendor leak
-TAGS_TO_REMOVE=("Red" "Needs-Image") # keep color:Smoke and everything else
+NEW_IMAGE_FILE="$(dirname "$0")/coastal-mist-hires.jpg"
+NEW_IMAGE_ALT="Portofino Bamboo - Coastal Mist Wallcovering | Phillipe Romano"
TOKEN="${SHOPIFY_ADMIN_TOKEN:-}"
if [ -z "$TOKEN" ]; then
@@ -33,12 +33,12 @@ fi
[ -z "$TOKEN" ] && { echo "ERROR: SHOPIFY_ADMIN_TOKEN not in env or secrets-manager/.env"; exit 1; }
BASE="https://$SHOP/admin/api/$API"
-H_TOK=(-H "X-Shopify-Access-Token: $TOKEN")
+H_TOK=(-H "X-Shopify-Access-Token: $TOKEN") # always passed as a quoted array — never eval'd
H_JSON=(-H "Content-Type: application/json")
DRY=1; [ "${CONFIRM:-0}" = "1" ] && DRY=0
say(){ printf '\n\033[1m%s\033[0m\n' "$*"; }
-run(){ if [ "$DRY" = "1" ]; then echo " [dry-run] ${*//$TOKEN/shpat_****${TOKEN: -4}}"; else eval "$*"; fi; } # mask token in dry echoes
+DESC(){ echo " [dry-run] would: $*"; } # human description only — never prints the token
say "Target: $PRODUCT_ID on $SHOP (api $API) | mode: $([ "$DRY" = 1 ] && echo DRY-RUN || echo LIVE)"
@@ -48,7 +48,10 @@ curl -s "${H_TOK[@]}" "$BASE/products/$PRODUCT_ID.json" \
say "1) Add full-res image as base64 attachment (Shopify hosts it on its CDN)"
[ -f "$NEW_IMAGE_FILE" ] || { echo "ERROR: image file not found: $NEW_IMAGE_FILE"; exit 1; }
-PAYLOAD="$(mktemp /tmp/cm-img.XXXXXX)" # BSD mktemp: X's must be at the end (no suffix)
+# idempotency: skip if our image is already attached (so a re-run can't duplicate it)
+ALREADY="$(curl -s "${H_TOK[@]}" "$BASE/products/$PRODUCT_ID.json" | python3 -c 'import json,sys;print("yes" if any("portofino-bamboo-coastal-mist" in (i.get("src") or "") for i in json.load(sys.stdin)["product"]["images"]) else "no")')"
+if [ "$ALREADY" = "yes" ]; then echo " already attached — skipping add"; SKIP1=1; else SKIP1=0; fi
+PAYLOAD="$(mktemp /tmp/cm-img.XXXXXX)"
python3 - "$NEW_IMAGE_FILE" "$NEW_IMAGE_ALT" > "$PAYLOAD" <<'PY'
import base64, json, sys
path, alt = sys.argv[1], sys.argv[2]
@@ -57,15 +60,29 @@ with open(path, "rb") as f:
print(json.dumps({"image": {"attachment": b64, "filename": "portofino-bamboo-coastal-mist.jpg",
"position": 1, "alt": alt}}))
PY
-run "curl -s -X POST \"$BASE/products/$PRODUCT_ID/images.json\" ${H_TOK[*]} ${H_JSON[*]} \
- --data-binary @\"$PAYLOAD\" \
- | python3 -c 'import json,sys;i=json.load(sys.stdin).get(\"image\",{});print(\" added image id\",i.get(\"id\"),i.get(\"width\"),\"x\",i.get(\"height\"),i.get(\"src\"))'"
-[ "$DRY" = "0" ] && rm -f "$PAYLOAD" || echo " [dry-run] payload built at $PAYLOAD (alt: $NEW_IMAGE_ALT)"
+if [ "$SKIP1" = 1 ]; then
+ rm -f "$PAYLOAD"
+elif [ "$DRY" = 1 ]; then
+ DESC "POST $BASE/products/$PRODUCT_ID/images.json (base64 attachment, alt: $NEW_IMAGE_ALT)"
+ echo " [dry-run] payload built at $PAYLOAD ($(wc -c < "$PAYLOAD") bytes)"
+ rm -f "$PAYLOAD"
+else
+ RESP="$(curl -s -w $'\n%{http_code}' -X POST "$BASE/products/$PRODUCT_ID/images.json" "${H_TOK[@]}" "${H_JSON[@]}" --data-binary @"$PAYLOAD")"
+ CODE="${RESP##*$'\n'}"; BODY="${RESP%$'\n'*}"
+ rm -f "$PAYLOAD"
+ echo " POST HTTP $CODE"
+ case "$CODE" in 200|201) echo "$BODY" | python3 -c 'import json,sys;i=json.load(sys.stdin).get("image",{});print(" added image id",i.get("id"),i.get("width"),"x",i.get("height"),i.get("src"))';;
+ *) echo " ERROR body: $BODY"; exit 1;; esac
+fi
say "2) Delete the oil-filter image ($OIL_FILTER_IMAGE_ID)"
-run "curl -s -X DELETE \"$BASE/products/$PRODUCT_ID/images/$OIL_FILTER_IMAGE_ID.json\" ${H_TOK[*]} -o /dev/null -w ' delete HTTP %{http_code}\n'"
+if [ "$DRY" = 1 ]; then
+ DESC "DELETE $BASE/products/$PRODUCT_ID/images/$OIL_FILTER_IMAGE_ID.json"
+else
+ curl -s -X DELETE "$BASE/products/$PRODUCT_ID/images/$OIL_FILTER_IMAGE_ID.json" "${H_TOK[@]}" -o /dev/null -w ' delete HTTP %{http_code}\n'
+fi
-say "3) Strip wrong tags (${TAGS_TO_REMOVE[*]}) — reads LIVE tags first, removes only these"
+say "3) Strip wrong tags (Red, Needs-Image) — reads LIVE tags first, removes only these"
CUR_TAGS="$(curl -s "${H_TOK[@]}" "$BASE/products/$PRODUCT_ID.json" | python3 -c 'import json,sys;print(json.load(sys.stdin)["product"]["tags"])')"
NEW_TAGS="$(python3 - "$CUR_TAGS" <<'PY'
import sys
@@ -76,21 +93,23 @@ PY
)"
echo " before: $CUR_TAGS"
echo " after : $NEW_TAGS"
-run "curl -s -X PUT \"$BASE/products/$PRODUCT_ID.json\" ${H_TOK[*]} ${H_JSON[*]} \
- -d \"\$(python3 -c 'import json,sys;print(json.dumps({\"product\":{\"id\":int(\"$PRODUCT_ID\"),\"tags\":sys.argv[1]}}))' \"\$NEW_TAGS\")\" \
- -o /dev/null -w ' tag update HTTP %{http_code}\n'"
+if [ "$DRY" = 1 ]; then
+ DESC "PUT $BASE/products/$PRODUCT_ID.json tags -> (the 'after' line above)"
+else
+ TAGJSON="$(python3 -c 'import json,sys;print(json.dumps({"product":{"id":int(sys.argv[1]),"tags":sys.argv[2]}}))' "$PRODUCT_ID" "$NEW_TAGS")"
+ curl -s -X PUT "$BASE/products/$PRODUCT_ID.json" "${H_TOK[@]}" "${H_JSON[@]}" -d "$TAGJSON" -o /dev/null -w ' tag update HTTP %{http_code}\n'
+fi
say "4) Verify"
-[ "$DRY" = "0" ] && curl -s "${H_TOK[@]}" "$BASE/products/$PRODUCT_ID.json" \
- | python3 -c 'import json,sys;p=json.load(sys.stdin)["product"];print(" images:",[i["src"].split("/")[-1] for i in p["images"]]);print(" tags :",p["tags"])' \
- || echo " (dry-run: skipped verify)"
+if [ "$DRY" = 0 ]; then
+ curl -s "${H_TOK[@]}" "$BASE/products/$PRODUCT_ID.json" \
+ | python3 -c 'import json,sys;p=json.load(sys.stdin)["product"];print(" images:",[i["src"].split("/")[-1] for i in p["images"]]);print(" tags :",p["tags"])'
+else
+ echo " (dry-run: skipped verify)"
+fi
say "Done ($([ "$DRY" = 1 ] && echo 'DRY-RUN — re-run with CONFIRM=1 to apply' || echo 'LIVE applied'))."
# ── Notes ────────────────────────────────────────────────────────────────────
-# • Image: full-res 960x1200 (coastal-mist-hires.jpg, sourced from the real
-# product BA469). Uploaded as a base64 attachment so Shopify rehosts it on the
-# DW CDN — no hotlink/referer dependency on the source site.
-# • Alt text is OUR info only ("Portofino Bamboo - Coastal Mist Wallcovering |
-# Phillipe Romano") — the source vendor (Washington Wallcoverings/BA469) is
-# never exposed customer-facing.
-# • To keep Needs-Image set for now, remove "Needs-Image" from TAGS_TO_REMOVE.
+# • Curl always receives the auth header via the "${H_TOK[@]}" array (never eval),
+# so the token is correctly quoted and never printed.
+# • To keep Needs-Image set for now, edit the drop set in step 3 to {"red"}.
← e0e417a2 fix script: mask admin token in dry-run echoes + BSD-safe mk
·
back to Designer Wallcoverings
·
Boost infinite-scroll override: force boostSDAppConfig pagin fafab4ef →