← back to Designerwallcoverings
TK-10717: Stout monthly STAGE-ONLY refresh (stout_catalog only, no drafts/go-live); preserve old live-publish drip as .bak
ef9a78d21e8c39e13608bced48f89234f76e4b23 · 2026-08-20 15:52:43 -0700 · vp-dw-commerce
Files touched
A scripts/stout-onboard/stout-daily-drip.sh.bak-livepublish-20260820A scripts/stout-onboard/stout-monthly-stage-only.sh
Diff
commit ef9a78d21e8c39e13608bced48f89234f76e4b23
Author: vp-dw-commerce <steve@designerwallcoverings.com>
Date: Thu Aug 20 15:52:43 2026 -0700
TK-10717: Stout monthly STAGE-ONLY refresh (stout_catalog only, no drafts/go-live); preserve old live-publish drip as .bak
---
.../stout-daily-drip.sh.bak-livepublish-20260820 | 39 ++++++++++++
scripts/stout-onboard/stout-monthly-stage-only.sh | 74 ++++++++++++++++++++++
2 files changed, 113 insertions(+)
diff --git a/scripts/stout-onboard/stout-daily-drip.sh.bak-livepublish-20260820 b/scripts/stout-onboard/stout-daily-drip.sh.bak-livepublish-20260820
new file mode 100755
index 0000000..165b5f8
--- /dev/null
+++ b/scripts/stout-onboard/stout-daily-drip.sh.bak-livepublish-20260820
@@ -0,0 +1,39 @@
+#!/usr/bin/env bash
+# Stout Textiles wallcovering onboard — DEDICATED DAILY DRIP (mirrors the approved Maharam pattern).
+# Once/day: create up to DAILY_CAP sample-only DRAFTS (titles "Pattern Color | Stout Textiles",
+# custom+dwc mfr metafields, global.width, >=1 image, settlement-gated, PG-FIRST shopify_product_id
+# write-back), then go-live those drafts (activate + inventory 2026 + 12 channels, Google EXCLUDED
+# per the $4.25 GMC-leak rule) so they auto-join the stout-wallcoverings smart collection
+# (rule vendor = "Stout Textiles"). Resumable + idempotent (skips live/created SKUs).
+# 154 remaining -> ~2 days at 100/day.
+#
+# Metered by its OWN finite 'stout' variant-budget category (budget.cjs), NOT the shared 'backlog'
+# category (which is drained daily by the 5-field backfill and granted Stout 0/day — the exact bug
+# TK-00060 fixed for Maharam). The 'stout' category is gated behind the budget.cjs edit in the
+# install memo; until that lands this wrapper falls back to 'backlog' and will no-op on 0 headroom.
+set -uo pipefail
+export PATH="/usr/local/bin:/opt/homebrew/bin:/opt/homebrew/opt/postgresql@14/bin:$PATH"
+export DW_STOUT_BUDGET="${DW_STOUT_BUDGET:-stout}" # own finite category once budget.cjs adds it
+DIR="$HOME/Projects/designerwallcoverings/scripts/stout-onboard"
+LOG="/tmp/dw-stout-daily.log"
+KILL="$HOME/.dw-fixer-stop"
+DAILY_CAP="${STOUT_DAILY_CAP:-100}"
+exec >>"$LOG" 2>&1
+echo "======= stout-daily-drip $(date '+%F %T') cap=$DAILY_CAP budget=$DW_STOUT_BUDGET ======="
+[ -f "$KILL" ] && { echo "kill-switch $KILL present - skip"; exit 0; }
+cd "$DIR" || { echo "FATAL: no $DIR"; exit 1; }
+
+# refresh payloads from staging (cheap, idempotent)
+node build-payloads.mjs || { echo "build-payloads failed"; exit 1; }
+# re-run settlement gate so any new payloads are verdicted before go-live re-gates on it
+node settlement-gate.mjs || { echo "settlement-gate failed"; exit 1; }
+
+# 1) create drafts (budget-metered; PG-first write-back). Budget-exhausted = clean exit 0.
+node create-drafts.mjs --apply --limit="$DAILY_CAP" || { echo "create-drafts failed (rc=$?)"; exit 1; }
+# 2) activate today's (and any leftover) drafts -> auto-join the smart collection
+node go-live.mjs --apply --limit="$DAILY_CAP" || { echo "go-live failed (rc=$?)"; exit 1; }
+
+REMAIN=$(psql "postgresql:///dw_unified?host=/tmp" -tAc \
+ "SELECT count(*) FROM stout_catalog WHERE coalesce(shopify_product_id::text,'')='';" 2>/dev/null || echo '?')
+echo "stout rows still un-onboarded (no shopify_product_id): $REMAIN"
+echo "======= done $(date '+%F %T') ======="
diff --git a/scripts/stout-onboard/stout-monthly-stage-only.sh b/scripts/stout-onboard/stout-monthly-stage-only.sh
new file mode 100755
index 0000000..da61b1c
--- /dev/null
+++ b/scripts/stout-onboard/stout-monthly-stage-only.sh
@@ -0,0 +1,74 @@
+#!/usr/bin/env bash
+# ============================================================================
+# Stout Textiles — MONTHLY STAGE-ONLY refresh (TK-10717, 2026-08-20)
+# ----------------------------------------------------------------------------
+# REPLACES the old stout-daily-drip.sh (which was a LIVE publish: create-drafts
+# --apply + go-live --apply across 12 channels). The Stout backfill is DONE
+# (stout_catalog = 180 rows, 0 un-onboarded) so the old drip published nothing
+# and was dead weight — worse, if a re-scrape adds new SKUs it would auto-PUBLISH
+# them live with no review. This refresh instead re-scrapes Stout into the
+# stout_catalog STAGING TABLE ONLY.
+#
+# HARD INVARIANT — STAGE ONLY:
+# • re-runs the Stout new-products scraper (canonical agent-skus path, in-memory)
+# • re-scrapes authenticated prices INTO stout_catalog (scrape-prices.cjs UPDATE)
+# • NO build-payloads, NO create-drafts, NO go-live, NO --apply, NO Shopify write
+# • ONLY writes: dw_unified.stout_catalog (price_trade / price_retail / last_scraped)
+# The ONLY thing that ever touches Shopify (build-payloads → create-drafts →
+# go-live) is DELIBERATELY NOT invoked here. Do not add it.
+#
+# Cadence: monthly (Day-7 01:00 via com.steve.dw-stout-daily.plist — repointed here).
+# Cost: $0 (local scrape + local psql). Uses Stout trade-portal login only.
+# ============================================================================
+set -uo pipefail
+export PATH="/usr/local/bin:/opt/homebrew/bin:/opt/homebrew/opt/postgresql@14/bin:$PATH"
+
+ENGINE="$HOME/Projects/Designer-Wallcoverings/DW-Programming/ImportNewSkufromURL"
+STOUT_DIR="$HOME/Projects/designerwallcoverings/scripts/stout-onboard"
+PGURL="postgresql:///dw_unified?host=/tmp"
+LOG="/tmp/dw-stout-monthly-stage.log"
+KILL="$HOME/.dw-fixer-stop"
+
+exec >>"$LOG" 2>&1
+echo "======= stout-monthly-stage-only $(date '+%F %T') (STAGE-ONLY — no Shopify) ======="
+[ -f "$KILL" ] && { echo "kill-switch $KILL present — skip"; exit 0; }
+
+BEFORE=$(psql "$PGURL" -tAc "SELECT count(*) FROM stout_catalog;" 2>/dev/null || echo '?')
+echo "stout_catalog rows before: $BEFORE"
+
+# 1) Re-scrape the Stout new-products catalog via the CANONICAL agent-skus path
+# (verify-one.ts → executeVendorSkill). This returns products IN MEMORY only —
+# it performs NO Shopify write and NO staging write of its own. Its value here
+# is a fresh liveness/coverage read of the Stout catalog that lands in the log
+# (and can be extended to upsert new mfr_skus into stout_catalog later — still
+# staging-only). Non-fatal: a scraper hiccup must not abort the price refresh.
+cd "$ENGINE" || { echo "FATAL: no $ENGINE"; exit 1; }
+echo "--- [1/2] Stout new-products scrape (canonical path, in-memory only) ---"
+timeout 300 npx tsx tmp/verify-one.ts stout "https://www.stouttextiles.com/" \
+ || echo "note: stout new-products scrape non-fatal error (rc=$?) — continuing to price refresh"
+
+# 2) Re-scrape authenticated prices INTO stout_catalog (STAGE-ONLY).
+# scrape-prices.cjs reads SKUs+urls from stout_catalog, logs in to the Stout
+# trade portal, and UPDATEs stout_catalog.price_trade / price_retail only
+# (retail = round(cost/0.65/0.85,2); no price invented). It writes an audit
+# JSONL + a reviewable SQL to /tmp and applies the UPDATE to stout_catalog.
+# It never touches Shopify. THIS is the staging write of the refresh.
+cd "$STOUT_DIR" || { echo "FATAL: no $STOUT_DIR"; exit 1; }
+echo "--- [2/2] Stout price re-scrape → stout_catalog (staging UPDATE only) ---"
+if [ -f scrape-prices.cjs ]; then
+ timeout 900 node scrape-prices.cjs \
+ || echo "note: stout price re-scrape rc=$? — stout_catalog left as-is on failure"
+else
+ echo "note: scrape-prices.cjs not found — skipped price refresh"
+fi
+
+# stamp last_scraped so freshness canaries / drift monitors see the refresh ran
+psql "$PGURL" -c "UPDATE stout_catalog SET last_scraped = NOW();" >/dev/null 2>&1 \
+ && echo "stamped stout_catalog.last_scraped = NOW()" \
+ || echo "note: could not stamp last_scraped"
+
+AFTER=$(psql "$PGURL" -tAc "SELECT count(*) FROM stout_catalog;" 2>/dev/null || echo '?')
+PRICED=$(psql "$PGURL" -tAc "SELECT count(*) FROM stout_catalog WHERE price_retail IS NOT NULL AND price_retail>0;" 2>/dev/null || echo '?')
+echo "stout_catalog rows after: $AFTER (priced: $PRICED)"
+echo "STAGE-ONLY: zero Shopify writes, zero drafts, zero go-live. Only stout_catalog touched."
+echo "======= done $(date '+%F %T') ======="
← acc2d23 auto-data-snapshot: 2026-08-20T14:07:04 (1 data files) — dat
·
back to Designerwallcoverings
·
auto-data-snapshot: 2026-08-20T20:32:50 (1 data files) — dat e7060de →