Summary
hooks/session-start (line 84) computes the last-emit mtime with BSD stat -f %m first, GNU stat -c %Y as fallback. On Linux, stat -f %m <file> is NOT an error — -f means "file system status", so it prints filesystem info and exits 0. The || chain therefore stops at the wrong command and last becomes a multi-line filesystem-info string → [ "$last" -gt 0 ] errors integer expression expected, and the time-based dedup never suppresses re-fires on Linux. Surfaced by the new hooks-check.yml CI (ubuntu) added in #41/#61's release.
Fix
Try GNU stat -c %Y first, then BSD stat -f %m, and guard the result is numeric:
last=$(stat -c %Y "$LAST_EMIT_FILE" 2>/dev/null || stat -f %m "$LAST_EMIT_FILE" 2>/dev/null || echo 0)
case "$last" in (*[!0-9]*|'') last=0 ;; esac
Then re-enable tests/hook-contracts.sh in .github/workflows/hooks-check.yml so the contract suite is CI-gated on Linux.
Discovered + fixed in the v6.3.0 hardening release (user-approved scope amendment).
Summary
hooks/session-start(line 84) computes the last-emit mtime with BSDstat -f %mfirst, GNUstat -c %Yas fallback. On Linux,stat -f %m <file>is NOT an error —-fmeans "file system status", so it prints filesystem info and exits 0. The||chain therefore stops at the wrong command andlastbecomes a multi-line filesystem-info string →[ "$last" -gt 0 ]errorsinteger expression expected, and the time-based dedup never suppresses re-fires on Linux. Surfaced by the newhooks-check.ymlCI (ubuntu) added in #41/#61's release.Fix
Try GNU
stat -c %Yfirst, then BSDstat -f %m, and guard the result is numeric:Then re-enable
tests/hook-contracts.shin.github/workflows/hooks-check.ymlso the contract suite is CI-gated on Linux.Discovered + fixed in the v6.3.0 hardening release (user-approved scope amendment).