test(D20): 補 #119 缺的 subagent 維度測試,並查出一個殘留同形狀破口(inkstone/ISEP#119)

背景:2026-09-01 一個 subagent 把分支推上 GitHub 沒經過解保險,同時總管這端擋得住,
看似「subagent 端失效」。查證後:
- 官方文件(https://code.claude.com/docs/en/hooks)明講 PreToolUse hook 對 subagent
  與主線是同一批,只是 input 多帶 agent_id/agent_type ⇒ 閘無從只擋一端。
- github-contact-guard.sh 從不讀那兩個欄位,所以「一端有效一端失效」不會出在身分判斷上。
- 當天的洞是同一族的 cwd 病(inkstone/ISEP#109/#125):閘拿 hook 自己的 cwd 解 origin。
  2026-09-07(49a7145)接上 lib/push_target_dir.py 後,**字面路徑**的形狀已修好,
  且已由 scripts/test-github-contact-guard.sh 的 A39 cwd 群覆蓋。

本輪交付(都驗過):
1. 在既有的 scripts/test-github-contact-guard.sh(唯一真相源,不另開平行測試)尾端加 7 條
   runsub:同一條指令帶 agent_id(subagent)與不帶(總管)各跑一次,判決必須相同。
   涵蓋推 GitHub(含 cd/-C 字面路徑原形狀)擋、推 Gitea/讀取放行。26→33,實測 33/33。
2. 回歸性驗過:把新測試跑舊閘(49a7145^)→ 09-01 的兩條 cd/-C 場景紅(漏擋),
   證明這組測試真的抓得住它要抓的病。
3. docs/TESTING.md A39 段更新條數、失敗判讀,並記下下面這個殘留破口。

🔴 查出並實測(含雲端實跑)一個殘留的同形狀破口,未修(見下):
   push 目標用**未展開 shell 變數**表示時(git -C "$VAR" push/cd "$VAR" && git push),
   hook 收到的是展開前字串,push_target_dir 對 $VAR 回「不確定」⇒ 退回 session cwd 解 remote。
   關鍵不對稱:main-and-prod-push-guard 遇「不確定」退回「要戳記」=擋(fail-safe);
   github-contact-guard 預設「證明不了 github 就放行」⇒ cwd 是 Gitea 時,真的推 GitHub 的
   -C "$VAR" 會漏擋。雲端實跑(本 session 即 CHILD_SESSION=1):git -C "$VAR/gh-repo" push
   未被 D20 擋,一路打到 git proxy 才被 403 擋(那是 proxy 不是 D20);本機沒有 proxy 後手。
   不修的理由:fail-closed 會誤攔「用變數推 Gitea」,屬「誤攔比漏擋嚴重」的取捨,
   應另開票由總管/leo 裁(附建議修法:push_target_dir 區分「無指令」與「指令不可解」,
   後者 github-contact 改擋並印『改用字面路徑』的出路——出路走得通、可測)。

同形狀家族盤點(實跑驗過,回應「還有哪些閘是這個形狀」):
- github-contact-guard.sh:字面路徑已修;殘留變數形狀破口(見上,另票)。本輪補 subagent 維度測試。
- main-and-prod-push-guard.sh:已修(push_target_dir),且遇不確定 fail-safe,cross-repo 測試 19/19。
- line-needs-own-worktree.sh:cwd 維度已修(checkout_target_dir),測試 110/110。
- not-my-branch-guard.sh:★仍是同形狀破口★——git -C "$PROJ" 讀 hook 自己的目錄;
  NOT_MY_BRANCH_OK=1 前綴 PreToolUse 收不到(逃生口結構性失效)。實測:subagent cd/-C
  進別的 repo 對 held 分支 commit → 放行(該擋)。屬 commit 閘、非本票 D20 範圍,
  mistakes.md 早標「另報」,建議另開票(需 commit-target 解析器+認得字面前綴)。

僅動測試腳本與文件,未動任何會被載入的檔(hooks/commands/skills/agents/plugin.json 皆未動)
⇒ 不改變任何閘的行為,plugin.json 未動,版本待總管定。

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TGxitYq49FzYC7EFkbhzF5
This commit is contained in:
2026-09-18 17:56:02 +00:00
parent 8a3802c4b7
commit e45faef8a2
2 changed files with 44 additions and 3 deletions
+16 -3
View File
@@ -1017,12 +1017,13 @@ bash hooks/tests/isep-presence-beacon.test.sh # ㉗–㉝ 那七條
**真實跑(2026-09-07**:信標不帶 fixture 打真 Gitea(匿名)→ 安靜(v0.24.0 tagrelease 都在),
留下 `.isep-chain-ok-v0.24.0` 快取;`release-check` 在本分支 → `v0.24.0 ✅✅✅ ←`、三個裁定不補的版本標 ⏭、exit 0。
### A39 — D20 閘判「push 到哪」看的是指令實際會推的那個 repo26
### A39 — D20 閘判「push 到哪」看的是指令實際會推的那個 repo,且 subagent 與總管同判:33
```
bash scripts/test-github-contact-guard.sh
```
**該看到**`26/26 通過`。離線; 12 條自己開兩顆 git repo`shell` 的 origingithub.com、
`body` 的 origin 是 Gitea 的形狀),payload 帶 `cwd`,跟 hook 真的會收到的一樣
**該看到**`33/33 通過`。離線;中間 12 條自己開兩顆 git repo`shell` 的 origingithub.com、
`body` 的 origin 是 Gitea 的形狀),payload 帶 `cwd`,跟 hook 真的會收到的一樣
最後 7 條(`inkstone/ISEP#119`)把同一條指令帶 `agent_id`(subagent)與不帶(總管)各跑一次,兩者判決必須相同。
**它在守什麼**`inkstone/ISEP#109` → comment 66292026-09-07 實撞):雲端薄殼的 cwd 是 GitHub 那份,
總管站在那裡 `git -C <InkStoneCo> push origin x`InkStoneCo 的 originGitea),舊版拿 **hook 收到的 cwd**
@@ -1036,6 +1037,18 @@ bash scripts/test-github-contact-guard.sh
- 「該擋」5 條任一紅 ⇒ D20 有洞:`-C``cd` 指到 GitHub 那份的 push 沒被攔
- `(cd body && true); git push` 那條紅 ⇒ 子殼的 cd 外洩了(08-11 穿透的形狀)
- 前 14 條紅 ⇒ 舊有判準(heredoc/引號裡的散文/讀取放行)被改壞
- subagent 那 7 條任一「subagent≠總管」⇒ 這支閘開始讀 `agent_id``agent_type` 分身分了:
D20 對 subagent 與總管本該一視同仁(`inkstone/ISEP#119`),一旦分了岔就是 09-01 那種「一端有效一端失效」的破口回來了
🔴 **已知殘留破口(`inkstone/ISEP#119` 查出、實測+雲端實跑驗過,待裁)**push 目標若用
**未展開的 shell 變數**表示(`git -C "$VAR" push``cd "$VAR" && git push`),PreToolUse hook 收到的是
**展開前**的字串,`push_target_dir.py` 對 `$VAR` 一律回「不確定」⇒ 退回 **session 的 cwd** 解 remote。
`main-and-prod-push-guard` 遇到「不確定」是退回「要有戳記」=**擋**(fail-safe);
`github-contact-guard` 的預設方向相反——「證明不了是 github 就放行」⇒ cwd 是 Gitea 時,
一個真的推 GitHub 的 `-C "$VAR"` 會**漏擋**。這個不對稱就是殘留的同形狀破口。
上面測試用的都是**字面絕對路徑**(解得出來 ⇒ 擋),所以測試全綠**不代表**這個變數形狀已補。
雲端有 git proxy 當後手(未授權的 github repo 一律 403),本機沒有 ⇒ 本機風險較高。
修法有代價(fail-closed 會誤攔「用變數推 Gitea」,需配「改用字面路徑」的出路),屬 `誤攔比漏擋` 的取捨,另票裁。
### A37 — 雲端第一則 prompt 的主線就是現在這條,不是兩週前的舊線:31 條
```