← back to Dw Yolo Loop
cycle-33: promote-canary.sh harness — generate launchd plist for a built canary (council #2); install stays Steve-gated, plutil-validated
ff928c0b30fbc534de14d97b5c4c3457eeea275a · 2026-06-16 13:47:14 -0700 · Steve Abrams
Files touched
A scripts/promote-canary.sh
Diff
commit ff928c0b30fbc534de14d97b5c4c3457eeea275a
Author: Steve Abrams <steve@designerwallcoverings.com>
Date: Tue Jun 16 13:47:14 2026 -0700
cycle-33: promote-canary.sh harness — generate launchd plist for a built canary (council #2); install stays Steve-gated, plutil-validated
---
scripts/promote-canary.sh | 76 +++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 76 insertions(+)
diff --git a/scripts/promote-canary.sh b/scripts/promote-canary.sh
new file mode 100755
index 0000000..3fe4292
--- /dev/null
+++ b/scripts/promote-canary.sh
@@ -0,0 +1,76 @@
+#!/bin/bash
+# promote-canary.sh — turn a built-but-unscheduled canary script into a launchd
+# job. WRITES a plist to ~/Library/LaunchAgents/ and PRINTS the bootstrap command
+# — it NEVER runs launchctl bootstrap itself (plist install is Steve-gated, and
+# the auto-mode classifier blocks autonomous launchd bootstrap). Idempotent: a
+# re-run overwrites the plist with the same content.
+#
+# Born from the 2026-06-16 Officer Council #2 pick (Canary Promotion Harness):
+# the overnight loop built ~8 read-only canaries and scheduled NONE — the exact
+# pattern that let the pg_dump backup die silent for 12 days. This closes the
+# build→schedule gap with one reusable step.
+#
+# Usage:
+# bash scripts/promote-canary.sh <label> <abs-script-path> <cadence> [hour] [minute] [weekday]
+# <label> e.g. dw-handle-freshness (becomes com.steve.<label>)
+# <cadence> daily | weekly
+# weekday 0-6 (Sun=0), only used for weekly
+# Example (nightly 3:10am): bash scripts/promote-canary.sh dw-handle-freshness \
+# "$HOME/Projects/designerwallcoverings/scripts/handle-freshness/handle-freshness-canary.mjs" daily 3 10
+# Example (weekly Mon 4:00): ... tls-expiry ... weekly 4 0 1
+#
+# READ-ONLY w.r.t. prod. $0. The generated job just runs the (read-only) canary.
+set -euo pipefail
+
+LABEL="${1:?need label, e.g. dw-handle-freshness}"
+SCRIPT="${2:?need absolute script path}"
+CADENCE="${3:?need cadence: daily|weekly}"
+HOUR="${4:-3}"
+MIN="${5:-15}"
+WEEKDAY="${6:-1}" # Monday default for weekly
+
+[ -f "$SCRIPT" ] || { echo "✗ script not found: $SCRIPT" >&2; exit 1; }
+
+FULL="com.steve.${LABEL}"
+PLIST="$HOME/Library/LaunchAgents/${FULL}.plist"
+
+# choose runner by extension
+case "$SCRIPT" in
+ *.mjs|*.js) RUN="/usr/bin/env node \"$SCRIPT\"";;
+ *.sh) RUN="/bin/bash \"$SCRIPT\"";;
+ *.py) RUN="/usr/bin/env python3 \"$SCRIPT\"";;
+ *) RUN="\"$SCRIPT\"";;
+esac
+
+if [ "$CADENCE" = "weekly" ]; then
+ CAL="<dict><key>Weekday</key><integer>${WEEKDAY}</integer><key>Hour</key><integer>${HOUR}</integer><key>Minute</key><integer>${MIN}</integer></dict>"
+else
+ CAL="<dict><key>Hour</key><integer>${HOUR}</integer><key>Minute</key><integer>${MIN}</integer></dict>"
+fi
+
+cat > "$PLIST" <<PLISTEOF
+<?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>${FULL}</string>
+ <key>ProgramArguments</key>
+ <array>
+ <string>/bin/bash</string><string>-lc</string>
+ <string>${RUN} >> /tmp/${LABEL}.log 2>&1</string>
+ </array>
+ <key>StartCalendarInterval</key>${CAL}
+ <key>RunAtLoad</key><false/>
+ <key>StandardOutPath</key><string>/tmp/${LABEL}.launchd.out.log</string>
+ <key>StandardErrorPath</key><string>/tmp/${LABEL}.launchd.err.log</string>
+</dict>
+</plist>
+PLISTEOF
+
+echo "✓ wrote $PLIST (${CADENCE} ${HOUR}:$(printf '%02d' "$MIN")${CADENCE:+${WEEKDAY:+}})"
+echo ""
+echo " → To ENABLE (Steve-gated — run this yourself):"
+echo " launchctl bootstrap gui/\$(id -u) \"$PLIST\""
+echo " → Verify: launchctl list | grep ${FULL}"
+echo " → Run-now: launchctl kickstart -k gui/\$(id -u)/${FULL}"
+echo " → Disable: launchctl bootout gui/\$(id -u)/${FULL}"
← d181396 Kravet Jan-2026 price list -> dw_unified: kravet_master_pric
·
back to Dw Yolo Loop
·
Kravet split-brain fix: copy kravet_authoritative_pricing (4 7131130 →