併進 main(v0.12.0)並接上「主線唯一答案」(inkstone/ISEP#83)

衝突只有 docs/TESTING.md 一處,兩邊都留:
  main 的 A16(gate-ok)/A17(未推警察)/A18(信標)原樣保留,
  我那節改編號成 A19——main 上 A16、A17 已經被 #90#99 各佔一次,不動別人的。

接上 inkstone/ISEP#82 帶進來的 hooks/lib/mainline.py(不自己養第二套判斷):
  · 有標主線 ⇒ 只排除「屬於主線那一條」的票;掛在**別條** milestone 的仍算債
    (那條線現在沒有人在推,正是它躺著的原因)
  · 沒標主線 ⇒ 退回保守做法:掛 open milestone 的一律不領,並在清單上照實說沒標
  · 別條 milestone 已逾期 ⇒ +15「答應過人家的日期已經過了」

測試 40 → 47 條(+7 條全給主線這段),並把 ISEP_COUNTDOWN_STATE_DIR 也隔離:
不然這支測試的結果會取決於「這台機器現在標了哪條主線」。

盤點數字在合併後的樹上重數:
  hooks/*.sh = 54、hooks.json "command": = 71(本 PR 沒新增 hook,兩個都沒變)
  scripts 一層檔案 = 39(本 PR +2)⇒ plugin.json description 37→39
  版本號沒動(0.12.0,總管統一定版)
This commit is contained in:
2026-08-28 00:51:06 +00:00
25 changed files with 2358 additions and 46 deletions
+117 -6
View File
@@ -154,6 +154,49 @@ bash hooks/tests/countdown-guard.test.sh
subagent 的回覆不是給 leo 看的),所以在一條 subagent 裡跑,B 群會**全綠而且是假綠**。
測試檔自己會把它清掉——2026-08-28 第一次跑就撞到這個,才補上去的。
### A16 — 現在的主線是哪一個:24 條
```
bash hooks/tests/mainline-focus-guard.test.sh
```
**該看到**`通過 24 條,失敗 0 條`。**全程離線**:狀態走 `ISEP_COUNTDOWN_STATE_DIR`
時鐘走 `ISEP_COUNTDOWN_NOW`、「那張票掛在哪」走 `ISEP_MAINLINE_FIXTURE`
並把 `TICKET_HOST` 指到一個連不上的位址——**任何一條真的走到網路,會在那裡當場失敗,
不會靜靜地變成假綠**。
**它在守什麼**`inkstone/ISEP#82`leo 2026-08-27):
「**14 個里程碑同時亮著卻不知道該看哪個**」。當天實查 14 個 open milestone
其中「Mira 現代化」這個名字同時活在 5 個 repo,5 個已經逾期
⇒ SOP 說的「只做**該** milestone 的事」**沒有指涉對象,等於不存在**。
**判準**:問「現在的主線是哪一個」,`scripts/mainline` 回一個答案,而且只有一個。
**失敗**
- A 群任一條紅 ⇒ 答案不唯一,或標了新的舊的還在——那就回到 14 個一起亮的狀態
- ⑧ 紅 ⇒ **目標宣告沒有自己出現**(票上驗收第 2 條),leo 又要自己去翻
- ⑨ 紅 ⇒ 主線的名字沒掛在被查核的 ⏱ 那一行上 ⇒ 它到不了 leo 眼前
(本票刻意不另立第二道會擋人的閘,就是靠這一格成立)
- ⑩ 紅 ⇒ 用「期限最近的那個」蓋過了被指定的主線。**猜的不准蓋過指定的**
- ⑬⑭ 紅 ⇒ 派了不在主線上的票沒被問一次,或問了卻沒給補收/跳線兩條路
- ⑯ 紅 ⇒ **鬼打牆**:同一張票被擋第二次。至多擋一次是硬要求
- **⑲–㉔(不該擋)任一條紅 ⇒ 誤攔,這比漏擋嚴重**。特別是 ⑲(沒有主線就一律放行,
票上驗收第 4 條)與 ㉒(問不到那張票掛在哪 ⇒ 放行)——
**「讀不到」不等於「不屬於」,把這兩件事講成同一句是最貴的錯**
📌 這一條線的另一半是**指令**,不是閘:`scripts/mainline``show``list``set`
`clear``refresh``adopt``has`)。要打網路的那幾個(list/set/adopt)不在測試裡跑,
因為它們**只有真的 Gitea 才驗得了**——驗法見 A17。
### A17 — 主線那支指令真的問得到 Gitea(要網路)
```
python3 scripts/mainline list
```
**該看到**:一份 open milestone 清單,**跨多個 repo**`ISEP` / `Arcrun` / `arcrun-rag` / `mira`…),
每一行左邊是可以直接貼給 `set``owner/repo#<milestone id>`
**失敗**
- `一個 open milestone 都沒讀到` ⇒ 這台機器拿不到 Gitea。**那是讀不到,不是沒有**
——不要因此標一個猜的主線
- 只有單一 repo ⇒ 沒有跨 repo,等於沒看到全局
### A15 — 發通知不等於部署:37 條
```
bash hooks/tests/prod-write-guard.test.sh hooks/prod-write-guard.sh
@@ -165,12 +208,73 @@ bash hooks/tests/prod-write-guard.test.sh hooks/prod-write-guard.sh
- 「卡點二」2 條任一紅 ⇒ 「談論它」又被當成「執行它」(同款第八次),
或是剝了內文之後連真的部署都放行了
### A16 — 舊票每天有固定管道被撈出來:40
### A16 — 「總管可以放行」那道門真的打得開:17
```
bash hooks/tests/gate-ok.test.sh
```
**該看到**`通過 17 條,失敗 0 條`
**它在守什麼**inkstone/ISEP#90 ④,2026-08-28 實查):三支閘
`prod-write-guard``main-and-prod-push-guard``stage-before-prod-guard`
都是「擋下來、但**總管看過就能放行**」的設計,而**既有測試只驗了擋得住,
一條都沒驗過放得開**。於是這個 bug 活了很久:`stat -f %m` 在 GNU coreutils 上
是「檔案系統資訊」,它一邊回非零一邊吐一整段文字 ⇒ 秒數被污染 ⇒
**在 Linux(=每一個雲端 session)上戳記永遠不被接受** ⇒ 那三支閘在雲端等於純擋,
而閘不會告訴你門是壞的。
**失敗**
- ②⑤(門打得開)紅 ⇒ 逃生口又焊死了,那三支閘在雲端變回純擋
- ⑥⑦⑧(門沒變寬)任一紅 ⇒ **更嚴重**:綁 repo/有效期/空戳記那三條性質是
08-11、08-12 兩次真的被穿透之後才補上的,不准為了「好放行」而鬆掉
- ⑫ 紅 ⇒ `gate-ok` 在解不出 repo 時留下了一枚註定打不開的空戳記,
那會讓人以為門開了(而空戳記本身就是 08-12 那把萬用鑰匙的形狀)
- ⑰ 紅 ⇒ 判準又變回「行為取決於 cwd 裡有沒有一個叫 `%m` 的檔」。
這條是這個 bug 的最後一層:**GNU 的 `-f` 是布林旗標,`%m` 被當成另一個檔名運算元**
⇒ 有那個檔就 `exit 0``||` 連跑都不跑。所以修法不能只是「把順序反過來」,
**每一步都要驗它是不是純數字**
⚠️ 這支會真的寫 `/tmp` 的戳記檔(那是閘寫死的路徑)。**不要在「已經蓋好戳記正要推東西」
的當下跑它**——它會把那枚戳記洗掉。
### A17 — 未推警察不會對雲端的工作分支亂叫:10 條
```
bash hooks/tests/unpushed-police.test.sh
```
**該看到**`通過 10 條,失敗 0 條`。全離線(用本機 bare repo 當「遠端」)。
**它在守什麼**inkstone/ISEP#90 ①):雲端 session 開出來的工作分支天生沒有 upstream,
內容卻等於遠端 main ⇒ 舊判準「沒 upstream=從沒推過」讓**每個雲端 session、每次收工
都被攔一次**08-27 一個 session 五次全是誤報)。
**失敗**
- A 群(①—⑥,不該報)任一紅 ⇒ **誤攔**,比漏擋嚴重:永遠在響的警報=訓練人忽略它,
下一條真的失蹤的分支會混在雜訊裡
- B 群(⑦—⑩,該報)任一紅 ⇒ 為了不吵而改成放行了,那是把閘關掉不是修好
- 特別看 ④:問不到遠端(離線)**不准當成「你沒推」**
### A18 — 信標會講出雲端會壞掉的那三件事:14 條
```
bash hooks/tests/isep-presence-beacon.test.sh
```
**該看到**`通過 14 條,失敗 0 條`。全離線。
**它在守什麼**inkstone/ISEP#90 ②③):信標原本只證明「有一份 plugin 載入了」,
不證明「載入的是哪一份」——08-27 雲端載 0.3.9、main 0.9.0,差 7 個 release
而它照樣是綠的。
**失敗**
- ⑫⑬⑭(信標永遠在)任一紅 ⇒ **最嚴重**:那一行消失=leo 會判定這個 session 零閘
- ③ 紅 ⇒ 版本落差報不出來,`ISEP#67` 那個病又變回看不見
- ⑤⑦ 紅 ⇒ 舊複本遮蔽正門抓不到(`InkStoneCo/scripts/ticket` 那件)
- ⑥ 紅 ⇒ **誤攔**:同步過的複本被念,人就學會忽略它
- ⑪ 紅 ⇒ 內層迴圈變數撞名的回歸(同一個 `.claude` 下第二個殘骸會被靜靜跳過)
### A19 — 舊票每天有固定管道被撈出來:47 條
```
bash scripts/test-debt-worklist.sh
```
**該看到**`通過 40 條,失敗 0 條`。**全程離線**:資料走 `ISEP_DEBT_FIXTURE`
時鐘走 `ISEP_DEBT_NOW`、狀態走 `ISEP_DEBT_STATE_DIR`——**不打 Gitea、不留測試票、不碰 `$HOME`**。
**該看到**`通過 47 條,失敗 0 條`。**全程離線**:資料走 `ISEP_DEBT_FIXTURE`
時鐘走 `ISEP_DEBT_NOW`、狀態走 `ISEP_DEBT_STATE_DIR`、主線走 `ISEP_COUNTDOWN_STATE_DIR`
——**不打 Gitea、不留測試票、不碰 `$HOME`**。
**它在守什麼**`inkstone/ISEP#83`leo 2026-08-27):
「現在每天都在追新的,所以**要用 Routine 去消化舊的**,可以有多個條件」。
@@ -190,6 +294,10 @@ bash scripts/test-debt-worklist.sh
- 「成包」兩條紅 ⇒ 互相指著/母子票被拆開領,做到一半才發現卡在另一張
- **「單向引用不成包」紅 ⇒ 這是誤攔的那一面**:第一版用單向引用成包,
結果 190 張裡 121 張黏成一包(大家都會順手引用 hub 票)=**一包沒有人領得動**
- 「接上主線」那組紅 ⇒ 還債線又自己養了一套「哪個是 active」的判斷。
唯一答案在 `hooks/lib/mainline.py``inkstone/ISEP#82`),**兩份必然漂移**。
特別看「沒標主線」那兩條:**沒有答案時要退回保守**(掛 open milestone 的一律不領),
不准猜一條出來
- 最後一組(SessionStart)紅 ⇒ 要嘛沒有人會被提醒去撈,
要嘛那一段偷偷去打網路——**開 session 自動撈就是輪詢**,紅線擋著
@@ -197,8 +305,8 @@ bash scripts/test-debt-worklist.sh
```
python3 scripts/debt-worklist list --top 5
```
**該看到**15 個 repo 逐一列出 issuePR 數,扣除數字講得出來
(掛 open milestone 的幾張、等 leo 的幾張、已領走的幾張),
**該看到**15 個 repo 逐一列出 issuePR 數,講得出**現在的主線是哪一條**
(或誠實說沒標),扣除數字講得出來(掛 open milestone 的幾張、等 leo 的幾張、已領走的幾張),
前幾名旁邊看得到「為什麼排這裡」的分項。
**失敗**:前幾名一眼看不出為什麼在那裡 ⇒ 那些權重就是錯的,去改 `score()`
**不要改成「總管覺得」**——這份清單的價值就在於它不靠記憶。
@@ -400,13 +508,16 @@ B2(信標那行)/B3(setup 輸出)/B4(閘的訊息)三個畫面
| **A15 pr-verdict --dry-run** | 總管 | ✅(2026-08-28 |
| **A14 回覆自己說出拖了多久** | 總管 | ✅ 20/202026-08-28inkstone/ISEP#63 |
| **A15 發通知不等於部署** | 總管 | ✅ 37/372026-08-28inkstone/ISEP#63 |
| **A16 舊票有固定管道被撈** | 總管 | ✅ 40/402026-08-28inkstone/ISEP#83)+真實 229 張唯讀實跑 |
| A5 搜尋跨 repo | 總管 | ✅ |
| A6 標籤對齊+冪等 | 總管 | ✅ 14 repo,第二次 0/0 |
| **A9 人閘警察管路** | 總管 | ✅ 14/142026-08-26 |
| **A10 人閘警察準度** | 總管 | ✅ 9/9,連跑三次(2026-08-26),A 群誤攔 0 |
| **A11 派工單只剩票號** | 總管 | ✅ 19/192026-08-27 |
| **A12 留言身份欄(兩道門)** | 總管 | ✅ 11/112026-08-27 |
| **A16 放行的門真的打得開** | 總管 | ✅ 17/172026-08-28inkstone/ISEP#90 |
| **A17 未推警察不誤攔雲端分支** | 總管 | ✅ 10/102026-08-28inkstone/ISEP#90 |
| **A18 信標會報雲端接線缺陷** | 總管 | ✅ 14/142026-08-28inkstone/ISEP#90 |
| **A19 舊票有固定管道被撈** | 總管 | ✅ 47/472026-08-28inkstone/ISEP#83)+真實 229 張唯讀實跑 |
| A7 plugin 裝得起來 | 總管 | ✅ |
| **A8 新 session 閘會觸發** | 總管 | 見本版 release note |
| **B1B5 雲端** | **leo** | 還沒跑(機器碰不到 Cloud environment |