#!/bin/bash # 管什麼: 直接打 Gitea API 貼留言到票上時,內文第一行沒有【身份】就擋。 # 為什麼: scripts/ticket 已經在正門擋了,但貼留言還有 API 這條側門——只封一條等於沒封。 # 誤觸時怎麼關: 內文第一行加【身份】<總管|subagent|leo>//<分支>,或在指令裡加 reply-identity-ok 留痕放行。 # # reply-identity-guard.sh — PreToolUse(Bash):**票上的每一則留言都要認得出是誰寫的** # # 來由(leo 2026-08-27,inkstone/ISEP#30 comment 4325): # 「**subagent 回覆時要表明身份**」 # 實害(同日):多條線並行,票上的留言看不出是誰寫的, # **總管寫的診斷被當成 subagent 的結論,而其中一則是錯的**。 # # 為什麼要有這一支,正門的檢查不夠: # `scripts/ticket say` 已經在貼上去之前擋了。但**貼留言這個動作有兩條路**—— # 而 `ticket-api-bypass-guard.sh` 是**刻意放行**「對既有票的留言」的(它管的是開新票)。 # ⇒ 側門大開。這正是那支閘檔頭自己記過的一課: # 「規範有、閘也有,但閘長在『工具』上,而那個動作有兩條路,只封了一條。」 # # 判準是**結構**不是文字(leo 2026-08-17:「封的是動作,不是文字」): # 本閘不看留言寫什麼,只看**那個欄位在不在**。 # 三個條件都命中才擋:① 打到 /issues//comments 這條端點 ② 指令裡有 POST # ③ 內文找不到【身份】。純讀取(GET 撈留言)一律放行。 # # 逃生口:指令裡加 `reply-identity-ok`(會留在指令歷史上)。 # # 測試:hooks/tests/reply-identity.test.sh set -uo pipefail INPUT=$(cat) CMD=$(printf '%s' "$INPUT" | python3 -c " import sys, json try: print(json.load(sys.stdin).get('tool_input', {}).get('command', '')) except Exception: print('') " 2>/dev/null) [ -n "$CMD" ] || exit 0 # 逃生口(留痕) case "$CMD" in *reply-identity-ok*) exit 0 ;; esac # 正門有自己的閘,重複擋只會互相打架 case "$CMD" in *scripts/ticket*|*"ticket say"*|*"ticket decide"*|*"ticket close"*) exit 0 ;; esac # ① 打到「某張票的留言」這條端點(帶票號的子路徑,這是本閘唯一管的東西) printf '%s' "$CMD" | grep -qE 'issues/[0-9]+/comments' || exit 0 # ② 純讀取一律放行。同 ticket-api-bypass-guard 的理由:只認一個裸字就夠—— # ①已經確定這是留言端點,讀取的指令不會出現 POST,而且沒有跳脫的破口。 printf '%s' "$CMD" | grep -qw 'POST' || exit 0 # ③ 內文有沒有身份欄。只問「在不在」,不問「寫什麼」。 printf '%s' "$CMD" | grep -q '【身份】' && exit 0 cat >&2 <<'MSG' 🚫 貼到票上的留言,第一行要表明身份(leo 2026-08-27:「**subagent 回覆時要表明身份**」) 你正在用 Gitea API 直接貼留言,而內文裡找不到【身份】。 第一行照這個寫(角色三選一:總管/subagent/leo): 【身份】subagent/inkstone/ISEP/feat/my-branch 【身份】總管/inkstone/InkStoneCo/- **這條管所有人,不是只管 subagent。** 總管寫在票上的東西同樣要標—— 實害(2026-08-27):多條線並行時票上看不出誰寫的, **總管寫的診斷被當成 subagent 的結論,而其中一則是錯的**。 ── 走正門比較省事 ───────────────────────────────────────────── scripts/ticket say -F <內文檔> 它會回一行「定址」,那串就是下次派工要寫進【工單】的東西。 規約全文:docs/governance/dispatch-and-reply-format.md §3 真的有例外 → 指令裡加 reply-identity-ok(會留在指令歷史上)。 MSG exit 2