diff --git a/docs/TESTING.md b/docs/TESTING.md index 1cd61e1..99d6946 100644 --- a/docs/TESTING.md +++ b/docs/TESTING.md @@ -131,11 +131,11 @@ bash hooks/tests/prod-write-guard.test.sh hooks/prod-write-guard.sh - 「卡點二」2 條任一紅 ⇒ 「談論它」又被當成「執行它」(同款第八次), 或是剝了內文之後連真的部署都放行了 -### A16 — 「總管可以放行」那道門真的打得開:16 條 +### A16 — 「總管可以放行」那道門真的打得開:17 條 ``` bash hooks/tests/gate-ok.test.sh ``` -**該看到**:`通過 16 條,失敗 0 條`。 +**該看到**:`通過 17 條,失敗 0 條`。 **它在守什麼**(inkstone/ISEP#90 ④,2026-08-28 實查):三支閘 (`prod-write-guard`/`main-and-prod-push-guard`/`stage-before-prod-guard`) @@ -151,6 +151,10 @@ bash hooks/tests/gate-ok.test.sh 08-11、08-12 兩次真的被穿透之後才補上的,不准為了「好放行」而鬆掉 - ⑫ 紅 ⇒ `gate-ok` 在解不出 repo 時留下了一枚註定打不開的空戳記, 那會讓人以為門開了(而空戳記本身就是 08-12 那把萬用鑰匙的形狀) +- ⑰ 紅 ⇒ 判準又變回「行為取決於 cwd 裡有沒有一個叫 `%m` 的檔」。 + 這條是這個 bug 的最後一層:**GNU 的 `-f` 是布林旗標,`%m` 被當成另一個檔名運算元** + ⇒ 有那個檔就 `exit 0`、`||` 連跑都不跑。所以修法不能只是「把順序反過來」, + **每一步都要驗它是不是純數字** ⚠️ 這支會真的寫 `/tmp` 的戳記檔(那是閘寫死的路徑)。**不要在「已經蓋好戳記正要推東西」 的當下跑它**——它會把那枚戳記洗掉。 @@ -389,7 +393,7 @@ B2(信標那行)/B3(setup 輸出)/B4(閘的訊息)三個畫面 | **A10 人閘警察準度** | 總管 | ✅ 9/9,連跑三次(2026-08-26),A 群誤攔 0 | | **A11 派工單只剩票號** | 總管 | ✅ 19/19(2026-08-27) | | **A12 留言身份欄(兩道門)** | 總管 | ✅ 11/11(2026-08-27) | -| **A16 放行的門真的打得開** | 總管 | ✅ 16/16(2026-08-28,inkstone/ISEP#90) | +| **A16 放行的門真的打得開** | 總管 | ✅ 17/17(2026-08-28,inkstone/ISEP#90) | | **A17 未推警察不誤攔雲端分支** | 總管 | ✅ 10/10(2026-08-28,inkstone/ISEP#90) | | **A18 信標會報雲端接線缺陷** | 總管 | ✅ 14/14(2026-08-28,inkstone/ISEP#90) | | A7 plugin 裝得起來 | 總管 | ✅ | diff --git a/docs/governance/cloud-wiring.md b/docs/governance/cloud-wiring.md index 54b0f9f..138ee24 100644 --- a/docs/governance/cloud-wiring.md +++ b/docs/governance/cloud-wiring.md @@ -102,6 +102,53 @@ stat: cannot read file system information for '%m': No such file or directory ⇒ `case "$NOW$MT" in *[!0-9]*) return 1` 必然命中 ⇒ **在 Linux(=每一個雲端 session)上,那三支閘的戳記永遠不會被接受。** +### 再往下一層:`-f` 根本不吃格式參數,所以連離開碼都不可靠 + +總管 2026-08-28 自己驗這一格時量到 `exit=0`,而我量到 `exit=1`。 +**兩個都是真的**,而分歧本身就是這個 bug 最後一塊拼圖: + +**GNU 的 `-f` 是 `--file-system`,它是布林旗標、不接格式字串** +⇒ `%m` 不是格式,它被當成**另一個檔名運算元** ⇒ 離開碼取決於 +「當前目錄裡有沒有一個叫 `%m` 的檔」: + +``` +$ cd /tmp/statprobe && touch .mt + +# A. 沒有名為 %m 的檔(一般情況) +$ stat -f %m .mt ; echo "exit=$?" +stat: cannot read file system information for '%m': No such file or directory + File: ".mt" … ← 檔案系統資訊照樣印到 stdout +exit=1 ← `||` **會**跑 ⇒ 正確的秒數接在那堆垃圾後面 + +# B. 剛好有一個叫 %m 的檔 +$ touch '%m' && stat -f %m .mt ; echo "exit=$?" +exit=0 ← `||` **不會**跑 ⇒ 整包連一個數字都沒有 +``` + +(實測環境:`stat (GNU coreutils) 9.4`) + +**兩種情況下閘的結果一模一樣**——`MT` 都不是純數字, +`case "$NOW$MT" in *[!0-9]*) return 1` 都必然命中,戳記都作廢: + +``` +情況 A:非數字 ⇒ return 1 ⇒ 戳記作廢 +情況 B:非數字 ⇒ return 1 ⇒ 戳記作廢 +``` + +🔴 **這一層才是真正該記住的教訓**:舊寫法的 `||` fallback 之所以救不了, +**不是因為它沒跑,而是因為「跑不跑」根本不由這支腳本決定** +——它由「當前目錄裡有沒有一個叫 `%m` 的檔」決定。 +一個**行為取決於 cwd 裡有沒有某個檔名**的判斷式,不管跑不跑都是壞的。 + +⇒ 所以 `lib/mtime.sh` 的修法不是「把順序反過來」而已,是 +**每一步都驗它是不是純數字**:這個 bug 的成因正是「命令失敗了卻還是印了東西」, +**只看離開碼會再被騙一次**。 + +📌 這一格的實害(總管 2026-08-28 原話):「我今天為了發一則通知, +用了兩種方式蓋 `prod-write-ok` 都無效,一度以為是權限問題。」 +⇒ **閘不會告訴你門是壞的**,所以人會往錯的方向查(權限、classifier、設定), +而根因在閘自己身上。 + 🔴 **後果**:那三支閘在雲端**等於純擋**。總管照著閘自己印的指示做, 做幾次都打不開,**而閘不會告訴他門是壞的**。 diff --git a/hooks/lib/mtime.sh b/hooks/lib/mtime.sh index 530e283..02a47f2 100644 --- a/hooks/lib/mtime.sh +++ b/hooks/lib/mtime.sh @@ -29,7 +29,23 @@ # 總管照著閘自己印的指示做,做幾次都打不開,而閘不會告訴他門是壞的。 # inkstone/InkStoneCo#99 記的「連續四次蓋不出戳記」就是這個形狀。 # -# 🔴 順序反過來就好,但不能只靠順序:改成先 `-c %Y`(GNU)再 `-f %m`(BSD), +# ── 再往下一層:`-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() { diff --git a/hooks/tests/gate-ok.test.sh b/hooks/tests/gate-ok.test.sh index 0c5de96..f8ceb7c 100755 --- a/hooks/tests/gate-ok.test.sh +++ b/hooks/tests/gate-ok.test.sh @@ -76,6 +76,12 @@ M=$(file_mtime "$TMP/probe") case "$M" in ''|*[!0-9]*) R=bad ;; *) R=ok ;; esac ok "⑮ file_mtime 回純數字(舊寫法在 Linux 上會回一整段文字)" "$R" ok ok "⑯ 檔案不存在 → 回 0,不是空字串" "$(file_mtime "$TMP/nope")" 0 +# 🔴 ⑰ 守的是這個 bug 的最後一層:舊寫法的行為**取決於 cwd 裡有沒有一個叫 `%m` 的檔** +# (GNU 的 -f 是布林旗標,`%m` 被當成另一個檔名運算元)。有 ⇒ exit 0 ⇒ `||` 連跑都不跑。 +# 一個行為取決於 cwd 有沒有某個檔的判斷式,不管跑不跑都是壞的。 +M2=$( cd "$TMP" && touch '%m' && file_mtime "$TMP/probe" ) +case "$M2" in ''|*[!0-9]*) R2=bad ;; *) R2=ok ;; esac +ok "⑰ cwd 裡有一個叫 %m 的檔 → 仍要回純數字" "$R2" ok echo printf '通過 %s 條,失敗 %s 條\n' "$PASS" "$FAIL"