Filed out of PR #331, which fixed six of them. This is the pattern, not another instance.
The count
10# has now been added, one issue at a time, by #321, #325, #329 and #331. Seven sites still read a case-guarded value straight into $(( )) with no 10#:
| File |
Variable |
scripts/post-tool-hook.sh:302 |
LAST_LINE |
scripts/session-start-hook.sh:875 |
DELIVERIES |
scripts/save-session.sh:342 |
_prev_count |
scripts/run-consolidation.sh:213,218 |
staging_consumed |
scripts/log.sh:1213 |
prev |
scripts/lib-lock.sh:191 |
_mtime |
scripts/doctor.sh:261 |
_mtime |
Fifteen sites total across four issues. Each issue was correct about the sites it named and silent about the rest, so each fix looked complete and none was.
Why the guard does not guard
case "$X" in ""|*[!0-9]*) X=0 ;; esac rejects non-digits. 08 and 09 are all digits, so they pass — and then $(( )) reads them as octal and refuses. The guard's whole premise is a corrupt marker, and the two values it cannot handle are the ones a zero-padded timestamp or counter produces.
Bounded, which is what keeps this finite: [ "$x" -lt "$y" ] parses base 10 without complaint ([ 08 -lt 9 ] is true, no diagnostic). $(( )) is the only sink. So the sweep is enumerable rather than open-ended.
Why a helper alone is not the answer
The obvious fix is _remember_epoch_or_zero in scripts/log.sh, which the hooks and save-session.sh all source. That would cover almost every site — and it changes the sourcing contract for every hook in a repo that has taken ten Windows issues from seven external reporters. Worth doing, worth doing on its own, and not worth bundling into a bugfix PR.
The part that actually prevents recurrence is a lint rule, because the failure mode is a new call site written next month by someone who never read any of these four issues:
A repo-local check in the test suite is probably enough — it needs no new dependency and it fails in the one place people look.
What this is really about
Four issues, four correct partial fixes, and the tracker still holding the same defect. Fixing instances by hand is what produced #327: an issue whose own inventory said "10# appears in save-session.sh and lib-lock.sh and nowhere else" was already out of date when it was filed, because #329 had added two more sites. Every hand-fix invalidates the next issue's inventory.
Filed out of PR #331, which fixed six of them. This is the pattern, not another instance.
The count
10#has now been added, one issue at a time, by #321, #325, #329 and #331. Seven sites still read acase-guarded value straight into$(( ))with no10#:scripts/post-tool-hook.sh:302LAST_LINEscripts/session-start-hook.sh:875DELIVERIESscripts/save-session.sh:342_prev_countscripts/run-consolidation.sh:213,218staging_consumedscripts/log.sh:1213prevscripts/lib-lock.sh:191_mtimescripts/doctor.sh:261_mtimeFifteen sites total across four issues. Each issue was correct about the sites it named and silent about the rest, so each fix looked complete and none was.
Why the guard does not guard
case "$X" in ""|*[!0-9]*) X=0 ;; esacrejects non-digits.08and09are all digits, so they pass — and then$(( ))reads them as octal and refuses. The guard's whole premise is a corrupt marker, and the two values it cannot handle are the ones a zero-padded timestamp or counter produces.Bounded, which is what keeps this finite:
[ "$x" -lt "$y" ]parses base 10 without complaint ([ 08 -lt 9 ]is true, no diagnostic).$(( ))is the only sink. So the sweep is enumerable rather than open-ended.Why a helper alone is not the answer
The obvious fix is
_remember_epoch_or_zeroinscripts/log.sh, which the hooks andsave-session.shall source. That would cover almost every site — and it changes the sourcing contract for every hook in a repo that has taken ten Windows issues from seven external reporters. Worth doing, worth doing on its own, and not worth bundling into a bugfix PR.The part that actually prevents recurrence is a lint rule, because the failure mode is a new call site written next month by someone who never read any of these four issues:
caseand later appears inside$(( ))without10#.A repo-local check in the test suite is probably enough — it needs no new dependency and it fails in the one place people look.
What this is really about
Four issues, four correct partial fixes, and the tracker still holding the same defect. Fixing instances by hand is what produced #327: an issue whose own inventory said "
10#appears insave-session.shandlib-lock.shand nowhere else" was already out of date when it was filed, because #329 had added two more sites. Every hand-fix invalidates the next issue's inventory.