# 開發階段 TODO ## 階段一:基本流程串接 - 目標:確保 action 可以被觸發,pipeline 各步驟依序執行,log 出每個主要階段的進入與完成。 - 驗收:log 中能看到每個階段(如「Step1: Pipeline 啟動」、「Step2: Findings 產生」、「Step3: Findings 合併」等)明確訊息,且流程能走完(即使還沒產生 findings)。 - 已驗收:`code-review` job 的 log 已完整出現 `Step1` 到 `Step8`,並以 `Pipeline 完成` 結束。 ## 階段二:Git Diff 排除 .gitea/ 資料夾 - 目標:讀取 Git Diff 時排除 `.gitea/` 資料夾內的所有檔案,以及 `.github/`、`TODO.md`、`README.md`,避免 AI 分析 workflow 設定與文件等非業務程式碼。 - 驗收:PR 中有上述路徑或檔案的變更時,diff 內容不包含該區塊,AI 分析結果不含這些路徑相關問題。 - 已驗收:`app/gitea.js` 已在取得 diff 時過濾 `.gitea/` 區塊,且相關單元測試已覆蓋。 ## 階段三:Findings 產生與合併 - 目標:各角色(style/security/performance/maintainability/testing)能產生 findings,並正確合併新舊 findings。 - 驗收:log 中能看到每個角色 findings 數量、合併後 findings 統計,並有「Step3 merged findings total=...」等訊息。 - 已驗收:log 已顯示 5 個角色皆有分析結果,並出現 `Step3 merged findings total=...` 與去重統計訊息。 ## 階段四:AI 語意去重 - 目標:嘗試呼叫 LLM 進行 findings 語意去重,API 額度不足時要有降級處理 log。 - 驗收:log 中能看到 `AI 去重: N -> M 筆` 的成功訊息,或在失敗時出現 `AI 去重失敗(...),降級:保留所有問題` 之類的明確訊息。 - 已驗收:log 已出現 `AI 去重: 13 -> 11 筆`,且程式具備失敗時保留所有問題的降級處理。 ## 階段五:AI 排除問題過濾 - 目標:讀取排除問題檔案(`.gitea/ai-review/exclusions.json`)時先去除重複條目、整理成語意群組摘要;若檔案不是頂層陣列格式,需主動修正成正確格式,再進行規則過濾並呼叫 AI 判斷剩餘問題是否為誤報或不適用,兩層過濾後產生最終問題清單。 - 驗收:log 中能看到排除問題檔案讀取成功或不存在的訊息、重複排除條目的整理摘要、格式修正訊息、規則過濾數量變化,以及「AI 誤報過濾: N -> M 筆」或降級訊息。 - 已驗收:`app/findings.js` 會先整理與去重 exclusions,再進行規則過濾與 AI 誤報過濾;若格式不是頂層陣列,會先修正為陣列後再繼續流程。 - 補充紀錄:當 `排除過濾` 後仍保留 findings 時,log 會出現 `AI 誤報過濾: N -> M 筆`;若 API 額度不足或回傳失敗,則會出現 `AI 誤報過濾失敗(...),降級:保留所有問題`。 ## 階段六:findings 寫入與 comment 發布 - 目標:`.gitea/ai-review/findings.json` 正確寫入,comment 發布順序正確(舊問題→非嚴重→嚴重),每步有 log。 - 驗收:log 中能看到 `.gitea/ai-review/findings.json` 寫入、comment sync 的詳細訊息與順序。 - 已驗收:`findings.json` 會被正確寫入,且 comment 流程會依序嘗試舊問題、非嚴重新問題與嚴重新問題三段。 - 補充紀錄:當最終 findings 沒有對應類型時,會以 `無舊問題,跳過`、`無新的非嚴重問題,跳過`、`無新的嚴重問題,跳過` 的方式略過;若有問題,則會分別發布對應 comment。 ## 階段七:階段六後驗證 JSON 格式 - 目標:階段六完成後驗證 `findings.json` 與 `exclusions.json` 是否為合法 JSON 格式,格式錯誤時先嘗試透過 AI 修正內容,再重新驗證;修正後仍不合法才 exit 1;之後才檢查檔案是否存在,不存在則建立並寫入 `[]`。 - 驗收:log 中能看到兩個檔案的驗證結果(成功或失敗),格式錯誤時有 AI 修正嘗試與修正後再次驗證的訊息;若檔案不存在,會在驗證完成後看到建立並寫入 `[]` 的訊息;修正失敗時 workflow 狀態為失敗。 - 已驗收:log 已明確顯示 `.gitea/ai-review/findings.json` 與 `.gitea/ai-review/exclusions.json` 都是 `JSON 格式正確`。 ## 階段八:記憶區 commit/push 與錯誤處理 - 目標:記憶區能成功 commit/push,且只提交 workspace 中實際存在的 `.gitea/ai-review/findings.json` 與 `.gitea/ai-review/exclusions.json`;workspace 沒有的問題檔就略過;錯誤時有明確 log,流程結束有總結訊息。 - 驗收:log 有「persisted findings」、「commit=...」、「push=...」等訊息;git add 只包含新的問題檔;錯誤時有「Runner failed: ...」等明確錯誤說明。 - 已驗收:commit/push 成功時會出現 `persisted findings commit=... push=... review_outcome=...`,且只提交問題檔的行為已有單元測試覆蓋。 ## 階段九:阻擋嚴重問題 PR(第 8 點) - 目標:如果 PR 問題表格中有嚴重(critical)問題,workflow 需直接 exit 1,不讓流程成功。 - 驗收:log 中能看到「critical 問題存在,workflow 結束(exit 1)」等明確訊息,且 workflow 狀態為失敗。 - 已驗收:`app/main.js` 會在 Step8 檢查 `critical` 數量,若大於 0 就直接 `process.exit(1)`;因此只要最終 findings 含有 critical,workflow 就會失敗。 - 補充紀錄:`Step8` 的退出訊息屬於預期行為,不代表 Step7 commit/push 失敗。 ## 階段十:API Key 輪替 - 目標:所有平台的 API Key 支援逗號分隔傳入多個,隨機順序各嘗試一次,單一 Key 失敗時自動換下一個,全部失敗則 exit 1。 - 驗收:log 中能看到「key[N/M] 失敗」等訊息,換 key 後繼續執行;傳入單一 Key 時行為與原本相同;全部 Key 失敗時 log「所有 API Key 均失敗,終止流程」且 workflow 狀態為失敗。 - 已驗收:`review.yaml` 已以逗號串接多把 Gemini key,且 `app/llm.js` 與單元測試已覆蓋輪替與失敗退出行為。 ## 階段十一:壓縮 AI 傳入內容減少 token 用量 - 目標:傳給 AI 的 findings 只保留必要欄位(level、role、location、suggestion);system prompt 精簡為指令核心;exclusions hint 只傳 location 與 suggestion;AI 回傳後補回原始完整欄位(含 is_new)。 - 驗收:AI 呼叫的 payload 不含 is_new 等內部欄位,去重與誤報過濾後的 findings 仍保有完整欄位供後續流程使用。 - 已驗收:`app/findings.js` 已只傳必要欄位給 AI,並在回傳後補回原始 findings 的完整欄位。 ## 階段十二:啟動前置驗證所有驗證相關設定 - 目標:action 一開始(Step1 之後、其餘步驟之前)就集中檢查所有「驗證相關設定」是否可用,全部通過才繼續,任何一項失敗就印出明確原因並 `exit 1`。檢查項目: 1. 必要環境變數齊全:`GITEA_TOKEN`、`GITEA_REPOSITORY`、`PR_NUMBER`(缺一即失敗)。 2. Gitea API 可連線且 `GITEA_TOKEN` 能讀取此 repo(`GET /api/v1/repos/{repo}`)。 3. 若有提供 `GITEA_COMMENT_TOKEN`,另外用它驗證可用(`GET /api/v1/user`)。 4. git push 認證可用:用與階段八 commit/push 相同的 askpass + remote URL 機制跑唯讀的 `git ls-remote`,提前抓出 askpass 無法執行或 HTTP 認證失敗(`could not read Username`)的問題;此檢查為 fatal,失敗即 `exit 1`。 5. 已選定一個 LLM provider(`getLLMConfig().provider` 非 null)。 6. LLM API Key 至少一把通過驗證:送出最小請求確認認證可用,逗號分隔多把只要一把成功即可並逐把記錄成敗;Ollama 改為檢查 `OLLAMA_BASE_URL` 可連線。 - 驗收:log 中能看到 `Step1.5`(或對等)前置驗證的每一項結果(成功/失敗),任一失敗時 log 指出是哪一項與錯誤訊息,且 workflow 狀態為失敗;全部通過時 log 出「前置驗證通過」後才進入後續流程;驗證邏輯由 `app/preflight.js` 提供並有單元測試覆蓋(成功、缺環境變數、Gitea token 無效、comment token 無效、所有 LLM key 失敗、Ollama base url 等情境)。 - 補充紀錄:前置驗證不應發布任何 PR comment,只做唯讀的認證/連線確認;LLM 驗證請用最小 payload,避免浪費 token。 - 已驗收:`app/preflight.js` 提供 `checkRequiredEnv` / `verifyGiteaToken` / `verifyCommentToken` / `verifyLLM` / `runPreflight`,git push 認證驗證由 `app/git.js` 的 `verifyRemoteAccess`(`git ls-remote`)提供;`main.js` 已在 Step1 之後、bot-check 之前呼叫 `runPreflight(WORKSPACE)`,未通過即印出原因並 `exit 1`;`app/preflight.test.js` 與 `app/git.test.js` 覆蓋上述情境(含 git push 認證成功/失敗、token 不外洩、askpass 清理),`node --test *.test.js` 全數通過。 ## 階段十三:PR 對話收斂(讀留言判斷解決狀態) - 目標:前置驗證通過、且非 AI 助理自動提交後(Step2),讀取 PR 上所有行內 review comment 並收斂成對話,請 AI 對照 PR head 最新程式碼判斷每個對話指出的問題是否已解決:已解決者用 Gitea 官方 API resolve 對話,並在 Step4 從問題清單移除;未解決且可解析回 bot finding 者,於 Step4 加回問題清單。納入判斷的對話包含所有人的留言;任一外部呼叫失敗都降級為「視為未解決」,不中斷流程。 - 驗收:log 中能看到 `Step2` 的對話總數/已解決/待判斷統計,以及 `對話已解決並 resolve: :`、`對話收斂完成: resolved=.. unresolved=.. 加回 findings=..`;Step4 能看到 `對話收斂套用: N -> M 筆`;resolve / list comments / 取檔案內容 / AI 判斷任一失敗時有對應降級警告。 - 已驗收:`app/resolve.js` 提供 `parseBotReviewComment` / `groupConversations` / `codeWindow` / `judgeConversationsResolved` / `reconcileConversations` / `dropResolvedFindings` / `addCarriedFindings`;`app/gitea.js` 新增 `listPullReviews` / `getPullReviewComments` / `listAllReviewComments` / `resolvePullReviewComment` / `getFileContentAtRef`;`main.js` 以 `Step2` 呼叫並於 `Step4` 套用結果;`app/resolve.test.js` 與擴充後的 `app/gitea.test.js` 覆蓋解析、收斂、AI 判斷對齊、resolve/降級、移除/加回去重等情境,`node --test *.test.js` 全數通過。 ## 階段十四:AI 助理使用量統計(多平台,呈現剩餘可用百分比) - 目標:統計階段(Step6 發布 Review 前)一併蒐集目前所採用 AI 助理的使用量,並同時寫入 action log 與 PR Review 本文。使用量含:本次審查累計的 token 消耗,以及「剩餘可用百分比」。需支援本工作流的所有 AI 助理平台(openai、claude、gemini、ollama、amazonq、opencode)。 - 設計:本次 token 由 `app/llm.js` 的 `chat` 集中以 `recordUsage` 累計,`extractUsage` 容錯解析各平台回應的 usage 欄位。剩餘可用百分比由 `resolveRemainingPercent` 依優先序擇一:(1) 帳號額度有上限(OpenRouter `GET /auth/key`)→ 剩餘 credits / 上限;(2) 否則用回應 header 的速率配額(`recordRateLimit` 擷取 `x-ratelimit-*-tokens` 或 `anthropic-ratelimit-tokens-*`,退而用 requests 維度)→ 當前視窗剩餘 / 上限。`fetchAccountQuota` 依平台分流(本地/自架回報「不適用」、官方平台需 org/admin 權限時回報原因);兩種來源皆無法取得時降級為「無法計算百分比」+原因,不中斷流程。 - 驗收:log 中能看到 `使用量統計: 本次 /: 提示N + 回應M = T token(K 次呼叫);剩餘可用: X%(<來源> ...)` 或「無法計算」;PR Review 本文在「AI Code Review 統計」之後附上「🤖 AI 助理使用量」區塊(token 表格 + 剩餘可用百分比或無法計算原因)。 - 已驗收:`app/usage.js` 提供 `extractUsage` / `recordUsage` / `getRunUsage` / `resetRunUsage` / `recordRateLimit` / `getRateLimit` / `resetRateLimit` / `resolveRemainingPercent` / `fetchAccountQuota` / `formatUsageStats` / `formatUsageStatsLine`;`app/llm.js` 於 OpenAI 相容路徑呼叫 `recordUsage` 與 `recordRateLimit`、OpenCode 路徑呼叫 `recordUsage`;`app/comments.js` 的 `postFindingsReview` / `buildReviewSummary` 支援附加 `usageSection`;`main.js` 於 `Step6` 蒐集(含 `getRateLimit`)並寫入 log 與 Review;`app/usage.test.js` 與擴充後的 `app/comments.test.js` 覆蓋 usage 解析/累計、速率 header 擷取、百分比解析與降級、OpenRouter 額度、格式化與 Review 本文附加等情境,`node --test *.test.js` 全數通過。