[object Object]

← back to Designer Wallcoverings

gate-readiness.py: read-only Activation-Gate pre-flight over merged Lilycolor records — quantifies publish-readiness per dimension (surfaced 1580 title-less SKUs as the main non-cost blocker)

fedfa292d63c00701405288453af7ffc3cc2acf5 · 2026-07-01 14:46:06 -0700 · Steve

Files touched

Diff

commit fedfa292d63c00701405288453af7ffc3cc2acf5
Author: Steve <steve@designerwallcoverings.com>
Date:   Wed Jul 1 14:46:06 2026 -0700

    gate-readiness.py: read-only Activation-Gate pre-flight over merged Lilycolor records — quantifies publish-readiness per dimension (surfaced 1580 title-less SKUs as the main non-cost blocker)
---
 .../sangetsu-lilycolor/scripts/gate-readiness.py   | 134 +++++++++++++++++++++
 1 file changed, 134 insertions(+)

diff --git a/onboarding/sangetsu-lilycolor/scripts/gate-readiness.py b/onboarding/sangetsu-lilycolor/scripts/gate-readiness.py
new file mode 100644
index 00000000..550c5281
--- /dev/null
+++ b/onboarding/sangetsu-lilycolor/scripts/gate-readiness.py
@@ -0,0 +1,134 @@
+#!/usr/bin/env python3
+"""
+gate-readiness.py — Activation-Gate PRE-FLIGHT over the merged Lilycolor records.
+
+READ-ONLY. Runs the DW Activation-Gate checks (CLAUDE.md "Never Activate SKU
+Without Specs + Description + ALL Images" + title guards + cost) against every
+merged record and reports, per dimension, how many SKUs pass — so we know exactly
+what publish will look like the moment Steve supplies net cost, and which SKUs are
+blocked for some OTHER reason (missing title, etc.).
+
+This NEVER writes, activates, or touches dw_unified/Shopify. Pure reporting.
+
+Dimensions (per merged record):
+  width        global.width present + non-empty                 (HARD spec req)
+  image        >=1 product image (image_count>0 / preview_images)
+  title        a pattern title exists (title_ja or title_en)     (no Unknown/blank)
+  title_guard  title has no banned "Wallpaper" and no "Unknown"
+  description  a non-placeholder description (enrichment design.description)
+  cost         cost_confirmed == True                            (Steve-gated blocker)
+
+Bottom line = SKUs that clear EVERYTHING except cost (i.e. would go live the
+instant cost lands) vs SKUs blocked on a non-cost dimension that needs work.
+
+Usage: gate-readiness.py [merged_jsonl]
+  defaults to staging/lilycolor-merged-063026A.jsonl
+"""
+import json
+import os
+import sys
+
+HERE = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
+DEFAULT = os.path.join(HERE, "staging", "lilycolor-merged-063026A.jsonl")
+BANNED = "wallpaper"
+BAD_TITLE_TOKENS = ("unknown", "page not found", "404", "not found", "undefined", "null")
+
+
+def has_width(d):
+    return bool(str(d.get("width") or "").strip())
+
+
+def has_image(d):
+    return (d.get("image_count") or 0) > 0 or bool(d.get("preview_images"))
+
+
+def title_text(d):
+    return (d.get("title_en") or d.get("title_ja") or "").strip()
+
+
+def has_title(d):
+    return bool(title_text(d))
+
+
+def title_guard_ok(d):
+    t = title_text(d).lower()
+    if not t:
+        return False
+    if BANNED in t:
+        return False
+    return not any(tok in t for tok in BAD_TITLE_TOKENS)
+
+
+def has_description(d):
+    desc = (d.get("design") or {}).get("description") or ""
+    desc = desc.strip()
+    if not desc:
+        return False
+    return not any(tok in desc.lower() for tok in BAD_TITLE_TOKENS)
+
+
+def cost_ok(d):
+    return d.get("cost_confirmed") is True
+
+
+def main():
+    path = sys.argv[1] if len(sys.argv) > 1 else DEFAULT
+    total = 0
+    counts = {k: 0 for k in ("width", "image", "title", "title_guard", "description", "cost")}
+    ready_but_cost = 0          # clears everything except cost
+    fully_ready = 0             # clears everything incl. cost
+    blocked_noncost = 0         # fails at least one non-cost dimension
+    block_reasons = {"width": 0, "image": 0, "title": 0, "title_guard": 0, "description": 0}
+
+    with open(path) as f:
+        for line in f:
+            line = line.strip()
+            if not line:
+                continue
+            d = json.loads(line)
+            total += 1
+            checks = {
+                "width": has_width(d),
+                "image": has_image(d),
+                "title": has_title(d),
+                "title_guard": title_guard_ok(d),
+                "description": has_description(d),
+                "cost": cost_ok(d),
+            }
+            for k, v in checks.items():
+                if v:
+                    counts[k] += 1
+            noncost_ok = all(checks[k] for k in block_reasons)
+            if noncost_ok:
+                ready_but_cost += 1
+                if checks["cost"]:
+                    fully_ready += 1
+            else:
+                blocked_noncost += 1
+                for k in block_reasons:
+                    if not checks[k]:
+                        block_reasons[k] += 1
+
+    def pct(n):
+        return f"{100*n//total if total else 0}%"
+
+    print(f"Activation-Gate readiness — {os.path.basename(path)}  (total SKUs: {total})\n")
+    print("  per-dimension pass counts:")
+    for k in ("width", "image", "title", "title_guard", "description", "cost"):
+        print(f"    {k:12} {counts[k]:5} / {total}  ({pct(counts[k])})")
+    print()
+    print(f"  READY except cost (go-live the instant cost lands): {ready_but_cost} ({pct(ready_but_cost)})")
+    print(f"  fully ready (incl. cost):                           {fully_ready}")
+    print(f"  blocked on a NON-cost dimension:                    {blocked_noncost} ({pct(blocked_noncost)})")
+    print("    non-cost block reasons (a SKU can hit several):")
+    for k, n in sorted(block_reasons.items(), key=lambda x: -x[1]):
+        if n:
+            print(f"      {k:12} {n}")
+    print()
+    print("  NOTE: cost is universally unconfirmed (Steve-gated Activation blocker);")
+    print("  description coverage climbs as local enrichment completes.")
+    return 0
+
+
+if __name__ == "__main__":
+    sys.exit(main())

← b7c30130 auto-save: 2026-07-01T14:36:50 (6 files) — DW-Programming/Im  ·  back to Designer Wallcoverings  ·  gate-readiness.py: check error-tokens only in pattern-name s d76076d1 →