Files
ISEP/hooks/shadow-table-guard.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

111 lines
5.5 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.
#!/bin/bash
# PreToolUse hook — 影子表警察:抓 D91 的指紋(宣告 schema 在一邊,資料倒進 JSON 團在另一邊)
#
# 🚫 **本檔目前是死的:沒有註冊進 .claude/settings.json,不會被執行。**
# leo 2026-08-15:「**我是要你提案封鎖方式,不是要你就去做,急着做造成太多疊牀架屋了。**」
# ⇒ 總管在討論「疊牀架屋是病」的同時去疊了這一層。已測過(該擋兩條擋、六條不該擋全放),
# 但**要不要掛由 leo 裁**——它只在「過渡期止血」這一個用途上值得存在。
# ⇒ 真正的封鎖方式是**刪掉 metadata_json 與 entry_type 兩個欄位**(減,不是加),
# 提案見 matrix/arcrun 的 pending-changes.md。
#
# ⚠️ 這支是**過渡期的止血帶,不是答案**(leo 2026-08-15 定調)。
# 真正的解是「**刪掉那兩個欄位,讓儲存層沒有位置放壞東西**」——提案在 matrix/arcrun 的 pending-changes.md。
# 偵測永遠落後一步:D91 就是從「多開一張表」變異成「JSON 團」的。
# 在收窄的 API 出來之前,這支至少擋得住已知的那個形狀。**別因為有閘就以為安全。**
#
# 【為什麼 kbdb-api-wall-guard 抓不到】
# 那支守著 D38「三張表打天下,永不加表」,而 D91 **完全沒有違反它**——一張表都沒加。
# 它做的是:seed 一張宣告 6 個欄位的 template,然後把資料倒進 metadata_json。
# ⇒ **一張沒人管得到的影子表,而表數是三。現有的閘數表,而病不在表數上。**
#
# 【病徵】kbdb/migrations/0004_execution_log_template.sql 自己的註解就寫著
# 「template 這裡只負責 schema 文件化⋯⋯結構化欄位打包進 metadata_json」
# ⇒ 那張宣告了 6 個欄位的 template,沒有任何東西讀它。
# 【擴散】同註解:「recipe_stat 早已示範這個模式合法」
# ⇒ 活著的 7 種 entry_type 裡有 6 種是這形狀——不是角落,是多數。
#
# 攔的是**形狀不是關鍵字**:型別標籤與 JSON 團打包必須**同時**出現才命中
# (今天已有四次閘因為比對指令形狀而誤攔,不再犯)。註解、測試、文件一律放行。
# 豁免:該行尾加 `shadow-table-ok`(留痕,commit 說明理由)。
#
# 全文:頂層 system-dev/wiki/decisions-summary.md D91、D92。
exec python3 -c '
import json, re, sys
try:
d = json.load(sys.stdin)
except Exception:
sys.exit(0)
if d.get("tool_name") not in ("Write", "Edit", "MultiEdit"):
sys.exit(0)
i = d.get("tool_input") or {}
path = i.get("file_path") or ""
if not path:
sys.exit(0)
parts = [i.get("content") or "", i.get("new_string") or ""]
for e in (i.get("edits") or []):
parts.append(e.get("new_string") or "")
body = "\n".join(p for p in parts if p)
if not body:
sys.exit(0)
low = path.lower()
if re.search(r"\.(md|txt|json|yaml|yml)$", low): sys.exit(0)
if re.search(r"(^|/)(wiki|docs|tests?)/", low): sys.exit(0)
if re.search(r"(test|spec)\.[a-z]+$|_test\.go$", low): sys.exit(0)
if not re.search(r"\.(ts|js|mjs|sql|go)$", low): sys.exit(0)
live = []
for line in body.splitlines():
if "shadow-table-ok" in line:
continue
s = re.sub(r"--.*$", "", line)
s = re.sub(r"//.*$", "", s)
if re.match(r"\s*[*#]", s):
continue
live.append(s)
live = "\n".join(live)
# A:把型別標籤釘在 entries 上
a = len(re.findall(r"""entry_type\s*[:=]\s*[\x27"][a-z_]+[\x27"]""", live))
# B:結構化欄位被打包進 JSON 團
b = len(re.findall(r"""metadata_json[^\n]{0,80}(JSON\.stringify|json_object|\{)|(JSON\.stringify|json_object)\([^\n]{0,80}metadata_json""", live))
# Cseed 一張宣告了欄位的 template
c = len(re.findall(r"""INSERT\s+(OR\s+IGNORE\s+)?INTO\s+templates\b[^;]{0,200}slots_json|slots_json\s*[:=]""", live, re.I | re.S))
if not ((a and b) or (c and b)):
sys.exit(0)
sys.stderr.write(f"""❌ BLOCKED — 影子表警察(D91 的指紋)
檔案:{path}
命中:型別標籤 {a} 處/JSON 團打包 {b} 處/template 宣告 {c} 處
你正在寫的形狀是:**宣告一個 schema,然後把資料倒進 metadata_json**。
那會做出一張**沒人管得到的影子表**——表數還是三,但欄位從此改不動:
要加一欄,得同時改「一個沒人讀的宣告」和「一段打包程式」,
而**沒有任何機制會告訴你它們已經對不上了**。
leo 2026-08-15:「這每個都是一個 template⋯⋯**以後你不能修改 fields,因為表禁止動**,
如果要多一欄,要不就是說要改表,**要不就是全塞在能塞的格子裡**。」
先回答這三題,答不出來就不是這樣寫:
① 這些被打包的欄位,**誰會讀 schema 宣告**?沒有人讀 ⇒ 那個宣告是裝飾品
② 要加第八個欄位時,**有什麼東西會擋住「只改了一半」**?
③ 這批資料的形狀是**我們決定**的,還是**使用者的想法**決定的?
使用者決定 ⇒ 不准有 schema,走 v-table
Arcrun#133 的判準:這張表要改的時候,誰付 migration 的代價?)
真的有理由這樣寫(例:那條路徑的寫入放大有實測數字,且已在票上記錄)
⇒ 該行尾加 `shadow-table-ok`,並在 commit 說明理由。
⚠️ 本閘是過渡期止血帶。真正的解是刪掉 metadata_json 與 entry_type(減,不是加)。
參考:system-dev/wiki/decisions-summary.md **D91**(病徵與擴散)、**D92**(同族形狀)
""")
sys.exit(2)
'