Files
ISEP/commands/issue-handle.md
Leo c2638668e3 ISEP 0.1.0:環境設定收成一個 plugin,本機與雲端共用一份
leo 2026-08-20:「同一個 plugin 你用,薄殼也用,保證兩邊同步」
              「我要你幫雲端做薄殼,永遠都有問題,你要做的就是這組設定
                你自己可以 dogfooding」

搬進來:41 支 hook(51 條註冊)/7 支 command/2 支 skill/23 支腳本。
不搬 .env、wiki、docs——那些是知識不是環境。

51 條 hook 路徑全部從 $CLAUDE_PROJECT_DIR/.claude/hooks/ 改成 ${CLAUDE_PLUGIN_ROOT}/hooks/,
零漏網。那正是薄殼一直壞掉的根:雲端 cwd 不是真身,寫死路徑就斷。

尚未驗證:Claude Code 能不能從私有 Gitea repo 裝 marketplace(要憑證)。
下一步就是在本機實際裝一次,通了才動雲端 bootstrap.sh。

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-20 11:41:46 +08:00

20 KiB
Raw Permalink Blame History

description
description
Gitea issuemilestone/看板的實際操作流程——issue 是 tasks 池、milestone 是 sprint、label 是狀態、看板是 leo 的畫面

/issue-handle — issue 就是 tasks 池子(Gitea 版,2026-08-09 leo 定調)

🔴 **本檔 2026-08-09 整份重寫。**舊版寫的是 GitHub + gh CLI—— 那條路 D20 擋著(機器寫入 GitHub 要 leo 開閘),而真實世界早就在 Gitea 上。 gh 打不到 Gitea。照舊版做會什麼都做不成。

世界觀(先讀這段,其餘都是它的細節)

  • issue tasks 池子——不管從哪來,所有要做的事都是 issue
    • SDD 定案推導出來的、外面回報進來的、開發中才發現的,三種都是 issue
    • 差別只在 body 裡標來源,不在「住哪裡」
  • milestone sprint(一次交貨)——AI 建得了,所以 AI 管
  • project(看板)= 大目標的視覺——AI 建不了,leo 建、leo 拖
  • label s/* 狀態——機器的真相住這裡,不住看板欄位
  • CP 選擇 tasks 的指南——決定「這次交貨先拿哪幾張」,它自己不發號
  • wiki 過程、證據、為什麼——引用 #號,不重複任務狀態

一句話:leo 拖畫面,AI 管資料。兩邊看同一批 issue,不各養一份清單。

認證(最容易安靜出錯的一步)

token 從該 repo 的 gitea remote URL 取,不另存:

TOKEN=$(git remote get-url gitea | sed -E 's|.*//[^:]+:([^@]+)@.*|\1|')
  • 🔴 不帶 token 打私有 repo 會回 {"message":"not found"}——長得像「這裡沒東西」
    • 2026-08-09 實錯:據此把 24 個 open issue 宣告成不存在,還寫了一份錯的提案
    • 「查不到」有兩種:真的沒有 vs 我沒權限看見。分不出來就不准下結論
  • 分辨法:沒權限回 JSON {"message":"not found"};路由不存在回純文字 404 page not found

三條標準作業流程

  • ① SDD 定案 → tasks 寫進 issues
    • POST /api/v1/repos/{o}/{r}/issues
    • 🔴 labelslabel idint,傳名字會 422 ⇒ 先 GET /labels 拿 id
    • body 標來源:design 推導 修改自 #42 原生
  • ② 外面進來的先驗傷,再決定要不要進 sprint
    • 新 issue 預設不掛 s/,先用內建 bugquestioninvalidduplicate 分類
    • 確認「要做、且屬於某次交貨」才掛 s/todo 掛 milestone
    • 不是每個 issue 都是 task(查完是設定問題就 close,不進 SDD)
  • ③ 先決條件(issue 之間的關聯)
    • POST /issues/{n}/dependencies(我被誰擋)//issues/{n}/blocks(我擋誰)
    • 🔴 body 是 IssueMeta,要三個欄位:{"index":22,"owner":"Leo","repo":"arcrun-rag"}
      • 只傳 index 會回 404 IsErrRepoNotExist(同 repo 也一樣要帶 owner/repo
      • 📌 這條原本是我憑 swagger 路徑列表寫進本 skill 的,沒實測,一測就錯。 端點存在 ≠ 我知道怎麼呼叫它——寫進 skill 前要真跑一次。
    • 實測通過(2026-08-09):#21 blocked by #22 建立後, GET /issues/21/dependencies[22]GET /issues/22/blocks[21]
    • 派工前先檢查前置是否 closed不要靠人記得順序

狀態標籤:互斥 scope label

一條線走完,從進池子到用戶拿得到:

s/triage → s/backlog → s/todo → s/doing → s/stage →(leo 蓋章 + arm + 推 prod)→ closed
                                     ↘ s/pending(卡住/等外部/等人)
  • s/triage 新進來的,還沒驗傷——還沒決定要不要做
    • 🔴 為什麼不用「留白」代表未驗傷2026-08-09 leo 建 Triage 看板時暴露): 留白查不出來。撈得到 s/todo,卻撈不到「所有還沒驗傷的」,只能肉眼掃 ——那正是這套要拔掉的東西。沒有標籤 ≠ 一種狀態,它是查詢的死角。
  • s/backlog 驗過了、確定要做,但還沒排進任何 sprint
    • leo 說的「wishlist/功能需求/後續要規劃的
    • s/triage 的差別:要不要做,已經有答案了
    • 與 milestone 的關係:s/backlog 沒掛 milestone;掛上 milestone 就該進 s/todo
  • s/todo  已排進 sprint,等開工
  • s/doing 現在有人在做
  • s/stage 已推上 stage,等 leo 去 youlin 的 stage 環境驗收(出貨流程第⑤步)
    • leo 2026-08-09 提:「就知道哪些要驗收」
    • 🔑 它的價值是可查詢leo 問「我今天要驗什麼」=撈 labels=s/stage 一次答完, 不必總管回想、不必翻對話
  • s/pending 卡住——等外部/等人,不是沒人做(例:#8 等 leo 在真 Windows 截圖)
  • closed  用戶拿得到了——不是「程式碼寫完」,也不是「leo 看過 stage」

優先級是另一個軸:p/ scope

p/high(擋住交付或有時間壓力)/p/low(想做,但晚一點沒關係)。

  • 🔴 為什麼不併進 s/s/ 回答「它走到哪」,p/ 回答「它多重要」—— 一個 s/backlog 的東西也可以是 p/high(想做很久、很重要、只是還沒排進這次 sprint)。 併成一組就表達不出來。
  • 兩組各自 exclusive,互不干擾:一個 issue 同時有一個 s/ 和至多一個 p/

對照 leo 的 Triage 看板欄位

  • Needs Triages/triage
  • Backlogs/backlog
  • High PriorityLow Priorityp/highp/low
  • Closed ← issue 的 closed 狀態

欄位仍是 leo 的視覺、標籤仍是機器的真相,兩邊講同一件事但各自可用。

🔴 為什麼用 s/stage 不用 s-stage s-stage平名標籤,會跟 s/doing 同時掛著 s/stage 在同一個 scope 內,貼上去舊狀態自動掉 ⇒ 才是狀態機。 狀態標籤一律走 s/ 前綴,別建平名的。

  • 建法:POST /labelsbody 帶 {"name":"s/doing","exclusive":true}
  • scope/name 格式 exclusive ⇒ 同一 scope 下一個 issue 只能掛一個
    貼 s/todo   → ['bug', 's/todo']
    再貼 s/doing → ['bug', 's/doing']   ← 舊狀態自動掉,不相干的 bug 留著
    
  • 🔴 不建 s/doneclosed 就是 done,多一個就是同一件事兩個真相
  • 🔴 label 是 repo-scoped,沒有跨 repo 繼承——新 repo 要自己補

AI 做得到 / 做不到(別浪費時間試)

  • 做得到:開 issue、改 issue、掛/換 s/* 標籤、建 milestone、把 issue 掛進 milestone、 建先決條件、留言、結案、從 GET /issues/{n}/timeline 讀出「這張卡在哪個看板」
  • 做不到:建 project把卡拖進 project知道卡在哪一欄
    • POST …/projects純文字 404 page not found=路由不存在
    • 對照組證法:同一把 token 打 POST /labels 回 201 ⇒ 證明「不是我沒權限,是 Gitea 沒開這條路」。這個手法值得複用。
    • issue 物件無 project 欄位;timelineproject_board 事件也沒有欄位名/id
  • 不採:直寫 Gitea 的 project_issue 表、web-only 路由 POST /{owner}/{repo}/issues/projects/column(吃 session cookieCSRF
    • 理由不只是升級風險:那是別人家 app 的內部 schema/未公開契約, 用了等於在系統裡多一條沒人維護的隱性契約——與「兩份真相」同病

兩個會咬人的坑

  • labels 在兩支 API 是兩種型別
    • 建 issue POST /issues → 吃 idint,傳名字會 422 json: cannot unmarshal JSON string into Go int64 within "/labels/0"
    • 貼標籤 POST /issues/{n}/labels → 吃 名字(string
  • 本機是 zsh,不做參數字串切分bash 才會)
    • for n in $LIST 在 zsh 會把整串當成一個字 ⇒ 用 ${=LIST} 或逐一傳參

🔴 怎麼寫一張 issueleo 2026-08-09 連罵三次立的)

leo:「請問這個是人話嗎?誰看得懂?」 「你的 issues 要寫清楚 5W1H,你會做重複工,表示你不知道自己在幹嘛」 「先寫問題,再寫解法,不是先寫一堆細節

🔴 為什麼要寫人話——不是為了友善,是為了讓 leo 抓得到我的錯

leo 2026-08-09

你搞不清楚的時候,我也無法幫你看,因為我看不懂你在寫說明。

這是所有「白話鐵律」底下真正的理由: 看不懂 審查失效。 我今天在同一個 session 裡錯了好幾次 (把 24 個 open issue 讀成 0、給 leo 一台他根本沒在用的機器的網址)—— 如果我寫的東西他看得懂,這些會更早被擋下來。

用內部代號寫票,等於把唯一能發現我出錯的人的眼睛關掉。

🔴 修好的當下就關票(今天一天撞三次)

2026-08-09 派工做 #9#14#17三張都是早就修好、只是沒人關 ⇒ 三個 subagent 的時間全花在確認「這件其實已經好了」。

  • 不是那三張票的問題,是「修好時沒有人回來關」的問題
  • ⇒ 改完 → push → 當場回票寫清楚怎麼驗的、然後 close
  • ⇒ 撈任務前先問「這張票的日期多久了?」,超過一週的先驗現況再派工

起點:issue 是「報告」,不是「工單」

leo 2026-08-09

「如果我要丟,我會寫『我遇到一個問題是…』『發現一個 bug 是…』 『我希望有一個功能是…』,怎麼會寫成這樣?

一張 issue 應該長得像一個人開口講話。 先用第一人稱把事情講一遍,再談要做什麼。寫成規格條目就沒人看得下去。

那三種開頭正好就是三種類型,驗傷就是在判斷它是哪一種:

  • 「我遇到一個問題是…」→ 卡住了 → question
  • 「我發現一個 bug 是…」→ 壞掉了 → bug
  • 「我希望有一個功能是…」→ 願望/功能需求 → enhancement(多半配 s/backlog

範例(#7 現在的樣子):

我遇到一個問題是:我要把 AI 接上我的知識庫,claude.ai 第一格就要我填 MCP 網址, 但我不知道我的是什麼。那串網址是安裝的時候長出來的,每個人都不一樣,我沒地方查。

標題:寫症狀,不寫修法

反例(真的誤導了好幾個月的那一句):

t151 收尾:安裝器要生成並下發 arcrun-mcp 的 MCP_OWNER_SECRET(擋封測,缺它每個封測者都死在同意頁)
└代號┘ └────────── 這整段是「怎麼修」,而且是後來被推翻的舊解法 ──────────┘ └─ 唯一人話在括號裡 ─┘

改成:「用戶不知道自己的 MCP 網址是什麼,所以接不上 AI」

  • ① 標題寫症狀不寫修法——修法會變,症狀不會。 上面那句最毒的地方不是難懂,是它把「當時以為的修法」寫死在標題上; 後來 owner secret 這條路被 portal 帳密取代,標題還停在舊解法,於是持續指錯方向
  • ② 標題裡不准出現只有我們懂的字:代號(t151)、零件名(MCP_OWNER_SECRET)、 檔名(build-bundles.mjs)一律降到細節區。
  • ③ 一句話講完「誰+卡在哪」,看標題就知道要不要點進去。

內文:問題 → 解法 → 細節(細節收進 <details>

## 問題
<用戶視角一句話:他想做什麼、卡在哪、結果怎樣>

## 解法
<一句話:要做出什麼>

## 怎麼驗
<走用戶真的會走的那條路;貼實際畫面,HTTP 200 不算>

---
<details><summary>細節</summary>
現況實查(指令+輸出)/真身在哪個 repo 哪個檔/可照抄的既有寫法/
leo 原話/不屬於本 issue 的(已拆到 #N
</details>

為什麼細節要收起來:leo 掃 issue 是在決定「這件要不要現在做」, 細節是做的人才需要的。攤開來就掃不動 ⇒ 他掃不動 ⇒ 這件事不會發生。

🔴 討論票 → 執行票;追蹤大的(2026-08-10 定案,D58

leo:「這個 issue 是對於某個建議的討論過程,中間會產生一個新的 issue 就是依照討論執行某個修正追蹤大的。」 leo:「充分利用 gitea 的機制,不要硬做個不支援的機制,容易出錯。

  • 討論票=一個建議的討論過程(脈絡、來回、為什麼)。不當工單追。
  • 執行票=從討論裡長出來的「去做某個修正」。有自己的擋點、驗收、s/* 狀態。
  • 追蹤執行票;孫任務留在執行票裡當 checkbox(見下一段,那條沒變)。

定址:用 Gitea 原生的 issue 引用,不要自製結構

Gitea 原生就會把 issue 引用自動變成可點連結 ⇒ CP/SDD 不必手貼網址、不必造錨點。

  • 🔴 最好用的判準需要指到票裡的某一行 = 那張票該拆了。 這個限制反而強迫出正確的顆粒度
  • 刀口不是「大小」,是「狀態」:問「它會不會需要跟隔壁那條不同的狀態?
    • 會 → 獨立成執行票
    • 不會 → 留在票裡當 checkbox

兩個極端都撞過,別再撞

  • 太大Leo/mira#2 一張票 29 個 checkbox、五個段落五種狀態,卻只掛得了一個 s/pending
    • ⑤-1⑤-3 整段作廢了好幾小時沒人發現——29 條的票不會有人逐條稽核
  • 太小08-09 把 stage 一件事拆成五張(#27#29#32),leo 打開就看不下去

⚠️ 已作廢:隱形錨點 <a id="tNNN">D53,同日上線同日推翻)

不要撿回去。 它是塞 raw HTML 偽造 checkbox 錨點,靠渲染器願意保留、 靠沒人用網頁編輯器把它洗掉——脆弱,而且壞掉時看不出來

📌 它錯在哪值得記:整段推理是嚴謹的(實測三則、對照兩個反例、接上既有鐵律), 但沒有先問「這個平台原生支援嗎」。與 08-09「平台已經做了的事我又做一次」同族, 只是這次是平台沒做的事我硬做。 ⇒ 造任何機制之前先問兩句:「原生支援嗎?」「不支援是不是在告訴我方向錯了?」

🔴 子任務用 issue 內的工作項目清單,不要開成一堆票(2026-08-09 實撞)

leo:「子任務在這裏寫就好了」(指 Gitea 編輯器的工作項目清單按鈕)「現在這樣太亂了

  • 一件事 一張票,票裡用 - [ ] 列子任務(每條帶錨點,見上一段
  • 不要把每個子任務開成獨立的票
    • 2026-08-09 實錯:總管把 stage 這一件事拆成 #27#29#30#31#32 五張, leo 打開就看不下去。已收攏回 #27 一張,其餘關閉
    • 病根:把 leo 說的「寫在 issues 裡的新增工作項目清單」讀成「開新 issue」, 而他指的是編輯器裡那顆 checkbox 按鈕
  • 🔑 規則(leo 原話)

    整個 tasks 內容用 markdown 寫進去,除非某個任務開始工作後變大了,就要獨立成一張票。

    • 預設全部寫成 checkbox,包含整份 SDD 的 tasks
    • 獨立成票是「事後」的動作,不是規劃時就先拆
      • 觸發時機=開始做了之後才發現它變大(要多輪、要另一個 repo、要另一個人)
      • 那時再把那一條抽出來開票,並在原票的該行改成 - [ ] 見 #NN
    • 不要在規劃階段就預先拆票——那正是 2026-08-09 弄亂的做法
  • 另開一張的另一個正當理由主詞不同(見下一段) ——例如「網址看得到」vs「進得去」是兩個人在抱怨兩件不同的事,那本來就不是同一件

🔴 一筆 issue 只做一件事(2026-08-09 實撞)

leo:「#18 其實是 2 個分開的任務⋯⋯一半已經完成卡在另一半,但另一半根本不相依

  • 綁在一起的代價:做完的那半被沒做的那半拖著,看起來像沒完成 ⇒ 進度儀表失真,而 leo 是靠這個判斷還剩什麼
  • 判準(同 cp-write 鐵律 5主詞不同就拆
    • 「把設定搬進應用視窗」=設定放哪的問題
    • 「新增 onboarding」=第一次的人怎麼上手的問題
    • 兩者互不依賴 ⇒ 一定是兩張
  • 🔴 不准用「併入此案」把別的 issue 吞進來
    • #18 原本寫「併入此案:狀態顯示(見另一 issue)」,而那是獨立存在的 #17
    • ⇒ 同一件事在兩張票上,又是兩份真相;要關聯用 dependenciesblocks,不用文字吞併
  • 拆單時要留痕:兩邊都寫明「從 #N 拆出」與「為什麼不相依」, 否則下一個人會以為是重複開單

🔴 設計寫在票上——不要每次回去 grep 源碼(leo 2026-08-09 立)

leo:「你應該把 design 寫在 issues 裡,你就可以去查 issues,而不是每次去查源碼。」

為了回答問題而查出來的「它現在怎麼運作」,當場寫回那張票,附 檔案:行 當出處。

  • 當天實錄:同一個 session 裡,為了回答四個問題各翻了一次源碼—— console 現在用來做什麼(installer/oauth-prototype/worker.js:3237)/ 迎新引導為什麼看不到(main.js:73 只在零帳號時出現)/ 重裝會不會重設密碼(/console/setup 已存在回 409)/ bundle 是快照還是連結(manifest 的 source 欄與 js_bytes)。 四題的答案都值得留在票上,卻只留在對話裡 ⇒ 對話結束就沒了,下次再翻一次。
  • 🔴 而且翻源碼拿到的是「稿子」:它反映「還沒清乾淨」,不等於「還在用」。 同一天就因為讀源碼推論而把兩台實例的定位講反(geek=prodyoulinstage)。
  • 票上該有的三件它現在怎麼運作/為什麼這樣設計/動它會影響誰——不只是「要做什麼」。
  • 順序:先查票 → 查不到才翻源碼 → 翻完回頭補進票(與 wiki 同一階梯)。
  • 判準一句話如果我剛剛翻了源碼才知道答案,那就是一條該補進票的設計記錄。

界線(沿用,未變)

  • 跨 repo 的 issuecomment 一律開頭署名 [<本 repo> CC][InkStoneCo 總管]
    • 所有 repo 共用同一個帳號,author 看不出是誰發的,身份只能在內容層自報
  • 發 issue 給別的 repo 要先問人;對自己 repo 開 issue 記待辦則直接做
  • 🔴 有事才讀,禁止自動輪詢——禁 Actionscronwebhook 因 issue 事件 fan-out (那正是當初害 GitHub 帳號被 flag 的流量模式;換成 Gitea 也不放寬)

🔴s/stage 不等於交棒完成——要附「怎麼測」

leo 2026-08-09:「要我來測試是嗎?如果這樣,你要在總管回覆中說明測試方式, 而不是『要測試』,我就會按照它來測試。

  • 把票改成 s/stages/pending 只是改了一個欄位,leo 看到的是「該你了」卻不知道怎麼做 ⇒ 等於把找路的成本丟給他,正是 principles「不增加 leo 負擔」要防的
  • 每一件推給 leo 的事,回覆裡必須帶三樣
    • 他要打開什麼(確切網址/確切指令,不是「去 portal 設定頁找」
    • 他該看到什麼(正確的樣子長怎樣)
    • 什麼情況算失敗(看到什麼就是壞的,回一個詞給我)
  • 🔴 指令必須自己先打過。給沒驗過的網址/指令 = 把除錯成本丟給他
    • 2026-08-09 實例:我差點把 arcrun-mcp.yuga3bse.workers.dev 給他—— 實打是 HTTP 000DNS 不解析)。正確的是 WORKER_SUBDOMAIN deployment-map.md:176 youlin-hsieh-dev),實打回 401 unauthorized =端點在、要 OAuth這才是對的行為
    • 401 和 000 都不是 200,但意義天差地遠:前者證明東西在,後者證明我在瞎猜

收工判準

回覆 issue 要寫做了什麼、為什麼這樣決定、動了哪些檔,不是只回「done」。 結案要有實測證據——「程式碼寫完了」不是狀態(見 CRITICAL-PATH.md)。

尚未驗證的一條(別當成已知)

把卡拖到 Done 欄,Gitea 會不會自動 close 那個 issue 2026-08-09 想測,但那張測試卡被刪掉了 ⇒ 無證據,不編。 若答案是「會」,拖拉就天然被機器看見,這條縫自動消失。