雲端要拿得到票、主線與通知:白名單住 ISEP、主線檔隨 repo 走、leo21c 讀放寫擋(inkstone/ISEP#130)

- docs/permissions-allow.json + scripts/settings-allow-sync:四個 Gitea 正門工具的權限白名單一份,
  setup script 裝完 plugin 寫一次、SessionStart 每次再對一次(只加不減、冪等)
- hooks/lib/mainline.py/scripts/mainline:家目錄沒主線就讀 InkStoneCo/system-dev/mainline.json;
  set/adopt/clear 兩份一起寫,refresh 只寫家目錄
- hooks/leo21c-write-guard.sh:唯讀 -d(tr/cut/sort…)先剪掉再判、notify_leo trigger 放行
  (與 prod-write-guard 同一份白名單)、改法段改印 09-02 起的 youlin 子網域;補第一支測試(26 條)
- prod-write-guard/main-and-prod-push-guard/kbdb-live-exam:認得 youlin 新子網域 arcrun-yuga3bse
- scripts/ticket:收件 repo 寫 inkstone/ISEP 不再 404(org 寫錯當場講)
- scripts/isep-notify:有 TELEGRAM_BOT_TOKEN/TELEGRAM_CHAT_ID 先走 Bot API 直送(雲端唯一通的路)
- 測試:A31–A35 共 79 條;README/plugin.json/hooks-inventory 數字實數(61 支、85 條、53 支腳本)

假設(記在這裡等 review):權限規則的形狀沿用 leo 09-07 親手加、實測有效的那四條;
「分類器真的不擋」要雲端一趟 run 的 permission_denials 才驗得到,本 PR 驗不了。
版本:待總管定版(plugin.json 仍 0.22.0)。

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DTZ9QtvjY7MNxjfQbexAm7
This commit is contained in:
isep-hand
2026-09-07 01:17:15 +00:00
parent 605f1fe5a6
commit 65120a1c65
27 changed files with 1141 additions and 34 deletions
+66
View File
@@ -229,3 +229,69 @@ leo 當場:「**這些為什麼不寫到票裡?**」
**推廣**:repo 裡任何「這台機器該有什麼」的清單,都要有一支東西定期拿它去問機器。
清單自己不會知道它漏了什麼。
## ⚠️ MISTAKE: 兩支閘對同一條指令說不同的話,而讀的那一半從來沒被測過
票: `inkstone/ISEP#130`
日期: 2026-09-07
症狀: 09-04 雲端 Routine 的 run log:讀 `notify_leo` 定義被 `leo21c-write-guard.sh` 擋下。
閘的判準明寫「**讀可以,寫不行**」,卻連讀都擋 ⇒ 雲端什麼都拿不到。
實查: ① 舊判準 `-d[[:space:]]``| tr -d '\r'``cut -d=` 這種唯讀的 `-d` 當成 curl 的 body。
`prod-write-guard.sh` 在 2026-08-12 修過**一模一樣**的洞(它的檔頭寫著「一堆唯讀工具也用 -d」),
這支漏了——同一批補丁只改其中一份(`main-and-prod-push-guard.sh` 檔頭記過同款)。
`…/webhooks/named/<ns>/notify_leo/trigger``prod-write-guard.sh` 是白名單(ISEP#63),
在這支卻因為尾巴是 `/trigger` 照擋 ⇒ 撞人閘時發 Telegram 叫 leo 會被擋(InkStoneCo#110 的病)。
③ 這支閘從 08-20 建立到現在**沒有任何測試**,`hooks/tests/` 裡 30 支測試沒有它的——
「讀會不會被誤攔」這格從來沒被驗過,只有「寫會不會被擋」被人肉試過。
原因: 同一個判準(「這條指令會不會真的寫到那台」)散在三支閘裡各寫一份,修一份不會帶動另外兩份;
而沒有測試的那支,誤攔發生時**不會有任何東西喊一聲**——被擋的人在雲端、沒有人在旁邊。
正確做法:
- 修判準時 `grep -l` 找同一族的閘(`prod-write-guard``main-and-prod-push-guard``leo21c-write-guard`),
一次改齊;本次三支都補了 youlin 09-02 起的新子網域 `arcrun-yuga3bse`
- 每一支「擋」的閘都要有「不該擋」那半的測試,測資用**現場真的會下的指令**
(這次是 `cloud-worker.md``progress-guard.md` 裡的原句),不是自己挑好抓的例子。
- 修之前拿新測試跑舊閘,紅的那幾條才是真的改到的(本次 5 條紅);全綠就是測試順著新閘寫的。
## ⚠️ MISTAKE: 雲端連不到 youlin 被記成「HTTP 000」,而 000 有三種病,兩種都不是連線
票: `inkstone/ISEP#130`
日期: 2026-09-07
症狀: 09-04 run log「youlin 從雲端 HTTP 000」;09-07 總管查到舊子網域 `youlin-hsieh-dev` DNS 已死,
以為換名字就通。從雲端打新名 `arcrun-yuga3bse` **還是 000**
實查: `curl` 印的是 `CONNECT tunnel failed, response 403``$HTTPS_PROXY/__agentproxy/status` 記為
`connect_rejected … gateway answered 403 (policy denial)`。同一個 sessionleo21c 200、
git.uncle6.me 200、api.telegram.org 302——**只有 youlin 的主機沒被雲端 egress policy 放行**。
`/root/.ccr/README.md` 明寫:403/407 是組織的 egress policy,不要重試、不要繞路,回報主機名。
原因: `-w '%{http_code}'` 對「DNS 沒有」「proxy 拒絕 CONNECT」「對方沒回」三種都印 000,
而三種的修法各在不同人手上(改名字/leo 放行主機/看實例)。把它們記成同一個數字,
下一個人就會往錯的方向查(09-07 就差點只修名字收工)。
正確做法: 雲端看到 000 先跑 `curl -sS "$HTTPS_PROXY/__agentproxy/status" | jq .recentRelayFailures`
`connect_rejected` 403 ⇒ 是 policy,交給 leo 在 Cloud environment 放行 `*.arcrun-yuga3bse.workers.dev`
不是 ⇒ 才去查名字或實例。記 log 時寫 curl 的錯誤字串(`response 403``Could not resolve host`),不要只寫 000。
## ⚠️ MISTAKE: 正門工具吃 `owner/repo` 會回 404,於是人走了側門
票: `inkstone/ISEP#130` → comment 6120 第 2 件
日期: 2026-09-07
症狀: `scripts/ticket new inkstone/ISEP -F …` 回 Gitea 404;同一天 `ticket say` 對 InkStoneCo 正常。
總管當場改成直接打 API 開了四張票。
實查: `cmd_new` 把第一個參數原樣塞進 `/repos/inkstone/{repo}/issues``/repos/inkstone/inkstone/ISEP/issues`
404 讀起來像「ISEP 這個 repo 不存在」,跟真正的原因隔了一層。`subtask --to` 同款。
原因: 票號的鐵律是全稱 `owner/repo#N`(CLAUDE.md),人自然會把收件 repo 也寫成 `owner/repo`
工具只收短名、錯了又不講清楚 ⇒ **正門壞了,人就走 `ticket-api-bypass-guard.sh` 在防的那條側門**
一道閘把人逼去走它自己禁止的路,那道閘就是在製造違規(`docs/governance/cloud-wiring.md` ② 記過同一句)。
正確做法: `repo_arg()` 兩種寫法都收;org 寫錯(`Leo/`)當場講「org 是 inkstone」,不讓它變成一個要人猜的 404。
測試 `scripts/test-ticket-repo-arg.sh``api()` 換成假貨記路徑,離線驗路徑裡不准出現 `inkstone/inkstone`