← back to Wallco Ai
refresh_designs_snapshot: catastrophe guard against sparse-PG clobber
e91c2ad56adf8d24c9d6a8306479f44235ba3160 · 2026-05-26 10:39:56 -0700 · Steve Abrams
Root cause of the 2026-05-26 wallco.ai catalog collapse to 157 served:
server.js spawns this script on a recolor-bake (line ~19963). On PROD that
runs against prod's PG (only ~350 rows; the real 47k catalog lives in Mac2's
PG and ships via designs.json), regenerating designs.json down to ~285 items.
The server's 30s setInterval(load(true)) then reloaded the gutted file.
Guard: refuse to overwrite when the new snapshot is <50% of the existing one
(and existing >= 1000). Writes a designs.json.rejected sidecar for debugging.
Override with WALLCO_FORCE_SNAPSHOT=1. Protects regardless of where regen runs.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Files touched
M scripts/refresh_designs_snapshot.py
Diff
commit e91c2ad56adf8d24c9d6a8306479f44235ba3160
Author: Steve Abrams <steve@designerwallcoverings.com>
Date: Tue May 26 10:39:56 2026 -0700
refresh_designs_snapshot: catastrophe guard against sparse-PG clobber
Root cause of the 2026-05-26 wallco.ai catalog collapse to 157 served:
server.js spawns this script on a recolor-bake (line ~19963). On PROD that
runs against prod's PG (only ~350 rows; the real 47k catalog lives in Mac2's
PG and ships via designs.json), regenerating designs.json down to ~285 items.
The server's 30s setInterval(load(true)) then reloaded the gutted file.
Guard: refuse to overwrite when the new snapshot is <50% of the existing one
(and existing >= 1000). Writes a designs.json.rejected sidecar for debugging.
Override with WALLCO_FORCE_SNAPSHOT=1. Protects regardless of where regen runs.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
---
scripts/refresh_designs_snapshot.py | 26 ++++++++++++++++++++++++++
1 file changed, 26 insertions(+)
diff --git a/scripts/refresh_designs_snapshot.py b/scripts/refresh_designs_snapshot.py
index 97eb287..7fe56d5 100644
--- a/scripts/refresh_designs_snapshot.py
+++ b/scripts/refresh_designs_snapshot.py
@@ -146,6 +146,32 @@ for r in rows:
"unpublish_reason": extract_edges_tag(r.get('notes'))
})
+# ── CATASTROPHE GUARD (2026-05-26) ────────────────────────────────────────
+# Never let a regen overwrite the catalog with a drastically smaller snapshot.
+# The real ~47k catalog lives in Mac2's PG and is shipped to prod via this
+# designs.json. PROD's own PG holds only ~350 rows, so any prod-side regen
+# trigger (e.g. the recolor-bake `spawn refresh_designs_snapshot.py` in
+# server.js) regenerates from the sparse prod PG and collapses designs.json to
+# ~285 items — which dropped the LIVE catalog to 157 served on 2026-05-26.
+# If an existing snapshot is materially larger than what we're about to write,
+# refuse and keep the good file. Override with WALLCO_FORCE_SNAPSHOT=1.
+if OUT.exists() and os.environ.get('WALLCO_FORCE_SNAPSHOT') != '1':
+ try:
+ _existing = json.loads(OUT.read_text())
+ _existing_n = len(_existing) if isinstance(_existing, list) else 0
+ except Exception:
+ _existing_n = 0
+ if _existing_n >= 1000 and len(out) < _existing_n * 0.5:
+ print(f"ABORT: refusing to shrink designs.json {_existing_n} → {len(out)} "
+ f"(<50% of existing). Almost certainly a sparse-PG regen "
+ f"(this PG has {len(rows)} rows). Keeping the existing snapshot. "
+ f"Set WALLCO_FORCE_SNAPSHOT=1 to override intentionally.")
+ try:
+ (OUT.parent / 'designs.json.rejected').write_text(json.dumps(out))
+ except Exception:
+ pass
+ raise SystemExit(0)
+
OUT.write_text(json.dumps(out, indent=2))
print(f"Wrote {len(out)} designs → {OUT}")
print(f" Categories: {dict((c, sum(1 for x in out if x['category']==c)) for c in sorted({x['category'] for x in out}))}")
← 39c5e38 geometric pilot: record ghost-detector false-fail verificati
·
back to Wallco Ai
·
snapshot: prefix-match generators to end recurring allowlist 4c2836c →