From 0d0b777e74b1b66d261b9127c74498fff5cfa0cc Mon Sep 17 00:00:00 2001 From: Jesse Bridgeman Date: Tue, 16 Jun 2026 12:31:54 -0500 Subject: [PATCH] fix(crons): GNU-first stat probe in writeback.sh marker refresh MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `_refresh_reconciling_if_fresh` probed the .reconciling marker's mtime with BSD-style `stat -f %m` first. On GNU coreutils `-f` means "filesystem status", so `stat -f %m ` prints a multi-line filesystem block to stdout and exits non-zero. The `|| stat -c %Y` fallback then appends the real epoch, leaving `marker_mtime` as a blob containing the word "File". The arithmetic on the next line dereferences `File` as a variable and, under `set -u`, exits the whole script. This crashes both `audit` and `set-alive` (each calls the helper at its tail, after the registry write) whenever the marker is present — i.e. during every `/agent:crons reconcile` and heartbeat cron-repair on a GNU/Linux host (reproduced on WSL2 / Ubuntu coreutils). Probe `stat -c %Y` first: it succeeds on GNU and fails cleanly with no stdout on BSD/macOS, where `stat -f %m` then takes over. Matches the GNU-first, value-checked convention already used by `_detect_date_flavor`. A numeric guard is added so any future stat oddity can't re-trip `set -u`. Co-Authored-By: Claude Opus 4.8 --- skills/crons/writeback.sh | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/skills/crons/writeback.sh b/skills/crons/writeback.sh index 999d879..e017563 100755 --- a/skills/crons/writeback.sh +++ b/skills/crons/writeback.sh @@ -55,7 +55,14 @@ _refresh_reconciling_if_fresh() { local marker="$MEMORY_DIR/.reconciling" [[ -f "$marker" ]] || return 0 local marker_mtime now_s age - marker_mtime=$(stat -f %m "$marker" 2>/dev/null || stat -c %Y "$marker" 2>/dev/null || echo 0) + # GNU-first, value-checked probe: on GNU coreutils `stat -f %m` means + # "filesystem status" and prints a multi-line block to stdout while exiting + # non-zero, so the old BSD-first order let the `||` fallback APPEND the real + # epoch — leaving marker_mtime as a blob containing the word "File", which + # then trips `set -u` in the arithmetic below. `stat -c %Y` succeeds on GNU + # and fails cleanly (no stdout) on BSD/macOS, where `stat -f %m` takes over. + marker_mtime=$(stat -c %Y "$marker" 2>/dev/null || stat -f %m "$marker" 2>/dev/null || echo 0) + [[ "$marker_mtime" =~ ^[0-9]+$ ]] || marker_mtime=0 now_s=$(date +%s) age=$((now_s - marker_mtime)) if [[ $age -ge 0 && $age -lt 600 ]]; then