Leo
e474b4cb6f
棒子不會掉:子票相依+tag+指派三格,全部用 Gitea 原生欄位
...
leo 2026-08-27:「子票相依是 gitea 原有機制,改 tag 和指定也是,這些全部都要」
08-26 掉的那一次(arcrun-rag#136 comment 4267「等雲端那半出貨才驗得了」躺了
14 小時)三格全缺:沒有子票、tag 沒動、沒有指派給任何人。根因不是誰忘了,
是那件事沒有落在任何一個「撈一次就看得到」的欄位上。
實測(Gitea 1.26.4):子票還開著時關母票 → HTTP 412
"cannot close this issue or pull request because it still has open dependencies"
⇒ 相依是平台保證的硬擋,不是提醒。跨 repo 也成立(201)。
- scripts/ticket 新增三個動詞:subtask(長子票+掛相依)/
handback(指派+改 tag+寫下一步,一個動作)/mine(撈一次看棒子在誰手上)
- hooks/comment-carries-task-guard.sh:留言帶未完成的未來式卻沒開子票 → 擋一次
- hooks/baton-handback-guard.sh:一條線收工,三格缺哪一格當場說出來(提醒不擋)
- docs/governance §16:三個維度/粒度(傾向多開票)/既有票只從今天起適用/
與 s/* 的關係/實測輸出
- 測試 20+10 全綠,用 fixture 跑不打網路、不在票池留測試票
📌 踩到並記進閘的檔頭:hook 裡比對中文一律用 python3 的 re,不要用 grep 的字元類
(等[^。]{0,20}出貨 在真實 hook 呼叫路徑下對「等雲端那半出貨」不匹配,閘靜默失效)
票:inkstone/ISEP#30(comment 4334/4335/4346/4347)
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com >
2026-08-27 12:05:53 +08:00
Leo
8e7e265589
merge: 閘掛在收工後,而 leo 是在被問到的當下被打擾——補上那一刻的攔截(InkStoneCo#55)
...
leo 2026-08-26:「今天已經好幾次問我,為什麼 hooks 沒有攔下來?」
總管複驗過才併:
- 掛點:PreToolUse / matcher AskUserQuestion(自己從 hooks.json 解出來的)
- 封動作不封文字:整支 261 行零個判擋用的正則,決定 exit 2 的只有判官 verdict;
判官掛掉/回空/不在 PATH 一律 fail-open
- 測試自己跑過 14/14(含「樹要不要捲」那種真人閘必須放行的案例)
- 版本 0.3.9 → 0.4.0:第一版忘了升版,而產物是按版本號分資料夾的
⇒ 不升版=新閘永遠不會被載入。退回補後已驗產物資料夾長得出來。
🔴 仍有一格是 report 不是 deliver:「新 session 真的會叫它」驗不到——
AskUserQuestion 官方明載 subagent 不可用,它撞不到自己這道閘。
那一格要 leo 開新 session 才驗得了。
v0.4.0
2026-08-26 22:43:34 +08:00
claude-code
e0557334bc
閘掛在「收工後」,而 leo 是在「被問到」的當下被打擾——補上那一刻的攔截
...
leo 2026-08-26:「今天已經好幾次問我,為什麼 hooks 沒有攔下來?」
實查:總管問 leo 走的動作是 AskUserQuestion,而 hooks.json 裡它出現 0 次,
沒有任何 matcher。判準其實早就寫好了——self-drive-police / self-drive-judge
用的就是同一套四題公式——但那兩支只掛在 Stop 與 SubagentStop,
是回合結束後才跑的。問題早就送到他眼前了,事後再反問「你查過了嗎」,
成本已經轉嫁出去。判準對了,時機錯了。
新增 hooks/ask-user-question-guard.sh(PreToolUse / AskUserQuestion):
- 觸發條件是那個動作本身,不是任何句型或關鍵字——全檔零個判擋用的正則,
換句話說閃不過去,講得謹慎也不會被多罰(leo 2026-08-17 文字層封路的檢驗)
- 進來之後用四題公式的 haiku 判官分「該問 / 不該問」,命中任一題一律放行
- 同一個問題只擋一次(雜湊戳記):判官誤判時重送即過,
leo 該收到的問題不會因為一支閘而永遠送不到
- 判官掛掉/沒網路/claude 不在 PATH 一律 fail-open,壞掉等於它不存在
版本 0.3.9 → 0.4.0。這不是儀式,是傳輸機制本身:產物按版本號分資料夾
(~/.claude/plugins/cache/inkstone/isep/<版本>/),版本沒動就不會長出新資料夾,
這支閘一個 session 都載入不到。docs/TESTING.md 開頭那段講的就是這件事,
而第一版我漏了——總管複驗時量出來的。
假設(沒有前例可循,先裁再記):跳 0.4.0 而不是 0.3.10。理由是這一版第一次
掛上 AskUserQuestion 這個事件面,是新能力不是修補;而且 0.3.10 在
plugin 快取目錄的 ls 裡會排到 0.3.1 旁邊,肉眼不好認。錯了打回,改號很便宜。
順手修掉自己寫出來的一個坑:訊息原本用沒加引號的 heredoc,
反引號被當命令執行,wiki 路徑與豁免指令兩行變成空白(閘照擋,只看離開碼看不出來)。
已收成 mistakes.md 一條,並由 ⑩b 這條測試守著。
實測:
- hooks/tests/ask-user-question-guard.test.sh 14/14(離線,不花錢)
- hooks/tests/ask-user-question-guard.live.test.sh 9/9 連跑三次(真的叫 haiku)
A 群 5 條真人閘(花錢/不可逆/跨專案結構/品味方向/物理人閘)全部放行,誤攔 0
B 群 4 條純技術路徑選擇全部擋下
- 既有 7 支測試與改動前逐條對照,結果完全相同(沒有被我弄壞)
順手對帳:plugin.json 與 marketplace.json 的描述寫「43 支機械閘、53 條註冊」,
實際數過是 46/56(含本次新增這支)。docs/hooks-inventory.md 一併更正。
【工單】inkstone/InkStoneCo#55
2026-08-26 22:36:08 +08:00
claude-code
d4547bfec3
Merge pull request '信標自己講「這一份是誰」——vendor 還是 plugin 快取' ( #55 ) from fix/beacon-says-which-copy into main
v0.3.9
2026-08-23 13:03:37 +00:00
Leo
2f43ecc346
信標自己講「這一份是誰」——vendor 還是 plugin 快取
...
leo 的雲端驗收整整卡了一輪在這個問題上:同一台機器可能有兩份 ISEP,
兩份都會印信標,版本號一樣時分不出誰在說話。而「閘從哪一份走」正是決定
「另一份能不能拆」的唯一判準。
總管上一版設計的判準(叫雲端跑 env | grep CLAUDE_PLUGIN_ROOT)也是錯的:
那個變數是 hook 呼叫當下才注入的,在 Bash 工具的 env 裡本來就看不到
⇒ 空輸出不代表沒載入,那個探針從一開始就答不了這題。
改法:讓路徑自己講。快取在 plugins/cache/ 底下,vendor 的在 repo 的 .claude/ 底下。
三向實測:
/root/.claude/plugins/cache/inkstone/isep/0.3.9 → 來源:plugin(marketplace 裝的)
/home/user/inkstoneco/.claude/isep → 來源:vendor(repo 裡的複製本)
其他路徑 → 來源不明(不假裝知道)
inkstone/InkStoneCo#57
2026-08-23 21:02:45 +08:00
claude-code
1b95388a0f
Merge pull request '身為總管,我要這道閘擋的是「宣告」而不是「提到宣告」,我才不會一舉報 bug 就被它擋下' ( #54 ) from fix/factory-idle-guard-blocks-actions-not-mentions into main
v0.3.8
2026-08-23 09:58:41 +00:00
Leo
e3d05df341
再修兩類:主詞是 leo 的下一步、票號放寬把閘變鈍——都是真 transcript 量出來的
...
交付警察擋回來是對的:前一顆只驗了「我自己造的假 transcript」。
改用本機一條 2068 行的真 session(26 個真實回合終止點)重驗,當場多找到兩個問題:
① 主詞是「你」的下一步,被當成我的宣告。
舊閘在 26 個真實回合裡擋了 2 次,兩次咬的都是我在交代 leo 該做什麼:
「下一步還是那一個動作:你把 feat/... 併進 main」
「## 你下一步(兩招,先便宜的)」
⇒ 這是全新的第四類誤攔,我原本一向都沒列到。
修法是主詞檢查(誰要動手),只掛在「下一步」這條 alternative 上;
用 finditer 逐個檢查前 8 字有沒有第二人稱,蓋得到「你的下一步」這種
單字 lookbehind 蓋不到的變體。「你點頭我就做」主詞本來就是我,不受影響。
② 票號從「宣告句附近」放寬成「整段」,把閘變鈍了。
真數據:26 個真實回合有 20 個是靠「文中某處剛好有票號」放行的——
而報告幾乎一定會提到票號 ⇒ 這道閘在實務上等於不會響。
當初放寬是因為票號常寫在行內 code 裡,剝掉就找不到。
⇒ 改成**等長**替換(蓋成同樣長度的哨兵而非刪除),位移就能對回原文,
locality 與「行內 code 裡的票號也算數」兩件同時成立。
收緊後的判定分佈:21 no-declaration / 3 dispatched / 2 ticket-referenced
(原本是 20 ticket-referenced / 3 no-declaration / 3 dispatched)。
真 transcript 實測:舊閘擋 2 次(兩次都是誤攔)→ 新閘擋 0 次。
測試 33 向(+6):舊版 21/33 → 新版 33/33。
版號 0.3.7 → 0.3.8。
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com >
2026-08-23 17:54:17 +08:00
Leo
a594decb78
稼動率警察改成擋「宣告」,不擋「提到宣告」
...
2026-08-23 雲端驗收連續三次被這道閘誤攔,三次都不是宣告意圖:
① 否認自己有下一步 ②引用閘自己的訊息 ③貼閘自己的正則原始碼舉報 bug。
而訊息教人走的「選項③:說明它在等什麼」,程式碼裡根本沒有那條分支
——唯一走得通的路是不寫那三個字,正是同一則訊息明文禁止的動作。
四個真兇,沒有一個是「例外沒列夠」:
(a) DECL 會匹配裸的「下一步」三個字(每一節都可選 ⇒ 退化成關鍵字)
⇒ 收緊:每一條 alternative 都必須接到動作動詞才算命中
(b) 只剝 > 引言與長「」,不認 code fence 與行內 code ⇒ 引用被當成主張
⇒ 引用性標記整段換成哨兵(不是刪掉):內層宣告消失、外層句構留著
——刪掉正是 08-17 漏掉「回『規劃』我就派人」的原因,兩個方向一起修
(c) 取 blocks_text[-1],但那則文字後面可能還有 tool_use ⇒ 宣告其實兌現了
⇒ 只看「最後一個動作之後」的文字;收尾在動作上就不觸發
(d) 訊息承諾的出路只有兩條真的存在
⇒ 出路③ 給一個機械形式 ⏸ 等:<在等什麼>(白名單標記,要刻意寫,留痕)
方向刻意與「再加幾個關鍵字例外」相反——例外清單會越加越長、越長越誤攔。
守 leo 的封路哲學:紅線寫得越細,命中關鍵字的機率越高 ⇒ 那些閘在懲罰謹慎。
順手:擋下與放行都留痕(InkStoneCo#48:只記擋下的話分母未知);
log 目錄不在時安靜跳過,不再噴 redirect 錯誤到 stderr。
測試 hooks/tests/factory-idle-guard.test.sh 27 向,誤攔與漏攔兩個方向都測:
舊版 18/27(9 敗)→ 新版 27/27。
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com >
2026-08-23 17:42:22 +08:00
claude-code
5789f0a917
Merge pull request '信標改數「真的被註冊的閘」——它一直多報一支樣板' ( #53 ) from fix/beacon-counts-registered-gates into main
v0.3.6
2026-08-23 08:52:30 +00:00
Leo
b1f399f8b9
信標改數「真的被註冊的閘」——它一直多報一支樣板
...
leo 的雲端驗收(2026-08-23)抓到:信標說 44 支,`ls hooks/ | wc -l` 是 49,對不上。
追下去是 `ls "$ROOT"/hooks/*.sh | wc -l`:
把 `pre-write-guard.template.sh`(樣板,不是閘)與兩支沒掛註冊的輔助檔一起算進去。
🔴 這個數字是 leo 判斷「這個 session 到底有沒有閘」的唯一介面——**多報就是假綠**。
改成數 `hooks.json` 裡註冊過的唯一 `.sh`;hooks.json 讀不到才退回檔案數(且排除樣板)。
實測兩向:
正向(真的 plugin 根目錄) → 42(與 hooks.json 註冊數一致)
反向(沒有 hooks.json 的假根) → 2(三個檔裡有一個是 .template.sh,沒算進去)
查過歷史:本檔自 daa1674 建立以來只有那一版,沒有別的分支修過這段。
inkstone/InkStoneCo#57
2026-08-23 16:52:01 +08:00
claude-code
a446ad6d1d
Merge pull request '推 main 的戳記改綁「push 真正的目標 repo」,不再綁 hook 自己的 cwd' ( #52 ) from fix/push-guard-cross-repo-stamp into main
v0.3.5
2026-08-23 07:31:01 +00:00
Leo
41c56acd32
推 main 的戳記改綁「push 真正的目標 repo」,不再綁 hook 自己的 cwd
...
跨 repo 交辦時(總管站在 A repo,要推 B repo 的 main)main-and-prod-push-guard
的戳記機制永遠對不上:HERE 讀的是 hook 自己的 cwd(=session 的真身,不會變),
WANT 是總管替目標 repo(B)寫進戳記的路徑——兩者結構性地不可能相等,不是
判斷錯,是這個情境在舊模型裡根本不存在(inkstone/ISEP#30 comment 3949,
脈絡 inkstone/InkStoneCo#57,2026-08-21 實撞)。
新增 hooks/lib/push_target_dir.py:純 tokenize(不執行任何指令)解析指令裡
`cd <path> && git push` 或 `git -C <path> push` 真正會落地的目錄,對多層 cd
鏈與子殼(`(cd A && ...); git push` 這種子殼 cd 不能外洩出去)都做了範圍化——
這條範圍化是防穿透的關鍵,不是順手:沒有它,`(cd A && true); git push`
會被誤判成推向 A,讓替 A 開的舊戳記錯誤地放行推到殼外真正的目標。解不出來
一律退回舊行為(hook 自己的 cwd),維持 fail-closed 方向不變。
順手修掉補測時自己抓到的另一個洞:`(git push origin HEAD:main)`——單純加一層
括號——舊版目的地判斷完全偵測不到,整段直接放行,跟戳記無關。成因是截斷
refspec 尾巴的 sed 只認 `;`/`&`/`|` 三種字元,沒算到 `)`;補上即可,git 的
refspec 語法本來就不允許出現 `)`,這裡截斷永遠安全。
綁 repo+單次用完即丟兩條 2026-08-11/12 用血換來的性質完全沒有鬆動:只是把
「現在人在哪個 repo」問得更準,比對邏輯一個字沒動。
實測:
- hooks/tests/main-and-prod-push-guard.test.sh 舊有 8 向:8/8
- scripts/test-main-and-prod-push-guard.sh 舊有 11 向:11/11
- 新增 hooks/tests/main-and-prod-push-guard-cross-repo.test.sh 17 向
(跨 repo 正向/反向不准鬆/git -C/子殼範圍化/括號洞/單次用完即丟/
900 秒逾時/空戳記/既有行為零回歸):17/17
本輪只驗證,未拿去放行任何真實推送;plugin.json 隨慣例 bump 0.3.4 -> 0.3.5
並重跑 vendor-to-shell.py(.shell-payload 為 gitignore 產物,不入版控)。
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com >
2026-08-23 14:18:28 +08:00
claude-code
2eb9b2aaaa
Merge pull request '身為總管,我要閘擋的是動作不是字面,我才不會一晚被自己的閘擋六次' ( #51 ) from fix/push-guard-target-not-substring into main
v0.3.4
2026-08-20 17:30:46 +00:00
Leo
67dae3b814
推送閘改成判目標,不判整條指令裡有沒有那個字
...
一個晚上誤攔六次,全都不是在推預設分支:
① checkout -b 建新分支時把預設分支寫在後面,再推那條新分支
② gh pr create 指定 base——根本不是 git push
③ 推 tag(refs/tags/…)
④ 推 feature 分支(帶 -u)
⑤ 它擋住了我用來**測試它自己**的那條指令
⑥ 它擋住了這一筆的 commit——因為 message 裡引用了那幾個字
leo 2026-08-17 早就講過這個形狀:文字層封路必敗,
「紅線寫得越細,命中關鍵字的機率越高 ⇒ 那些閘在懲罰謹慎」。
舊版掃整條指令字串,正是文字層。
改成解析 push 的目標 refspec:
旗標跳過/第一個非旗標=remote/a:b 取 b/refs/tags/* 不算分支
一個 refspec 都沒給,才退回看當前分支
八向實測(hooks/tests/main-and-prod-push-guard.test.sh,8/8):
五種該放行的(今晚誤攔的原形狀,含分支名帶 domain 那種)全過
三種該擋的全擋
中途自己抓到一個 bug:tag 被跳過後目標清單變空 → 退回猜當前分支
⇒ 當前分支剛好叫預設名時誤擋。改成看到 refspec 就不退回猜測。
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com >
2026-08-21 01:29:55 +08:00
claude-code
43c328d26f
Merge pull request 'feat/milestone-must-have-due' ( #50 ) from feat/milestone-must-have-due into main
v0.3.3
2026-08-20 17:26:08 +00:00
claude-code
bcb736ed19
Merge pull request 'fix/worksheet-dedup-by-content' ( #49 ) from fix/worksheet-dedup-by-content into main
2026-08-20 17:26:06 +00:00
Leo
6772ca67d3
每個里程碑都要有真的期限,9999 也擋
...
leo 2026-08-21:「以後所有的 milestone 限制時間」「你根本沒有時間概念,浪費一整天」
實查七個 open milestone:六個期限是 9999-01-01、一個空白。
9999 比空白更糟——盤點時每一格看起來都有值,
於是沒有人發現這裡從來沒有時間壓力。七個已全部改成真日期。
新增 hooks/milestone-due-guard.sh,四向實測:
無 due_on → exit 2
due_on 帶 9999 → exit 2
真期限 → exit 0
只是讀 milestone → exit 0
規範補 M4.8(怎麼定期限、過期只對帳不自動關)。
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com >
2026-08-21 01:25:12 +08:00
Leo
1b5551274a
待驗工作單改用宣稱內容去重,驗過的不再冒出來
...
stamp 原本雜湊「交件路徑」⇒ 同樣的宣稱每回合生一個新檔名,
而且不知道總管已經驗過了。
實際發作(2026-08-21 一個 session 內):同兩條 sdd-guard 宣稱連生四張單
1c97d461/fcb285dc/256de849/394b97ae——驗掉一張下一回合又冒一張,
Stop 閘於是變成永遠過不去。閘在懲罰有照做的人。
改成雜湊宣稱內容本身,並在寫檔前檢查 verified/ 底下有沒有同名。
雙向實測:
兩次不同 transcript、同樣宣稱 → 只生 1 個檔
移進 verified/ 後再跑 → SKIP:already-verified,沒再冒出來
過程中兩個自己的錯,記下來免得下次重犯:
① 先猜了變數名 blocked/ok/nogo,實際是 ok_hits/ng_hits
——猜錯的話 _claims 永遠是空的、悄悄退回舊行為,不會報錯
② 測試資料先寫成 role:assistant,再寫成 role:user 都不觸發
——它要的是 <task-notification> 裡的 <result>
前兩次「0 個檔」我差點當成去重成功
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com >
2026-08-21 01:18:16 +08:00
claude-code
1920d4cb06
Merge pull request '身為 leo,我要雲端 clone 下來就有閘,我才不用先處理憑證' ( #48 ) from fix/b4-real-probe into main
v0.3.2
2026-08-20 17:11:52 +00:00
Leo
47ed778cc4
改走「直接複製進薄殼 repo」,並修掉一支會偷跑指令的閘
...
leo 2026-08-21:「你應該把 Plugin 直接裝進 Github repo,從本地直接複製就好了」
為什麼這條對:雲端 session 是 fresh clone 薄殼 repo,而 setup script 讀不到
環境變數。走 marketplace 就得同時處理憑證、repo 可見性、環境快取三件事——
今天這三件各失敗過一次。複製進 repo 之後,clone 下來就有,沒有任何前置條件。
新增 scripts/vendor-to-shell.py:
把 hooks/skills/commands/scripts/.claude-plugin 整份複製到 .claude/isep/,
並把 54 條 hook 註冊改寫成薄殼裡的絕對路徑。
保留 CLAUDE_PLUGIN_ROOT 這個變數名(44 支閘內部靠它定位自己的 lib/),
只是把它指到複製過來的那份。
冒煙測試(54 條註冊全跑一遍,找路徑壞掉的):
第一輪 4 條壞 → 3 條是 log 目錄不存在(已補建 .claude/hooks/)
1 條在真身也一樣壞 ⇒ 不是複製造成的
第二輪 0 條壞
順手修掉那支既有 bug:wiki-first-search.sh
python3 -c 用雙引號,註解裡的反引號被 shell 當指令替換
⇒ 這支閘每次觸發都在偷跑 bge-m3 與 head changelog.md。
改成全形引號後實測靜默 exit 0。
(crude grep 掃出 6 支疑似,但冒煙測試證明只有這一支真的中——
再一次:證據勝過掃描。)
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com >
2026-08-21 00:51:00 +08:00
Leo
03d9782f22
查了官方文件才發現:setup script 根本讀不到環境變數,而 exit 1 會鎖死 session
...
leo 問「寫以前為什麼不查」——沒有藉口,我假設了兩件事都沒查,兩件都是錯的。
① setup script 讀不到 Environment variables
官方原文:Each session copies the environment's values once, at startup,
into ordinary environment variables
而 setup script 是 before Claude Code launches 跑的 ⇒ 注入在它之後。
⇒ 把 token 放進 Environment variables 再要 setup script 讀,永遠讀不到。
證據吻合:leo 的變數設對了、值也跟本機同一把(長度 40、頭尾一致),
而腳本回報找不到。
② exit 非零會讓整個 session 開不起來
官方原文:Exit zero: if the script exits non-zero, the session fails to start.
前一版為了大聲失敗用 exit 1 ⇒ 直接造成 Session initialization failed。
⇒ 現在一律 exit 0,失敗寫進 /tmp/.isep-setup-report。
本機三向實測:
無變數(雲端真實情況)→ exit 0,印出說明,不擋 session
有變數(未來平台若改行為)→ 設 git 認證並驗證
ls-remote 加 timeout 45(本機曾掛住近 4 分鐘)
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com >
2026-08-21 00:39:48 +08:00
Leo
36d8e05a32
認證驗證加 timeout:掛住比失敗更糟
...
2026-08-21 本機隔離測試:git ls-remote 掛住近 4 分鐘不回。
setup 卡在那裡看起來像還在跑,不會有人知道它壞了。
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com >
2026-08-21 00:34:34 +08:00
Leo
135637291c
B4 的探針我自己沒撞過,實撞後發現它根本不會擋
...
v0.3.1 我把 B4 從 git tag 換成「寫 __GITEA_TOKEN__ 進 /tmp/x.md」,
說它會被 credential-only-guard 擋下。今天實撞:exit 0,閘完全沒反應。
原因:那支閘刻意豁免 .md/docs//wiki/(文件本來就要能談論這些字串)。
它只管會被執行的產物:*workflow*/.yaml/.yml/installer/worker.js/wrangler。
改成 /tmp/wf.yaml 後三向實測:
違規 workflow.yaml 帶佔位符 → exit 2 credential 鐵律攔截
同檔用 {{credential.gitea_token}} → exit 0(正確放行)
.md 談論同一個字串 → exit 0(正確豁免)
薄殼指標端同樣實測:真身在+違規 → exit 2。
這是同一個病的第四次:修假綠的那一刀,自己又是沒撞過就寫。
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com >
2026-08-21 00:33:44 +08:00
claude-code
d3061585e8
Merge pull request '身為 leo,我要雲端 env 檔能直接產在桌面,我才不用去翻隱藏目錄' ( #46 ) from feat/cloud-env-outdir into main
2026-08-20 15:13:38 +00:00
Leo
9099c3f533
make-cloud-env.sh:輸出路徑可指定,且不再對使用者指定的目錄動權限
...
leo 要產在桌面。兩個改動:
- OUT_DIR 可被環境變數覆寫(OUT_DIR=~/Desktop bash scripts/make-cloud-env.sh)
- chmod 700 只在「這個目錄是我們自己造的預設位置」時才做
原本無條件對 $OUT_DIR chmod 700,一旦 OUT_DIR 指到既有目錄
(例如 ~/Desktop)就會改動使用者目錄的權限。產出檔本身仍是 600。
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com >
2026-08-20 23:13:25 +08:00
Leo
5bceb03478
v0.3.1
...
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com >
v0.3.1
2026-08-20 23:03:05 +08:00
claude-code
4e73b8b03d
Merge pull request '身為 leo,我要雲端驗收步驟在閘死掉時真的變紅,我才不會再被三個綠燈騙一次' ( #45 ) from fix/testing-b-section-discriminating into main
2026-08-20 15:03:04 +00:00
Leo
291787eaaa
TESTING.md B 段整段換掉——舊版在閘全滅時會回綠
...
2026-08-20 雲端實證:B2/B3/B4 三步在 plugin 完全缺席時**全部通過**。
一個在閘死掉時也會給出正確答案的測試,不是測試。
三處各自為什麼假:
- B2「跑 claude plugin list」→ 要它自己報自己,沒有獨立證據
- B3「Skills (9) / Hooks (5)」→ 那組數字剛好是薄殼自己的 .claude/ 產生的
- B4「git tag -a v9.9.9」→ git tag 在三支閘的白名單裡
新版三步都改成有鑑別力的:
- B2 找 v0.3.0 新增的載入信標(那支腳本住在 plugin 裡,沒載入就不可能發聲)
- B3 看 setup script 的自我驗證輸出(v0.3.0 新增)
- B4 改用金鑰佔位符觸發 credential-only-guard(不在任何白名單上)
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com >
2026-08-20 23:02:47 +08:00
claude-code
3a951210a5
雲端零閘的兩個真因:setup 不自驗+沒有 release 撐版本號 ( #44 )
v0.3.0
2026-08-20 14:59:55 +00:00
Leo
daa1674a20
雲端零閘的兩個真因:setup 從不驗證自己+沒有任何 release 撐版本號
...
2026-08-20 雲端 session 的閘全滅,而三個驗證步驟全部回綠。
今晚在隔離 HOME(GIT_CONFIG_NOSYSTEM=1)重現,把兩件事分開了:
① setup script 的寫法是對的
裸環境失敗、加了 url.insteadOf 就成功 —— x-access-token 這個使用者名稱
Gitea 也接受。所以先前我對 leo 說「URL 重寫沒作用到 marketplace 這條路徑」
是錯的,這裡更正。
(前兩次測試之所以誤導,是因為 /etc/gitconfig 的 macOS keychain helper
還在幫忙 —— 「隔離 HOME」並沒有隔離系統層設定。同一個病第三次。)
② 真正的缺陷是這支腳本從不驗證自己
設完就結束。token 沒生效也不出聲 ⇒ setup log 一片綠、
session 開起來才發現 marketplace 拉不下來,而那時已經沒有任何線索。
本次加兩道自我驗證:git 認證通不通、marketplace 有沒有就位,
任一不通就 exit 1 並印出該查什麼。
③ 新增 isep-presence-beacon.sh(信標,不是閘)
SessionStart 報「ISEP v幾 已載入、幾支閘」。
它的全部意義是鑑別力:這行住在 plugin 裡,所以看得到就一定載入了,
看不到就是零閘。不像 git tag(在三支閘的白名單裡,閘死了照樣過)。
④ plugin.json 0.0.0 → 0.3.0
查清楚了:0.0.0 不是漂移,是誠實 —— ISEP 一個 tag 都沒有,從沒發過 release。
而這正是「雲端拿不到更新」的另一半:claude plugin update 比對版本號,
沒有 release 就永遠沒有新號碼可比。所以這一刀的收工是真的打 tag 發版。
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com >
2026-08-20 22:59:21 +08:00
claude-code
c48495d911
Merge pull request 'fix(hooks): sdd-guard.sh 修「解析失敗仍照擋、且訊息洩漏 /nonexistent」' ( #42 ) from fix/sdd-guard-path-resolution into main
...
sdd-guard 路徑解析修好+ADR 訂正(InkStoneCo#22)
2026-08-20 13:09:10 +00:00
Leo
87186585d6
fix(hooks): sdd-guard.sh 修「解析失敗仍照擋、且訊息洩漏 /nonexistent」(InkStoneCo#22)
...
症狀(總管 2026-08-12 實撞):寫暫存腳本進 scratchpad
(/private/tmp/.../scratchpad/foo.py)被 sdd-guard.sh 攔下,訊息印出字面的
「/nonexistent/3-specs/ 下找不到任何 SDD」。
兩個洞:
- 洞 A:scratchpad 不在任何 git repo 裡,卻被當成「repo 裡的 code 變動」誤判
需要 SDD。改成先問 path_in_git_worktree()(見 hooks/lib/path-resolve.sh):
不在任何 git repo 裡 → SDD 天生管不到,直接放行,不必先猜專案根。
這個檢查放在 $_root 的 case 分岔之前、對兩邊都適用——第一版只放進「專案外」
分支,被本次新增的 hooks/tests/sdd-guard.test.sh 抓到一個不對稱漏洞(cwd 剛好
等於 scratchpad 祖先目錄時會漏判),改成統一檢查後修掉。
- 洞 B:舊版用內部 sentinel `/nonexistent/3-specs` 重用既有的擋下路徑,但這個
假路徑被直接印進使用者看到的訊息。改用 RESOLVED 旗標記解析成不成功,訊息
改用人話描述原因,不洩漏假路徑。
fail-closed / fail-open 的判準(票上明確要求回答,不能各憑運氣):
真的落在某個 git repo 裡、但那個 repo 沒有 3-specs(或沒有 active SDD)→
仍然 fail-closed(擋)。理由:這道閘存在的目的就是防止「沒有 SDD 卻能動
code」,把「判斷不出來」直接放行,等於把環境跑歪(cwd 被切走、
$CLAUDE_PROJECT_DIR 沒設)悄悄變成「這道閘關掉了、且沒人知道」——silent
bypass 的代價遠高於多打一次確認。#22 紅線亦明寫「不要把閘改成解析失敗就
放行」。
順手修的殘留 cwd 依賴:SPECS_DIR 的預設值原本是相對路徑
「system-dev/docs/3-specs」,專案內迴圈找不到時會被拿去跟 hook 執行當下的
cwd 兜;改成絕對路徑 $_root/system-dev/docs/3-specs。
同時修 ADR-0001(ISEP 自建 wiki):標題與內文原本會讓人誤解成「ISEP plugin
裝到哪個 repo,就會在那裡自建一份 wiki」,但實際查證(marketplace.json 只宣告
hooks/commands/skills、README 明文排除 wiki/docs、hooks 一律用
${CLAUDE_PLUGIN_ROOT} 讀自己不是寫別處)並非如此——那份 wiki 只是 ISEP 這個
repo自己的開發歷史,跟裝 plugin 無關。唯一真的會在某 repo 建 wiki 的
scripts/install.sh 是 system-dev-template 的獨立安裝器殘留,要手動執行,
作用對象是 cwd 不是「plugin 裝到的地方」——這多半是誤解的真正來源,已在
ADR 的「常見誤解」段說明。
驗證:
- 造出 08-12 原始事故情境(cwd=InkStoneCo、CLAUDE_PROJECT_DIR 未設、寫
scratchpad),修前擋(印 /nonexistent)、修後放行——實測輸出見票留言。
- 造出「真的在 git repo 裡但沒有 3-specs」情境,修後仍擋、訊息不含
/nonexistent。
- 新增 hooks/tests/sdd-guard.test.sh:8 案例全過(洞 A/洞 B/fail-open
陷阱/單一活性違反/恰好一份 active/改文件放行)。
- 既有六套 scripts/test-*.sh 全過,無退步。
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com >
2026-08-20 21:03:14 +08:00
claude-code
e6d183d038
Merge pull request '產生雲端 env 設定給 leo 貼(InkStoneCo#14)' ( #41 ) from feat/cloud-env-generator into main
...
雲端 env 產生器
2026-08-20 12:45:50 +00:00
Leo
f855d82573
產生雲端 env 設定,不要 leo 自己拼湊(InkStoneCo#14)
...
leo 2026-08-20:「應該是你會幫我產生正確的 env 我去貼⋯⋯不然就是我要自己拼湊」
「這些值你都有,你可以只寫名字然後 build 一個檔案給我」
總管原本的說法是「你複製設定給我、值遮掉」——那是錯的,兩個理由:
① 把拼湊的工作丟回給 leo ② 讓值經過對話本身就是洩漏面。
scripts/make-cloud-env.sh:
- 腳本裡只寫**變數名字**(NEEDED 陣列,要加變數就加在那裡)
- 值在執行當下才從既有 .env 讀(credentials-map 記的六個位置都找)
- 產物寫到 ~/.claude/cloud-env/<時間>.txt,權限 600,**刻意不在任何 repo 底下**
- 只印路徑不印值;找不到值的變數會標出來而不是靜默留空
- 產物最後一行就是刪除指令
docs/TESTING.md 補 B0 段:先跑產生器,再貼。
實跑過:1 個變數、0 個找不到、產物 45 行、權限 600、不在任何 repo。
2026-08-20 20:45:02 +08:00
claude-code
17de121017
Merge pull request 'fix(hooks): 補上 leo21c-write-guard.sh 第二、三次誤攔的修法(反向移植)' ( #40 ) from fix/one-copy-per-gate into main
...
補兩個空殼閘(InkStoneCo#57):ISEP 成為完整超集
2026-08-20 12:06:51 +00:00
Leo
c1d80756d7
審核補件:第二個空殼閘(irreversible_dispatch_check.py)+既有測試套
...
總管複驗 PR 時發現 subagent 只補了一個空殼,還有第二個同款的:
arcrun-intent-guard.sh → exec 不存在的 .py ⇒ 擋掉全部(吵,PR 已修)
irreversible-dispatch-guard.sh → 同款,但寫法是
python3 <不存在> 2>/dev/null || echo '{"verdict":"OK"}'
⇒ **靜默放行全部**(危險,本 commit 補)
實測同一份派工單「驗過了就把舊分支刪掉」:
InkStoneCo 版(有 .py)exit=2 擋 / ISEP 版(缺 .py)exit=0 放行
補完後:不可逆派工 exit=2、正常派工 exit=0、合規工作流 exit=0。
系統性掃描 ISEP 全部 hooks 引用的同目錄檔案:補完後 0 個缺檔(InkStoneCo 本來就是 0)。
順帶把 InkStoneCo 的 hooks/tests/ 四支既有測試一併帶過來。
🔴 這件事很重要:若先併 InkStoneCo#64(刪掉 .claude/hooks/),
這台機器會失去那支唯一還能用的副本——變成真的沒有那道閘。
2026-08-20 20:06:23 +08:00
Leo
b790c3a78d
fix(hooks): 補回 arcrun_intent_guard.py 主體——這支閘自 ISEP 建立以來就是空殼
...
發現於 inkstone/InkStoneCo#57/inkstone/ISEP#32:要把 InkStoneCo 本機
`.claude/hooks/arcrun-intent-guard.sh`(連同它同目錄的 arcrun_intent_guard.py)
移除、改吃 ISEP 這份之前,實測 ISEP 版才發現 `hooks/arcrun-intent-guard.sh`
呼叫的 `arcrun_intent_guard.py` 從未進過 ISEP 的 git 歷史(`git log --all` 0
命中)——`exec python3 "$DIR/arcrun_intent_guard.py"` 找不到檔案,
python3 直接噴 OS 層錯誤、exit 2,**每一次 Write/Edit/MultiEdit 都被無條件擋下**,
不是「規則判定違規才擋」,是檔案不存在導致的硬當機。
這比 kbdb_cmd_check.py 那次(PR #39)更嚴重:那次是「該擋的沒擋」(靜默放行),
這次是「不管寫什麼都擋」——方向相反,但同一個病根:hook 的 shell 外殼進了
git,Python 主體沒有。
修法:把 InkStoneCo 本機那份(唯一存在的正本)原樣搬進來。這支腳本本來就設計
成離開 InkStoneCo 語境會優雅放行(`rules_path` 不存在 → return 0),只是
之前連跑到那行都做不到。
實測 3 種情境:
- ISEP 語境(沒有 intent-rules.json):exit 0(修好前是 exit 2,任何寫入都被當機式擋下)
- InkStoneCo 語境、合法內容:exit 0
- InkStoneCo 語境、真違規(`ON_FAILURE` 這種不存在的邊):exit 2(行為不變,沒有變寬鬆)
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com >
2026-08-20 19:49:34 +08:00
Leo
1dc97f0bd6
fix(hooks): 補上 leo21c-write-guard.sh 第二、三次誤攔的修法(InkStoneCo 反向移植)
...
InkStoneCo/.claude/hooks/leo21c-write-guard.sh 2026-08-20 當天已修過兩次
誤攔(bc8a529/5e62d28),但沒人把修法帶回 ISEP——ISEP 那份還是上線當天
的舊版,會誤擋「git commit 訊息提到 leo21c」「Gitea PATCH 內文提到
leo21c」這類完全碰不到 CF 帳號的動作。
發現於 inkstone/InkStoneCo#57/inkstone/ISEP#32「同一件事只留一份」:
要把 InkStoneCo 本機的閘收斂成只剩 ISEP 這份之前,先確認 ISEP 沒有落後
——結果這支剛好是唯一反向的(其餘 4 支已修過的閘 ISEP 都領先)。
判準改成只認「這個指令會不會真的寫到那台」:
① 寫入請求打到 *.leo21c.workers.dev(POST/PUT/PATCH/DELETE/--data/-d/trigger)
② 用它的 CF 帳號 id 跑 wrangler/acr
其餘一律放行,不管文字裡提到什麼。
實測 6/6(4 該放行/2 該擋,含這次要修的兩個誤攔案例):見 PR 說明。
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com >
2026-08-20 19:46:24 +08:00
claude-code
a7ea0283ac
Merge pull request 'fix(hooks): 讓四支閘認得出「指令位置」跟「heredoc/引號裡的文字」' ( #39 ) from fix/group0-gates into main
...
群0 第一刀:閘擋得到真動作、放得過只是提到它的句子(InkStoneCo#23/#56)
2026-08-20 11:00:33 +00:00
Leo
0cdb6f2c05
fix(hooks): 讓四支閘認得出「指令位置」跟「heredoc/引號裡的文字」
...
inkstone/InkStoneCo#23、#56 同一個病:閘對整條指令字串做關鍵字掃描,
把「檔案內容/留言引用裡剛好提到某個關鍵字」當成「真的在執行」,
同時放過包一層讀取指令、或藏在 heredoc body 裡的真動作。
- 新增共用輔助 hooks/lib/strip_heredoc.py:heredoc body 是資料不是指令,
四支閘(github-contact / main-and-prod-push / stage-before-prod /
kbdb-api-wall 的 Bash 分支)呼叫前一律先拿掉 body 再比對。
- main-and-prod-push-guard.sh:修掉跟 release-tag-guard.sh 同款的
「開頭是讀取工具就整條放行」前綴繞過洞;git push 的偵測改成指令位置比對;
main/master 目標改用單字邊界,不再誤中 "domain" 這種子字串。
- github-contact-guard.sh:拿掉 gh CLI/git push 判準裡「前面隨便一個空白
就算數」的鬆散邊界,只認真正的指令分隔符。
- kbdb-api-wall-guard.sh:Bash 分支原本引用不存在的 kbdb_cmd_check.py,
python3 找不到檔案就吃掉錯誤印 "OK",該分支形同虛設——任何
`wrangler d1 execute` 直打 kbdb 都會被放行。邏輯搬進新檔
hooks/lib/kbdb_cmd_check.py(shlex 分詞、quote-aware),把 .sh 的
參照路徑改過去,補回 Bash 分支的 kbdb-sql-ok 逃生口。
四支各補 InkStoneCo#40 §1 要求的三行中文檔頭。
新增四支可重跑測試(scripts/test-*.sh),共 69 條斷言全過,
含 #23/#56 票上實撞的原始形狀(寫 docs/TESTING.md 的 heredoc、
貼引用 #56 敘述的留言、`grep git push`)。
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com >
2026-08-20 18:51:52 +08:00
claude-code
c01a39541e
Merge pull request '撤回 v0.2.0 / v0.2.1:未達交件水準' ( #38 ) from chore/withdraw-versions into main
...
撤回未達交件水準的兩版
2026-08-20 10:36:39 +00:00
Leo
92a4138f93
撤回 v0.2.0 與 v0.2.1:未達交件水準
...
leo 2026-08-20:「偷工減料的不能算,這不是交件被退回,是根本未達足以交件的水準。」
兩個 release 與 tag 已從 Gitea 刪除;plugin.json 回到哨兵值 0.0.0(尚未發過正式版)。
那兩版做的都是 ISEP 自己的鷹架,而當時 45 張管理票一張都沒關。
下一個版本的門檻:至少關掉一張既有的管理票,release note 寫明關了哪張。
2026-08-20 18:35:58 +08:00
claude-code
b9d450eb37
Merge pull request 'v0.10.0:M4.0 里程碑怎麼組成+M4.3 不准自己打折' ( #37 ) from rule/milestone-formation into main
...
v0.10.0
2026-08-20 09:58:11 +00:00
Leo
0946f702d7
v0.10.0:補 M4.0(里程碑怎麼組成)、M4.3 改寫成「不准自己打折」
...
leo 2026-08-20 訂正總管寫錯的規則:
「我跟你說要達成的目標,你從 issues 池遍歷找出要完成哪些可以達成,如果沒有才增加 issues,
定下後就不改,而不是要做 5 件事,做不到就改成 2 件,自己打折」
原本 M4.3 寫成「增減都是 leo 的裁決,不是總管的操作」——把重點放錯在「誰有權」,
而 leo 說的是**組成方式**與**不准打折**。
- 新增 M4.0:leo 給目標 → 總管遍歷票池找出達成它需要哪些票 → 池裡沒有才補開 → 定下不改
- M4.3 改寫:定下就不改,尤其不准因為做不完而縮減;做不完就是還沒完成,
里程碑開著、百分比顯示真實完成度。把分母改小只是讓它說謊。
唯一例外是目標本身變了 ⇒ 重走 M4.0,不是打折。
- §15.2 補記:六群的組成就是照 M4.0 走出來的(44 張既有票,沒補開任何新任務票)
2026-08-20 17:58:08 +08:00
claude-code
df0d4ed396
Merge pull request 'v0.9.0:全面 PR-only(leo 裁定)+跨 repo 群票載體' ( #36 ) from rule/pr-only into main
...
v0.9.0 全面 PR-only
2026-08-20 09:55:56 +00:00
Leo
e16d21435f
v0.9.0:全面 PR-only(leo 裁定)+跨 repo 群票載體入規範
...
leo 2026-08-20:「所有 subagent 都是 PR only,在雲端地端總管所做的都是 PR-only。」
⇒ 推翻總管原提案(只有 ISEP 走 PR-only,其他 repo 總管自裁)。
DIVERGENCE §E1 已加註被推翻,原文保留作歷史。
⇒ main-and-prod-push-guard.sh 現行判準只擋 subagent、放行總管,與本條不符,
要改成不分角色一律擋;/tmp/.main-push-ok 戳記隨之作廢(PR review 就是那道確認)。排群 4。
另 §15.5:六個群在 ISEP 各有一張 hub 票(#30-#35),別的 repo 的舊票用 Gitea 原生
dependency 指過去(跨 repo dependency 已實測 201 可用)——milestone 管不到跨 repo,這是載體。
2026-08-20 17:55:35 +08:00
claude-code
b5c4ef3540
Merge pull request '治理規範 v0.8.0:三源整合定案(§15)+刪 M4.7+M4.3 改寫' ( #29 ) from docs/three-source into main
...
治理規範 v0.8.0
2026-08-20 09:50:17 +00:00
Leo
410771e883
E5 對齊 M4.3:到期只通知,不移票
2026-08-20 17:50:15 +08:00
Leo
64dae34efa
v0.8.0 修訂:刪 M4.7、M4.3 改寫(里程碑內容不增不減)、§15 精簡成方向性規劃
...
leo 2026-08-20 兩則指正:
① 「milestone 確定後怎麼可以再把東西移除?定下工作自己刪掉是什麼意思?根本就沒有什麼降」
⇒ M4.7(降 scope 留痕)整段刪除——它把一個不該存在的操作合法化了。
M4.3 改成:到期只通知 leo;內容增減都是 leo 的裁決,不是總管的操作。
② 「規劃書要大的規劃,方向性,不是寫廢話」
⇒ §15 從逐領域十行大表精簡成:四句方向+六里程碑順序表+三矛盾定案+三件待裁。
2026-08-20 17:42:51 +08:00
Leo
6d61c10cb8
治理規範 v0.8.0:三源整合定案(§15,leo 核准)
...
leo:「把 claude.ai 的規劃、舊有票的需求、現在已經有的機制全部整合,
修正出最終版規劃,核准再動工。」
- §15.1 逐領域十欄對照(A–J),每格標定案
- §15.2 三處矛盾的解法:#40 只減不增 vs E 清單要新閘(→伺服器端優先);
warn-first vs 全 block(→四層定位,#48 是前提);E5 自動打 tag(維持否決)
- §15.3 已成立清單;§15.4 動工順序;§15.5 三件待裁(不擋群 0)
- 最大發現:claude.ai 兩份規劃都沒有「觀測」這一章,而舊票最痛的就是它
2026-08-20 17:40:52 +08:00