← 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
M scripts/daemon-health.sh
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 →