← back to Designer Wallcoverings
hero manifest finalizer: socket-timeout hardening reuse + private-label handle sanitizer + clean .md summary
8bb35f8f99fd7ac292ed34af45749cbc26a9feb5 · 2026-06-23 13:20:56 -0700 · Steve
Files touched
A shopify/finalize-hero-manifest.py
Diff
commit 8bb35f8f99fd7ac292ed34af45749cbc26a9feb5
Author: Steve <steve@designerwallcoverings.com>
Date: Tue Jun 23 13:20:56 2026 -0700
hero manifest finalizer: socket-timeout hardening reuse + private-label handle sanitizer + clean .md summary
---
shopify/finalize-hero-manifest.py | 102 ++++++++++++++++++++++++++++++++++++++
1 file changed, 102 insertions(+)
diff --git a/shopify/finalize-hero-manifest.py b/shopify/finalize-hero-manifest.py
new file mode 100644
index 00000000..938f43db
--- /dev/null
+++ b/shopify/finalize-hero-manifest.py
@@ -0,0 +1,102 @@
+#!/usr/bin/env python3
+"""
+Post-process the collection-hero manifest:
+ 1. Sanitize private-label / true-vendor tokens out of any product-handle text
+ surfaced in the manifest (the hero IMAGE URL is a generic CDN hash and is
+ safe; only the chosen_product_handle can carry an upstream name). We keep
+ the real product id for the apply write, but mask the handle string.
+ 2. Emit a clean human-readable .md summary with full counts + flagged buckets.
+
+READ-ONLY w.r.t. Shopify. Rewrites the manifest .json + .md in place.
+"""
+import os, json, re, datetime
+
+HERE = os.path.dirname(os.path.abspath(__file__))
+MANIFEST = os.path.join(HERE, "audit", "collection-hero-manifest.json")
+MANIFEST_MD = os.path.join(HERE, "audit", "collection-hero-manifest.md")
+
+# upstream/private-label names that must NEVER be surfaced in clean output
+LEAK = re.compile(
+ r"wallquest|chesapeake|nextwall|seabrook|brewster|command54|"
+ r"saybrook|sister[-_ ]?parish|desima|carlsten|nicolette[-_ ]?mayer|"
+ r"a-?s-?creation|as[-_]creation",
+ re.I,
+)
+
+
+def mask(s):
+ if not s:
+ return s
+ return LEAK.sub("[private-label]", s)
+
+
+def main():
+ m = json.load(open(MANIFEST))
+ masked = 0
+ for r in m.get("rows", []):
+ for k in ("chosen_product_handle",):
+ v = r.get(k)
+ if v and LEAK.search(v):
+ r[k] = mask(v)
+ masked += 1
+ for f in m.get("flagged", []):
+ for k in ("title",):
+ pass # collection titles are customer-facing & already clean
+
+ m["sanitized_at"] = datetime.datetime.now().isoformat()
+ m["private_label_handles_masked"] = masked
+ json.dump(m, open(MANIFEST, "w"), indent=2)
+
+ c = m["counts"]
+ fl = m.get("flagged", [])
+ empty = [x for x in fl if x.get("member_image_count") == 0
+ or x.get("reason", "").startswith("no candidate")]
+ failv = [x for x in fl if "vision" in x.get("reason", "")]
+
+ md = []
+ md.append("# Collection Hero Image Manifest — Designer Wallcoverings")
+ md.append(f"\n_generated {m.get('generated_at')} | sanitized {m['sanitized_at']}_")
+ md.append(f"_store {m['store']} · API {m['api']} · vision {m['model']}_\n")
+ md.append("## Counts\n")
+ md.append(f"| bucket | n |")
+ md.append(f"|---|---|")
+ md.append(f"| **RESOLVED** (room-setting hero found inside collection, vision-confirmed → write candidate) | **{c['resolved']}** |")
+ md.append(f"| **ALREADY-GOOD** (existing collection.image already a room-setting → kept, no change) | **{c['already_good']}** |")
+ md.append(f"| **FLAGGED** (no room-setting image inside collection) | **{c['flagged']}** |")
+ md.append(f"| ↳ empty collection (0 member products) | {len(empty)} |")
+ md.append(f"| ↳ has products but only flat swatches (no interior shot) | {len(failv)} |")
+ md.append(f"\n**Vision spend:** ${m['vision']['cost_usd']:.4f} over {m['vision']['calls']} Gemini calls.")
+ md.append(f"**Private-label handles masked in this manifest:** {masked}.\n")
+
+ md.append("## FLAGGED — need a generated render or a Steve decision\n")
+ md.append("These collections have NO room-setting image sourced from inside them. "
+ "Per the standing rule we did NOT fill them with a swatch or an outside image. "
+ "Resolution path: generate a render (room-setting-generator / "
+ "commercial-room-setting-generator) for a member product, OR (for empty "
+ "collections) assign products / decide to leave on the gradient fallback.\n")
+ md.append("### Empty collections (0 products — assign products or generate)\n")
+ for x in sorted(empty, key=lambda r: r["handle"]):
+ md.append(f"- `/{x['handle']}` — {x['title']} [{x['kind']}]")
+ md.append("\n### Has products but only swatches (need a room render)\n")
+ for x in sorted(failv, key=lambda r: r["handle"]):
+ md.append(f"- `/{x['handle']}` — {x['title']} [{x['kind']}]")
+
+ md.append("\n## RESOLVED — write candidates (first 60)\n")
+ md.append("| collection | kind | source product (id) | hero image |")
+ md.append("|---|---|---|---|")
+ for r in m["rows"][:60]:
+ img = r["chosen_image_src"].split("/")[-1].split("?")[0]
+ md.append(f"| `/{r['handle']}` | {r['kind']} | {r.get('chosen_product_handle','')[:40]} "
+ f"(`{r['chosen_product_id']}`) | {img[:48]} |")
+ if len(m["rows"]) > 60:
+ md.append(f"\n_…and {len(m['rows'])-60} more resolved rows in the JSON._")
+
+ open(MANIFEST_MD, "w").write("\n".join(md) + "\n")
+ print(f"sanitized {masked} private-label handle(s)")
+ print(f"counts: {c}")
+ print(f"flagged: {len(empty)} empty + {len(failv)} swatch-only")
+ print(f"wrote {MANIFEST_MD}")
+
+
+if __name__ == "__main__":
+ main()
← 144635ea Add REGEN mode to room generator: replace our 557 rooms at c
·
back to Designer Wallcoverings
·
harden hero pipeline: global 45s socket timeout (no hung soc 4f5bbe67 →