[object Object]

← back to Nas Setup

daemon-health: fix jq --argjson crash on '(never exited)' — the loaded-but-never-run belts (runs=0) returned non-JSON exit text, throwing jq so the heartbeat latest.json never landed and fleet-health-rollup went blind on the Henry mirror (the exact TK-10546 anti-pattern). Sanitize exit codes to a JSON number-or-null, add a never-ran vs exit:N status word, and rewrite the up=0 FAIL reason to say 'no scheduled writer has landed a clean run — kept fresh by manual rescue only' instead of the misleading 'both writers broken' (TK-10547)

9fc25719883f807fa2533d0d1671fe84b9fbeefb · 2026-08-15 20:32:34 -0700 · Steve Abrams

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

Files touched

Diff

commit 9fc25719883f807fa2533d0d1671fe84b9fbeefb
Author: Steve Abrams <steve@designerwallcoverings.com>
Date:   Sat Aug 15 20:32:34 2026 -0700

    daemon-health: fix jq --argjson crash on '(never exited)' — the loaded-but-never-run belts (runs=0) returned non-JSON exit text, throwing jq so the heartbeat latest.json never landed and fleet-health-rollup went blind on the Henry mirror (the exact TK-10546 anti-pattern). Sanitize exit codes to a JSON number-or-null, add a never-ran vs exit:N status word, and rewrite the up=0 FAIL reason to say 'no scheduled writer has landed a clean run — kept fresh by manual rescue only' instead of the misleading 'both writers broken' (TK-10547)
    
    Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
---
 scripts/daemon-health.sh | 26 +++++++++++++++++++-------
 1 file changed, 19 insertions(+), 7 deletions(-)

diff --git a/scripts/daemon-health.sh b/scripts/daemon-health.sh
index 29b70c6..5aefb32 100755
--- a/scripts/daemon-health.sh
+++ b/scripts/daemon-health.sh
@@ -48,11 +48,23 @@ check(){
   # broken writer is never hidden — the ticket's whole point.
   rootp=$(launchctl print "system/${base}-root" 2>/dev/null)
   userp=$(launchctl print "gui/$MYUID/${base}" 2>/dev/null)
-  root_ec=$(printf '%s\n' "$rootp" | awk -F'= ' '/last exit code/{print $2; exit}')
-  user_ec=$(printf '%s\n' "$userp" | awk -F'= ' '/last exit code/{print $2; exit}')
+  # launchd shows "last exit code = (never exited)" for a loaded-but-never-run job (runs=0). That
+  # raw text is NOT valid JSON, so feeding it to `jq --argjson` throws and the heartbeat latest.json
+  # never lands — blinding fleet-health-rollup (the TK-10546 anti-pattern). Capture the raw text for
+  # the human-readable writers string, but derive a JSON-safe number (or null) for the heartbeat, and
+  # a status word so the reason can say "never ran" instead of the misleading "broken" (TK-10547).
+  local root_raw user_raw root_json user_json root_word user_word
+  root_raw=$(printf '%s\n' "$rootp" | awk -F'= ' '/last exit code/{print $2; exit}')
+  user_raw=$(printf '%s\n' "$userp" | awk -F'= ' '/last exit code/{print $2; exit}')
+  ec_json(){ case "$1" in ''|*[!0-9]*) echo null;; *) echo "$1";; esac; }
+  ec_word(){ # $1=loaded-print $2=raw-exit -> unloaded|never-ran|exit:N
+    [ -z "$1" ] && { echo unloaded; return; }
+    case "$2" in ''|'(never'*|*[!0-9]*) echo never-ran;; 0) echo exit:0;; *) echo "exit:$2";; esac; }
+  root_json=$(ec_json "$root_raw"); user_json=$(ec_json "$user_raw")
+  root_word=$(ec_word "$rootp" "$root_raw"); user_word=$(ec_word "$userp" "$user_raw")
   local root_ok=0 user_ok=0
-  [ -n "$rootp" ] && [ "${root_ec:-1}" = "0" ] && root_ok=1
-  [ -n "$userp" ] && [ "${user_ec:-1}" = "0" ] && user_ok=1
+  [ -n "$rootp" ] && [ "$root_json" = "0" ] && root_ok=1
+  [ -n "$userp" ] && [ "$user_json" = "0" ] && user_ok=1
   local up=$(( root_ok + user_ok ))
 
   newest=$(ls -t "$dir/${prefix}"_*.dump 2>/dev/null | head -1)
@@ -62,13 +74,13 @@ check(){
     [ "$age_h" -le "$STALE_WARN_H" ] && fresh="fresh" || fresh="stale"
   else age_h=-1; fresh="missing"; fi
 
-  local writers="root(sys)=exit:${root_ec:-NA}$([ -n "$rootp" ]||echo /unloaded) user(gui)=exit:${user_ec:-NA}$([ -n "$userp" ]||echo /unloaded)"
+  local writers="root(sys)=${root_word} user(gui)=${user_word}"
   # Henry-freshness is authoritative. Fresh via at least one healthy writer = data safe; both writers
   # healthy = PASS; exactly one down = WARN (degraded redundancy, fix the down belt); both down or
   # Henry missing/stale = FAIL (no working writer → the silent-death setup).
   if [ "$fresh" = "missing" ]; then verdict="FAIL"; reason="no Henry dump for $prefix — neither writer landed [$writers]"
   elif [ "$fresh" = "stale" ]; then verdict="FAIL"; reason="Henry dump ${age_h}h old (> ${STALE_WARN_H}h) — neither writer is landing [$writers]"
-  elif [ "$up" -eq 0 ]; then verdict="FAIL"; reason="Henry fresh (${age_h}h) but BOTH writers broken — nothing will refresh it [$writers]"
+  elif [ "$up" -eq 0 ]; then verdict="FAIL"; reason="Henry fresh (${age_h}h) but NO scheduled writer has landed a clean run — the mirror is only being kept fresh by manual rescue; the automated belts are not proven [$writers]"
   elif [ "$up" -eq 1 ] && [ "$REQUIRE_BOTH_BELTS" = "1" ]; then verdict="WARN"; reason="Henry fresh (${age_h}h) via one writer — redundancy DEGRADED, other belt down [$writers]"
   elif [ "$up" -eq 1 ]; then verdict="PASS"; reason="Henry fresh (${age_h}h) via the accepted primary belt (root belt accepted-down pending FDA, Steve 8/15) [$writers]"
   else verdict="PASS"; reason="Henry fresh (${age_h}h), both writers healthy [$writers]"; fi
@@ -77,7 +89,7 @@ check(){
   echo "  $name -> $verdict ($reason)"
   echo "     henry: $fresh ${newest:+$(basename "$newest")} (${age_h}h) | writers: $writers"
   rows+=("$(jq -n --arg l "$name" --arg v "$verdict" --arg r "$reason" \
-     --argjson rec "${root_ec:-null}" --argjson uec "${user_ec:-null}" --argjson age "${age_h:-null}" \
+     --argjson rec "${root_json:-null}" --argjson uec "${user_json:-null}" --argjson age "${age_h:-null}" \
      '{db:$l,verdict:$v,reason:$r,root_last_exit:$rec,user_last_exit:$uec,henry_dump_age_h:$age}')")
 }
 

← 672d698 daemon-health: auto-detect belt strictness from the loaded r  ·  back to Nas Setup  ·  backup-daemon-health: credit the user belt via its verified 1256a2d →