← back to Designer Wallcoverings
onboarding/sangetsu-lilycolor/scripts/gate-readiness.py
144 lines
#!/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 re
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)
if not t:
return False
if BANNED in t.lower(): # "wallpaper" is banned ANYWHERE in the title
return False
# Error tokens (unknown/404/not found/…) are only meaningful in the PATTERN-NAME
# segment (before the first pipe), AND only as WHOLE words — a substring check
# false-matched "404" inside SKU codes like LL6404 / LV2404 / LW1404 (the
# MFR-SKU-fallback titles are legitimately just the SKU code).
pattern_seg = re.split(r"[||]", t)[0].strip().lower()
for tok in BAD_TITLE_TOKENS:
if re.search(r"(?<![0-9a-z])" + re.escape(tok) + r"(?![0-9a-z])", pattern_seg):
return False
return True
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())