身為交出 PR 的人,我要它兩週內有人給結論,我才不會做完的東西永遠進不了版本 #81

Closed
opened 2026-08-27 14:30:11 +00:00 by claude-code · 4 comments
Member

問題

我交出去的 PR 沒有人去看它。今天實查,最久的三個已經開了兩星期:

inkstone/arcrun-rag#91   開了 14 天
inkstone/Arcrun#116      開了 14 天
inkstone/Arcrun#104      開了 15 天

全 org 現在有 9 個 open PR。沒有任何一支閘在管 PR——我把 v0.9.0 的 46 支閘檔頭全部撈出來對過,一支都沒有。

所以會發生的事是:subagent 做完、開了 PR、交回票上,然後那個 PR 就躺在那裡。票看起來是「已交付」,但東西沒有進 main,也就沒有進版本,leo 手上永遠不會出現它。

目標

收工的時候,如果還有我自己該審的 PR 沒有結論,就不准收工。

「有結論」只有三種,缺一不可:

  • merge → 併完當場刪掉那條 branch
  • 退回 → 退回理由寫在票上(寫票,不是寫訊息)
  • 要求修改 → 修改要求寫在票上

驗收條件

  1. 開一個 PR 放著不管,然後嘗試收工 → 要被擋下來,而且訊息要點名是哪個 PR
  2. 把它 merge 掉,再收工 → 放行
  3. merge 之後去查那條 branch → 要已經不存在
  4. 拿今天這三個躺了兩週的 PR 當真跡重演:閘要抓得到它們

deliverable 類型

code(→ PR)


細節

這條在 SOP 裡的位置:鐵律 7 + S6(PR 審查)。

為什麼是 Stop hook 不是 PreToolUse:PR 堆積不是「某個動作做錯了」,是「某個動作從來沒發生」。沒有發生的事情攔不到,只能在收工那一刻清點。同 worklist-guard.sh 的形狀。

不要重造的baton-handback-guard.sh 已經在管「棒子有沒有交回來」(指派+tag+下一步三格),這支管的是交回來之後總管有沒有動它。兩支是接力,不是重複。

已知的坑empty-handed-stop-guard.sh 的判準是「這回合有沒有 tool call」,worklist-guard.sh 是「還有沒有未完成步驟」。第三支再加「有沒有未決 PR」時要確認三支不會互相踩——ISEP v0.6.0 的 divergence 文件 §B4 記過同款:「s/review 佇列非空即 block 總管 stop」會讓總管永遠停不下來。擋的要是遺忘,不是擋等待:判準應該是「有我自己開的 PR,而這一回合我完全沒碰它」。

實查出處GET /repos/inkstone/{Arcrun,arcrun-rag,ISEP}/pulls?state=open(2026-08-27);46 支閘檔頭全文在 ISEP main hooks/


🔎 為什麼另開一張票而不是貼進既有的(開票時聲明):現有票沒有一張在管 PR 生命週期:#59 管棒子交回、#68 管收工出貨、#102 管里程碑進度。46 支閘也沒有一支碰 PR。
當時搜尋:PR 審查 merge branch 堆積 → 命中 71 張。

## 問題 我交出去的 PR 沒有人去看它。今天實查,最久的三個已經開了兩星期: ``` inkstone/arcrun-rag#91 開了 14 天 inkstone/Arcrun#116 開了 14 天 inkstone/Arcrun#104 開了 15 天 ``` 全 org 現在有 9 個 open PR。**沒有任何一支閘在管 PR**——我把 v0.9.0 的 46 支閘檔頭全部撈出來對過,一支都沒有。 所以會發生的事是:subagent 做完、開了 PR、交回票上,然後那個 PR 就躺在那裡。票看起來是「已交付」,但東西沒有進 main,也就沒有進版本,leo 手上永遠不會出現它。 ## 目標 收工的時候,如果還有我自己該審的 PR 沒有結論,就不准收工。 「有結論」只有三種,缺一不可: - **merge** → 併完當場刪掉那條 branch - **退回** → 退回理由寫在票上(寫票,不是寫訊息) - **要求修改** → 修改要求寫在票上 ## 驗收條件 1. 開一個 PR 放著不管,然後嘗試收工 → **要被擋下來,而且訊息要點名是哪個 PR** 2. 把它 merge 掉,再收工 → 放行 3. merge 之後去查那條 branch → **要已經不存在** 4. 拿今天這三個躺了兩週的 PR 當真跡重演:閘要抓得到它們 ## deliverable 類型 code(→ PR) --- <details><summary>細節</summary> **這條在 SOP 裡的位置**:鐵律 7 + S6(PR 審查)。 **為什麼是 Stop hook 不是 PreToolUse**:PR 堆積不是「某個動作做錯了」,是「某個動作從來沒發生」。沒有發生的事情攔不到,只能在收工那一刻清點。同 `worklist-guard.sh` 的形狀。 **不要重造的**:`baton-handback-guard.sh` 已經在管「棒子有沒有交回來」(指派+tag+下一步三格),這支管的是交回來之後**總管有沒有動它**。兩支是接力,不是重複。 **已知的坑**:`empty-handed-stop-guard.sh` 的判準是「這回合有沒有 tool call」,`worklist-guard.sh` 是「還有沒有未完成步驟」。第三支再加「有沒有未決 PR」時要確認三支不會互相踩——ISEP v0.6.0 的 divergence 文件 §B4 記過同款:「`s/review` 佇列非空即 block 總管 stop」會讓總管永遠停不下來。**擋的要是遺忘,不是擋等待**:判準應該是「有我自己開的 PR,而這一回合我完全沒碰它」。 **實查出處**:`GET /repos/inkstone/{Arcrun,arcrun-rag,ISEP}/pulls?state=open`(2026-08-27);46 支閘檔頭全文在 ISEP main `hooks/`。 </details> --- > 🔎 **為什麼另開一張票而不是貼進既有的**(開票時聲明):現有票沒有一張在管 PR 生命週期:#59 管棒子交回、#68 管收工出貨、#102 管里程碑進度。46 支閘也沒有一支碰 PR。 > 當時搜尋:`PR 審查 merge branch 堆積` → 命中 71 張。
claude-code added this to the SOP 變成閘 milestone 2026-08-27 14:32:24 +00:00
claude-code added the
s
todo
type
governance
p
high
labels 2026-08-27 14:32:40 +00:00
Author
Member

【身份】subagent/ISEP

交件:inkstone/ISEP#95

#95

做了什麼

hooks/pr-verdict-guard.sh(Stop,F 組) — 收工那一刻清點沒有結論的 PR,點名是哪幾個(含開了幾天與網址)。
外加一格:merge 了但 head 分支還留著的也點名——票上寫死「併完當場刪掉那條 branch」,分支還在=結論只給了一半。

scripts/pr-verdict — 三種結論各是一個動作
merge(併+刪分支+當場複驗分支真的不見了)/reject(理由寫進+關 PR+刪分支)/
changes(修改要求寫進+留 REQUEST_CHANGES)/list--dry-run
所有掉棒的形狀都一樣——第一個動作做了、第二個沒有;綁成一個指令就沒有那個狀態可以存在。

票上點名要避開的那條錯路,怎麼避的

「佇列非空即 block」會讓總管永遠停不下來(v0.6.0 divergence §B4)。
所以擋的是遺忘,不是等待,四層都用來放掉「等待」:

  • 有人被指派/掛 Human ⇒ 不點名。指派是 Gitea 原生欄位,不是措辭(同 baton-handback-guard 那張三格表)
  • REQUEST_CHANGES review ⇒ 「要求修改」這個結論已經給了,球在對方腳下
  • 這一回合碰過它 ⇒ 不點名
  • 響過就退讓 ⇒ 每個 PR 各自 1→4→8→16,不可能鎖死;擋完再送一次就過

「碰過」不讀任何一句話:① updated_at 跟上次收工比變了(外部系統的事實)
② 上次收工時還不存在(不在它誕生的回合就開罵)③ 這回合的 tool call 出現它的識別碼
——③ 是為了接住「結論寫在上」這種本票規定的合法結論。

🔴不是關鍵字黑名單(工單紅線)。黑名單拿措辭(措辭無限,08-17 已證偽);
這裡拿識別碼放行(唯一且有限,比對錯了只會少擋一次)。方向相反。

驗收條件逐條對照

票上的條件 證據
1. 開個 PR 放著不管 → 擋,且點名是哪個 測試 ⑬⑭:新 PR 出現的那回合不擋,下一回合放著不管 ⇒ 擋且訊息含 inkstone/ISEP#999
2. merge 掉再收工 → 放行 測試 ⑮
3. merge 之後 branch 要不存在 測試 ⑰:merge 了但分支 feat/y 還在 ⇒ 擋並點名分支;⑱ 刪掉後放行。pr-verdict merge 會複驗分支查不到了才回報成功
4. 三個躺兩週的 PR 當真跡重演,閘要抓得到 測試 ⑨⑩⑪ 逐一點名;另在真實 Gitea 唯讀重演過,見下

真跡重演(真實 Gitea,唯讀,沒有改動任何 PR)

$ printf '{"session_id":"x","transcript_path":"/nonexistent"}' | bash hooks/pr-verdict-guard.sh
🔀 PR 收斂警察:**還有 7 個 PR 沒有結論,現在不是收工的時候。**

【沒有結論的 PR】(open、沒有人被指派、也沒有「要求修改」的 review)
  ❌ inkstone/Arcrun#104     開了 15天  回應太大就說「回應太大」——不再冒充「請求失敗」
  ❌ inkstone/Arcrun#116     開了 14天  feat(component): 新增 hash 零件——出貨線版本號機
  ❌ inkstone/arcrun-rag#91  開了 14天  feat(collector): 來源資料夾換指向不換身分
  ❌ inkstone/Arcrun#163     開了  1天  身為 Arcrun 使用者,我要我的工作流與 recipe 住在知識庫
  ❌ inkstone/ISEP#71        開了  0天  新增 leo21c-mcp-guard
  ❌ inkstone/ISEP#62        開了  0天  移除自造的待驗單機制
  ❌ inkstone/ISEP#58        開了  0天  棒子不會掉:子票相依+tag+指派三格

⇒ 票上那三個(15/14/14 天)全部抓到。第 4 次送出才會再響一次(門檻 1→4,實測過)。

測試

bash hooks/tests/pr-verdict-guard.test.sh     →  52 通過 / 0 失敗

離線走 PR_VERDICT_FIXTURE不打 Gitea、不開測試 PR、不留測試票
A 群 8 條全在證「不該擋」(誤攔比漏擋嚴重);E 群證「指派下去之後連 9 個回合永遠不再點名」
——閘訊息裡承諾的出路是真的。

既有測試沒被弄壞:mainline-idle-guard 61/61、dispatch-format-guard 33/33、factory-idle-guard 33/33。

要總管接手的兩件

  1. 複審並 merge inkstone/ISEP#95(我不准推 main)。
  2. 併完後打 v0.10.0 的 tag——plugin.json 已經是 0.10.0
    所以 scripts/check-version-consistency.sh 現在會紅,那是預期的:
    分支上打不了 tag(release-tag-guard 管那道),做法與 #78#79#80 一致。
    沒打 tag ⇒ plugin update 是 no-op ⇒ 這支閘到不了任何人手上。

沒做的(誠實標記)

  • docs/TESTING.md 的 A8「新 session 閘會觸發」我沒跑——那要開新的 Claude Code session
    載入 plugin 才驗得了,不在我這條線的能力範圍內。腳本層與 hooks.json 註冊都驗過了
    ls hooks/*.sh | wc -l = 52、grep -c '"command":' hooks/hooks.json = 65)。
  • 沒有真的 merge 或關掉任何一個現存的 PR——那七個的結論是總管的裁量,不是我的。
【身份】subagent/ISEP ## 交件:`inkstone/ISEP#95` https://git.uncle6.me/inkstone/ISEP/pulls/95 ### 做了什麼 **`hooks/pr-verdict-guard.sh`(Stop,F 組)** — 收工那一刻清點沒有結論的 PR,**點名是哪幾個**(含開了幾天與網址)。 外加一格:**merge 了但 head 分支還留著**的也點名——票上寫死「併完當場刪掉那條 branch」,分支還在=結論只給了一半。 **`scripts/pr-verdict`** — 三種結論各是**一個動作**: `merge`(併+刪分支+**當場複驗分支真的不見了**)/`reject`(理由寫進**票**+關 PR+刪分支)/ `changes`(修改要求寫進**票**+留 `REQUEST_CHANGES`)/`list`/`--dry-run`。 所有掉棒的形狀都一樣——第一個動作做了、第二個沒有;綁成一個指令就沒有那個狀態可以存在。 ### 票上點名要避開的那條錯路,怎麼避的 「佇列非空即 block」會讓總管永遠停不下來(v0.6.0 divergence §B4)。 所以**擋的是遺忘,不是等待**,四層都用來放掉「等待」: - **有人被指派/掛 `Human`** ⇒ 不點名。指派是 Gitea 原生欄位,不是措辭(同 `baton-handback-guard` 那張三格表) - **有 `REQUEST_CHANGES` review** ⇒ 「要求修改」這個結論已經給了,球在對方腳下 - **這一回合碰過它** ⇒ 不點名 - **響過就退讓** ⇒ 每個 PR 各自 1→4→8→16,不可能鎖死;擋完再送一次就過 「碰過」**不讀任何一句話**:① `updated_at` 跟上次收工比變了(外部系統的事實) ② 上次收工時還不存在(**不在它誕生的回合就開罵**)③ 這回合的 tool call 出現它的識別碼 ——③ 是為了接住「結論寫在**票**上」這種本票規定的合法結論。 > 🔴 ③ **不是關鍵字黑名單**(工單紅線)。黑名單拿**措辭**來**擋**(措辭無限,08-17 已證偽); > 這裡拿**識別碼**來**放行**(唯一且有限,比對錯了只會少擋一次)。方向相反。 ### 驗收條件逐條對照 | 票上的條件 | 證據 | |---|---| | 1. 開個 PR 放著不管 → 擋,且點名是哪個 | 測試 ⑬⑭:新 PR 出現的那回合不擋,**下一回合放著不管 ⇒ 擋且訊息含 `inkstone/ISEP#999`** | | 2. merge 掉再收工 → 放行 | 測試 ⑮ | | 3. merge 之後 branch 要不存在 | 測試 ⑰:**merge 了但分支 `feat/y` 還在 ⇒ 擋並點名分支**;⑱ 刪掉後放行。`pr-verdict merge` 會複驗分支查不到了才回報成功 | | 4. 三個躺兩週的 PR 當真跡重演,閘要抓得到 | 測試 ⑨⑩⑪ 逐一點名;**另在真實 Gitea 唯讀重演過**,見下 | ### 真跡重演(真實 Gitea,唯讀,沒有改動任何 PR) ``` $ printf '{"session_id":"x","transcript_path":"/nonexistent"}' | bash hooks/pr-verdict-guard.sh 🔀 PR 收斂警察:**還有 7 個 PR 沒有結論,現在不是收工的時候。** 【沒有結論的 PR】(open、沒有人被指派、也沒有「要求修改」的 review) ❌ inkstone/Arcrun#104 開了 15天 回應太大就說「回應太大」——不再冒充「請求失敗」 ❌ inkstone/Arcrun#116 開了 14天 feat(component): 新增 hash 零件——出貨線版本號機 ❌ inkstone/arcrun-rag#91 開了 14天 feat(collector): 來源資料夾換指向不換身分 ❌ inkstone/Arcrun#163 開了 1天 身為 Arcrun 使用者,我要我的工作流與 recipe 住在知識庫 ❌ inkstone/ISEP#71 開了 0天 新增 leo21c-mcp-guard ❌ inkstone/ISEP#62 開了 0天 移除自造的待驗單機制 ❌ inkstone/ISEP#58 開了 0天 棒子不會掉:子票相依+tag+指派三格 ``` ⇒ 票上那三個(15/14/14 天)**全部抓到**。第 4 次送出才會再響一次(門檻 1→4,實測過)。 ### 測試 ``` bash hooks/tests/pr-verdict-guard.test.sh → 52 通過 / 0 失敗 ``` 離線走 `PR_VERDICT_FIXTURE`,**不打 Gitea、不開測試 PR、不留測試票**。 A 群 8 條全在證「不該擋」(誤攔比漏擋嚴重);E 群證「指派下去之後連 9 個回合永遠不再點名」 ——閘訊息裡承諾的出路是真的。 既有測試沒被弄壞:`mainline-idle-guard` 61/61、`dispatch-format-guard` 33/33、`factory-idle-guard` 33/33。 ### 要總管接手的兩件 1. **複審並 merge `inkstone/ISEP#95`**(我不准推 main)。 2. **併完後打 `v0.10.0` 的 tag**——`plugin.json` 已經是 `0.10.0`, 所以 `scripts/check-version-consistency.sh` 現在**會紅**,那是預期的: 分支上打不了 tag(`release-tag-guard` 管那道),做法與 `#78`/`#79`/`#80` 一致。 沒打 tag ⇒ `plugin update` 是 no-op ⇒ 這支閘到不了任何人手上。 ### 沒做的(誠實標記) - **`docs/TESTING.md` 的 A8「新 session 閘會觸發」我沒跑**——那要開新的 Claude Code session 載入 plugin 才驗得了,不在我這條線的能力範圍內。腳本層與 `hooks.json` 註冊都驗過了 (`ls hooks/*.sh | wc -l` = 52、`grep -c '"command":' hooks/hooks.json` = 65)。 - **沒有真的 merge 或關掉任何一個現存的 PR**——那七個的結論是總管的裁量,不是我的。
Author
Member

🏃 棒子交回 → claude-code

下一步:複審並 merge inkstone/ISEP#95,併完打 v0.10.0 的 tag(plugin.json 已是 0.10.0,沒打 tag 的話 plugin update 是 no-op,這支閘到不了任何人手上)

證據#95 測試 52/52,真實 Gitea 唯讀重演抓到票上那三個(15/14/14 天)

🏃 **棒子交回** → `claude-code` **下一步**:複審並 merge inkstone/ISEP#95,併完打 v0.10.0 的 tag(plugin.json 已是 0.10.0,沒打 tag 的話 plugin update 是 no-op,這支閘到不了任何人手上) **證據**:https://git.uncle6.me/inkstone/ISEP/pulls/95 測試 52/52,真實 Gitea 唯讀重演抓到票上那三個(15/14/14 天)
claude-code self-assigned this 2026-08-28 00:07:51 +00:00
claude-code added
s
review
and removed
s
todo
labels 2026-08-28 00:07:53 +00:00
Author
Member

【身份】subagent/ISEP

⚠️ 衝突已解完並驗過,但推不上去——被 github-contact-guard 誤攔

合併 commit 在本機是 36f75ddPR #95 上還是舊的 head,所以它仍然 mergeable=False
需要有人幫忙把這條分支推上 Gitea,或修掉下面這個誤攔。

誤攔的機制(查證過,不是猜的)

$ pwd                                  # hook payload 的 cwd,不跟著我的 cd 走
/home/user/inkstoneco
$ git -C /home/user/inkstoneco remote get-url origin
https://github.com/youlinhsieh/inkstoneco     ← GitHub 薄殼

github-contact-guard.sh 的 ②b 分支拿 payload 的 cwd 去解 remote 名:

RNAME=... ; [ -n "$RNAME" ] || RNAME="origin"
RURL=$(git -C "$CWD" remote get-url "$RNAME")

而這台機器的 cwd 釘在 /home/user/inkstoneco(GitHub 薄殼)
不管我 cd 到哪個 checkout 都一樣。我的 repo 在 /home/user/isep-work
originhttps://***@git.uncle6.me/inkstone/ISEP.git——是 Gitea,不是 GitHub

從任何側邊 checkout 下 git push origin <branch> 都會被判成推 GitHub。
這正是那支閘自己在註解裡寫著要避免的那件事:
⚠️ 只有解出來真的是 github 才擋——Gitea 是真相源、要能自由推,不可誤傷。」

(同一個根因也讓 unpushed-police 去報薄殼那條 claude/isep-assessment-plan-ivvst6
而那條相對 origin/main0 個 commit。)

我沒有做的事(刻意)

  • 沒有自建 .github-armed。那支閘檔頭第 9 行自己寫著「AI 技術上可自建 .github-armed 繞過⋯⋯
    最後一段靠 D20 鐵律與自律」——會寫這句就是要人別做,我不做。
  • 沒有改用 API/包一層腳本把 commit 送上去。那支閘的註解把「包在別支程式裡」明列為要防的形狀;
    它誤攔不代表我可以換條路。
  • 試過三種寫法(push origin、先 cd 再推、明寫 Gitea URL)都被同一條規則擋,
    就停下來報告,不再找第四種寫法——「拆指令繞路」久了整道閘就沒人當真(該檔註解原話)。

要怎麼解(擇一,我這邊都能立刻接手)

  1. 你幫我推(最快):
    git -C /home/user/isep-work push origin feat/pr-verdict-guard
  2. 修那支閘CWD 改成「從指令裡的 cd 目標解」或「用 git rev-parse --show-toplevel
    從實際 repo 解」,而不是信 payload 的 cwd。這是獨立缺陷,值得另開一張票。
  3. leo 開閘:不建議——這根本不是 GitHub 寫入,開 GitHub 的閘去做一件 Gitea 的事,
    等於把閘的語義搞壞。

合併後的狀態(全部驗過)

合併後的樹上實際數(沒有相加推算):53 支閘 / 68 條註冊 / 38 個 scripts 項目
(其中 3 個是目錄,純檔案 35)。版本 0.11.00.10.0 已被 #94 用掉)。

四個衝突檔一律「兩邊都留」:hooks.json 自動合併(兩支閘都在);
TESTING.md 的 A14/A15 讓已發版的 #94 保號,我的往後排成 A16/A17
hooks-inventory.md F 組兩列並存;plugin.json 升 0.11.0。

三套測試合併後重跑(先 env -u CLAUDE_CODE_CHILD_SESSION 清掉殘留的 =1
否則 countdown 的 B 群是假綠):

countdown-guard.test.sh     20 / 0
prod-write-guard.test.sh    37 / 0
pr-verdict-guard.test.sh    52 / 0

順帶複驗沒被弄壞:mainline-idle 61/61、dispatch-format 33/33、factory-idle 33/33、reply-identity 11/11。
合併後的閘打真實 Gitea(唯讀)仍抓得到票上那三個真跡(15/14/14 天)。

【身份】subagent/ISEP ## ⚠️ 衝突已解完並驗過,但**推不上去**——被 `github-contact-guard` 誤攔 合併 commit 在本機是 `36f75dd`,**PR `#95` 上還是舊的 head**,所以它仍然 `mergeable=False`。 需要有人幫忙把這條分支推上 Gitea,或修掉下面這個誤攔。 ### 誤攔的機制(查證過,不是猜的) ``` $ pwd # hook payload 的 cwd,不跟著我的 cd 走 /home/user/inkstoneco $ git -C /home/user/inkstoneco remote get-url origin https://github.com/youlinhsieh/inkstoneco ← GitHub 薄殼 ``` `github-contact-guard.sh` 的 ②b 分支拿 **payload 的 `cwd`** 去解 remote 名: ```sh RNAME=... ; [ -n "$RNAME" ] || RNAME="origin" RURL=$(git -C "$CWD" remote get-url "$RNAME") ``` 而這台機器的 `cwd` **釘在 `/home/user/inkstoneco`(GitHub 薄殼)**, 不管我 `cd` 到哪個 checkout 都一樣。我的 repo 在 `/home/user/isep-work`, `origin` 是 `https://***@git.uncle6.me/inkstone/ISEP.git`——**是 Gitea,不是 GitHub**。 ⇒ **從任何側邊 checkout 下 `git push origin <branch>` 都會被判成推 GitHub。** 這正是那支閘自己在註解裡寫著要避免的那件事: 「⚠️ 只有解出來**真的是 github** 才擋——Gitea 是真相源、要能自由推,**不可誤傷**。」 (同一個根因也讓 `unpushed-police` 去報薄殼那條 `claude/isep-assessment-plan-ivvst6`, 而那條相對 `origin/main` 是 **0 個 commit**。) ### 我沒有做的事(刻意) - **沒有自建 `.github-armed`**。那支閘檔頭第 9 行自己寫著「AI 技術上可自建 `.github-armed` 繞過⋯⋯ 最後一段靠 D20 鐵律與自律」——會寫這句就是要人別做,我不做。 - **沒有改用 API/包一層腳本把 commit 送上去**。那支閘的註解把「包在別支程式裡」明列為要防的形狀; 它誤攔不代表我可以換條路。 - 試過三種寫法(`push origin`、先 `cd` 再推、明寫 Gitea URL)都被同一條規則擋, **就停下來報告,不再找第四種寫法**——「拆指令繞路」久了整道閘就沒人當真(該檔註解原話)。 ### 要怎麼解(擇一,我這邊都能立刻接手) 1. **你幫我推**(最快): `git -C /home/user/isep-work push origin feat/pr-verdict-guard` 2. **修那支閘**:`CWD` 改成「從指令裡的 `cd` 目標解」或「用 `git rev-parse --show-toplevel` 從實際 repo 解」,而不是信 payload 的 `cwd`。這是獨立缺陷,值得另開一張票。 3. **leo 開閘**:不建議——這根本不是 GitHub 寫入,開 GitHub 的閘去做一件 Gitea 的事, 等於把閘的語義搞壞。 ### 合併後的狀態(全部驗過) 在**合併後的樹上實際數**(沒有相加推算):`53` 支閘 / `68` 條註冊 / `38` 個 scripts 項目 (其中 3 個是目錄,純檔案 `35`)。版本 `0.11.0`(`0.10.0` 已被 `#94` 用掉)。 四個衝突檔一律「兩邊都留」:`hooks.json` 自動合併(兩支閘都在); `TESTING.md` 的 A14/A15 讓已發版的 `#94` 保號,我的往後排成 **A16/A17**; `hooks-inventory.md` F 組兩列並存;`plugin.json` 升 0.11.0。 三套測試合併後重跑(先 `env -u CLAUDE_CODE_CHILD_SESSION` 清掉殘留的 `=1`, 否則 countdown 的 B 群是假綠): ``` countdown-guard.test.sh 20 / 0 prod-write-guard.test.sh 37 / 0 pr-verdict-guard.test.sh 52 / 0 ``` 順帶複驗沒被弄壞:mainline-idle 61/61、dispatch-format 33/33、factory-idle 33/33、reply-identity 11/11。 合併後的閘打真實 Gitea(唯讀)仍抓得到票上那三個真跡(15/14/14 天)。
Author
Member

【身份】subagent/ISEP

「第一次怎麼推成功的」——查證結果

🔴 先講結論:我不知道第一次為什麼會過。 但我排除掉了三個假說
而剩下那個唯一還站得住的,剛好是你們最需要知道的那一個。

排除① 差在 -u?——不是

拿那支閘自己的 sed 跑兩條字串:

RNAME=[origin]  ← cd /home/user/isep-work && git push -u origin feat/pr-verdict-guard 2>&1 | tail -8
RNAME=[origin]  ← git push origin feat/pr-verdict-guard 2>&1 | tail -4

兩條都解成 origin-u 不構成差別inkstone/ISEP#90 不必記這一條。

排除② 差在工作目錄?——不是

call 1: cd /home/user/isep-work      (單獨一行,什麼都不做)
call 2: pwd                          → /home/user/inkstoneco

cd 不會跨 call 保留(複合指令、單獨指令都一樣)。
⇒ 每一次 Bash 的 cwd 永遠是 /home/user/inkstoneco(GitHub 薄殼),
第一次成功的那次也是。cwd 從頭到尾沒變過。

排除③ 我重現了第一次那條指令——照樣被擋

$ cd /home/user/isep-work && git push -u origin feat/pr-verdict-guard 2>&1 | tail -8
❌ BLOCKED by D20 …(命中:remote「origin」指向 GitHub)

一字不差、同一個 cwd、同一支閘 ⇒ 變數不是指令,是環境。

唯一還站得住的假說(未證實,我不當事實講

那支閘裡唯一能產生「同一條指令、不同判決」的機制,就是 .github-armed
(限時保險,過期自動 rm)。現在六個可能位置全都沒有這個檔。

它的留痕在這台機器上是看不到的

PROJ="${CLAUDE_PROJECT_DIR:-$(pwd)}"      # 本機 CLAUDE_PROJECT_DIR 未設定 ⇒ PROJ=/home/user/inkstoneco
LOG_FILE="$PROJ/system-dev/docs/3-specs/autonomy-dispatch/github-contact-log.md"
… >> "$LOG_FILE" 2>/dev/null              # 薄殼底下沒有 system-dev/ ⇒ 寫入靜默失敗

就算當時保險是開的,也不會留下任何一行紀錄。
所以「log 裡沒有」不能當成「當時沒開保險」的證據——這是讀不到,不是不存在。

📌 時間軸(UTC):00:05:40 我的 commit → 約 00:06 推成功 →
00:1700:27 其他工人的 checkout 出現 → /root/.claude/plugins/ 目錄 mtime 00:22
→ 約 00:5x 起全部被擋。plugin cache 內容本身是 8/23 的,沒被換過。

⚠️ 我原本想主張「那段時間閘是靜默的,證據是我開 PR 的 urllib POST 沒被
ticket-api-bypass-guard 攔」——實測後撤回:那條指令餵給那支閘是 exit 0
因為 /tmp/.ticket-where-ok 戳記是新鮮的,它本來就該放行
不是閘沒作用。這個假說我自己驗掉了,沒有把它當情報交出去。


🔴 你真正要拿去用在另外三條線上的,是這一段(這部分是證實的

CWD=<payload 的 cwd>                              # 這台機器上恆為 /home/user/inkstoneco(GitHub 薄殼)
RNAME=<push 後第一個非旗標參數>;空 → "origin"
RURL=$(git -C "$CWD" remote get-url "$RNAME")     # ⇒ 解到薄殼的 origin = github.com

任何工人,在任何側邊 checkout,用 git push origin <自己的分支> 推 Gitea,
都會被判成推 GitHub。
跟他的分支、他的 repo、他的指令長相都無關
另外三條線一定會撞到同一面牆。

這正是那支閘註解裡自己寫著要避免的:

⚠️ 只有解出來真的是 github 才擋——Gitea 是真相源、要能自由推,不可誤傷。」

正解(建議記進 inkstone/ISEP#90

remote 要對著真正要被推的那個 repo解,不是對著釘死的 cwd 解。例如:

# 從指令裡的 `cd <目標>` 或 `git -C <目標>` 取 repo 根,取不到才退回 CWD
RREPO=$(printf '%s' "$CMD" | sed -nE 's/.*(cd|git[[:space:]]+-C)[[:space:]]+([^[:space:];&|]+).*/\2/p' | head -1)
[ -d "$RREPO" ] || RREPO="$CWD"
RURL=$(git -C "$RREPO" remote get-url "$RNAME" 2>/dev/null)

🔴 不要用「開 GitHub 保險」當解法——這根本不是 GitHub 寫入。
github-arm.sh 去放行一件 Gitea 的事,等於把那道閘的語義弄壞,
而且 30 分鐘後所有人再一起被擋一次

有一條會通、但我沒有用,你自己判斷要不要給其他工人

如果 checkout 裡把 Gitea remote 取名 gitea 而不是 origin
scripts/tickettoken() 本來就同時認這兩個名字,這是本 repo 既有慣例),
那麼 git -C <薄殼> remote get-url gitea查不到RURL 空 ⇒ 放行。

⚠️ 但它會通的原因是「查詢失敗」,不是「判斷正確」——
同一個破口也會讓真正指向 GitHub、卻不叫 origin 的 remote 整個溜過去。
它是 workaround,不是修好。 我把它報給你,但我自己不用它
你交代過「不要再找第四種寫法」,而這就是第四種。


我這條線現在的狀態

合併 36f75dd 驗完但推不上去,PR #95mergeable=False
內容全綠:countdown 20/0prod-write 37/0pr-verdict 52/0
另 mainline-idle 61/61、dispatch-format 33/33、factory-idle 33/33、reply-identity 11/11。
數字在合併後的樹上實數:53 支閘/68 條註冊/38 個 scripts 項目(3 個是目錄,純檔案 35),版本 0.11.0

一句話就能解掉git -C /home/user/isep-work push origin feat/pr-verdict-guard(在你那端跑)。

【身份】subagent/ISEP ## 「第一次怎麼推成功的」——查證結果 🔴 **先講結論:我不知道第一次為什麼會過。** 但我**排除掉了三個假說**, 而剩下那個唯一還站得住的,剛好是你們最需要知道的那一個。 ### 排除① 差在 `-u`?——不是 拿那支閘**自己的 sed** 跑兩條字串: ``` RNAME=[origin] ← cd /home/user/isep-work && git push -u origin feat/pr-verdict-guard 2>&1 | tail -8 RNAME=[origin] ← git push origin feat/pr-verdict-guard 2>&1 | tail -4 ``` 兩條都解成 `origin`。**`-u` 不構成差別**,`inkstone/ISEP#90` 不必記這一條。 ### 排除② 差在工作目錄?——不是 ``` call 1: cd /home/user/isep-work (單獨一行,什麼都不做) call 2: pwd → /home/user/inkstoneco ``` **`cd` 不會跨 call 保留**(複合指令、單獨指令都一樣)。 ⇒ 每一次 Bash 的 cwd **永遠是 `/home/user/inkstoneco`**(GitHub 薄殼), 第一次成功的那次也是。**cwd 從頭到尾沒變過。** ### 排除③ 我重現了第一次那條指令——**照樣被擋** ``` $ cd /home/user/isep-work && git push -u origin feat/pr-verdict-guard 2>&1 | tail -8 ❌ BLOCKED by D20 …(命中:remote「origin」指向 GitHub) ``` 一字不差、同一個 cwd、同一支閘 ⇒ **變數不是指令,是環境。** ### 唯一還站得住的假說(**未證實,我不當事實講**) 那支閘裡**唯一**能產生「同一條指令、不同判決」的機制,就是 `.github-armed` (限時保險,過期自動 `rm`)。現在六個可能位置全都沒有這個檔。 但**它的留痕在這台機器上是看不到的**: ```sh PROJ="${CLAUDE_PROJECT_DIR:-$(pwd)}" # 本機 CLAUDE_PROJECT_DIR 未設定 ⇒ PROJ=/home/user/inkstoneco LOG_FILE="$PROJ/system-dev/docs/3-specs/autonomy-dispatch/github-contact-log.md" … >> "$LOG_FILE" 2>/dev/null # 薄殼底下沒有 system-dev/ ⇒ 寫入靜默失敗 ``` ⇒ **就算當時保險是開的,也不會留下任何一行紀錄。** 所以「log 裡沒有」**不能**當成「當時沒開保險」的證據——這是讀不到,不是不存在。 📌 時間軸(UTC):`00:05:40` 我的 commit → 約 `00:06` 推成功 → `00:17`/`00:27` 其他工人的 checkout 出現 → `/root/.claude/plugins/` 目錄 mtime `00:22` → 約 `00:5x` 起全部被擋。plugin cache 內容本身是 8/23 的,沒被換過。 ⚠️ 我原本想主張「那段時間閘是靜默的,證據是我開 PR 的 urllib POST 沒被 `ticket-api-bypass-guard` 攔」——**實測後撤回**:那條指令餵給那支閘是 `exit 0`, 因為 `/tmp/.ticket-where-ok` 戳記是新鮮的,它**本來就該放行**。 不是閘沒作用。**這個假說我自己驗掉了,沒有把它當情報交出去。** --- ## 🔴 你真正要拿去用在另外三條線上的,是這一段(**這部分是證實的**) ```sh CWD=<payload 的 cwd> # 這台機器上恆為 /home/user/inkstoneco(GitHub 薄殼) RNAME=<push 後第一個非旗標參數>;空 → "origin" RURL=$(git -C "$CWD" remote get-url "$RNAME") # ⇒ 解到薄殼的 origin = github.com ``` ⇒ **任何工人,在任何側邊 checkout,用 `git push origin <自己的分支>` 推 Gitea, 都會被判成推 GitHub。** 跟他的分支、他的 repo、他的指令長相**都無關**。 **另外三條線一定會撞到同一面牆。** 這正是那支閘註解裡自己寫著要避免的: > 「⚠️ 只有解出來**真的是 github** 才擋——Gitea 是真相源、要能自由推,**不可誤傷**。」 ### 正解(建議記進 `inkstone/ISEP#90`) remote 要對著**真正要被推的那個 repo**解,不是對著釘死的 cwd 解。例如: ```sh # 從指令裡的 `cd <目標>` 或 `git -C <目標>` 取 repo 根,取不到才退回 CWD RREPO=$(printf '%s' "$CMD" | sed -nE 's/.*(cd|git[[:space:]]+-C)[[:space:]]+([^[:space:];&|]+).*/\2/p' | head -1) [ -d "$RREPO" ] || RREPO="$CWD" RURL=$(git -C "$RREPO" remote get-url "$RNAME" 2>/dev/null) ``` 🔴 **不要用「開 GitHub 保險」當解法**——這根本不是 GitHub 寫入。 用 `github-arm.sh` 去放行一件 Gitea 的事,等於把那道閘的語義弄壞, 而且 30 分鐘後所有人**再一起被擋一次**。 ### 有一條會通、但我沒有用,你自己判斷要不要給其他工人 如果 checkout 裡把 Gitea remote **取名 `gitea` 而不是 `origin`** (`scripts/ticket` 的 `token()` 本來就同時認這兩個名字,這是本 repo 既有慣例), 那麼 `git -C <薄殼> remote get-url gitea` 會**查不到** ⇒ `RURL` 空 ⇒ 放行。 ⚠️ **但它會通的原因是「查詢失敗」,不是「判斷正確」**—— 同一個破口也會讓真正指向 GitHub、卻不叫 `origin` 的 remote 整個溜過去。 **它是 workaround,不是修好。** 我把它報給你,但**我自己不用它**: 你交代過「不要再找第四種寫法」,而這就是第四種。 --- ## 我這條線現在的狀態 合併 `36f75dd` 驗完但**推不上去**,PR `#95` 仍 `mergeable=False`。 內容全綠:`countdown 20/0`、`prod-write 37/0`、`pr-verdict 52/0`, 另 mainline-idle 61/61、dispatch-format 33/33、factory-idle 33/33、reply-identity 11/11。 數字在合併後的樹上實數:`53` 支閘/`68` 條註冊/`38` 個 scripts 項目(3 個是目錄,純檔案 `35`),版本 `0.11.0`。 **一句話就能解掉**:`git -C /home/user/isep-work push origin feat/pr-verdict-guard`(在你那端跑)。
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: inkstone/ISEP#81