Files
ISEP/hooks/unpushed-police.sh
Leo c2638668e3 ISEP 0.1.0:環境設定收成一個 plugin,本機與雲端共用一份
leo 2026-08-20:「同一個 plugin 你用,薄殼也用,保證兩邊同步」
              「我要你幫雲端做薄殼,永遠都有問題,你要做的就是這組設定
                你自己可以 dogfooding」

搬進來:41 支 hook(51 條註冊)/7 支 command/2 支 skill/23 支腳本。
不搬 .env、wiki、docs——那些是知識不是環境。

51 條 hook 路徑全部從 $CLAUDE_PROJECT_DIR/.claude/hooks/ 改成 ${CLAUDE_PLUGIN_ROOT}/hooks/,
零漏網。那正是薄殼一直壞掉的根:雲端 cwd 不是真身,寫死路徑就斷。

尚未驗證:Claude Code 能不能從私有 Gitea repo 裝 marketplace(要憑證)。
下一步就是在本機實際裝一次,通了才動雲端 bootstrap.sh。

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-20 11:41:46 +08:00

191 lines
11 KiB
Bash
Executable File
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/usr/bin/env bash
# unpushed-police.sh — 未推警察(Stop / SubagentStop hook
#
# 🔴 2026-08-05:本機 LANG=C.UTF-8 下,把變數(分支名/日期)串進含全形標點的字串會**吃掉字元**
# (實測 `- $b:$cnt 筆` → 分支名整個消失、只剩「:」的殘骸位元組 bc 9a)。
# LC_ALL=C 下同一段完全正常 ⇒ 固定 locale,別讓報告內容被環境吃掉。
export LC_ALL=C
#
# 病根(leo 2026-08-02 原話):「如果讓改了沒推會被警告?因為太常發生了」
#
# 一天之內同一個病發作六次,全部是「**改對了,但沒送到用戶手上**」:
# ① Gemini key 修好了(Arcrun 0d49989)→ 沒重建 cypher bundle ⇒ 線上 admin/ai=0
# leo 與全體封測者填 key 一律無效,rag_chat 全壞一整天
# ② CIS logo 換好了(512ea9f 7/31 20:39)→ daemon zip 是 15:35 打的 ⇒ 早 5 小時,
# 發出去的永遠是舊圖(leo 肉眼在 Finder 抓到)
# ③ manifest.daemon 欄被 release.mjs 吃掉 → 全體「檢查更新」壞掉一天
# ④ 推了 GitHub 但 jsDelivr @main 還在吐舊的 ⇒ 推對了用戶仍拿不到
# ⑤ fix/cis-round3-install 分支從未 push(今天才發現)
# ⑥ 07-30 同款前科:t143-t150 十筆 commit 只在本機,雲端誤報「線上程式碼失蹤」
#
# 為什麼要 hook 而非規則:scripts/check-deploy-drift.sh 七月三十就寫好了,
# 但**沒有任何東西會自動呼叫它**——只在文件裡被提到。
# 「機制存在 ≠ 會被讀到」。所以這支要自己跳出來,不能等人想到要跑。
#
# 機制:收工那刻掃所有已知 repo,有「未 commit」或「已 commit 未推」就 exit 2 提醒。
# 只提醒不阻擋——push 需要 leo 開閘(D20),hook 無權也不該代按。
# 豁免:純對話回合(沒動過檔)/已在本回合推過/stop_hook_active。
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
TOP="${CLAUDE_PROJECT_DIR:-$(cd "$(dirname "$0")/../.." && pwd)}"
# 掃描對象:頂層 + 各子 repo(存在才掃)。worktree 一併涵蓋(git -C 會自己解)。
REPOS="$TOP $TOP/matrix/arcrun $TOP/products/arcrun-rag $TOP/polaris/mira"
problems=""
for r in $REPOS; do
[ -d "$r/.git" ] || [ -f "$r/.git" ] || continue
name="${r#$TOP/}"; [ "$name" = "$TOP" ] && name="(頂層 InkStoneCo"
# ① 有改動沒 commit(排除 untracked,那多半是暫存/產物)
dirty="$(git -C "$r" diff --shortstat 2>/dev/null | head -1)"
staged="$(git -C "$r" diff --cached --shortstat 2>/dev/null | head -1)"
# ② 已 commit 但沒推(**當前分支**
unpushed=""
br="$(git -C "$r" branch --show-current 2>/dev/null)"
if [ -n "$br" ]; then
up="$(git -C "$r" rev-parse --abbrev-ref "@{upstream}" 2>/dev/null || true)"
if [ -n "$up" ]; then
n="$(git -C "$r" rev-list --count "$up".."$br" 2>/dev/null || echo 0)"
[ "$n" != "0" ] && unpushed="領先 $up $n 筆"
else
# 沒 upstream=這條分支從來沒推過(今天 fix/cis-round3-install 就是這樣)
has_commits="$(git -C "$r" rev-list --count HEAD 2>/dev/null || echo 0)"
[ "$has_commits" != "0" ] && unpushed="分支 $br **從未推過**(無 upstream"
fi
fi
# ③ 🔴 2026-08-05 t195 補:**散落分支**——有 commit 沒併回當前分支的其他本地分支。
# 原本只查 ①②(當前分支的髒樹/未推),所以 D36 授權修補躺在 fix/cis-round3-install
# 整整一週都沒被報過,害 feat/daemon 的收卡端全 401、新卡一張都寫不進去。
# leo 2026-08-05:「為什麼有分支沒 merge?都是你寫的,驗過就要 merge⋯⋯
# 如果它會留分支無明顯理由不 merge,那就要整理」。
# 判準=「這條分支有沒有當前分支沒有的 commit」,不是分支數量多寡。
stray=""
held=""
# 🔴 2026-08-15 訂正:基準**不能用當前簽出的分支**。
# 共用工作區常停在某條舊功能分支上(08-15 實撞:matrix/arcrun 停在
# fix/credential-resolve-without-auth-worker,而 main 已經領先它 17 筆)
# ⇒ 四條分支全被報成「散落」,其中三條早就併進 main 了。
# **這正是同一晚重複八次的那個病:檢查跑了,但檢查的對象是錯的。**
# ⇒ 基準改成「整合分支」:gitea/main > origin/main > main > 當前分支。
base="$br"
for cand in gitea/main origin/main main; do
if git -C "$r" rev-parse --verify -q "$cand" >/dev/null 2>&1; then base="$cand"; break; fi
done
if [ -n "$base" ]; then
for b in $(git -C "$r" for-each-ref --format='%(refname:short)' refs/heads/ 2>/dev/null); do
[ "$b" = "$base" ] && continue
git -C "$r" merge-base --is-ancestor "$b" "$base" 2>/dev/null && continue
cnt="$(git -C "$r" rev-list --count "$base".."$b" 2>/dev/null || echo 0)"
[ "$cnt" = "0" ] && continue
# ── 刻意保留的分支(2026-08-12 加)─────────────────────────────────
# 本閘原本只認得「該併」與「該刪」兩種,但**還有第三種:刻意不併**
# (例:`wip/stopped-agents-2026-08-10` 是 arcrun-rag#56 那六個 Go 檔的唯一一份,
# 保留理由與刪除條件記在該票上)。
# 沒有這個狀態 ⇒ 那條分支**每一回合都被叫一次**,而且叫的是同一件已經交代過的事。
# 🔴 **永遠在響的警報,等於訓練人忽略這個警報**——那才是真正的損失,
# 因為下一條真的失蹤的分支會混在同一堆雜訊裡。
# 門檻刻意留著:要寫一行理由進 `.claude/branch-holds.md` 才生效,
# **不能只在對話裡說**(對話會消失,這正是本閘存在的理由)。
_holds="$TOP/.claude/branch-holds.md"
if [ -f "$_holds" ] && grep -qF "$b" "$_holds" 2>/dev/null; then
# 🔴 這裡**不要**用 sed 去切掉「分支名:」那段前綴。
# 本檔開頭第 4 行已經記過:`LC_ALL=C` 下處理含全形標點的字串會咬掉位元組,
# 實測切完會吐出 `刻意保留——\xef\xbf\xbd它是` 這種殘骸。
# 整行照印就好——那一行本來就寫得像人話,前綴留著也讀得通。
_why="$(grep -F "$b" "$_holds" | head -1)"
held="$held
$_why"
continue
fi
age="$(git -C "$r" log -1 --format='%ad' --date=format:'%m-%d' "$b" 2>/dev/null)"
# 用真實換行累積,不用 \n 逃脫——否則後面的 printf '%b' 會把分支名再解讀一次
stray="$stray
- $b$cnt 筆未併入 $base(最後動 $age"
done
fi
line=""
[ -n "$dirty" ] && line="$line
· 未 commit$dirty"
[ -n "$staged" ] && line="$line
· 已 staged 未 commit$staged"
[ -n "$unpushed" ] && line="$line
· $unpushed"
[ -n "$stray" ] && line="$line
· 🔀 **散落分支**(有 commit 沒併回 $br):$stray"
# 刻意保留的分支**不會自己觸發警報**(那正是加它的目的),
# 但這個 repo 若本來就有別的問題要報,就順便把它列出來當脈絡——
# 免得「保留」變成另一種看不見。
[ -n "$line" ] && [ -n "$held" ] && line="$line
· ⏸️ 刻意保留(不觸發警報,理由在 .claude/branch-holds.md):$held"
[ -n "$line" ] && problems="$problems
📍 $name$line"
done
[ -z "$problems" ] && exit 0
cat >&2 <<EOF
🚓 未推警察:有東西改了但還留在本機。
【leo 2026-08-02】「如果讓改了沒推會被警告?**因為太常發生了**」
【判準】改對了 ≠ 送到用戶手上。同一天發作六次,每次都是這個病。
$problems
收工前擇一處理(**一句話交代也算,別默默留著**):
1. 該推就推 → Gitea 直接 push(不需開閘);GitHub 要 leo 跑 scripts/github-arm.sh
2. 還不該推 → 在回覆裡**明說**「X 先不推,因為 Y」,讓 leo 知道有東西在路上
3. 是暫存/產物不該進版控 → 加 .gitignore 或刪掉,別讓它每次都來吵
⚠️ 特別注意「**從未推過**」的分支:那是最容易變成失蹤程式碼的一種
07-30 t143-t150 十筆、08-02 fix/cis-round3-install 都是這樣被發現的)。
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
🔀 **散落分支怎麼處理**leo 2026-08-05:「驗過就要 merge⋯⋯無明顯理由不 merge 就要整理」)
**它害過什麼**:D36 授權修補(07-29)躺在 fix/cis-round3-installfeat/daemon 沒合併
⇒ 收卡端全 401、**新卡整整一週一張都沒寫進 youlin**,還害人往「token 過期」查錯方向。
**分支沒合併不是整潔問題,是會產生「修好了卻沒生效」的假象。**
每條散落分支擇一,**不准放著不管**:
A. **驗過了 → 合併**(這是預設):git merge 該分支,或 cherry-pick 需要的 commit
B. **還沒驗完 → 說明它在等什麼**,並在回覆裡標「等 X 才能併」
C. **已作廢 → 刪掉**git branch -D 該分支(留著只會讓下一個人以為它有效)
📌 判準是「**這條分支有沒有當前分支缺的東西**」,不是分支數量。
合併前先看它改了什麼:git log --oneline 當前..該分支、
git diff 當前...該分支 --stat
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
🎯 CP 對帳(leo 2026-08-02:「**現在有 CP 要求到最終用戶會看到更新,
但還是會出現 commit 沒推,這表示完全忽略了 CP**」)
CRITICAL-PATH.md 早就寫死:
· 狀態只有三種:✅ 通(**附實測證據**)/◐ 半通(標明缺什麼)/❌ 斷
· **「程式碼寫完了」不是狀態**
· code 寫完沒部署,不准標 ✅
規則寫在那裡卻照樣違反,因為**自評的人跟做事的人是同一個**。所以在這裡問你:
Q1 這回合改的東西,服務的是哪一條 CP 的哪一步?
Q2 那一步現在是 ✅ / ◐ / ❌?**上面列出的未推項目,就是它還不能算 ✅ 的證據**
Q3 最終用戶現在去看,會看到這次的改動嗎?
看不到 → 它是 ◐,**不准在回覆裡寫「完成」「修好了」**,要寫「已改,未送達」
📌 推完 ≠ 用戶拿得到——中間夾 CDN/bundle/實例快取時,
必須從**用戶端的網址、用用戶端的參數形式**再驗一次
08-02 實例:GitHub 推對了,jsDelivr @main 仍吐三代前的 manifest)。
EOF
exit 2