Commit Graph

177 Commits

Author SHA1 Message Date
Leo 69e0adce1b wiki 那條帳補上牽涉哪些 repo,並記下漏掉的那幾把的共同形狀(inkstone/ISEP#115)
記憶警察(wiki-first-police)抓到的:那條 mistakes 落帳沒有 repo 欄位也沒有三元組
⇒ 下次查「這件事在哪」還是要從頭找。補 4 個 repo 位址 + 4 條三元組。

順帶把 5577/5610 的推廣寫進同一條,因為那兩把不是「忘了寫」而是同一族:
  uncle6 出貨       能力住在 ~/.wrangler 的 OAuth 登入態
  ARCRUN_NS_YOULIN  能力住在 ~/.arcrun 的 api_key
⇒ 能力其實有,只是住在「不會跟著 clone 走」的地方 ⇒ 本機永遠綠、雲端永遠紅,
  而兩邊都不會有人發現,直到某條線做到最後一步要用它。

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-01 15:00:11 +08:00
Leo 47c73cdaa3 出貨線取座標那兩站也不會斷:ARCRUN_NS_YOULIN 進清單(inkstone/ISEP#115 → comment 5610)
inkstone/Arcrun#195 把實例座標拆成「網址隨 repo 走/namespace 走環境變數」之後,
一台乾淨機器只要有這一個環境變數就取得到座標。而它原本只住在
~/.arcrun/config.yaml 的 api_key——家目錄的東西不隨 repo clone 走,雲端拿不到。

- NEEDED 加 ARCRUN_NS_YOULIN(頂層 .env,長度 8)。清單 9 → 10 把。
- 在清單註解裡把這一族的共同形狀寫下來:**能力其實有,只是它住在
  「不會跟著 clone 走」的地方**——uncle6 是 ~/.wrangler 的 OAuth 登入態,
  這把是 ~/.arcrun。下一個遇到同款的人不必重新發現。
- docs/TESTING.md A26:9 把 → 10 把,狀態表補 5610。

只加這一把。
值全程沒有進版控、沒有進對話(只印名字與長度)。版本號待總管定。

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-01 14:56:43 +08:00
Leo 92a008fd28 定版 v0.20.0(出貨那台帳號的鑰匙也在清單裡了)
leo 2026-09-01:「它出貨雲端部分還要什麼帳密也要給」「需要 uncle6?」
——需要,而且是硬需求。

實查(總管與工人各自打過一次,結果一致):
  installer/ship.targets.json 六處 accountId 去重 → 58309bb90fd93ad6d0fe0aae99170e9d
  用 products/arcrun-rag/.env 的 UNCLE6_CF_API_KEY 打 CF API:
    /user/tokens/verify → success: True, status: active
    /accounts           → 58309bb9… Uncle6.me@gmail.com's Account   一致
  本機 env 沒有 CLOUDFLARE_API_TOKEN;~/.wrangler/config/default.toml 有 oauth_token
  ⇒ 本機出貨靠家目錄的 OAuth 登入態,那個檔不隨 clone 走 ⇒ 雲端拿不到

清單 8 把 → 9 把。

🔴 未知的一格(刻意留在清單註解裡):這把有沒有 Workers Scripts:Edit 沒驗過
——驗它必須寫入。已確認至少有 Read(讀得回 uncle6 的 75 顆 worker)。

🔴 雲端出貨仍然不通,缺的不是憑證:
  · Leo/arcrun-rag-bundles-staging 對 claude-code 是 push:False(stage 釘的就是它)
  · ARCRUN_SHIP_BASE/ARCRUN_SHIP_NS 只住在 ~/.arcrun/config.yaml,不在任何 repo
  · .github-armed 只存在本機根目錄
  三件都是 leo 的裁決,不在本版範圍。

票:inkstone/ISEP#115

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-01 12:38:04 +08:00
Leo 1f05bc2134 Merge 'uncle6 那把進雲端清單'(inkstone/ISEP#115) 2026-09-01 12:38:04 +08:00
Leo cb310e9c72 出貨那三站的帳號也進清單:uncle6(inkstone/ISEP#115 → comment 5577)
出貨的 installer/docsSite/mailRelay 三站,stage 與 prod 全部部署到 uncle6
(ship.targets.json 六處 accountId 去重後只有 58309bb90fd93ad6d0fe0aae99170e9d),
而本機是靠家目錄的 ~/.wrangler OAuth 登入態出貨——那個檔不隨 repo clone 走,
所以雲端會在部署當下才斷。UNCLE6_CF_API_KEY 這條路本來就存在且 active,只是沒進清單。

- NEEDED 加 UNCLE6_CF_API_KEY(住在 products/arcrun-rag/.env)。清單 8 → 9 把。
- 清單開頭那段「範圍」跟著改,否則說明與清單本身打架;
  並把「這把有沒有 Workers Scripts:Edit 沒驗」這個未知寫在旁邊,
  下一個讀清單的人不必重新發現它。
- docs/TESTING.md A26:把「清單現在是 8 把」改成 9 把,狀態表補 5577。

只加這一把。stage bundle repo 寫入權與 ARCRUN_SHIP_BASE/ARCRUN_SHIP_NS 沒碰
(comment 5577 明寫那是 leo 的裁決,不在範圍)。
值全程沒有進版控、沒有進對話(只印名字與長度)。版本號待總管定。

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-01 12:35:40 +08:00
Leo 6f37772a40 定版 v0.19.0(雲端拿得到兩台的帳密)
leo 2026-09-01:「youlin 和 geek6688 都全給,因為這兩個都是讓 CC 操作
一個 stage 一個 test prod,不給所有的就是受阻。」

make-cloud-env.sh 的清單從 1 把變 8 把(youlin 5 + geek6688 2 + Gitea 1)。
在併完的樹上實數:61 支閘/84 條註冊/7 command/7 agent/2 skill/49 腳本
——六個數字都是數出來的,與 description 一致。

🔴 這一筆是補的:766375c 併進 main 時版本沒跟著動(總管把 bump 寫在一個
被 main-push 閘擋下的複合指令裡,擋掉之後只有 merge 生效)。
版本號是 leo 唯一的驗收介面,功能進了 main 而版本沒動=等於沒交付。

票:inkstone/ISEP#115

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-01 12:11:29 +08:00
Leo 766375cd95 Merge '兩台全給:雲端拿得到 youlin+geek6688 的憑證'(inkstone/ISEP#115) 2026-09-01 12:08:29 +08:00
Leo 7aef9436c9 兩台全給(youlin+geek6688),清單守則從名字改成事實(inkstone/ISEP#115 → comment 5541/5542)
leo 2026-09-01:「youlin 和 geek6688 都全給,因為這兩個都是讓 CC 操作
一個 stage 一個 test prod,不給所有的就是受阻。」覆蓋 comment 5526 的 youlin-only。

- NEEDED 從 2 把變 8 把:youlin 五把(CF token/account id/MCP owner secret/
  stage 帳密兩把)+ geek6688 兩把(出貨 token/account id)+ 機器帳號 Gitea token。
  CLOUDFLARE_ACCOUNT_ID_YOULIN 是 comment 5542 才補進 .env 的,已實查在場。
  CLOUDFLARE_API_TOKEN_leo21c 沒加(leo 只點名那兩台)。
- 新增 B9:leo21c 那把住在頂層 .env,不在 polaris/mira/.env ⇒ B8 抓不到它。
  B9 比的是**值相等**不是名字——把它改名叫 CF_TOKEN_BACKUP 照樣紅,
  而黑名單會放它過去。兩條「該紅」方向都在拋棄式副本上實跑證過。
- 密碼類兩把進清單是給人拿的;credentials-map.md「密碼類憑證的用法限制」照舊:
  AI 不得把密碼打進登入欄位。這條寫在清單旁邊,下一個要加變數的人一定會看到。

順手修掉一個不是我造成的假綠:
- hooks/tests/sdd-guard-retired.test.sh 用了 mapfile,macOS 內建 bash 3.2 沒有這個內建,
  在乾淨的 origin/main 上就是離開碼 1(實驗證過),而 TESTING.md 標著「通過 8」。
  改成 while-read(同 scripts/gitea-labels-sync.sh:56 的前例)後:通過 8 / 失敗 0。

接基底與衝突:
- rebase 到 013a987,main 那 8 筆一筆沒掉。
- TESTING.md 兩邊都新增了 A25 ⇒ main 的 A25(sdd-guard 退役)保留原編號,
  本節改為 A26;狀態表那列跟著改。
- plugin.json 取 main 那邊(version 0.18.0 未動),六個數字在併完的樹上實數:
  61/84/7/7/2/49。
- mistakes.md 兩邊各自 append,兩條都留。

值全程沒有進版控、沒有進對話(只印名字與長度)。版本號待總管定。

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-01 12:04:49 +08:00
Leo ca9ea9993b 雲端拿得到 youlin 那把 CF token,而且清單會自己跟機器對帳(inkstone/ISEP#115 → comment 5526)
scripts/make-cloud-env.sh 的 NEEDED 只有 1 個變數,裡面從來沒有任何 CF 憑證
——雲端 `env | grep -ciE 'cloudflare|^CF_|wrangler'` 回 0 是這麼來的,不是漏設。

- NEEDED 加 CLOUDFLARE_API_TOKEN_YOULIN_CC_USE(youlin = AI 的 stage)。
  leo21c 正式環境那 7 把(B 段)一把都沒動,理由寫在清單旁邊。
- 新增 scripts/test-make-cloud-env.sh(docs/TESTING.md A25,10 條):
  A 段用假 .env 隔離跑(證明缺值會被標紅、什麼都不寫進 repo);
  B 段拿真 .env 對帳,判準是「值出自哪個檔案」而不是名字裡有沒有某個字,
  並用一個已知住在 polaris/mira/.env 的變數自驗偵測真的會亮。
- TESTING.md B1 原本點名「加一個 GITEA_TOKEN_CLAUDE_CODE」已經過時,
  改成「① 區塊每一行都貼」。
- README/plugin.json 的 scripts 數字在自己的樹上實數:48 → 49。

值全程沒有進版控、沒有進對話(只印名字與長度)。
版本號待總管定。

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-01 11:58:37 +08:00
claude-code 013a987688 Merge pull request '定版 v0.18.0(閘教的那條出路要真的走得通)' (#116) from chore/v0.18.0 into main v0.18.0 2026-08-31 11:12:40 +00:00
claude-code ce0fe87529 定版 v0.18.0:閘教的那條出路要真的走得通
inkstone/ISEP#112:comment-carries-task-guard 印出的
`scripts/ticket subtask` 子命令根本不存在,照著跑會噴四個動詞的
說明然後什麼都沒發生——閘擋對了,但它教的解法跑不動。

這一版讓那行指令原樣貼上就跑得完:補上 subtask 子命令(一個動作
同時掛 Gitea 原生相依+在母票留下指回來的留言),閘改成去
`ticket usage subtask` 拿用法而不自己抄一份,印的路徑改絕對路徑。

測試 29/29,其中 ⑱ 直接驗「訊息裡那塊指令原樣貼上跑得完」,
⑲ 是對照組證明舊的那組旗標確實跑不完。
2026-08-31 11:11:54 +00:00
claude-code 85f76c556a Merge '閘教的那條出路要真的走得通(inkstone/ISEP#112)' (#114) from fix/gate-teaches-a-command-that-runs into main 2026-08-31 11:10:40 +00:00
Leo 58c3689e4b 閘教的那條出路要真的走得通(inkstone/ISEP#112)
comment-carries-task-guard 擋下留言時教人跑 `scripts/ticket subtask …`,
而那一行跑不動。兩個獨立的原因,各自都足以害死它:

① 相對路徑對「貼上去那個人的 cwd」解析。總管的 cwd 是 InkStoneCo/,
   那裡的 scripts/ticket 是停在 08-27 之前的舊複本(grep -c subtask → 0),
   照著貼只印出它「四個動詞」的說明然後 exit 0。
② 用法是手抄的第二份,而它已經漂了:寫了 --assign 卻沒有 --next,
   正本會因為「指派了人卻沒寫 --next」直接擋下(exit 2,在打 API 之前)。

修法是拿掉可以漂的那一格,不是把兩份對齊一次:
- 路徑     scripts/ticket 的 self_path()(__file__ 的絕對路徑)
           ⇒ 印指令的人與跑指令的人是同一個檔案
- 用法     USAGE / usage_text() 一份,閘去 `ticket usage subtask --example` 現要
- 內文模板 body_template() 由 REQUIRED_SECTIONS 現生,跟檢查同源

新增動詞 `ticket usage <verb> [--example] [--parent] [--draft]`。
閘的訊息改成整塊可貼可跑:腳本絕對路徑、母票已填、內文草稿附在同一塊裡,
只剩標題/目標/驗收條件三格是機器不可能知道的。
正本叫不動時 fail-open 的只有「訊息內容」(只指路,不補一份手抄用法),
擋不擋不受影響。

測試 scripts/test-comment-carries-task-guard.sh 22 → 29 條。
新增的 ⑮–㉑ 驗的是「出路走不走得通」,不是「有沒有擋」——
⑱ 把閘 stderr 裡那塊指令原樣抽出來、只填三格、真的執行一次,
離開碼必須是 1(走到網路才停);是 2 就代表這個病復發。
六個變異全部驗過會紅(相對路徑/閘自己抄用法/--assign 沒配 --next/
模板寫死少一段/fallback 補回手抄用法)。

版本:待總管定版(plugin update 比的是版本號不是內容)。

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SP7DiGMfSkWjZ15CHMbPFE
2026-08-31 11:01:45 +00:00
claude-code b8676f5aa9 Merge pull request '定版 v0.17.0(sdd-guard 退役)' (#111) from chore/v0.17.0 into main v0.17.0 2026-08-31 01:04:50 +00:00
claude-code 6e18cc2072 定版 v0.17.0:sdd-guard 退役 2026-08-31 01:04:21 +00:00
claude-code 8b2c7cecac Merge pull request 'sdd-guard 退役:讓「取消 Active SDD」這個裁決真的執行得下去(inkstone/ISEP#91)' (#110) from feat/retire-sdd-guard into main 2026-08-31 01:04:01 +00:00
Claude c7af690c2b sdd-guard 退役:讓「取消 Active SDD」這個裁決真的執行得下去(inkstone/ISEP#91)
leo 2026-08-16 在 inkstone/InkStoneCo#40 → comment 2942 裁定「取消 Active SDD」,
11 天沒被執行。原因不是有人偷懶,是**執行它的第一步會鎖死自己**:

    sdd-guard.sh 是 fail-closed 的
      · 動 code 檔時 status: active 不是恰好 1 份 → 擋(0 份也擋)
      · 路徑所在的 repo 沒有 3-specs           → 也擋

⇒ 照裁決把最後那份 active 拿掉 → 變 0 份 → 任何人動任何 .ts/.py/.go 全被擋。

而 ISEP 這個 repo 自己就沒有 3-specs——本票開工第一件事實測到:

    $ echo '{"tool_name":"Edit","tool_input":{"file_path":".../hooks/lib/dispatch_parse.py"}}' \
        | bash hooks/sdd-guard.sh
    🚫 SDD 協議攔截:… 找不到任何 SDD        exit 2

**它一直在誤攔 ISEP 自己,只是沒人回報。**

── 這一版做了什麼 ────────────────────────────────────
· hooks/sdd-guard.sh 刪除,hooks.json 取消註冊(85 → 84 條,只少這一條)
· commands/sdd-check.md 改寫:SDD 只記「起初的樣子」,任務本體在 Gitea 票;
  找不到 SDD 不再是停下來的理由,找不到票才是
· agents/inkstoneco-hand.md 拿掉「任何時刻只允許一份 status: active」那條紅線
· hooks/lib/path-resolve.sh 只加註解:它的唯一 caller 走了,但別順手刪
  (#22 學到的東西住在裡面,十幾支閘還在用它要修的那個寫法)

── 迴歸測試:測的不是「檔案刪了沒」,是「那個擋還會不會發生」──
hooks/tests/sdd-guard-retired.test.sh(通過 8/失敗 0,離線):
把「裁決執行完之後的世界」(沒有 3-specs 的 repo、兩份 status: active 的 repo)
丟給 hooks.json 上**整組** Write|Edit|MultiEdit 的閘——清單當場從 hooks.json 讀、
不寫死——不准有任何一支用 SDD/3-specs 當理由擋下來。
⇒ 日後有人換個檔名把同一個形狀種回來,這支照樣紅。

🔴 判準刻意不是「一支閘都不准擋」:同組還住著跟 SDD 無關、且看 session 狀態
決定擋不擋的閘(history-first/subagent-first)。把它們算失敗,這支測試會在別人
改別的東西時無故變紅,紅久了就沒人看——誤攔比漏擋更該修,對測試一樣成立。

**紅的證明**:把 sdd-guard 暫時復原(檔案+註冊)重跑 → 通過 1/失敗 7,
四條行為格全部指名 sdd-guard.sh。已還原。

── 自己跑過的 ──────────────────────────────────────
· hooks/tests/sdd-guard-retired.test.sh                通過 8/失敗 0
· 逐支點名 hooks.json(README「裝什麼」那道指令)      84 條,對 main 做集合差:
  少了 sdd-guard.sh × 1,多出 0 支,其餘一支不差
· 盤點數字全部在這棵樹上實數,不是加減推:
  ls hooks/*.sh|wc -l = 61(原 62)
  grep -c '"command":' hooks/hooks.json = 84(原 85)
  agents 7/commands 7/skills 2/scripts 48(皆未變動)
· 盤點表對帳三格(hooks-inventory 自己寫死的那三道):三格皆無輸出
· scripts/check-version-consistency.sh                  0.16.2 一致
· claude plugin validate .                              Validation passed
· hooks/tests/ 全部離線測試 24 支重跑:本次改動 0 退步
  (dispatch-format-guard 40/52、prod-write-guard 18/19-fail、
   stage-before-prod-guard 9/7-fail、另 3 支需帶參數/建不起沙盒——
   **六支在 gitea/main 上逐支重跑結果一模一樣,是既有狀態不是本次造成**)

── 沒做、也不該由這張票做的 ─────────────────────────
· inkstone/InkStoneCo 那半(拿掉 active SDD + 刪 CLAUDE.md「單一活性鐵律」段)
  ——那是 inkstoneco-hand 的 repo;而且順序上本來就要等這一版出去、
  兩邊 /plugin update 之後才動得,先拿掉就鎖死
· checkbox 分診(掛 inkstone/InkStoneCo#49,票上明寫不要另開票)
· 版本號:待總管定版

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016ZBu4Sa1cGntKFRBYNZ6xs
2026-08-31 00:56:37 +00:00
Leo 4b91cd5d48 Merge 'feat/parallel-lines-cap':一條線一份自己的工作目錄(inkstone/ISEP#109)
總管逐筆審過並自己實測(不採信交回的數字):

  hooks/tests/line-needs-own-worktree.test.sh      59/59
  hooks/tests/parallel-lines-cap-guard.test.sh     33/33
  hooks/tests/main-and-prod-push-guard.test.sh     10/10   ← 共用檔改動的迴歸
  hooks/tests/…-cross-repo.test.sh                 19/19   ← 同上

實地打四次(不是只跑測試):
  · cd <repo> && git checkout       → 擋,且認出的是那個 repo(原缺陷已修)
  · git -C <repo> checkout          → 擋,認得對
  · cd <自己的 worktree> && checkout → 放行(舊版會誤攔正確做法)
  · cd <不是 repo 的地方> && checkout → 放行

🔴 併進來不等於送達:安裝端停在 0.9.0,repo 是 0.16.2,
   而這支閘在任何一份安裝裡都不存在(find 零命中)
   ⇒ 這一格仍是 ◐,缺的是 plugin update,不是 merge。

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
v0.16.2
2026-08-29 22:08:31 +08:00
Leo 358f67abf3 閘認得出「動的是哪個 repo」,不再拿 cwd 當答案(inkstone/ISEP#109 → comment 5398)
line-needs-own-worktree.sh 擋 `cd <別的 repo> && git checkout` 時擋對了,但訊息
指的是 payload 的 cwd 那個 repo,不是指令真正動的那個。comment 5398 實測:

    指令:cd .../matrix/arcrun && git checkout fix/library-lifecycle-187
    舊版:目錄:.../InkStoneCo   它現在在:feat/ticket-bell-webhook
                    ↑ 動的是 matrix/arcrun,講的卻是 InkStoneCo

🔴 照著那個訊息做的人,會在 InkStoneCo 開一份用不到的 worktree,真正要隔離的
matrix/arcrun 沒開到——而他以為自己隔離好了。**一道閘給錯下一步比不擋更糟。**
而 `cd X && git checkout` 正是這條線最常出現的寫法(本票的來由那次就是它)。

沒有另寫一支解析器:hooks/lib/push_target_dir.py 2026-08-23 已經替 git push 解過
同一題(inkstone/ISEP#30 comment 3949),多層 cd 鏈與「子殼的 cd 不外洩」都算過了。
本輪只把那支的動詞與「要不要去掉 env 前綴」變成參數(兩個都有預設值,push 那條路
一個 byte 都沒變,42 條既有測試重跑全綠),新增的 lib/checkout_target_dir.py 只放
checkout 專屬的兩件事:哪些形狀不動 HEAD、-C 贏過 cd 的優先序。

新增的一條性質(不是順手,是本票要的):目錄解不出來就放行。
`cd $VAR`/`cd -`/引號壞掉時回 "?",閘直接 exit 0。寧可漏擋,也不要指著錯的
repo 叫人去開 worktree——猜一個回去等於原地打轉。

實測:
- hooks/tests/line-needs-own-worktree.test.sh 35 條 → 59 條(新增 D 群 24 條)
  正向:cd 認出 A/-C 認出 B/兩者都在時 -C 贏/相對 cd/相對 -C 接在 cd 之後/
        從 worktree cd 回共用目錄/子殼不外洩
  反向(不該擋):cd 到非 repo、cd 進自己的 worktree、cd $VAR、cd -、還原檔案
  已知邊界也釘成測試:`);` 中間沒空白時 tokenize 會斷在那裡 ⇒ 漏擋(不是指錯)。
  那層是 push_target_dir 的 tokenizer,動它會連帶改到推 main 那道閘的偵測範圍,
  本票不動,另報。
- 推 main 那三套(共用被改到的 lib):10/10、19/19、13/13
- 實地打票上那條害過我們的指令:現在印的是 matrix/arcrun,worktree 指令也是它的

plugin.json 0.16.1 → 0.16.2 並重跑 vendor-to-shell.py(版本沒動=沒人吃得到)。

⚠️ 留痕:hooks/lib/checkout_target_dir.py 是用 Bash heredoc 寫的,因為 sdd-guard
對 ISEP 的 .py 是結構性永遠在響——這個 repo 根本沒有 system-dev/docs/3-specs,
而 hooks/lib/ 已經住了 9 支 .py。範圍就這一支新檔,沒有動那道閘(另報)。

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-29 21:31:42 +08:00
Leo f69ea11a58 一條線一份自己的工作目錄,同時跑的線有上限(inkstone/ISEP#109 → comment 5391)
兩支新閘,兩個方向的測試都有:

line-needs-own-worktree.sh(PreToolUse: Bash,35 條測試)— 治本那半
  一條線想在共用的工作目錄裡切分支就擋,叫它開自己的 worktree。
  判準是 git 自己回答的兩個問題(會不會動 HEAD/是不是主工作目錄),
  不靠任何人登記。擋下來時順手 prune 掉指向空氣的登記。
  實測:products/arcrun-rag 清掉 4 筆、ISEP 清掉 13 筆,
  還在的 worktree 與分支一根寒毛都沒動。

parallel-lines-cap-guard.sh(PreToolUse: Agent|Task,33 條測試)— 治標那半
  同時在跑的線超過上限(預設 3)就派不出下一條,訊息列出現在有哪幾條。
  條數是從 harness 自己寫的 subagents/*.meta.json + 主 transcript 的完工通知
  數出來的,不是計數器 —— 沒有任何東西要人去減,所以不會永久卡死。

三個機械前提是實測的,不是推測的:
  · SubagentStop 在實測 session 裡一次都沒觸發(不能拿它當減法)
  · PostToolUse:Agent 在送出後 5 秒就觸發(不是收工)
  · subagent 不是獨立行程(ps 只有一個 claude-code 行程)
  · Agent 的原生 isolation:"worktree" 只做頂層 repo,
    而 products/ matrix/ 是 gitignore 掉的子 repo,worktree 裡根本沒有它們
    ⇒ 原生那條路解不了這張票的問題

盤點數字在自己的樹上重數:62 支 / 85 條(main 上的 60/82 沒算到這兩支)。

🔴 版本沒動 = 沒有人吃得到。這條分支沒有定版,
   plugin.json 仍是 0.16.1(版本一致性檢查要求它等於最新 tag)。
   合併時要跟著一個新版本號,不然這兩支閘到不了任何人手上。

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-29 20:52:13 +08:00
claude-code aac6498850 Merge pull request 'roster-guard 認不出真實的 subagent_type,整個環境派不出工(inkstone/ISEP#86 迴歸)' (#108) from fix/roster-plugin-prefix into main v0.16.1 2026-08-28 12:37:36 +00:00
claude-code 5342917f2b roster-guard 認不出真實的 subagent_type,整個環境派不出工
Claude Code 給 plugin agent 的 subagent_type 帶前綴(isep:isep-hand),
而 agents/*.md 的 frontmatter name 沒有(isep-hand)。roster.find() 用完全相等比對
⇒ 真實派工一律被擋,而名單上的裸名字工具又不收 ⇒ 兩種寫法都派不出去。

測試沒抓到是因為 24 條全部餵手寫 payload,沒有一條用真實的 subagent_type 形狀。
補第 19 條專測這個:舊 roster.py + 新測試 → 紅;修好 → 25/25。

順手補 gate-ok 漏掉的 roster——v0.15.0 新增那支閘時沒同步逃生口清單,
所以撞上死鎖時連留痕的出口都沒有。
2026-08-28 12:36:13 +00:00
claude-code a0a34b629e Merge pull request '定版 v0.16.0(會主動叫人、wiki 太長有人整理)' (#107) from chore/v0.16.0 into main v0.16.0 2026-08-28 01:32:10 +00:00
claude-code 7df15f2e2b 定版 v0.16.0(.claude-plugin/plugin.json) 2026-08-28 01:31:27 +00:00
claude-code 7a3ce4e590 Merge pull request '沒人會叫的事會自己叫+讀不完的必讀檔要被整理(inkstone/ISEP#93、inkstone/ISEP#89)' (#106) from feat/nag-and-wiki-compress into main 2026-08-28 01:28:00 +00:00
Claude Code 672439bd95 把 main(v0.15.0,派工紀律三件 #105)併進來,逐支點名複驗過 hooks.json
## hooks.json:這次 git 自動合成功了,而那正是最危險的情況

`hooks.json` 是 JSON——**合錯了語法照樣合法、閘卻不見了,不會有任何東西喊一聲。**
所以「沒有衝突」不等於「沒掉東西」,一律再做兩件事:

① 逐支點名(指令已寫進 README「裝什麼」那段)→ 82 條,60 支各自幾條全部列出
② 跟**兩個父節點**做集合對照:

    merged 82 = main 79 + 我的 3
    從 main 掉了:0 條
    從我這邊掉了:0 條
    兩邊都沒有卻冒出來的:0 條

`#105` 三支與我的兩支同時在,位置也對:

    roster-guard.sh              PreToolUse  Agent / Task
    diagnosis-evidence-guard.sh  PreToolUse  Bash
    investigate-first-stamp.sh   PostToolUse Agent|Task / SendMessage
    overdue-nag-guard.sh         SessionStart
    wiki-size-guard.sh           SessionStart / PreToolUse Write|Edit|MultiEdit

(總管提醒的 `roster-guard` vs `mainline-focus-guard` 那種「同一陣列位置兩邊各一支」
 這次沒有再發生衝突,但兩支都在,各 2 條。)

## 三處文字衝突:兩邊都留,數字重數

- plugin.json     版本吃 main 的 **0.15.0**(總管已定版,**我沒有動它**);
                  描述欄補上 main 新增的「7 位有名字的工人」,三個數字重數
- README.md       吃 main 的新表格結構(多了 agents/commands/skills/scripts 四列),
                  數字重數;並把累積成**四列**的 `hooks/` 收成一列
- hooks-inventory 標頭被貼成兩份、「一句話結論」被貼成**三份**(數字還互相打架),
                  各收成一份;版本註記兩邊都留

## 盤點(全部在合併後的樹上實數,不是加減推的)

    ls hooks/*.sh | wc -l                    58 → 60
    grep -c '"command":' hooks/hooks.json    79 → 82
    ls agents/*.md | wc -l                        7
    ls -p scripts | grep -v / | wc -l            48
    未掛進 hooks.json 的 .sh                       3(與本頁原本寫的一致,重數過)

📌 抓到的漂移(每次併都有一個,這次三個):
   ① hooks-inventory 的標頭 ×2、一句話結論 ×3
   ② README 的 `hooks/` 列 ×4,其中兩列是舊數字
   ③ plugin.json 描述欄寫「49 支腳本」,而用本頁寫死的那道指令實數是 48

## 測試(合併後全部重跑,帶對參數)

hooks/tests 23 支全綠:ask-user-question 14、countdown 20、diagnosis-evidence 26、
dispatch-format 52、factory-idle 33、gate-ok 17、gitea-arm-check 16、
presence-beacon 14、cross-repo 19、main-and-prod-push 10、mainline-focus 24、
mainline-idle 61、milestone-account 22、**overdue-nag 36**、pr-verdict 52、
prod-write 37、reply-identity 11、roster 24、sdd 8、search-is-not-proof 31、
stage-before-prod 16、unpushed-police 10、**wiki-compress 25**

scripts/test-* 13 支全綠。
claude plugin validate . ✔ passed;check-version-consistency  0.15.0 = v0.15.0。
兩支新 SessionStart hook 併完實跑過,閘判定探針對 v0.15.0 的 prod-write-guard 回 pass。

🔴 跑之前清 `CLAUDE_CODE_CHILD_SESSION`;四支要帶參數
(三支帶 hook 絕對路徑、cross-repo 帶 hook 絕對路徑、gitea-arm-check 帶 repo 根目錄)
——不帶會紅,而那是假紅。
2026-08-28 01:25:49 +00:00
claude-code 5fca644b5a Merge pull request '工人有名字、診斷要有調查、回覆也只給票號(inkstone/ISEP#86、#87、#88)' (#105) from feat/named-workers-and-reply-gate into main v0.15.0 2026-08-28 01:20:12 +00:00
Claude 44f60ba0b8 merge main(v0.14.0,五批)into 派工紀律三件
hooks.json 兩處衝突:roster-guard 與 mainline-focus-guard 在同一個陣列位置,
兩支都是真的閘,展開成兩個項目保留(不是二選一)。
盤點在合併後重數:58 支 .sh/79 條註冊/49 支腳本/7 位具名工人。版本 0.15.0。
2026-08-28 01:20:00 +00:00
Claude Code 79ee9e62d6 把 main(v0.14.0,五批)併進來,解 5 處衝突,盤點數字在合併後的樹上重數
衝突全部**兩邊都留**,沒有丟掉任何一側:

- hooks.json      main 的 `scripts/mainline refresh` + 我的兩支,同一個
                  SessionStart 陣列裡並存
- plugin.json     版本吃 main 的 0.14.0(**沒有自己決定版本**),描述欄的
                  三個數字在合併後的樹上重數
- README.md       main 那格被貼成兩列(54 支/69 條 與 54 支/71 條,互相矛盾),
                  收成一列並填上實數
- hooks-inventory main 的 `mainline refresh` 那列 + 我的兩列;標頭與
                  「一句話結論」的數字重數
- TESTING.md      兩邊各自新增的格子都留。**我的兩格改號成 A21/A22**——
                  併之前兩邊都叫 A16/A17,撞號(這一頁本來就已經有
                  三組重複的 A14–A18,這次至少沒有再加一組)

盤點(`ls hooks/*.sh | wc -l`、`grep -c '"command":' hooks/hooks.json`、
`ls -p scripts | grep -v / | wc -l`,都在**合併後**的樹上跑):

    55 → 57 支 .sh
    72 → 75 條註冊
         47 支腳本

📌 順手抓到一個漂移:main 的 plugin.json 描述欄寫「48 支腳本」,
   而用 hooks-inventory 自己寫死的那道指令實數是 44(main)/47(合併後)。
   已記在 hooks-inventory 的版本註記裡——**同一個病,第 N 次,只是換一欄。**

📌 TESTING.md 的「已知斷點」那段補上總管用 MCP 查到的更廣範圍:
   不只 notify_leo 不見了,那台實例的工作流定義是**整批失效**;
   而且修它要寫 leo 的個人帳號 ⇒ `leo21c-write-guard.sh` 明文禁止
   ⇒ **那是真人閘,總管也不能解**。

測試(合併後全部重跑,帶對參數):
  我的兩套          overdue-nag 36/36、wiki-compress 25/25
  hooks/tests/*     ask-user-question 14、countdown 20、dispatch-format 33、
                    factory-idle 33、gate-ok 17、presence-beacon 14、
                    mainline-focus 24、mainline-idle 61、milestone-account 22、
                    pr-verdict 52、reply-identity 11、sdd 8、
                    search-is-not-proof 31、unpushed-police 10、
                    prod-write 37、stage-before-prod 16、
                    main-and-prod-push 10、cross-repo 19、gitea-arm-check 16
  scripts/test-*    13 支全綠(含 release-manifest 21、milestone-account 23、
                    debt-worklist 47、ticket-handoff-writeback 49)
  claude plugin validate . 通過;check-version-consistency 通過(0.14.0 = v0.14.0)
2026-08-28 01:15:54 +00:00
Leo 5ac06abc95 工人有名字+回覆也是派工+未經調查不寫診斷(inkstone/ISEP#86/#87/#88)
三張票同一族(誰在派、派給誰、派的內容住哪裡),做在同一條分支:

#86 工人名單:agents/ 七位有名字的工人+scripts/roster+hooks/roster-guard.sh
    派工用 Task 的 subagent_type 指名,派工單格式一個字都沒改;
    指對名字就把那位的檔案原文注入(你是誰/先讀什麼/你的紅線)。
    【身份】欄同時吃得下工人名字(原本三個角色照舊)。

#87 未經調查不寫診斷:hooks/diagnosis-evidence-guard.sh + investigate-first-stamp.sh
    三個結構訊號(派過人查沒/有沒有走得過去的出處/有沒有份量),
    一個關鍵字比對都沒有;轉述有出處不會被誤擋。

#88 回覆也是派工:不另造閘,把攔截點加掛上去。
    hooks/lib/dispatch_parse.py 的 tool_channel() 一次列全所有通往 subagent 的路
    (SendMessage/雲端 session・trigger/claude -p);擋下來時把那段內容原文印出來。
    subagent 往上回報(to: "main")=交件不是派工,刻意不管。

順手修掉一個真的會咬人的 flake:dispatch-format-guard 原本開四支 python 各讀一個欄位,
機器忙的時候某個欄位會靜靜變空字串(實測連跑 10 次有 1 次「豁免了卻還是被擋」)。
四個欄位改成一次讀完。

版本號待總管定(plugin.json 只更新了描述裡的數字,版本沒動)。

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016ZBu4Sa1cGntKFRBYNZ6xs
2026-08-28 01:15:09 +00:00
Claude Code 156d339308 退路留言要帶「為什麼發不出去」,不是只帶閘的判定(inkstone/ISEP#93)
2026-08-28 第一版漏了這一格,而它剛好就在實測時咬到:
閘判定 pass、Telegram 卻 404(實例上找不到 notify_leo 工作流),
退路留言貼上票之後**只看得到 pass**——真正的斷點一個字都沒進票。
⇒ 下一個人會去修閘,而斷點根本不在那裡。

把留言內文抽成 fallback_body()(獨立成一支就是為了測得到),
測試從「看原始碼有沒有那個字串」改成**真的產一份留言出來檢查**:
斷點原因、原文、身份欄三格都要在。36 條全綠。
2026-08-28 01:06:52 +00:00
claude-code 0a8bb2d9dd Merge pull request '跨 repo 版本清單+結案記帳(inkstone/ISEP#84、#85)' (#104) from feat/manifest-account-v2 into main v0.14.0 2026-08-28 01:04:55 +00:00
claude-code eb6c427421 跨 repo 版本清單+結案記帳(inkstone/ISEP#84、#85)(scripts/test-release-manifest.sh) 2026-08-28 01:04:22 +00:00
claude-code c6eb269551 跨 repo 版本清單+結案記帳(inkstone/ISEP#84、#85)(scripts/test-milestone-account.sh) 2026-08-28 01:04:20 +00:00
claude-code bc02c2645b 跨 repo 版本清單+結案記帳(inkstone/ISEP#84、#85)(scripts/release-manifest) 2026-08-28 01:04:18 +00:00
Claude Code da9bd53cae 沒人會叫的事會自己叫+讀不完的必讀檔要被整理(inkstone/ISEP#93、inkstone/ISEP#89)
兩張票放同一條分支:都是「該發生卻不會自己發生的事」,觸發點都在 session 邊界。

## inkstone/ISEP#93 —— 逾期和掛著沒人接的事會主動叫

- scripts/isep-nag        撈三種沒人會叫的事(逾期 milestone/等 leo 的票/掉在地上的棒子)
- scripts/isep-notify     發 Telegram,而且**發不出去的時候不會安靜**
- hooks/overdue-nag-guard.sh  SessionStart 跑一次(不輪詢、不 fan-out、不掛 Actions)
- hooks/tests/overdue-nag.test.sh  35 條,全程離線

實跑撈得出票上點名的那五個逾期 milestone(08-24 三個、08-26 兩個)。
沒東西可報時會說「查過了,沒有」——安靜跟壞掉長得一模一樣。

🔴 工單補的那個限制(今天實測出來的)已經處理:
「能不能發得出去」取決於這個 session 載到的 ISEP 是哪一版
(prod-write-guard v0.10.0 才認得出 notify_leo 不是部署,而 hook 註冊路徑
在 session 啟動當下就寫死了)。所以 isep-notify 會**先拿真的要送的那一則
去問這個 session 註冊的那支閘**,把判定寫成檔(誰都查得到),然後:
  · 放行 ⇒ 送,並驗內層 data.data.ok(外層 200 不算送到)
  · 會擋 ⇒ **不繞路**,改貼回票上並把原文印在眼前
用 git 歷史裡的真跡(c263866 那一版閘)測過「會擋」那條路。

 實測發現通道本身現在是斷的:實例上找不到 notify_leo 工作流(404)。
   不是閘、不是網路、不是金鑰。詳情與修法寫在 docs/TESTING.md 最後一段。
   退路兩次都走通了(inkstone/ISEP#93 comment 5182、5184)。

## inkstone/ISEP#89 —— wiki 太長時有人整理

- scripts/wiki-compress   audit/plan/apply/verify/bench 五個動詞
- hooks/wiki-size-guard.sh  SessionStart 點名太長的檔;寫檔時擋「沒走流程的壓縮」
- hooks/tests/wiki-compress.test.sh  25 條,全程離線、不碰真的 wiki

設計上最重要的一條:**只搬不改**,正文一個字都不動。
「合併同類、濃縮成一行」要重寫正文,而重寫的當下沒有人會發現弄丟了什麼。
所以機器只做「搬 + 目錄 + 標 ×N/↻」,合併留給人。

拿現在的 mistakes.md 複本實壓過(不動真的 wiki):
  7,681 行 → 1,199 行
  260 條一條都沒少(verify 用內文雜湊逐條對帳)
  80 個查詢命中率 80/80,定位成本 2,956 → 131 行,**快 22.5 倍**(bench)
verify 反向測過:真的弄丟一條時它抓得到(抓不到的對帳表比沒有更糟)。

## 盤點數字

在這棵樹上當場數的,不是拿上一版加減推的:
  ls hooks/*.sh | wc -l              → 55(was 53)
  grep -c '"command":' hooks.json    → 71(was 68)
plugin.json/README/hooks-inventory 三處同步改。
**版本號沒動**(0.11.0),待總管定版。
2026-08-28 01:04:16 +00:00
claude-code ca10e1db30 跨 repo 版本清單+結案記帳(inkstone/ISEP#84、#85)(scripts/milestone-account) 2026-08-28 01:04:16 +00:00
claude-code 5f6995b892 跨 repo 版本清單+結案記帳(inkstone/ISEP#84、#85)(hooks/tests/milestone-account-guard.test.sh) 2026-08-28 01:04:14 +00:00
claude-code 395d885ceb 跨 repo 版本清單+結案記帳(inkstone/ISEP#84、#85)(hooks/milestone-account-guard.sh) 2026-08-28 01:04:12 +00:00
claude-code a7ca29c180 跨 repo 版本清單+結案記帳(inkstone/ISEP#84、#85)(hooks/hooks.json) 2026-08-28 01:04:10 +00:00
claude-code ba30c49876 跨 repo 版本清單+結案記帳(inkstone/ISEP#84、#85)(docs/hooks-inventory.md) 2026-08-28 01:04:08 +00:00
claude-code 4eed944f68 跨 repo 版本清單+結案記帳(inkstone/ISEP#84、#85)(docs/TESTING.md) 2026-08-28 01:04:05 +00:00
claude-code b6405b2a5c 跨 repo 版本清單+結案記帳(inkstone/ISEP#84、#85)(README.md) 2026-08-28 01:04:03 +00:00
claude-code 521dda1b47 跨 repo 版本清單+結案記帳(inkstone/ISEP#84、#85)(.claude-plugin/plugin.json) 2026-08-28 01:04:01 +00:00
claude-code 90ed988d84 Merge pull request '舊票每天有固定管道被撈出來(inkstone/ISEP#83)' (#101) from feat/debt-worklist into main 2026-08-28 01:01:53 +00:00
claude-code d301810fb6 merge main(#98 下游做完時頂層跟著關),我的那節再讓一次編號
衝突只有 docs/TESTING.md 一列:#92 也用了 A19 ⇒ 我改成 A20,並把整節搬到 A19 後面。
(今天 main 上 A16、A17 各被佔兩次、A19 被佔兩次——編號撞是這條線的常態,
  讓號的成本比要求別人改小,所以一律我讓。)

plugin.json 的腳本數在合併後的樹上重數 40 → 43:
  main v0.13.0 那個「40」= `ls scripts | wc -l`(37 檔+3 目錄),
  合併後同一算法 = 43:+2 是本 PR,+1 是 #98 帶進來的 test-ticket-handoff-writeback.sh
  (#98 併進 main 時沒有回頭改這個數字)。版本號沒動(0.13.0)。
2026-08-28 00:58:49 +00:00
claude-code e575786be3 merge main(定版 v0.13.0),衝突只有 plugin.json 一行
版本號取 main 的 0.13.0(我不定版)。description 的腳本數在合併後的樹上重數:
  main 那個「40」= `ls scripts | wc -l`(37 個檔+3 個子目錄)
  合併後同一個算法 = 42(多的兩個是本 PR 的 debt-worklist 與 test-debt-worklist.sh)
  ⇒ 40 → 42。閘的兩個數字沒變(hooks/*.sh = 54、hooks.json "command": = 71,本 PR 沒新增 hook)
2026-08-28 00:55:30 +00:00
claude-code 5d6c4b5d63 Merge pull request '下游做完時頂層跟著關(inkstone/ISEP#92)' (#98) from feat/handoff-writeback-loose into main 2026-08-28 00:54:27 +00:00
claude-code 45adc10a52 併進 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,總管統一定版)
2026-08-28 00:51:06 +00:00
claude-code 87678a8a87 Merge pull request '定版 v0.13.0(現在的主線是哪一個,有唯一答案)' (#102) from chore/v0.13.0 into main v0.13.0 2026-08-28 00:49:13 +00:00