[object Object]

← back to Nas Setup

daemon-health: fix false-PASS regression — restore mechanism-loud verdict (broken daemon=FAIL even when Henry manually-fresh) + watch the -root SYSTEM daemons not the retired gui/ user agents (Steve ruled 8/14, TK-10547)

272bd47d2ee2aef6e176b285d57181f18c03d34a · 2026-08-14 09:54:23 -0700 · Steve Abrams

Files touched

Diff

commit 272bd47d2ee2aef6e176b285d57181f18c03d34a
Author: Steve Abrams <steve@designerwallcoverings.com>
Date:   Fri Aug 14 09:54:23 2026 -0700

    daemon-health: fix false-PASS regression — restore mechanism-loud verdict (broken daemon=FAIL even when Henry manually-fresh) + watch the -root SYSTEM daemons not the retired gui/ user agents (Steve ruled 8/14, TK-10547)
---
 scripts/daemon-health.sh | 33 +++++++++++++++++----------------
 1 file changed, 17 insertions(+), 16 deletions(-)

diff --git a/scripts/daemon-health.sh b/scripts/daemon-health.sh
index c4bffe1..ace052b 100755
--- a/scripts/daemon-health.sh
+++ b/scripts/daemon-health.sh
@@ -20,7 +20,7 @@ STALE_WARN_H="${STALE_WARN_H:-30}"   # a nightly 03:45 job's Henry dump older th
 HERE="$(cd "$(dirname "$0")" && pwd)"; DATA="$HERE/../data"; mkdir -p "$DATA"
 OUT="$DATA/daemon-health-latest.json"
 now=$(date +%s)
-MYUID=$(id -u)   # the mirror now runs as a USER LaunchAgent (gui/$MYUID) via /opt/homebrew/bin/bash
+MYUID=$(id -u)   # (kept for reference; the canonical scheduled job is the -root SYSTEM daemon per Option B)
 worst="PASS"   # PASS < WARN < FAIL
 rank(){ case "$1" in FAIL) echo 2;; WARN) echo 1;; *) echo 0;; esac; }
 rows=()
@@ -29,10 +29,10 @@ rows=()
 check(){
   local label="$1" dir="$2" prefix="$3"
   local print exit_code runs state verdict reason newest age_h fresh
-  # The mirror now runs as a USER LaunchAgent (gui/$MYUID) via /opt/homebrew/bin/bash — the non-SIP
-  # launcher that CAN write the /Volumes/Henry external volume (the old root daemon's signed launcher
-  # was TCC-denied on the removable volume, failing every night). So look it up in gui/, not system/.
-  print=$(launchctl print "gui/$MYUID/$label" 2>/dev/null)
+  # Option B (Steve-approved, TK-10547): the canonical scheduled job is the -root SYSTEM LaunchDaemon
+  # (runs nightly 03:45 as root). Option C (a user LaunchAgent) was REJECTED — it hits the same SIP-bash
+  # TCC wall and only runs while logged in. So look up the actually-scheduled job in system/, not gui/.
+  print=$(launchctl print "system/$label" 2>/dev/null)
   exit_code=$(printf '%s\n' "$print" | awk -F'= ' '/last exit code/{print $2; exit}')
   runs=$(printf '%s\n' "$print" | awk -F'= ' '/^\truns/{print $2; exit}')
   state=$(printf '%s\n' "$print" | awk -F'= ' '/^\tstate/{print $2; exit}')
@@ -44,15 +44,16 @@ check(){
     [ "$age_h" -le "$STALE_WARN_H" ] && fresh="fresh" || fresh="stale"
   else age_h=-1; fresh="missing"; fi
 
-  # Henry-freshness is AUTHORITATIVE — it's the actual off-machine backup we care about; the agent's
-  # exit code is secondary context. Missing/stale Henry = FAIL (the silent-death signal we exist to
-  # catch). A FRESH Henry dump means data is safe even if the agent hiccupped: agent-unloaded or
-  # exit!=0 with a fresh dump = WARN (worth a look, not a data emergency). Fresh + exit 0 = PASS.
-  if [ "$fresh" = "missing" ]; then verdict="FAIL"; reason="no Henry dump for $prefix — mirror never landed"
-  elif [ "$fresh" = "stale" ]; then verdict="FAIL"; reason="Henry dump ${age_h}h old (> ${STALE_WARN_H}h) — nightly mirror not landing"
-  elif [ -z "$print" ]; then verdict="WARN"; reason="agent not loaded but Henry dump fresh (${age_h}h) — data safe, scheduler gone"
-  elif [ "${exit_code:-1}" != "0" ]; then verdict="WARN"; reason="agent last_exit=${exit_code:-?} but Henry dump fresh (${age_h}h) — data safe, check writer"
-  else verdict="PASS"; reason="agent exit 0, Henry dump ${age_h}h old"; fi
+  # MECHANISM-LOUD verdict (Steve ruled 8/14, TK-10547): a broken SCHEDULED daemon = FAIL even when a
+  # manual rescue left Henry fresh — the entire point of this ticket is that monitoring must NOT hide a
+  # broken daemon behind a rosy manual PARTIAL. Henry-freshness enriches the REASON (data-safe vs
+  # data-lost), it does NOT soften the verdict. Only a clean daemon (exit 0) + fresh Henry = PASS.
+  if [ -z "$print" ]; then verdict="FAIL"; reason="launchd state unreadable for $label — scheduler gone"
+  elif [ "${exit_code:-1}" != "0" ] && [ "$fresh" = "fresh" ]; then verdict="FAIL"; reason="daemon last_exit=${exit_code:-?} but Henry dump fresh (${age_h}h) — mechanism broken, data safe (launcher swap pending)"
+  elif [ "${exit_code:-1}" != "0" ]; then verdict="FAIL"; reason="daemon last_exit=${exit_code:-?} — mechanism broken AND Henry ${fresh} (${age_h}h)"
+  elif [ "$fresh" = "missing" ]; then verdict="FAIL"; reason="no Henry dump for $prefix — mirror never landed"
+  elif [ "$fresh" = "stale" ]; then verdict="WARN"; reason="Henry dump ${age_h}h old (> ${STALE_WARN_H}h) — daemon exit 0 but mirror not landing"
+  else verdict="PASS"; reason="daemon exit 0, Henry dump ${age_h}h old"; fi
   [ "$(rank "$verdict")" -gt "$(rank "$worst")" ] && worst="$verdict"
 
   echo "  $label -> $verdict ($reason)"
@@ -63,8 +64,8 @@ check(){
 }
 
 echo "== on-prem backup daemon health ($(date -Iseconds)) =="
-check com.steve.nas-dwdump-mirror          /Volumes/Henry/dw-backups/dw_unified  dw_unified
-check com.steve.nas-realestate-dump-mirror /Volumes/Henry/dw-backups/realestate  realestate
+check com.steve.nas-dwdump-mirror-root          /Volumes/Henry/dw-backups/dw_unified  dw_unified
+check com.steve.nas-realestate-dump-mirror-root /Volumes/Henry/dw-backups/realestate  realestate
 echo "== overall: $worst =="
 
 # JSON heartbeat (PASS/WARN/FAIL vocab so fleet-health-rollup + meta-watchdog read it right)

← d3111b7 Henry mirror: switch to USER LaunchAgents via homebrew bash  ·  back to Nas Setup  ·  daemon-health: belt-and-suspenders model (Steve ruled 8/14 ' 4a447c8 →