Skip to content

fix(crons): probe GNU stat before BSD stat in marker-age checks - #37

Open
danielw97 wants to merge 1 commit into
crisandrews:mainfrom
danielw97:fix/gnu-stat-marker-age
Open

danielw97 wants to merge 1 commit into
crisandrews:mainfrom
danielw97:fix/gnu-stat-marker-age

Conversation

@danielw97

Copy link
Copy Markdown

Fixes the Linux breakage reported in #36.

The bug

All three marker-age checks probe stat -f %m before falling back to stat -c %Y. On GNU coreutils -f is --file-system, a boolean flag, not a format option — so stat -f %m <marker> treats %m and the marker as two operands. It exits 1 on the bogus %m, so the || fallback does fire, but it has already written a multi-line filesystem block to stdout. The command substitution captures that block alongside the fallback's epoch, and the arithmetic on the next line dies under set -u with File: unbound variable.

Impact on Linux

  • hooks/cron-pretool.sh — the CronCreate gate dies before it can emit exit 2
  • hooks/cron-posttool.sh — ad-hoc crons are never captured into the registry
  • skills/crons/writeback.shset-alive / audit abort for the duration of a SessionStart reconcile

Net effect: cron reconcile is unreliable on Linux, which is a plausible contributor to the orphan-entry symptom in #32.

The fix

Swap the probe order to GNU-first. This is the convention already used in bin/cron-from.sh and hooks/scope-trust-legacy-warn.sh — the latter from your own commit fix(hooks): scope-trust legacy-warn crashed on GNU/Linux (stat -f is --file-system, not format). This PR applies that same correction to the three sites it didn't reach.

The order is unambiguous in both directions: on BSD, stat -c fails cleanly with no stdout, so the fallback to -f %m works exactly as before.

Three lines, no behaviour change on macOS.

Testing

Verified on Debian (GNU coreutils 9.x, bash 5.2): before the change, a SessionStart reconcile aborted with File: unbound variable; after it, reconcile completes and the registry audit reports alive=2/2 orphaned=0. Confirmed again in normal use over the past two days.

macOS path unchanged and untested by me — worth a second pair of eyes there, though stat -c failing with empty stdout on BSD is well documented.


🤖 Generated with Claude Code

`stat -f` on GNU coreutils is --file-system, a boolean flag rather than a
format option, so `stat -f %m <marker>` treats both arguments as operands.
It exits 1 on the bogus `%m` operand -- so the `||` fallback does fire --
but it has already written a multi-line filesystem block to stdout, which
the command substitution captures alongside the fallback's epoch. The
arithmetic on the following line then dies under `set -u` with
"File: unbound variable".

On Linux this left the CronCreate PreToolUse gate dead before it could
emit exit 2, stopped the PostToolUse hook from capturing ad-hoc crons into
the registry, and aborted writeback.sh set-alive/audit for the duration of
a SessionStart reconcile.

Swap to GNU-first, matching the convention already used in
bin/cron-from.sh and hooks/scope-trust-legacy-warn.sh. On BSD `stat -c`
fails cleanly with no stdout, so this order is unambiguous.

Refs crisandrews#36

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant