#!/usr/bin/env bash # delivery-police.sh — 交付警察(Stop / SubagentStop hook) # # 病根(leo 2026-07-22 原話):「它是我的助理,如果都是我檢查,那我就是它的助理。」 # 一天內三次都是 leo 先發現:①雲端沒查文件就問 ②OAuth 做好但前端還是舊按鈕、網址沒設 DNS # ③連補規則也是 leo 講了才補。→ 角色顛倒=根本失敗。 # # 為什麼清單沒用:CRITICAL-PATH 使用規則 4/5(v2 覆蓋前為 6/7)寫了「要驗前端」,CLAUDE.md 規則二寫了四題公式, # 照樣被違反——因為**自評的人跟做事的人是同一個**,任何品管制度都不成立。 # 解法同 self-drive-police:在「要標完成/要收工」那一刻攔截 + 反問。 # # 機制:Claude 想停 → 掃這回合輸出有沒有「完成宣稱」→ 有、但沒看到實測證據 → exit 2 反問。 # 豁免:輸出已含實測證據(curl/dig 實際輸出、HTTP 碼、grep 到畫面字串、檔案:行號)→ 放行。 # 撞權限閘/明說四題命中 → 放行(不逼它做沒權限的事)。 # # 判準(leo 2026-07-22):「友善=前端。人去按了連到錯的機制、安裝失敗,不算友善。」 # → 有前端的東西,驗收只認用戶真的會走的那條路。 input="$(cat)" # 防無限迴圈 stop_active="$(printf '%s' "$input" | python3 -c ' import sys, json try: print(json.load(sys.stdin).get("stop_hook_active", False)) except Exception: print(False) ' 2>/dev/null)" [ "$stop_active" = "True" ] && exit 0 transcript="$(printf '%s' "$input" | python3 -c ' import sys, json try: print(json.load(sys.stdin).get("transcript_path", "")) except Exception: print("") ' 2>/dev/null)" [ -z "$transcript" ] || [ ! -f "$transcript" ] && exit 0 last_text="$(python3 -c ' import sys, json path = sys.argv[1] texts = [] try: with open(path) as f: lines = f.readlines() for line in reversed(lines): try: ev = json.loads(line) except Exception: continue msg = ev.get("message", ev) if msg.get("role") == "assistant": content = msg.get("content", "") if isinstance(content, list): for b in content: if isinstance(b, dict) and b.get("type") == "text": texts.append(b.get("text", "")) elif isinstance(content, str): texts.append(content) break except Exception: pass print("\n".join(texts)) ' "$transcript" 2>/dev/null)" [ -z "$last_text" ] && exit 0 # ── 豁免一:撞權限閘/明說四題命中 → 真的停不下來,放行 ── if printf '%s' "$last_text" | grep -qiE '權限閘|classifier|分類器擋|四題第|人閘|需要你放行|denied by'; then exit 0 fi # ── 是否有「完成宣稱」? ── if ! printf '%s' "$last_text" | grep -qiE '✅|完成了|已完成|通了|部署成功|已上線|已部署|搞定|做完了|全綠|收工'; then exit 0 fi # ── 豁免二:已有「夠格的」實測證據 → 放行 ── # # ⚠️ 本段自身踩過的雷(2026-07-22 實測 t2 抓到): # 舊版寫 `HTTP [0-9]{3}` 就放行 → 「curl 回 HTTP 200,這關通了」直接過關。 # 那正是要防的假綠原句(landing 回 200 但按鈕 href 還是舊的)。 # **把要防的東西寫成通行證**=規則 6「HTTP 200 不算驗過」被自己的 hook 架空。 # # 現在的判準:2xx/3xx 一律不算證據;只有下列才算—— # ① 抓到畫面內容(curl|grep / href= / )② 壞消息碼(4xx/5xx,誠實回報不罰) # ③ DNS 實查 ④ 測試數字 ⑤ 檔案:行號 ⑥ 明確標 ❌/◐(沒宣稱通就不必擋) if printf '%s' "$last_text" | grep -qiE 'curl -s[^|]*\| *grep|href=|<title>|HTTP (4[0-9]{2}|5[0-9]{2})|dig \+short|[0-9]+/[0-9]+ (通過|綠|passed)|\.md:[0-9]+|❌ 斷|◐ 半通'; then exit 0 fi cat >&2 << 'EOF' 🚓 交付警察:你宣稱完成,但我沒看到「實測證據」。標 ✅ 之前先答: 【最高規範】leo 2026-08-17(他當場更正自己前一句的範圍): 「我剛剛講錯一件事,我說你推給我的版本要測過,我錯了—— **不是版本要測過,是任何東西你交的貨都要測過,一個 PR 也要測過, 你不允許交出你不知道能不能用的東西。** **這不是對版本的規範,這是對你所有東西的規範。**」 ⇒ 「交貨」=**任何交到別人手上的東西**:版本/PR 分支/票上的結論/ 派工單裡寫的「現況」/回報的每一句。 ⇒ **判準只有一句:交出去之前,我知不知道它能不能用?** 不知道 → 它還沒到可以交的狀態,不是「交了再說」。 ⚠️ **「我沒有說它能用」不構成豁免**——接收方還是得自己驗,成本一樣轉嫁出去了。 🔴 **`report` 與 `deliver` 是兩個字**(leo 2026-08-17 當場糾正總管的後門寫法): 「你可以說你有進度,**這是 report**;**但驗完那一格才能交,這是 deliver**。」 report 有進度、有發現、有卡點 ← 可以有沒驗的部分,標明即可 deliver 交到別人手上的東西 ← 每一格都驗過 ⇒ **「有一格沒驗」不是交付的例外,它是「還沒到交付」的定義。** 那個狀態要說「我報告一下進度」,不是「我交給你但有一格沒驗」。 【判準】leo 2026-07-22:「它是我的助理,如果都是我檢查,那我就是它的助理。」 → leo 如果現在去測,會不會發現你沒發現的東西?會 → 現在就去測,別交。 【判準】leo 2026-07-22:「友善=前端。人去按了連到錯的機制、安裝失敗,不算友善。」 → 驗收只認**用戶真的會走的那條路**,不是你做的那個零件。 1. 有前端嗎?貼「抓實際畫面內容」的輸出: curl -s <public 網址> | grep <該出現的字串> ⚠️ HTTP 200 不算驗過——200 只證明伺服器活著,不證明用戶看到對的東西。 (真實踩雷:landing 回 200,但按鈕 href 還是舊的 deploy.workers.cloudflare.com) 2. 對外網址四件驗了嗎? ① custom domain 綁對專案?② DNS 解析得到?③ 前端 apiBase 對?④ CORS 放行? (真實踩雷:install.arcrun.dev 有 DNS 但 HTTP 522、畫面全空=網域建了沒綁 worker) 3. 網址還是 *.workers.dev / *.pages.dev? → 那叫「僅預設網域」,用戶拿不到,不准標 ✅,最多 ◐ 半通。 🔵 **例外:stage/測試環境不受此條約束**(leo 2026-08-07: 「Stage 直接用 workers.dev 不行?**反正只有你我用**」)。 本條的目的是「用戶拿不到=沒交付」——stage 的用戶就是 leo 與總管, *.workers.dev 我們拿得到 ⇒ 前提不成立。 而且 stage 不掛 custom domain 反而是優點(不會有人誤入/被搜尋引擎索引/ 把 stage 網址當正式的傳出去)。 ⇒ **stage 的驗收標準=「leo 與總管能不能在上面測到真東西」,不是網域形式。** 4. 你自己找到並修掉了哪些問題?一項都沒有=多半沒認真找。 答不出來就是還沒完成——現在去測。規格見 CRITICAL-PATH.md 使用規則 4、5。 EOF exit 2