diff --git a/docs/TESTING.md b/docs/TESTING.md index 42ded67..e784cdb 100644 --- a/docs/TESTING.md +++ b/docs/TESTING.md @@ -50,11 +50,11 @@ bash scripts/test-ticket-api-bypass-guard.sh **該看到**:`13/13 通過`。 **失敗**:任何一條不符,特別看「不該擋」那 8 條。 -### A11 — 討論串裡的任務要長成子票:20 條 +### A11 — 討論串裡的任務要長成子票:22 條 ``` bash scripts/test-comment-carries-task-guard.sh ``` -**該看到**:`20/20 通過`。 +**該看到**:`22/22 通過`。 **失敗**: - 「該擋」6 條任一紅 ⇒ 08-26 那則真的掉了的留言形狀(「等雲端那半出貨才驗得了」)會漏抓 - 「不該擋」10 條任一紅 ⇒ **誤攔,這比漏擋嚴重**——每次留言都被擋,人就學會忽略它 diff --git a/docs/governance/sdd-gitea-governance.md b/docs/governance/sdd-gitea-governance.md index 6ed08ae..a3a6722 100644 --- a/docs/governance/sdd-gitea-governance.md +++ b/docs/governance/sdd-gitea-governance.md @@ -836,7 +836,7 @@ scripts/ticket mine [--user <誰>] | 閘 | 掛在哪 | 抓什麼 | 測試 | |---|---|---|---| -| `hooks/comment-carries-task-guard.sh` | PreToolUse `Bash` | 留言裡帶「等 X 才…/驗不了/還沒…」這種**未完成的未來式**,卻沒開子票 → 擋一次 | `scripts/test-comment-carries-task-guard.sh`(20 條) | +| `hooks/comment-carries-task-guard.sh` | PreToolUse `Bash` | 留言裡帶「等 X 才…/驗不了/還沒…」這種**未完成的未來式**,卻沒開子票 → 擋一次 | `scripts/test-comment-carries-task-guard.sh`(22 條) | | `hooks/baton-handback-guard.sh` | PostToolUse `Agent\|Task` | 一條線收工,票上**指派/tag/下一步**缺哪一格 → 當場說出來(提醒,不擋) | `scripts/test-baton-handback-guard.sh`(10 條) | 🔴 **兩支閘的誤攔設計**:`comment-carries-task-guard` 同一 session 只擋一次, diff --git a/hooks/comment-carries-task-guard.sh b/hooks/comment-carries-task-guard.sh index 1e1d9c7..e35c584 100755 --- a/hooks/comment-carries-task-guard.sh +++ b/hooks/comment-carries-task-guard.sh @@ -28,6 +28,11 @@ # 所以關鍵詞刻意取**窄**的一組「未完成的未來式」,寧可漏抓也不要天天誤擋—— # `.claude/branch-holds.md` 檔頭那句:**永遠在響的警報,等於訓練人忽略這個警報。** # 漏抓的那些,由 baton-handback-guard.sh(收工那一刻)補第二層。 +# +# 已知極限(誠實標記):這支**認不出「我已經開好子票了」**——它只看得到指令文字。 +# 所以交件報告裡引用自己剛開的子票時仍會被擋一次;那一次用 `no-subtask:` 過, +# 或就當它是一次確認。刻意不去打 API 查「這張票有沒有相依」:那會讓每一個 +# 命中關鍵詞的 Bash 呼叫都多一次網路往返,代價落在錯的地方。 set -uo pipefail INPUT=$(cat) @@ -66,8 +71,13 @@ if [ -n "${FARG:-}" ] && [ -f "$FARG" ]; then BODY=$(cat "$FARG" 2>/dev/null || printf '%s' "$CMD") fi -# 已經帶著交棒標記的留言 = 走的是 handback 那條路,不重複擋 -case "$BODY" in *"🏃"*) exit 0 ;; esac +# 已經帶著交棒標記的留言 = 走的是 handback 那條路,不重複擋。 +# 🔴 只認**第一行**,不認「內文任何地方出現過」——這是 dogfood 當場抓到的漏(2026-08-27): +# 交件報告裡**引用了一段 `ticket mine` 的輸出**(那段有 🏃), +# 整則報告就被豁免掉了,而它裡面真的有「等雲端那半出貨」這種待辦。 +# ⇒ 這正是 `inkstone/InkStoneCo#23` 那個病的同款:**複述關鍵字被當成本人**。 +# `ticket handback` 寫的交棒留言,🏃 一定在第一行;引用別人的輸出則不會。 +case "$(printf '%s' "$BODY" | head -1)" in "🏃"*) exit 0 ;; esac # ③ 「未完成的未來式」——刻意取窄。這一組全部來自 08-26 那則真的掉了的留言的形狀。 # diff --git a/scripts/test-comment-carries-task-guard.sh b/scripts/test-comment-carries-task-guard.sh index 5ff01b1..ba92f94 100755 --- a/scripts/test-comment-carries-task-guard.sh +++ b/scripts/test-comment-carries-task-guard.sh @@ -37,6 +37,15 @@ run 0 'echo "這則留言等雲端出貨才驗得了 —— no-subtask: 它已 run 0 'curl -X POST "$API/repos/x/y/issues" -d "{\"title\":\"等出貨\"}"' # 開新票不歸這支管 run 0 'req("POST", f"{API}/repos/x/y/issues/3/comments", {"body":"🏃 **棒子交回** → claude-code\n\n**下一步**:等 #143"})' +echo "── 交棒標記只認第一行(2026-08-27 dogfood 抓到的漏)──" +# 真的交棒留言:🏃 在第一行 ⇒ 放行 +run 0 'req("POST", f"{API}/repos/x/y/issues/3/comments", {"body":"🏃 **棒子交回** → claude-code\n\n**下一步**:等 #143"})' +# 交件報告**引用**了一段有 🏃 的輸出,而報告本身帶著待辦 ⇒ 不准被豁免 +TMPR=$(mktemp) +printf '交件報告\n\n```\n🏃 在總管手上的棒子:2 根\n```\n\n還有一格等雲端那半出貨才驗得了\n' > "$TMPR" +run 2 "scripts/ticket say inkstone/ISEP#30 -F $TMPR" +rm -f "$TMPR" + echo "── 同一 session 只擋一次(不鬼打牆)──" rm -f "/tmp/.comment-task-guard-warned-$SID" C='req("POST", f"{API}/repos/x/y/issues/3/comments", {"body":"驗不了"})'