Files
ISEP/README.md
T
Claude c7af690c2b sdd-guard 退役:讓「取消 Active SDD」這個裁決真的執行得下去(inkstone/ISEP#91)
leo 2026-08-16 在 inkstone/InkStoneCo#40 → comment 2942 裁定「取消 Active SDD」,
11 天沒被執行。原因不是有人偷懶,是**執行它的第一步會鎖死自己**:

    sdd-guard.sh 是 fail-closed 的
      · 動 code 檔時 status: active 不是恰好 1 份 → 擋(0 份也擋)
      · 路徑所在的 repo 沒有 3-specs           → 也擋

⇒ 照裁決把最後那份 active 拿掉 → 變 0 份 → 任何人動任何 .ts/.py/.go 全被擋。

而 ISEP 這個 repo 自己就沒有 3-specs——本票開工第一件事實測到:

    $ echo '{"tool_name":"Edit","tool_input":{"file_path":".../hooks/lib/dispatch_parse.py"}}' \
        | bash hooks/sdd-guard.sh
    🚫 SDD 協議攔截:… 找不到任何 SDD        exit 2

**它一直在誤攔 ISEP 自己,只是沒人回報。**

── 這一版做了什麼 ────────────────────────────────────
· hooks/sdd-guard.sh 刪除,hooks.json 取消註冊(85 → 84 條,只少這一條)
· commands/sdd-check.md 改寫:SDD 只記「起初的樣子」,任務本體在 Gitea 票;
  找不到 SDD 不再是停下來的理由,找不到票才是
· agents/inkstoneco-hand.md 拿掉「任何時刻只允許一份 status: active」那條紅線
· hooks/lib/path-resolve.sh 只加註解:它的唯一 caller 走了,但別順手刪
  (#22 學到的東西住在裡面,十幾支閘還在用它要修的那個寫法)

── 迴歸測試:測的不是「檔案刪了沒」,是「那個擋還會不會發生」──
hooks/tests/sdd-guard-retired.test.sh(通過 8/失敗 0,離線):
把「裁決執行完之後的世界」(沒有 3-specs 的 repo、兩份 status: active 的 repo)
丟給 hooks.json 上**整組** Write|Edit|MultiEdit 的閘——清單當場從 hooks.json 讀、
不寫死——不准有任何一支用 SDD/3-specs 當理由擋下來。
⇒ 日後有人換個檔名把同一個形狀種回來,這支照樣紅。

🔴 判準刻意不是「一支閘都不准擋」:同組還住著跟 SDD 無關、且看 session 狀態
決定擋不擋的閘(history-first/subagent-first)。把它們算失敗,這支測試會在別人
改別的東西時無故變紅,紅久了就沒人看——誤攔比漏擋更該修,對測試一樣成立。

**紅的證明**:把 sdd-guard 暫時復原(檔案+註冊)重跑 → 通過 1/失敗 7,
四條行為格全部指名 sdd-guard.sh。已還原。

── 自己跑過的 ──────────────────────────────────────
· hooks/tests/sdd-guard-retired.test.sh                通過 8/失敗 0
· 逐支點名 hooks.json(README「裝什麼」那道指令)      84 條,對 main 做集合差:
  少了 sdd-guard.sh × 1,多出 0 支,其餘一支不差
· 盤點數字全部在這棵樹上實數,不是加減推:
  ls hooks/*.sh|wc -l = 61(原 62)
  grep -c '"command":' hooks/hooks.json = 84(原 85)
  agents 7/commands 7/skills 2/scripts 48(皆未變動)
· 盤點表對帳三格(hooks-inventory 自己寫死的那三道):三格皆無輸出
· scripts/check-version-consistency.sh                  0.16.2 一致
· claude plugin validate .                              Validation passed
· hooks/tests/ 全部離線測試 24 支重跑:本次改動 0 退步
  (dispatch-format-guard 40/52、prod-write-guard 18/19-fail、
   stage-before-prod-guard 9/7-fail、另 3 支需帶參數/建不起沙盒——
   **六支在 gitea/main 上逐支重跑結果一模一樣,是既有狀態不是本次造成**)

── 沒做、也不該由這張票做的 ─────────────────────────
· inkstone/InkStoneCo 那半(拿掉 active SDD + 刪 CLAUDE.md「單一活性鐵律」段)
  ——那是 inkstoneco-hand 的 repo;而且順序上本來就要等這一版出去、
  兩邊 /plugin update 之後才動得,先拿掉就鎖死
· checkbox 分診(掛 inkstone/InkStoneCo#49,票上明寫不要另開票)
· 版本號:待總管定版

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016ZBu4Sa1cGntKFRBYNZ6xs
2026-08-31 00:56:37 +00:00

93 lines
4.8 KiB
Markdown
Raw 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.
# ISEP — InkStone Environment Plugin
> leo 2026-08-20:「你把全部環境設定放在一個 claude code plugin**同一個 plugin 你用,薄殼也用,保證兩邊同步**⋯⋯
> 以後有任何變化,增加 command, hook⋯⋯都增加在這裡,再去跟它同步。」
> 「我要你幫雲端做薄殼,永遠都有問題,**你要做的就是這組設定你自己可以 dogfooding**。」
## 這個 repo 解什麼
在此之前,同一套環境有**兩份**
```
真身 InkStoneCo/.claude/ ← 本機在跑的
薄殼 由 generate-shell-payload.py 產生一份,塞進 GitHub 私 repo ← 雲端在跑的
```
兩份必然漂移。`inkstone/InkStoneCo#57` 記著實測結果:**薄殼比真身少 7 支閘**,
其中兩支是前一天才立的。`#14` 更早:雲端 33 支 guard **一支都沒生效**
⇒ 現在只有一份:**本 repo 就是唯一真相源**,本機與雲端裝同一個 plugin。
⇒ 而且**總管自己也用它**——壞掉的時候是我先踩到,不是雲端替我踩。
## 裝什麼
| | 數量 | 是什麼 |
|---|---|---|
| `hooks/` | 61 支 `hooks.json` | 全部機械閘(PreToolUseStopSubagentStopSessionStartPostToolUseUserPromptSubmit 共 84 條註冊)。**一支一行的白話盤點在 `docs/hooks-inventory.md`,那裡才是這兩個數字的家** |
| `agents/` | 7 位 | **工人名單**`inkstone/ISEP#86`)——派工時指名派給誰,規約見 `docs/governance/worker-roster.md` |
| `commands/` | 7 支 | `/wiki-recall` `/ship-check` `/cp-write` … |
| `skills/` | 2 支 | |
| `scripts/` | 48 支 | `ticket``roster``isep-nag``wiki-compress``github-arm.sh` …(頂層檔案,不含 `lib/` 等子目錄) |
> 🔴 這五個數字**每次都要在自己的樹上實數**,不准沿用上一版、也不准用加減推
> `system-dev/wiki/mistakes.md`:那是 leo 的驗收介面,多報就是假綠):
>
> ```sh
> ls hooks/*.sh | wc -l # hooks
> grep -c '"command":' hooks/hooks.json # 註冊條數(冒號不能省,省了會數到 "type": "command"
> ls agents/*.md | wc -l
> ls commands/*.md | wc -l ; ls -d skills/*/ | wc -l
> ls -p scripts | grep -v / | wc -l # scripts(只數檔案,不含子目錄)
> ```
>
> 🔴 **光看數字對不對還不夠**——`hooks.json` 合錯的時候「語法合法、閘卻不見了」,
> **不會有任何東西喊一聲**。合併過 `hooks.json` 之後一律再跑這一支,逐支點名:
>
> ```sh
> python3 -c "
> import json,collections
> d=json.load(open('hooks/hooks.json'))
> c=collections.Counter(h['command'].split('/')[-1] for ev in d['hooks'].values() for g in ev for h in g.get('hooks',[]))
> print('總註冊', sum(c.values()))
> for k in sorted(c): print(' ', k, c[k])
> "
> ```
>
> **這是 leo 的驗收介面**——多報就是假綠(`docs/hooks-inventory.md` 開頭記著同一個病)。
**不放**`.env`(金鑰,違 D36「金鑰只有一個家」)、`wiki/``docs/``_archive/`
——那些是**知識**不是**環境**。
## 路徑規約(薄殼一直壞掉的根)
hook 一律用官方的 `${CLAUDE_PLUGIN_ROOT}`**不准寫死絕對路徑、也不用 `$CLAUDE_PROJECT_DIR`
去指 hook 自己**:雲端的 cwd 不是真身,寫死就斷。
腳本**內部**要指專案檔案(wiki、docs)時才用 `$CLAUDE_PROJECT_DIR`——那是對的,
因為那些東西本來就住在被操作的那個 repo 裡。
## 改東西的規矩
🔴 **只改這裡,然後兩邊 `/plugin update`。**
不要再改 `InkStoneCo/.claude/hooks/`——那個目錄退場中。
## 這些閘各自在管什麼
**不用點開任何 `.sh`**——`docs/hooks-inventory.md` 一支一行白話,按「你會在什麼時候撞到它」分組。
測試手冊在 `docs/TESTING.md`,治理規範在 `docs/governance/`
🔴 **派工前先讀 `docs/governance/dispatch-and-reply-format.md`**——
派工單只給票號(其餘全部寫進票或共通規定),票上的每一則留言第一行要表明身份。
兩件事都有機械閘(`dispatch-format-guard.sh``reply-identity-guard.sh`)。
## 版本
**「ISEP 現在是哪一版」只有一個地方答得出來:[Gitea Releases](https://git.uncle6.me/inkstone/ISEP/releases)。**
每一版的內容、改了什麼、驗過什麼都寫在那裡的 release note,不寫在這份 README。
這份 README 本來寫死過「狀態:0.1.0」,但 repo 一個 tag 都沒打(`release_counter=0`)——
leo 當場指出這是違規:宣稱交付卻沒有 tag 撐它(`inkstone/ISEP#6`)。
現在改成結構性防漂移:`.claude-plugin/plugin.json``version` 欄位永遠跟最新 tag 一致,
`scripts/check-version-consistency.sh` 會擋下兩者對不上的狀態,
`hooks/release-tag-guard.sh` 則在打 tag 的當下直接擋住不一致的 tag——
細節與判準都寫在那兩支腳本開頭的註解。