下游做完時頂層跟著關(inkstone/ISEP#92) #98

Merged
claude-code merged 2 commits from feat/handoff-writeback-loose into main 2026-08-28 00:54:28 +00:00
Member

inkstone/ISEP#92

這個 PR 解什麼

票上的病一句話:「開在頂層的票,下游做完了卻沒人回來關⋯⋯頂層票會永遠掛著,而 leo 是看頂層的。」

票自己已經盤出「不要重造」的三樣(ticket where 戳記+側門閘、comment-carries-task-guard、Gitea 原生 issue dependency),
這一版全部長在它們上面,一樣都沒有重做。缺的只有三格:journey 標籤、雙向連結的強制、完工回寫。

四格,對上票裡的四條驗收條件

驗收條件 這版做了什麼 在哪
① 頂層 → 下游,兩張票互相看得到對方 subtask(新別名 handoff)一個動作同時掛 Gitea 原生相依 + 在頂層票的時間軸貼一則指回下游的留言 cmd_subtask
② 把下游關掉 → 頂層跟著被處理 ticket close 關完子票立刻問 /blocks(誰在等我),對每張還開著的頂層票貼回寫留言;下游全關的那張再指派回總管+改 s/review _writeback
撈得出來「沒有 open 下游、自己還開著」 新動詞 ticket loose cmd_loose
④ 拿現有頂層票跑一次 → 指得出哪幾張 已實跑,結果見下

另外票裡的第三格 journey:--journey <名>j/<名> 同時貼在母子兩端(只貼一端就聚不起來)。

三個刻意的設計判斷

① 不自動關母票。 回寫只做到「指派回總管+s/review」。
關票要有交付物、要有人看過(ticket close 既有的閘)——默默關掉跟默默留著是同一個病的兩面
票上要的是「不能默默留著」,不是「自動消失」。

looseclose 回寫的補網,不是重複。
關票的側門(直接 PATCH state=closed)是 ticket-api-bypass-guard.sh 刻意放行的(「帶了 ID 的子路徑」一律放行)。
⇒ 走正門的由回寫即時處理,走側門漏掉的由這張網撈回來。
不是輪詢(守 CLAUDE.md 的 flag 紅線)——掛在開場/收工對帳跑一次。

③ 判準全部是機械事實,沒有一格在猜文字。
is_loose 看的是相依邊在不在、state 是什麼;journey 走的是「要求那個標籤在 labels.yaml」,
不是「猜哪些名字合法」。leo 2026-08-17 已證偽關鍵字黑名單那條路(8 次誤攔、0 次正確攔截)。

驗收條件 ④:拿現有的頂層票實跑(2026-08-28,唯讀,沒動任何一張票)

$ python3 scripts/ticket loose
   open 票 245 張
   有下游相依的頂層票:17 張
   其中「下游都關了、自己還開著」:**2 張**

  ● inkstone/arcrun-rag#136  ['s/review']  指派:claude-code
    身為 leo,我要知道 docs-only 模式跳過了哪些資料夾…
      ☑ inkstone/arcrun-rag#143

  ● inkstone/Arcrun#100  []  指派:(沒有人)
    總圖說我一條關聯都沒有,但我有 1854 條
      ☑ inkstone/Arcrun#168

兩張的性質不一樣,這正好證明這張表有鑑別力:

  • arcrun-rag#136 已經是 s/review + 指派給 claude-code正在被處理(這就是回寫會把票放到的那個狀態)
  • Arcrun#100 沒標籤、沒指派 ⇒ 就是票上講的那種「永遠掛著、沒人回來關」

測試

bash scripts/test-ticket-handoff-writeback.sh   →  通過 49 條,失敗 0 條

全程離線:TICKET_HOST 指到連不上的位址;會打 API 的兩段(_writebackcmd_subtask
api() 換成錄音機跑,驗它到底打了哪些端點、送了什麼
——不打真實 Gitea、不開票、不關票、不留測試票。

八組,特別點名幾條:

  • ①「open + 從來沒有下游 → 不撈」——誤攔面。漏了它,ticket loose 會列出每一張普通票,那張表就變成雜訊
  • ⑦「沒有去關母票」必須是 False——回寫不准滑成「自動關掉」
  • ⑦「留言第一行有身份欄」——機器貼的留言一樣要標,否則下一個讀票的人會去找不存在的那個人
  • ⑧「母子兩端都被貼」必須是 2——journey 只貼一端比完全沒貼更危險(聚類時撈得到一半)
  • ⑤ 四條「齊全就放得過」——誤攔比漏擋更該修

/blocks 的回應形狀不是我猜的,是 2026-08-28 實查 inkstone/ISEP#75 拿到的。

回歸(都在這支改動的射程內,全綠):

  • A13 test-ticket-where-seen-guard.sh 17/17
  • A4 test-ticket-api-bypass-guard.sh 24/24
  • A12 hooks/tests/reply-identity.test.sh 11/11

🔴 沒驗到的那一格(要說在前面)

labels.yamlj/刻意留空:旅程怎麼切、叫什麼名字是方向題(四題公式第 4 題),
不該由工具或 subagent 代決;隨手造名字會讓同一條旅程長出三個拼法。
⇒ 在有人往那裡加第一條旅程之前,--journey 只會擋、不會貼
機制驗過了(⑧ 五條),資料還沒有。 第一條旅程名加進 labels.yamlgitea-labels-sync.sh 就能用。

版本號

待總管定版——今天多條線並行,版本號由總管統一收斂。這個 PR 沒有動 plugin.json

順帶回報兩件不屬於本 PR 的事

  1. README.md 的盤點數字已經對不上(我沒有改它,避免跟今天其他線撞):
    實數 hooks/*.sh 52 支、hooks.json 註冊 67 條、commands/ 7skills/ 2
    scripts/ 頂層 35 個檔(含本 PR 新增的 1 支);README 寫的是 48/59/7/2/23。
  2. github-contact-guard.sh 在 subagent 的環境會誤攔 Gitea 的 push
    它用 tool input 的 cwd 去解 remote 名稱,而 agent thread 的 cwd 每次都被重設回 session 根目錄
    (那個 repo 的 origin 指向 GitHub)⇒ 在別的 clone 裡 cd … && git push -u origin <branch> 會被判成推 GitHub。
    我沒有碰 .github-armed(那是 leo 的保險);改用一個名字誠實的 gitea remote 推,閘就正確放行了。
    要不要開票由總管決定。

🤖 Generated with Claude Code

關 `inkstone/ISEP#92`。 ## 這個 PR 解什麼 票上的病一句話:**「開在頂層的票,下游做完了卻沒人回來關⋯⋯頂層票會永遠掛著,而 leo 是看頂層的。」** 票自己已經盤出「不要重造」的三樣(`ticket where` 戳記+側門閘、`comment-carries-task-guard`、Gitea 原生 issue dependency), **這一版全部長在它們上面,一樣都沒有重做**。缺的只有三格:journey 標籤、雙向連結的強制、完工回寫。 ## 四格,對上票裡的四條驗收條件 | 驗收條件 | 這版做了什麼 | 在哪 | |---|---|---| | ① 頂層 → 下游,**兩張票互相看得到對方** | `subtask`(新別名 `handoff`)一個動作同時掛 Gitea 原生相依 **+ 在頂層票的時間軸貼一則指回下游的留言** | `cmd_subtask` | | ② 把下游關掉 → **頂層跟著被處理** | `ticket close` 關完子票立刻問 `/blocks`(誰在等我),對每張還開著的頂層票貼回寫留言;下游全關的那張再**指派回總管+改 `s/review`** | `_writeback` | | ③ **撈得出來**「沒有 open 下游、自己還開著」 | 新動詞 `ticket loose` | `cmd_loose` | | ④ 拿現有頂層票跑一次 → 指得出哪幾張 | 已實跑,結果見下 | — | 另外票裡的第三格 journey:`--journey <名>` 把 `j/<名>` **同時貼在母子兩端**(只貼一端就聚不起來)。 ## 三個刻意的設計判斷 **① 不自動關母票。** 回寫只做到「指派回總管+`s/review`」。 關票要有交付物、要有人看過(`ticket close` 既有的閘)——**默默關掉跟默默留著是同一個病的兩面**。 票上要的是「不能默默留著」,不是「自動消失」。 **② `loose` 是 `close` 回寫的補網,不是重複。** 關票的側門(直接 `PATCH state=closed`)是 `ticket-api-bypass-guard.sh` **刻意放行**的(「帶了 ID 的子路徑」一律放行)。 ⇒ 走正門的由回寫即時處理,走側門漏掉的由這張網撈回來。 **不是輪詢**(守 `CLAUDE.md` 的 flag 紅線)——掛在開場/收工對帳跑一次。 **③ 判準全部是機械事實,沒有一格在猜文字。** `is_loose` 看的是相依邊在不在、`state` 是什麼;journey 走的是「**要求那個標籤在 `labels.yaml` 裡**」, 不是「猜哪些名字合法」。leo 2026-08-17 已證偽關鍵字黑名單那條路(8 次誤攔、0 次正確攔截)。 ## 驗收條件 ④:拿現有的頂層票實跑(2026-08-28,唯讀,沒動任何一張票) ``` $ python3 scripts/ticket loose open 票 245 張 有下游相依的頂層票:17 張 其中「下游都關了、自己還開著」:**2 張** ● inkstone/arcrun-rag#136 ['s/review'] 指派:claude-code 身為 leo,我要知道 docs-only 模式跳過了哪些資料夾… ☑ inkstone/arcrun-rag#143 ● inkstone/Arcrun#100 [] 指派:(沒有人) 總圖說我一條關聯都沒有,但我有 1854 條 ☑ inkstone/Arcrun#168 ``` 兩張的性質不一樣,這正好證明這張表有鑑別力: - `arcrun-rag#136` 已經是 `s/review` + 指派給 `claude-code` ⇒ **正在被處理**(這就是回寫會把票放到的那個狀態) - `Arcrun#100` **沒標籤、沒指派** ⇒ 就是票上講的那種「永遠掛著、沒人回來關」 ## 測試 ``` bash scripts/test-ticket-handoff-writeback.sh → 通過 49 條,失敗 0 條 ``` 全程離線:`TICKET_HOST` 指到連不上的位址;**會打 API 的兩段(`_writeback`/`cmd_subtask`) 把 `api()` 換成錄音機跑,驗它到底打了哪些端點、送了什麼**——不打真實 Gitea、不開票、不關票、不留測試票。 八組,特別點名幾條: - ①「open + 從來沒有下游 → 不撈」——**誤攔面**。漏了它,`ticket loose` 會列出每一張普通票,那張表就變成雜訊 - ⑦「沒有去關母票」必須是 `False`——回寫不准滑成「自動關掉」 - ⑦「留言第一行有身份欄」——機器貼的留言一樣要標,否則下一個讀票的人會去找不存在的那個人 - ⑧「母子兩端都被貼」必須是 2——journey 只貼一端**比完全沒貼更危險**(聚類時撈得到一半) - ⑤ 四條「齊全就放得過」——誤攔比漏擋更該修 `/blocks` 的回應形狀不是我猜的,是 2026-08-28 實查 `inkstone/ISEP#75` 拿到的。 **回歸**(都在這支改動的射程內,全綠): - A13 `test-ticket-where-seen-guard.sh` 17/17 - A4 `test-ticket-api-bypass-guard.sh` 24/24 - A12 `hooks/tests/reply-identity.test.sh` 11/11 ## 🔴 沒驗到的那一格(要說在前面) `labels.yaml` 的 `j/` 段**刻意留空**:旅程怎麼切、叫什麼名字是方向題(四題公式第 4 題), 不該由工具或 subagent 代決;隨手造名字會讓同一條旅程長出三個拼法。 ⇒ 在有人往那裡加第一條旅程之前,**`--journey` 只會擋、不會貼**。 **機制驗過了(⑧ 五條),資料還沒有。** 第一條旅程名加進 `labels.yaml` → `gitea-labels-sync.sh` 就能用。 ## 版本號 **待總管定版**——今天多條線並行,版本號由總管統一收斂。這個 PR 沒有動 `plugin.json`。 ## 順帶回報兩件不屬於本 PR 的事 1. **`README.md` 的盤點數字已經對不上**(我沒有改它,避免跟今天其他線撞): 實數 `hooks/*.sh` **52** 支、`hooks.json` 註冊 **67** 條、`commands/` **7**、`skills/` **2**、 `scripts/` 頂層 **35** 個檔(含本 PR 新增的 1 支);README 寫的是 48/59/7/2/23。 2. **`github-contact-guard.sh` 在 subagent 的環境會誤攔 Gitea 的 push**: 它用 tool input 的 `cwd` 去解 remote 名稱,而 agent thread 的 cwd 每次都被重設回 session 根目錄 (那個 repo 的 `origin` 指向 GitHub)⇒ 在別的 clone 裡 `cd … && git push -u origin <branch>` 會被判成推 GitHub。 我沒有碰 `.github-armed`(那是 leo 的保險);改用一個名字誠實的 `gitea` remote 推,閘就正確放行了。 要不要開票由總管決定。 🤖 Generated with [Claude Code](https://claude.com/claude-code)
claude-code added 1 commit 2026-08-28 00:33:41 +00:00
票上的病:「開在頂層的票,下游做完了卻沒人回來關⋯⋯頂層票會永遠掛著,
而 leo 是看頂層的。」缺的三格是 journey 標籤、雙向連結的強制、完工回寫。

不重造既有的(票上明列):`ticket where` 戳記+側門閘(查票防重複)、
`comment-carries-task-guard`(留言藏任務)、Gitea 原生 issue dependency
(跨 repo 關聯的載體)——這一版全部長在它們上面。

四格,判準全部是機械事實(相依邊在不在、state 是什麼),沒有一格在猜文字:

① 雙向連結(驗收 1):`subtask`/新別名 `handoff` 一個動作同時掛原生相依
   + 在頂層票的時間軸貼一則指回下游的留言。相依邊只長在側欄,
   時間軸上什麼都沒有,跨 repo 時尤其看不出來。

② 完工回寫(驗收 2,最痛的那格):`ticket close` 關完子票立刻問 `/blocks`
   (誰在等我),對每張還開著的頂層票貼回寫留言;下游全關的那一張再
   指派回總管+改 s/review。**不自動關母票**——關票要有交付物、要有人看過,
   默默關掉跟默默留著是同一個病的兩面。

③ journey 標籤:`--journey <名>` 把 `j/<名>` 同時貼在母子兩端(只貼一端就
   聚不起來)。標籤不存在就擋下並指回 labels.yaml——要求標籤在場,
   不是猜哪些名字合法。labels.yaml 的 `j/` 段刻意留空:旅程怎麼切、叫什麼
   名字是方向題,不由工具代決。

④ 撈得出來(驗收 3、4):新動詞 `ticket loose` 掃 org 的 open 票,撈
   「有下游相依、下游全關、自己還開著」的頂層票。這是 ② 的補網——
   關票的側門(直接 PATCH state=closed)是刻意放行的,漏掉的由這張網撈回來。
   不是輪詢,掛在開場/收工對帳跑一次。

實跑(2026-08-28,唯讀,沒動任何票):245 張 open 票裡 17 張有下游相依,
其中 2 張是「下游都關了、自己還開著」——inkstone/arcrun-rag#136(已 s/review
且指派給 claude-code,正在被處理)與 inkstone/Arcrun#100(沒標籤、沒指派,
就是票上講的那種「永遠掛著」)。

測試:scripts/test-ticket-handoff-writeback.sh,49 條全綠,全程離線
(會打 API 的兩段把 api() 換成錄音機,不開票不關票不留測試票)。
回歸:A13 17/17、A4 24/24、A12 11/11 皆綠。
文件:docs/TESTING.md 新增 A16。

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016ZBu4Sa1cGntKFRBYNZ6xs
claude-code added 1 commit 2026-08-28 00:52:51 +00:00
唯一衝突在 docs/TESTING.md:兩條線各自加了一節、**都叫 A16**。
兩邊內容都留,我這一節改成 A19(main 已用掉 A16/A17/A18),
「目前狀態」表的那一列跟著搬到 A18 後面。

main 側本來就有的 A11/A12/A14/A15 重號**沒有動**——那是它兩條線各自編的,
不是這次造成的,動它會把 diff 炸開。

沒有碰的:.claude-plugin/plugin.json(維持 main 的 v0.12.0,版本由總管統一定)、
hooks/hooks.json、docs/hooks-inventory.md——這三個檔本線一個字都沒改,
自動合併拿到的就是 main 那份。

盤點數字在合併後的樹上重數(不是相加、不是抄別人的):
  hooks/*.sh 54 hooks.json 註冊 71 commands 7 skills 2 scripts 頂層 38
docs/hooks-inventory.md 寫的 54/71 **是對的**;README 寫的 48/59/23
**三個都錯**,已改成實數,並把「怎麼重數」的指令寫進去——
這種數字會漂是因為沒人知道它怎麼來的。

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016ZBu4Sa1cGntKFRBYNZ6xs
claude-code merged commit 5d6c4b5d63 into main 2026-08-28 00:54:28 +00:00
Sign in to join this conversation.
No Reviewers
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: inkstone/ISEP#98