← back to Elitis Price 2026
WS4 cleanup: retitle VP-963->Osorno, archive Raffia/Frivole SKU-collision dup (live)
b42aea3f6e4d3c290e4412e97fb1aa410e78a8bc · 2026-07-11 08:18:13 -0700 · Steve Abrams
Files touched
A exec/ledger/ws4_cleanup.csvA exec/ws4_cleanup.py
Diff
commit b42aea3f6e4d3c290e4412e97fb1aa410e78a8bc
Author: Steve Abrams <steve@designerwallcoverings.com>
Date: Sat Jul 11 08:18:13 2026 -0700
WS4 cleanup: retitle VP-963->Osorno, archive Raffia/Frivole SKU-collision dup (live)
---
exec/ledger/ws4_cleanup.csv | 4 +++
exec/ws4_cleanup.py | 72 +++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 76 insertions(+)
diff --git a/exec/ledger/ws4_cleanup.csv b/exec/ledger/ws4_cleanup.csv
new file mode 100644
index 0000000..98e02b2
--- /dev/null
+++ b/exec/ledger/ws4_cleanup.csv
@@ -0,0 +1,4 @@
+ts,action,product_id,before,after,status
+2026-07-11T08:17:32,retitle,7787408261171,Vp 963 01 Is Offered up in Broad Flat Areas As If Made From Pure Pigments. | Elitis,Osorno 01 | Elitis,OK
+2026-07-11T08:17:34,retitle,7789388660787,Vp 963 03 Is Offered up in Broad Flat Areas As If Made From Pure Pigments. | Elitis,Osorno 03 | Elitis,OK
+2026-07-11T08:17:35,archive,7787007475763,Raffia Ii Vp 1048 02 | Elitis,ARCHIVED,OK
diff --git a/exec/ws4_cleanup.py b/exec/ws4_cleanup.py
new file mode 100644
index 0000000..9dec3a4
--- /dev/null
+++ b/exec/ws4_cleanup.py
@@ -0,0 +1,72 @@
+#!/usr/bin/env python3
+"""WS4 - post-reconciliation cleanup of 3 pre-existing Elitis defects (live).
+1) Retitle scraped-sentence garbage titles on VP-963-01/-03 to real pattern 'Osorno'.
+2) Archive the mongrel 'Raffia Ii Vp 1048 02' product that dodged the WS2 VP-1048
+ archive and collides on SKU DWEL-142747 with the legit 'Frivole' VP-1036-44.
+Idempotent (checks current state first). $0 (Shopify Admin API is free).
+Steve-approved via AskUserQuestion 2026-07-11 (options A + B)."""
+import sys, os, csv, datetime
+sys.path.insert(0, "lib")
+from shopify import gql
+
+LEDGER = os.path.join("ledger", "ws4_cleanup.csv")
+M_UPD = """mutation($input:ProductInput!){ productUpdate(input:$input){
+ product{ id legacyResourceId title status handle } userErrors{ field message } } }"""
+
+RETITLE = [
+ (7787408261171, "Osorno 01 | Elitis"), # was: Vp 963 01 Is Offered up...
+ (7789388660787, "Osorno 03 | Elitis"), # was: Vp 963 03 Is Offered up...
+]
+ARCHIVE = [
+ (7787007475763, "Raffia Ii Vp 1048 02 | Elitis"), # mongrel dup -> ARCHIVED
+]
+
+
+def gid(pid):
+ return f"gid://shopify/Product/{pid}"
+
+
+def log(action, pid, before, after, status):
+ new = not (os.path.exists(LEDGER) and os.path.getsize(LEDGER) > 0)
+ with open(LEDGER, "a", newline="") as f:
+ w = csv.writer(f)
+ if new:
+ w.writerow(["ts", "action", "product_id", "before", "after", "status"])
+ w.writerow([datetime.datetime.now().isoformat(timespec="seconds"),
+ action, pid, before, after, status])
+
+
+def run():
+ for pid, newtitle in RETITLE:
+ cur = gql("query($id:ID!){ product(id:$id){ title } }", {"id": gid(pid)})["product"]
+ if cur["title"] == newtitle:
+ print(f" [skip] {pid} already '{newtitle}'")
+ log("retitle", pid, cur["title"], newtitle, "SKIP")
+ continue
+ r = gql(M_UPD, {"input": {"id": gid(pid), "title": newtitle}})
+ ue = r["productUpdate"]["userErrors"]
+ if ue:
+ print(f" [ERR] {pid}: {ue}")
+ log("retitle", pid, cur["title"], newtitle, "ERR:" + str(ue))
+ continue
+ print(f" [ok] retitle {pid}: {cur['title'][:40]!r} -> {newtitle!r}")
+ log("retitle", pid, cur["title"], newtitle, "OK")
+ for pid, title in ARCHIVE:
+ cur = gql("query($id:ID!){ product(id:$id){ status title } }", {"id": gid(pid)})["product"]
+ if cur["status"] == "ARCHIVED":
+ print(f" [skip] {pid} already ARCHIVED")
+ log("archive", pid, title, "ARCHIVED", "SKIP")
+ continue
+ r = gql(M_UPD, {"input": {"id": gid(pid), "status": "ARCHIVED"}})
+ ue = r["productUpdate"]["userErrors"]
+ if ue:
+ print(f" [ERR] {pid}: {ue}")
+ log("archive", pid, title, "ARCHIVED", "ERR:" + str(ue))
+ continue
+ print(f" [ok] archive {pid}: {title!r} ({cur['status']} -> ARCHIVED)")
+ log("archive", pid, title, "ARCHIVED", "OK")
+
+
+if __name__ == "__main__":
+ run()
+ print("\nWS4 cleanup done.")
← 0d7dccc WS3: build 328 Elitis DRAFT colorways (exact-pattern-join, r
·
back to Elitis Price 2026
·
WS3b: Bucket B accent/fuzzy matcher recovers 30 draft colorw 8620c28 →