← back to Designerwallcoverings
sanderson daily-batch: add post-golive anomaly guard (DTD verdict C) — halts via kill-switch on foreign-image/misprice/over-cap
f4eb073446b8036b4606b1e08cea0de401862ed1 · 2026-09-01 17:22:59 -0700 · Steve Abrams
Files touched
M scripts/sanderson-onboard/daily-batch.shA scripts/sanderson-onboard/guard.mjs
Diff
commit f4eb073446b8036b4606b1e08cea0de401862ed1
Author: Steve Abrams <steve@designerwallcoverings.com>
Date: Tue Sep 1 17:22:59 2026 -0700
sanderson daily-batch: add post-golive anomaly guard (DTD verdict C) — halts via kill-switch on foreign-image/misprice/over-cap
---
scripts/sanderson-onboard/daily-batch.sh | 3 +++
scripts/sanderson-onboard/guard.mjs | 37 ++++++++++++++++++++++++++++++++
2 files changed, 40 insertions(+)
diff --git a/scripts/sanderson-onboard/daily-batch.sh b/scripts/sanderson-onboard/daily-batch.sh
index 87a48a8..54eadd2 100755
--- a/scripts/sanderson-onboard/daily-batch.sh
+++ b/scripts/sanderson-onboard/daily-batch.sh
@@ -10,6 +10,9 @@ cd "$(dirname "$0")"
bash run.sh create --payloads=payloads-imgfixed.jsonl --apply
# 2. GOLIVE every CLEAR+valid draft not yet live (idempotent; settlement + 5-field gated)
bash run.sh golive --apply
+# 2b. GUARD (DTD verdict C): halt via kill-switch if anything published looks wrong
+"$NODE" guard.mjs 2>&1 || node guard.mjs 2>&1 || true
+
# 3. self-terminate: when created >= 506 AND golive-done >= CLEAR, bootout the job
CREATED=$(wc -l < out/created.jsonl | tr -d ' ')
CLEAR=$(grep -c '"settlement_verdict":"CLEAR"' out/created.jsonl)
diff --git a/scripts/sanderson-onboard/guard.mjs b/scripts/sanderson-onboard/guard.mjs
new file mode 100644
index 0000000..77dbe5b
--- /dev/null
+++ b/scripts/sanderson-onboard/guard.mjs
@@ -0,0 +1,37 @@
+// Post-golive anomaly guard (DTD verdict C, 2026-09-01): after each nightly batch,
+// verify the products published this run are sane; HALT the whole cron (kill-switch) on anomaly.
+import fs from 'node:fs';
+import { execSync } from 'node:child_process';
+const TOK=execSync("grep -E '^SHOPIFY_ADMIN_TOKEN=' ~/Projects/secrets-manager/.env|head -1|cut -d= -f2-|tr -d '\"'\\''' ").toString().trim();
+const API='https://designer-laboratory-sandbox.myshopify.com/admin/api/2024-10';
+const H={'X-Shopify-Access-Token':TOK};
+const pref=s=>{const m=(s||'').match(/([A-Z]{2,6})\d{4,7}/);return m?m[1]:'?'};
+const CAP=160; // per-night publish cap (~1 cap's worth of products)
+// new golive-done entries since last guard run
+const done=fs.readFileSync('out/golive-done.jsonl','utf8').trim().split('\n').filter(Boolean);
+const seenFile='out/.guard-seen'; const seen=fs.existsSync(seenFile)?+fs.readFileSync(seenFile,'utf8'):0;
+const fresh=done.slice(seen);
+fs.writeFileSync(seenFile,String(done.length));
+if(!fresh.length){console.log('guard: 0 new publishes, OK');process.exit(0);}
+// retail band by mfr
+const retail={};execSync('psql -h /tmp -d dw_unified -tAc "select mfr_sku,retail_usd from sanderson_catalog where retail_usd>0"',{maxBuffer:1e8}).toString().trim().split('\n').forEach(l=>{const[m,r]=l.split('|');retail[m]=+r;});
+let anomalies=[];
+if(fresh.length>CAP) anomalies.push(`published ${fresh.length} > per-night cap ${CAP}`);
+for(const line of fresh){let r;try{r=JSON.parse(line)}catch(e){continue}
+ const pid=(r.product_id||r.id||'').toString().replace(/\D/g,'');if(!pid)continue;
+ const d=await(await fetch(`${API}/products/${pid}.json?fields=title,image,images,variants`,{headers:H})).json();const p=d.product;if(!p)continue;
+ const hp=pref(p.image?.src);
+ if((p.images||[]).some(im=>pref(im.src)!==hp)) anomalies.push(`${p.title}: foreign-series image`);
+ const mfMeta=await(await fetch(`${API}/products/${pid}/metafields.json`,{headers:H})).json();
+ const mfr=(mfMeta.metafields||[]).find(m=>m.key==='manufacturer_sku')?.value; const roll=p.variants.find(v=>!/sample/i.test(v.title));
+ if(mfr&&retail[mfr]&&roll&&Math.abs(+roll.price-retail[mfr])>1) anomalies.push(`${p.title}: price $${roll.price} != band $${retail[mfr]}`);
+ if(anomalies.length>=5)break;
+}
+if(anomalies.length){
+ fs.writeFileSync(`${process.env.HOME}/.dw-fixer-stop`,`sanderson guard halt ${new Date().toISOString()}\n`);
+ fs.appendFileSync('out/guard-alerts.jsonl',JSON.stringify({ts:new Date().toISOString(),halted:true,anomalies})+'\n');
+ try{execSync(`curl -s -X POST http://127.0.0.1:3333/api/wins -H 'Content-Type: application/json' -d '${JSON.stringify({project:'sanderson-onboard',title:'⚠️ Sanderson auto-publish HALTED by guard',summary:anomalies.slice(0,3).join('; ')})}' -o /dev/null`)}catch(e){}
+ console.error('GUARD HALT — anomalies:',anomalies.join(' | '));
+ process.exit(2);
+}
+console.log(`guard: ${fresh.length} new publishes all sane (single-series, in price band, under cap ${CAP}). OK`);
← 9397fb1 auto-data-snapshot: 2026-09-01T17:22:42 (11 data files) — sc
·
back to Designerwallcoverings
·
TK-11070: hero-fix tool — re-upload correct per-colorway her 0d054c4 →