Files
ISEP/hooks/lib/mtime.sh
T
claude-code 4322deb23a 補上 stat -f 診斷的最後一層:連離開碼都不可靠(inkstone/ISEP#90 ④)
總管自己驗這一格量到 exit=0,我量到 exit=1。**兩個都是真的**,
而分歧本身就是最後一塊拼圖:

**GNU 的 `-f` 是 `--file-system`,布林旗標、不接格式字串**
⇒ `%m` 不是格式,它被當成**另一個檔名運算元**
⇒ 離開碼取決於「cwd 裡有沒有一個叫 `%m` 的檔」:

    A. 沒有(一般情況)  → exit 1 ⇒ `||` 會跑   ⇒ 正確的秒數接在垃圾後面
    B. 剛好有            → exit 0 ⇒ `||` 不會跑 ⇒ 整包連一個數字都沒有

兩種情況閘的結果一樣:MT 都不是純數字、戳記都作廢。(實測 GNU coreutils 9.4,兩種都重現過)

🔴 教訓比原本寫的更尖銳:舊寫法的 `||` fallback 救不了,
**不是因為它沒跑,而是因為「跑不跑」根本不由這支腳本決定**——
它由「cwd 裡有沒有某個檔名」決定。
一個行為取決於 cwd 有沒有某個檔的判斷式,不管跑不跑都是壞的。
⇒ 所以修法不能只是「把順序反過來」,**每一步都要驗它是不是純數字**
(lib/mtime.sh 本來就是這樣寫的,現在把理由寫進去了)。

gate-ok 測試補第 ⑰ 條守這一層:cwd 裡有一個叫 `%m` 的檔時,file_mtime 仍要回純數字。
16 → 17 條,TESTING.md 的 A16 一併更新。
2026-08-28 00:27:51 +00:00

58 lines
3.5 KiB
Bash
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# hooks/lib/mtime.sh — 「這個檔幾點被動的」,跨 macOS/Linux 都問得出來。
# 不是獨立掛的閘(沒進 hooks.json),給那幾支用 /tmp 戳記的閘 `source` 用。
#
# ══ 為什麼要有這支(inkstone/ISEP#90 ④,2026-08-28 實查)═══════════════
#
# 三支閘(prod-write-guard、main-and-prod-push-guard、stage-before-prod-guard
# 的戳記檢查都寫成這一行:
#
# MT=$(stat -f %m "$STAMP" 2>/dev/null || stat -c %Y "$STAMP" 2>/dev/null || echo 0)
#
# 在 macOSBSD stat)上它是對的:`-f %m` 就是 mtime。
# 在 **LinuxGNU coreutils)上 `-f` 是「顯示檔案系統資訊」**,而且它
# **一邊回非零、一邊往 stdout 吐一整段檔案系統的區塊**:
#
# $ stat -f %m /tmp/.probe
# stat: cannot read file system information for '%m': No such file or directory ← stderr
# File: "/tmp/.probe" ← stdout
# ID: 0 Namelen: 255 Type: ext2/ext3
# …
#
# ⇒ `2>/dev/null` 把錯誤訊息吃掉、`||` 接著跑 `stat -c %Y` 把正確的秒數**接在那堆垃圾後面**
# ⇒ `MT` 變成「一段多行文字+一個數字」
# ⇒ 閘下一行的 `case "$NOW$MT" in *[!0-9]*) return 1` 一定命中
# ⇒ **在 Linux 上,那三支閘的戳記永遠不會被接受。**
#
# 🔴 這件事的後果不是「少一個便利功能」:
# 那三支閘都是「擋下來,但**總管看過就可以放行**」的設計。
# 放行的那道門在雲端(Linux)打不開 ⇒ **它們在雲端等於純擋**,
# 總管照著閘自己印的指示做,做幾次都打不開,而閘不會告訴他門是壞的。
# inkstone/InkStoneCo#99 記的「連續四次蓋不出戳記」就是這個形狀。
#
# ── 再往下一層:`-f` 根本不吃格式參數,所以連離開碼都不可靠 ────────────
#
# 2026-08-28 兩個人量這一格,一個量到 `exit=0`、一個量到 `exit=1`。**兩個都是真的**
# 而分歧本身就是最後一塊拼圖:**GNU 的 `-f` 是 `--file-system`,布林旗標、不接格式字串**
# ⇒ `%m` 被當成**另一個檔名運算元** ⇒ 離開碼取決於「cwd 裡有沒有一個叫 `%m` 的檔」:
#
# A. 沒有(一般情況) → exit 1 ⇒ `||` **會**跑 ⇒ 正確的秒數接在垃圾後面
# B. 剛好有 → exit 0 ⇒ `||` **不會**跑 ⇒ 整包連一個數字都沒有
#
# 兩種情況下閘的結果一樣:`MT` 都不是純數字,戳記都作廢。(實測 GNU coreutils 9.4
#
# 🔴 **這才是該記住的教訓**:舊寫法的 `||` fallback 救不了,
# **不是因為它沒跑,而是因為「跑不跑」根本不由這支腳本決定**
# ——它由「cwd 裡有沒有某個檔名」決定。
# 一個**行為取決於 cwd 裡有沒有某個檔**的判斷式,不管跑不跑都是壞的。
#
# 🔴 所以修法不是「把順序反過來」而已:改成先 `-c %Y`(GNU)再 `-f %m`BSD),
# 而且**每一步都驗它是不是純數字**——因為這個 bug 的成因正是
# 「命令失敗了卻還是印了東西出來」,只看離開碼會再被騙一次。
file_mtime() {
_fm=$(stat -c %Y "$1" 2>/dev/null || true)
case "${_fm:-}" in ''|*[!0-9]*) _fm=$(stat -f %m "$1" 2>/dev/null || true) ;; esac
case "${_fm:-}" in ''|*[!0-9]*) _fm=$(python3 -c 'import os,sys; print(int(os.path.getmtime(sys.argv[1])))' "$1" 2>/dev/null || true) ;; esac
case "${_fm:-}" in ''|*[!0-9]*) _fm=0 ;; esac
printf '%s' "$_fm"
}