[object Object]

← back to Designer Wallcoverings

nightly runner: durable completion ping — check-complete.js verifies every resolvable collection is at 0-remaining, then runner fires macOS notification + CNCP win + .SWEEP-COMPLETE sentinel + self-disables the nightly job. Works with no Claude session open.

4d3e325e5ff1c5bf0d3d505d6e80dce710e032df · 2026-06-13 08:58:53 -0700 · Steve Abrams

Files touched

Diff

commit 4d3e325e5ff1c5bf0d3d505d6e80dce710e032df
Author: Steve Abrams <steve@designerwallcoverings.com>
Date:   Sat Jun 13 08:58:53 2026 -0700

    nightly runner: durable completion ping — check-complete.js verifies every resolvable collection is at 0-remaining, then runner fires macOS notification + CNCP win + .SWEEP-COMPLETE sentinel + self-disables the nightly job. Works with no Claude session open.
---
 DW-Programming/check-complete.js      | 22 ++++++++++++++++++++++
 DW-Programming/nightly-color-clean.sh | 19 +++++++++++++++++++
 2 files changed, 41 insertions(+)

diff --git a/DW-Programming/check-complete.js b/DW-Programming/check-complete.js
new file mode 100644
index 00000000..35079601
--- /dev/null
+++ b/DW-Programming/check-complete.js
@@ -0,0 +1,22 @@
+#!/usr/bin/env node
+// Exit 0 only when EVERY resolvable color collection has 0 products left to
+// process (i.e. all current members are in the .done state). Exit 1 otherwise.
+// Unresolvable colors (e.g. cream — no cream-only collection on this store) are
+// skipped, never block completion. Prints {remaining, per} JSON.
+const path = require('path'), fs = require('fs');
+const { resolveCollectionId, fetchCollectionProducts, COLOR_COLLECTIONS } = require('./color-collection-cleaner-v2.js');
+const STATE = path.join(__dirname, 'data', 'color-clean-state');
+const done = (c) => { try { return new Set(fs.readFileSync(path.join(STATE, c + '.done'), 'utf8').split('\n').filter(Boolean).map(Number)); } catch { return new Set(); } };
+(async () => {
+  let remaining = 0; const per = {};
+  for (const [color, cfg] of Object.entries(COLOR_COLLECTIONS)) {
+    const id = await resolveCollectionId(color, cfg);
+    if (!id) { per[color] = 'skip(unresolved)'; continue; }
+    const prods = await fetchCollectionProducts(id);
+    const d = done(color);
+    const left = prods.filter(p => !d.has(p.id)).length;
+    per[color] = left; remaining += left;
+  }
+  console.log(JSON.stringify({ remaining, per }));
+  process.exit(remaining === 0 ? 0 : 1);
+})().catch(e => { console.error('check-complete error:', e.message); process.exit(2); });
diff --git a/DW-Programming/nightly-color-clean.sh b/DW-Programming/nightly-color-clean.sh
index a68f1780..fecc8acd 100755
--- a/DW-Programming/nightly-color-clean.sh
+++ b/DW-Programming/nightly-color-clean.sh
@@ -48,4 +48,23 @@ else CAP=""; fi
 $CAP node color-collection-cleaner-v2.js --all --conc 3 >> "$LOG" 2>&1
 echo "--- report ---" >> "$LOG"
 node color-clean-report.js >> "$LOG" 2>&1
+
+# --- completion ping (durable; fires even with no Claude session open) ---
+SENTINEL="$DIR/data/.SWEEP-COMPLETE"
+if [ ! -f "$SENTINEL" ]; then
+  CHK=$(node check-complete.js 2>>"$LOG"); RC=$?
+  echo "$(date): completeness check rc=$RC $CHK" >> "$LOG"
+  if [ "$RC" -eq 0 ]; then
+    REMOVED=$(grep -c '"dry_run": *false\|"dry_run":false' data/color-clean-ledger.jsonl 2>/dev/null)
+    echo "$CHK" > "$SENTINEL"
+    # 1. desktop ping
+    /usr/bin/osascript -e "display notification \"Live color-collection sweep COMPLETE — all collections processed, ${REMOVED} mis-tagged products removed. Review then decide on cream.\" with title \"Color Cleaner ✅\" sound name \"Glass\"" 2>/dev/null
+    # 2. CNCP win (best-effort)
+    curl -s -X POST http://127.0.0.1:3333/api/wins -H 'Content-Type: application/json' \
+      -d "{\"project\":\"Designer-Wallcoverings\",\"title\":\"Live color-collection sweep COMPLETE\",\"summary\":\"All resolvable color collections fully processed on the live store. ${REMOVED} mis-tagged products removed (reversible via ledger). Cream skipped (no cream-only collection). Banner re-crawl queue populated.\"}" >/dev/null 2>&1
+    # 3. self-disable the nightly job — nothing left to do
+    launchctl bootout gui/$(id -u)/com.steve.color-cleaner-nightly 2>/dev/null
+    echo "$(date): SWEEP COMPLETE — notified, win logged, nightly job self-disabled" >> "$LOG"
+  fi
+fi
 echo "=== $(date) nightly color-clean end ===" >> "$LOG"

← 6f9d054f nightly runner: chain-aware quota probe (proceed if ANY chai  ·  back to Designer Wallcoverings  ·  nightly runner: add best-effort George email on sweep comple 49cb6051 →