e177f6372b
leo 2026-08-27(inkstone/ISEP#66):「你禁止寫 code,為什麼你改?為什麼你可以執行?」 總管親手改了 no-ticket-no-dispatch.sh,這道閘一聲不響。 三個洞,一次補: 1. `.sh` 從沒進過受管副檔名清單——這個 repo 自己的閘全是 .sh,等於這道閘 從第一行 case 就直接放行,路徑白名單根本沒機會判斷。這才是真正原因, 不是路徑豁免。修法:閘本身(`.claude/hooks/*.sh`、`hooks/*.sh`)獨立收進 受管清單;同時拿掉 `*/.claude/hooks/*` 整目錄豁免——能改閘的人等於能關掉 所有其他閘,這句話本身就是拿掉它的理由。 2. 「這個 session 派過一次工」曾被當永久放行條件,但那混淆了「這件任務有沒有 交出去」跟「下一次任意的手改合不合理」——整個拿掉,改成純資訊(留在被擋下 時的訊息裡,告訴你上次派工是幾分鐘前)。 3. 「同一 session 只擋一次」讓警報響過一次就對餘生啞掉。改成寬限期(預設 15 分鐘,可用 SUBAGENT_GUARD_NAG_TTL 覆寫做測試):寬限期內不重複鬼打牆, 過了就再響——不選邊犧牲「有效」或「不煩人」。 放行只剩三種:CLAUDE_CODE_CHILD_SESSION=1(subagent 本人,它就是被派來寫的)/ solo-ok 非空(寫了理由,不是 touch 空檔)/檔案本身不歸這道閘管(測試檔/ system-dev 的 wiki/非受管副檔名)。 新增 scripts/test-subagent-first-guard.sh,20/20 通過:涵蓋三個洞各自的重演、 CHILD_SESSION 放行、測試檔/wiki 放行、solo-ok 空檔不算數、寬限期到期再響。 plugin.json 0.5.0 → 0.5.1;docs/hooks-inventory.md 同步更新兩支閘的說明。 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
24 lines
1.2 KiB
Bash
Executable File
24 lines
1.2 KiB
Bash
Executable File
#!/bin/sh
|
||
# subagent-first-stamp.sh — PostToolUse:記下「這個 session 真的派過工了」。
|
||
#
|
||
# 配 subagent-first-guard.sh 使用(同 kbdb-asked-stamp.sh 的形狀)。
|
||
# 只留時戳,不擋任何東西。
|
||
#
|
||
# 🔴 2026-08-27(inkstone/ISEP#66):這個時戳**不再是放行條件**。
|
||
# 「派過一次工」曾經被 subagent-first-guard.sh 當成「這個 session 永久放行」——
|
||
# 但那混淆了「這件任務有沒有交出去」跟「下一次任意的手改合不合理」,是兩件事。
|
||
# 現在這支只負責留時戳,給被擋下時的訊息當脈絡用(「上次派工是幾分鐘前」),
|
||
# 純資訊、不影響放行判斷。
|
||
#
|
||
# 🔴 為什麼要按 session 分開存(而不是像 KBDB 那道用全域+1 小時 TTL):
|
||
# 「這一輪工作有沒有先考慮派工」是**每個 session 各自要回答**的問題。
|
||
# 用全域檔的話,昨天派過一次就等於永久解鎖,這道閘會在第二天起完全失效。
|
||
set -eu
|
||
SID=$(cat 2>/dev/null | python3 -c '
|
||
import sys, json
|
||
try: print(json.load(sys.stdin).get("session_id", "") or "nosid")
|
||
except Exception: print("nosid")
|
||
' 2>/dev/null || echo nosid)
|
||
date +%s > "/tmp/.subagent-spawned-$SID"
|
||
exit 0
|