--- name: move-board-card description: 島島阿學 Planning Board 狀態操作與稽核:七欄語意(含 PM 驗收的 Acceptance)、bin/pipeline/board.ts set/remove/audit、GitHub 內建 workflow 限制與除錯。 --- 先讀 [AI 檢核與人工審核共用流程](../../docs/ai-human-review-workflow.md),依當前客戶端可用工具執行;先完成適用檢核與修訂,再交人審核決策。 # move-board-card > **退役註記(2026-09-20)**:自動派工 Routine A/B(#241)與 merge 回寫 Routine C(同日稍後)皆已退役,`bin/pipeline/` 只剩 `board.ts` 這支人工 CLI(另有 `worktree-audit.ts` 稽核 worktree)。舊 prompt、pipeline 總覽與運維手冊封存於 `docs/archive/automation/`(索引見該目錄 `README.md`)。板上狀態現在由 request-feature/develop/verify-deploy 各自呼叫 `board.ts` 寫回,沒有任何移卡 cron。唯一排程是只讀的 `board.ts audit`(見下方「每日 audit 排程」),它不移卡。 Monorepo root: `/Users/xiaoxu/Projects/daodao` **任務管理層**(source of truth): - 中央 issues:https://github.com/daodaoedu/daodao/issues(feature 卡,product 視角) - Org board:https://github.com/orgs/daodaoedu/projects/10「Planning」 ## Status 七欄與誰負責移卡 稽核與決策紀錄見 `docs/plans/board-audit-2026-09-20.md`;Acceptance 欄的設計依據見 [#290](https://github.com/daodaoedu/daodao/issues/290)(2026-10-09)。 ``` Todo → Ready for Dev → In Progress → Review → Acceptance → Done ↑ │ └──── Need Fix ←──────┘ ``` | Status | 意思 | 誰移 | 球在誰手上 | |---|---|---|---| | `Todo` | 已開卡,還沒人做 | request-feature(`board.ts set todo`) | PM | | `Ready for Dev` | PRD/AC 定稿可開工;選用,允許空 | 人工,需使用者要求 | 工程師 | | `In Progress` | 有 worktree/branch 在跑 | develop start(`set wip --add-label human-driving`) | 工程師 | | `Review` | PR 已開,等 code review/merge;merged 後留在這裡直到 dev 冒煙 | develop finish(`set review`) | 工程師(AI review 引擎+人類 reviewer 核准) | | `Acceptance` | 已 merge、部署 dev、AI 冒煙通過,**等 PM 對照 AC 驗收** | verify-deploy 冒煙通過(`set accept`,自動 assign PM)+交接留言 @PM | PM(`peggy1213-create`) | | `Need Fix` | dev 冒煙失敗,或 PM 驗收退回且寫出**哪條 AC 失敗+看到什麼**,待修(受阻、新增要求不進這欄,見下方分流) | verify-deploy(`set needfix`)或 PM 退回;開修時 develop start 移回 In Progress | 工程師 | | `Done` | 符合 Definition of Done;issue closed | PM 關 issue(內建 `Item closed → Done` 自動移卡);工程驗收卡(`acceptance:engineering`)由 verify-deploy 冒煙通過後關;收尾時拔 `human-driving` | — | **Definition of Done**:AC 全部符合+已部署到 dev+驗收通過。預設由 PM 驗收;掛 `acceptance:engineering` 的卡由 dev 冒煙通過+證據留言即算驗收(見下方「誰驗收」)。merge 不等於 Done;沒掛這個 label 的卡,AI 冒煙通過也不等於 Done。 每張 Review/Acceptance/Need Fix 的卡要有一則最新的狀態 comment(PR 連結、等誰做什麼、驗收方式),PM 只看 board 也能追。Acceptance 的交接留言用 [issue-status-comment.md](../../templates/issue-status-comment.md) 的「交給 PM 驗收」段:PM 步驟只放畫面上做得到、看得到的操作;API 送出內容/回讀、regression test、log 放「工程證據(已驗,PM 不需重做)」;發布前跑 `pnpm -s tsx bin/pipeline/acceptance-lint.ts <留言檔>`。 同一張 PM 驗收卡裡,個別 AC 只能用 API 或測試驗時,在交接留言標「工程驗收項」、附工程證據,不列入 PM 步驟;PM 依畫面判定其餘 AC 即可關卡。 ### 誰驗收:PM 或工程(開卡時決定) 判斷只問一句:**PM 在 dev 環境操作,看得出改前改後的差別嗎?** - 看得出來 → PM 驗收(預設,不掛 label):畫面或版面改動;後端改動但使用者感受得到(收到的信、列表內容、錯誤訊息);使用者回報的 bug。 - 看不出來 → 工程驗收,掛 `acceptance:engineering`:開發流程與工具(skill、hook、CI、board);只在 dev 存在的端點;刻意零差異的重構(靠量測證明沒變);不改變輸出的後端內部調整。 - 判斷不了 → 預設 PM 驗收。 - **App(2026-10-29 起暫不由 PM 驗收)**:目前沒有可安裝的 App 測試版,PM 沒辦法在 App 上操作。只改 App 的卡走工程驗收(掛 `acceptance:engineering`,用 Expo web/模擬器冒煙+證據留言即可關);網頁+App 都有改的卡照常 PM 驗收,但只驗網頁,App 步驟寫在交接留言的「已知限制」。恢復 App 驗收時拿掉這條。 request-feature 開卡時判斷並掛 label,理由寫進 body 的「驗收方式」;開發中發現判斷錯了,直接加或拔 label。工程驗收卡冒煙通過後由 verify-deploy 留證據並關 issue,不進 Acceptance;`audit` 會列出誤進 Acceptance 的工程驗收卡。 ### Acceptance 欄的規則(Kanban Guide 2025.5 的 SLE) - **PM 怎麼操作**:每天看 board 的「PM 驗收」view(篩 `status:Acceptance`),可自己驗,或用 [test-acceptance](../test-acceptance/SKILL.md) 讓 AI 照請驗收留言實測、以 PM 帳號發報告並依下表移卡。通過 → 關 issue(卡自動到 Done);AC 沒過 → 留言寫**哪一條 AC 沒過、看到什麼**、卡移 `Need Fix`(`set needfix`)。測不了、想加新要求時依下方分流,不移 Need Fix。 - **不設欄位上限**(2026-10-09 使用者決定):冒煙通過就移 Acceptance,不排隊。 - **SLE 2 個工作天**(`ACCEPTANCE_SLE_BUSINESS_DAYS`,以台北時間的週一到週五計算,從卡片移進 Acceptance 起算):超過由 `audit` 列出並提醒 PM。累積實際 cycle time 後再調整。 - 驗收者帳號集中在 `bin/pipeline/types.ts` 的 `PM_LOGIN`。 ### Need Fix 只收 AC 失敗(#338) 2026-10-14 板上 17 張 Need Fix 只有 7 張是 AC 真的失敗,其餘是驗收受阻、驗收時新增的要求,或沒人收的母卡。驗收結果依下表分流: | 驗收時遇到 | 卡片去哪 | 誰做什麼 | |---|---|---| | 某條 AC 沒過 | `Need Fix` | 退回者留言寫**哪條 AC 失敗+看到什麼**(截圖或實際畫面);沒寫的退回不算數 | | 測不了:網址被瀏覽器擋(`ERR_BLOCKED_BY_CLIENT`)、截圖傳不上去、帳號或權限進不去 | 留在 `Acceptance`,加 `acceptance:blocked` | 留言 @ 工程說卡在哪一步;工程排除後拔 label 並 @PM 重驗。這不是產品失敗,不移 Need Fix | | 驗收當下想加的新要求、已通過後才改文案 | 原卡依**原 AC** 判定:過了就關;新要求另開卡 | 新卡內文寫「相關:#n」(不掛子卡),原卡留言連結新卡 | | 同一個 bug 出現在多張卡 | 指向一張卡追蹤 | 其餘卡留言連結那張,依各自 AC 判定,不重複退回 | - 工程接到 Need Fix 卻找不到 AC 失敗留言:先回頭請退回者補,或依上表改分流(例:#250 受阻移回 Acceptance、#279 文案修訂另開 #339)。 - `audit` 會抓:Need Fix 超過 24 小時沒有 AC 失敗留言(留言只有受阻時另報「驗收受阻,不是 AC 失敗」)、母卡子卡全關卻還沒進 Acceptance、`acceptance:blocked` 超過 48 小時未排除。門檻在 `types.ts`(`NEED_FIX_COMMENT_GRACE_HOURS`、`BLOCKED_ACCEPTANCE_MAX_HOURS`)。 ### 母卡與子卡(GitHub sub-issues) 正式定義見 **GitHub Issue 管理規範 §4**(repo 檔案 `docs/automation/github-issue-management.md#4-母卡子卡pr-與相依關係`,未隨本包出貨)(三種卡、要不要掛子卡、只有兩層、子卡開在中央 repo、母卡不直接掛 PR)。這裡只列 board 狀態的部分:驗收單位是**子卡或獨立卡**;母卡代表整個需求,狀態跟著子卡走。 | | 什麼時候進 Acceptance | PM 驗什麼 | |---|---|---| | 子卡(或沒有子卡的卡) | 冒煙通過就進,同上表 | 只驗這張的 AC,通過就關 | | 母卡 | 自己的 PR 都 merged **且所有子卡已關閉**(含 bug 子卡) | 整體串起來走一次,通過就關 | ``` 子卡各自走 Review → Acceptance → Done ─┐ ├→ 母卡進 Acceptance → PM 整體走一遍 → 關母卡 → Done 母卡自己的 PR 全 merged ────────────────┘ ``` - 還有子卡沒關的母卡留在 `Review`,不交 PM;不可先關母卡。 - **子卡只放本卡驗收範圍內的工作**(含驗收發現的 bug)。範圍外的後續改進/技術債開獨立 issue,body 寫「#n 的後續改進」,不掛子卡關係。已經掛錯的,確認後從母卡移除(GitHub 的 Remove sub-issue)並在該卡留言說明,不要讓母卡永遠關不掉。 - **2026-10-09 之前由工程師關閉、未經 PM 驗收的卡**:依原 issue 重新打開放 Review,重跑 dev 冒煙後移 Acceptance,不另開追蹤 issue。**只重開以「完成」(`COMPLETED`)關閉的卡**;以「不做」(not planned)或「重複」(duplicate)關閉的卡維持關閉,不需 PM 驗收(2026-10-10 曾誤重開 #170、#219,已改回)。 - 「PM 驗收」view 依 `Parent issue` 欄位分組,並開 `Sub-issue progress` 欄位顯示子卡完成數([GitHub Docs](https://docs.github.com/en/issues/planning-and-tracking-with-projects/understanding-fields/about-parent-issue-and-sub-issue-progress-fields))。 **Board 常數**:Project ID `PVT_kwDOBTLl0c4Bgxef`;Status field `PVTSSF_lADOBTLl0c4Bgxefzhfvwto`,七個 option id 只維護在 `bin/pipeline/types.ts`(`BOARD.statusOptions`、`STATUS_ALIASES`、`PM_LOGIN`、`DEAD_LABELS`),不要在文件複製 ID。新增 option 只能在 board 設定頁做(API 不支援),之後用 `gh project field-list 10 --owner daodaoedu --format json` 查 id 填回 `types.ts`;id 空著時 `set` 會直接報錯。 **GitHub 內建 workflow(board 設定頁,API 只讀得到名稱)**:`Item added → Todo`、`Item closed → Done`、`Auto-close issue`(設 Done 就 close issue)、`Pull request linked / merged`、`Auto-add (sub-)issues` 七個都開著。它們只認**同 repo** 用 closing keyword 連結的 PR;所有 PR(含 root)依 `docs/workflow.md` 用 `Refs daodaoedu/daodao#N`,所以不會自動移卡,也不會在 merge 時關卡跳過驗收(closing keyword 由 `pr-evidence-gate` 攔,#364)——這就是 2026-09-20 前卡片全堆在 In Progress 的原因。各 workflow 目標欄位(2026-09-20 於設定頁確認):Item added → Todo、PR linked → In Progress、**PR merged → Review**(原本是 Done,同日改掉以配合「merged 留 Review」)、Item closed → Done。這些只影響同 repo PR,仍以 skill 的 `board.ts set` 為準。 **Labels 現況**:`human-driving` 是人工開工標記(develop start 掛;卡到 Done 後拔,`audit` 會列出殘留)。`acceptance:blocked` 是驗收受阻標記(PM 測不了時掛、工程排除後拔,見「Need Fix 只收 AC 失敗」)。`board-audit` 只掛在每日 audit 的固定報告 issue。`auto`/`auto:plan-only`/`auto:auto-pr`/`needs-spec`/`dispatched`/`spec-pending`/`human-coding`/`manual` 等派工 labels **不再使用**(label 本身保留不刪;2026-09-20 已從活躍卡片整批移除,`audit` 會把殘留當「死 label」列出)。 ## board.ts — 移卡與稽核 CLI ```bash pnpm -s tsx bin/pipeline/board.ts set [--category <類別>] [--add-label x] [--remove-label y] [--dry-run] pnpm -s tsx bin/pipeline/board.ts category <類別> # 只改類別;別名 功能/bug/工程/技術債/清單 pnpm -s tsx bin/pipeline/board.ts remove # 從 board 移除(issue 保留),例:Review 彙整報告卡 pnpm -s tsx bin/pipeline/board.ts audit [--json] [--stale-days 3] pnpm -s tsx bin/pipeline/board.ts pending [--json] # 唯讀:誰在等誰決定什麼 ``` - status 接受別名:`todo` / `ready` / `wip`/`in-progress` / `review` / `accept`/`acceptance` / `needfix` / `done`(大小寫、`-`、`_`、空白互通) - `set` 不在 board 會先 `item-add`;改完回讀 Status,不一致直接 exit 1 - `set accept` 會順便 assign `PM_LOGIN` 並回讀 assignee;交接留言內容隨卡片不同,由 verify-deploy 另外發 - `audit` 把每張卡的 Status 對 issue open/closed、關聯 PR、labels 比對,列出:卡片沒有 Status、未完成卡沒有類別(「產品」「工程」看板依類別篩選,沒填就看不到)、Done 但 issue open、issue closed 卻卡在任一 open 欄(Todo/Ready/In Progress/Review/Acceptance/Need Fix)、有 open PR 卻沒到 Review、PR 全 merged 卻 N 天沒移 Review、Review 的 PR 全 merged 卻 N 天沒跑 verify-deploy、Acceptance 未指派 PM、Acceptance 超過 SLE、母卡已 close 仍有子卡未關、母卡在 Acceptance 但子卡未全關、出現第三層子卡、子卡不在中央 repo(母卡 Review 的 merged-stale 建議改成「等子卡關閉」)、Review 沒 PR、死 label、Done 仍掛 `human-driving`、Need Fix 超過 24 小時沒有 AC 失敗留言(或留言其實是驗收受阻)、母卡子卡全關卻未進 Acceptance、`acceptance:blocked` 超過 48 小時。留言只對 Need Fix 卡另外查(`getIssueComments`,從移進欄位前 24 小時起算):同一行有 AC ID+FAIL/未通過/❌、且那行不是受阻列,才算 AC 失敗留言;「🙋 請驗收」交接留言不算退回。Acceptance 的 SLE 與 Review 的「merged 後停留天數」都用 Status 欄位值的 `updatedAt`(移進欄位的時間),不用 issue `updatedAt`(留言會刷新它)。中央 repo 的 PR 只有**真正 link**(closing keyword 或 Development 面板,GraphQL `ConnectedEvent`),或 `Refs daodaoedu/daodao#N` 行明確指向本卡,才算實作;只在內文順手提到卡號的 docs PR(`CrossReferencedEvent`)不算——這樣 root-only 的工作仍抓得到,docs PR 順手提到的卡不會被誤判。sub-repo PR 若在標題(`— daodao#N`)或 `Refs`/`Closes`/`Fixes` 行**明確指向別張卡**,對本卡只算提及、不算實作(例:#218 的 PR 在內文列出後續卡 #244、#247);沒有任何標記的舊 PR 仍照算。純函式 `auditCards` 在 `lib.ts`,測試 `bin/pipeline/__tests__/board.test.ts` - `pending` 列出 Review/Acceptance/Need Fix 每張卡在等誰(reviewer/工程/PM)、要決定什麼、最新證據連結,同一組裡等越久越前面(參考 pstack ketchup):Review 有開著的 PR 等 reviewer 審查合併;PR 都合併了等工程跑 verify-deploy;Acceptance 等 PM 照最新請驗收留言驗收,掛 `acceptance:blocked` 的改成等工程排除;Need Fix 等工程,附最新 AC 失敗留言(找不到就提醒確認退回原因)。母卡的子卡還沒全關時在等子卡,不列出。`audit` 是找狀態落差,`pending` 是排今天要處理什麼;純函式 `pendingDecisions` 在 `lib.ts`,測試 `bin/pipeline/__tests__/pending.test.ts` - 不要用 `gh project item-list` 批次查:它每次拉全部欄位,跑十幾次就撞 Projects rate limit(2026-09-20 實測);`board.ts` 走精簡 GraphQL(`listBoardItemsLite`、`findBoardItemForIssue`) - GraphQL 額度是**使用者 PAT 共用的 5000/hr**,Actions 裡的 Sync Shared Config 也用同一顆;大批操作前先 `gh api rate_limit --jq .resources.graphql` ### 每日 audit 排程 `.github/workflows/board-audit.yml` 每天台北 09:00(也可 `gh workflow run board-audit.yml` 手動跑)執行 `board.ts audit`,結果寫進同一張置頂 issue(label `board-audit`;第一次由 workflow 建立後手動 `gh issue pin` 一次):有落差就更新內文並保持 open,沒落差就更新成 ✅ 並關閉,下次有落差再重開。不每天開新卡、不留言、不移卡;看到落差照建議欄用 `board.ts set` 處理。讀 board 用 secret `GIT_HUB_ACCESS_TOKEN`(需 `project` scope,`GITHUB_TOKEN` 讀不到 org project),寫 issue 用 `GITHUB_TOKEN`。這張 issue 是自動報告,不放 Planning board、不走驗收:board 的內建 Auto-add 會把它加進 Todo(2026-10-23 #389 實測),所以 workflow 最後一步用 PAT `board.ts remove` 移掉,`audit` 也略過掛 `board-audit` 的卡。 ```bash # 純函式測試(不要跑裸 pnpm test,會掃到 worktrees/) pnpm exec vitest run bin/pipeline --exclude "**/worktrees/**" ``` ## 除錯快查 | 症狀 | 檢查 | |---|---| | merge 了但卡沒動 | 正常——沒有移卡 cron;跑 verify-deploy,它會依冒煙結果 `set accept`/`set needfix` | | `set accept` 報「還沒有 option id」 | board 設定頁還沒加 `Acceptance` 選項,或 id 沒填回 `types.ts` | | PM 說沒收到通知 | 確認 `set accept` 有回讀到 assignee、交接留言有 `@peggy1213-create` | | `set` 回讀失敗 | 是否撞 rate limit(`gh api rate_limit`);item 是否被人手動刪掉(`audit` 會列不在 board 的卡) | | board 操作 403 | PAT 缺 `project` scope | | 卡 close 了卻在 Todo | 內建 `Item closed → Done` 應會處理;沒有就 `set done` | | 有人開 PR 但卡在 Todo | sub-repo PR 不會被內建 workflow 認到;`set wip` | ## 本 skill 不做的事 - 不派工、不開鏡像 issue、不在雲端實作——這些能力已退役,需要時另開卡重新設計。 - 不修改 labels 集合、不刪舊 label(從個別卡片拔死 label 是 `audit` 建議項,可以做)。 - 不改 GitHub 內建 workflow 的目標欄位(API 無 mutation,只能在 board 設定頁手動改)。 - `Ready for Dev` 只是管理狀態,設定它需在使用者要求範圍內。