[object Object]

← back to New Import Viewer

Add one-shot auto first-run (429-gated validated 42-batch + CNCP report + self-disable)

4573e7cc5265d4f2dddae2e88ed800ddfcf5e7ce · 2026-06-19 09:43:46 -0700 · SteveStudio2

Files touched

Diff

commit 4573e7cc5265d4f2dddae2e88ed800ddfcf5e7ce
Author: SteveStudio2 <stevestudio2@SteveStacStudio.lan>
Date:   Fri Jun 19 09:43:46 2026 -0700

    Add one-shot auto first-run (429-gated validated 42-batch + CNCP report + self-disable)
---
 auto-first-run.sh            | 76 ++++++++++++++++++++++++++++++++++++++++++++
 com.steve.dw-first-run.plist | 29 +++++++++++++++++
 2 files changed, 105 insertions(+)

diff --git a/auto-first-run.sh b/auto-first-run.sh
new file mode 100755
index 0000000..b96e15c
--- /dev/null
+++ b/auto-first-run.sh
@@ -0,0 +1,76 @@
+#!/bin/zsh
+# AUTO FIRST-RUN — one-shot validated 42-SKU live run, gated on the Shopify
+# daily-variant-429 clearing. Steve approved (2026-06-19) "auto-retry at ~4 PM PT,
+# run the first 42 and report".
+#
+# Behavior:
+#   • Fired hourly by launchd (com.steve.dw-first-run) but ONLY acts from 16:00
+#     local (PDT) onward — earlier ticks exit immediately (429 still active).
+#   • Runs ONE live 42-batch via run-cadence.sh (consumer.js publishes to Online
+#     Store via the 2026-06-19 publish fix; soft-stops on 429, creates nothing if
+#     still blocked).
+#   • Verifies via Shopify: products created in the last 15 min that are PUBLISHED.
+#   • On first success (>=1 published): posts a CNCP card, writes a result file,
+#     and BOOTS ITSELF OUT of launchd (one-shot — does NOT become a 24/7 cadence).
+#   • If still blocked: logs, leaves itself scheduled to retry next hour.
+#   • At/after 21:00 local with still no success: posts a one-time CNCP escalation.
+#
+# This validates the pipeline with ONE real batch. Enabling the full 42/hr × 24
+# cadence (com.steve.dw-cadence) is a SEPARATE Steve action after this proves out.
+set -uo pipefail
+cd "$(dirname "$0")"
+export PATH="/opt/homebrew/bin:/usr/bin:/bin:/usr/local/bin:$PATH"
+
+LABEL="com.steve.dw-first-run"
+LOG="data/first-run.log"
+RESULT="data/first-run-result.json"
+ESC_FLAG="data/.first-run-escalated"
+HOUR_LOCAL=$(date +%H)
+TS=$(date -u +%Y-%m-%dT%H:%M:%SZ)
+
+log(){ echo "[$(date -u +%Y-%m-%dT%H:%M:%SZ)] $*" >> "$LOG"; }
+
+# Gate: do nothing before 16:00 local (429 window not expected to clear yet).
+if [ "$HOUR_LOCAL" -lt 16 ]; then
+  log "tick skipped (local hour $HOUR_LOCAL < 16 — 429 window not yet expected to clear)"
+  exit 0
+fi
+
+log "tick: attempting one live 42-batch (local hour $HOUR_LOCAL)"
+LAUNCH_CONSUMER_LIVE=1 CADENCE_LIMIT=42 ./run-cadence.sh || log "run-cadence.sh returned non-zero (logged in cadence log)"
+
+# Verify: how many products were created+published in the last 15 minutes?
+TOKEN=$(grep '^SHOPIFY_ADMIN_TOKEN=' "$HOME/Projects/secrets-manager/.env" | cut -d= -f2-)
+STORE=$(grep '^SHOPIFY_STORE=' "$HOME/Projects/secrets-manager/.env" | cut -d= -f2-)
+PUBN=$(python3 - "$STORE" "$TOKEN" <<'PY'
+import sys,json,urllib.request,datetime
+store,token=sys.argv[1],sys.argv[2]
+since=(datetime.datetime.now(datetime.UTC)-datetime.timedelta(minutes=15)).strftime('%Y-%m-%dT%H:%M:%SZ')
+url=f"https://{store}/admin/api/2024-10/products/count.json?created_at_min={since}&published_status=published"
+r=urllib.request.Request(url,headers={"X-Shopify-Access-Token":token})
+try:
+    print(json.loads(urllib.request.urlopen(r).read()).get("count",0))
+except Exception as e:
+    print(0)
+PY
+)
+log "verify: $PUBN products created+published in last 15 min"
+
+cncp(){ curl -s -X POST http://127.0.0.1:3333/api/wins -H 'Content-Type: application/json' -d "$1" >/dev/null 2>&1 || true; }
+
+if [ "${PUBN:-0}" -ge 1 ]; then
+  printf '{"ts":"%s","published_last15m":%s,"status":"success"}\n' "$TS" "$PUBN" > "$RESULT"
+  cncp "{\"project\":\"dw-cadence\",\"title\":\"First 42-SKU live run validated — $PUBN published\",\"summary\":\"Daily-variant 429 cleared; one cadence batch created+published $PUBN SKUs to Online Store via the publish-gap fix (consumer.js 96cd954). Pipeline proven. Ready to enable the full 42/hr x 24 cadence (com.steve.dw-cadence).\",\"valueToday\":\"Unblocks the 1,000-published-SKUs/24h target\"}"
+  log "SUCCESS ($PUBN published) — posting CNCP card and self-disabling (bootout)"
+  launchctl bootout gui/$(id -u)/$LABEL 2>/dev/null || true
+  exit 0
+fi
+
+# Still blocked — escalate once if it's getting late and we still haven't succeeded.
+if [ "$HOUR_LOCAL" -ge 21 ] && [ ! -f "$ESC_FLAG" ]; then
+  touch "$ESC_FLAG"
+  cncp "{\"project\":\"dw-cadence\",\"title\":\"First 42-SKU run STILL 429-blocked at $HOUR_LOCAL:00 PDT\",\"summary\":\"The Shopify daily-variant-creation limit has not cleared by evening. The auto-retry is still armed and will keep trying hourly, but this needs a manual look — the burst window may be wider than ~24h.\"}"
+  log "ESCALATION posted (still blocked at local hour $HOUR_LOCAL)"
+fi
+log "still blocked — staying scheduled, will retry next hour"
+exit 0
diff --git a/com.steve.dw-first-run.plist b/com.steve.dw-first-run.plist
new file mode 100644
index 0000000..c49346d
--- /dev/null
+++ b/com.steve.dw-first-run.plist
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+  <key>Label</key>
+  <string>com.steve.dw-first-run</string>
+  <key>ProgramArguments</key>
+  <array>
+    <string>/bin/zsh</string>
+    <string>/Users/stevestudio2/Projects/new-import-viewer/auto-first-run.sh</string>
+  </array>
+  <key>WorkingDirectory</key>
+  <string>/Users/stevestudio2/Projects/new-import-viewer</string>
+  <!-- Fire hourly at HH:07. The script self-gates to act only from 16:00 local
+       (PDT) onward, runs ONE validated 42-batch when the 429 clears, then boots
+       itself out. RunAtLoad false so bootstrapping it now does not fire instantly. -->
+  <key>StartCalendarInterval</key>
+  <dict>
+    <key>Minute</key>
+    <integer>7</integer>
+  </dict>
+  <key>RunAtLoad</key>
+  <false/>
+  <key>StandardOutPath</key>
+  <string>/Users/stevestudio2/Projects/new-import-viewer/data/first-run.launchd.log</string>
+  <key>StandardErrorPath</key>
+  <string>/Users/stevestudio2/Projects/new-import-viewer/data/first-run.launchd.log</string>
+</dict>
+</plist>

← b4ec23e phase8-fix: real create+publish module (mirrors consumer.js  ·  back to New Import Viewer  ·  Retire new-import-viewer duplicate cadence plists — consolid d85bde7 →