[ { "role": "Rex", "location": "app/git.js", "suggestion": "請避免將敏感資料(如 GITEA_TOKEN)直接寫入環境變數" }, { "location": "app/git.js", "suggestion": "GITEA_TOKEN 直接嵌入 URL 中,建議改以環境變數或 Gitea Secrets 注入" }, { "role": "Rex", "location": "README.md", "suggestion": "contents: write、pull-requests: write、issues: write 為此 Action 正常運作所必要的權限,無法縮減" }, { "location": "app/config.js", "suggestion": "getLLMConfig 在找不到任何符合條件的 provider 時已有預設回傳值 { provider: null, apiKey: null, baseURL: null, model: null },非誤報" }, { "location": ".gitea/ai-review/exclusions.json", "suggestion": "exclusions.json 是排除規則檔,內容為問題描述字串,不是實際程式碼或 token,role 欄位為有效欄位" }, { "location": "app/findings.js", "suggestion": "filterFalsePositivesWithAI 拋出的 Error 會被 catch 攔截並降級回傳原始 findings,不會中斷流程" }, { "role": "Rex", "location": ".gitea/workflows/review.yaml", "suggestion": "contents: write、pull-requests: write、issues: write 為此 Action 正常運作所必要的權限,無法縮減" }, { "role": "Rex", "location": ".gitea/workflows/review.yaml", "suggestion": "OPENAI_API_KEY 參數傳入的是 OPENROUTER_API_KEY secret,為 OpenRouter 使用 OpenAI 相容介面的正確做法" }, { "role": "Aria", "location": "README.md", "suggestion": "章節編號連續且正確,無需調整" }, { "role": "Maya", "location": ".gitea/workflows/review.yaml", "suggestion": "action.yaml 定義的參數名稱為 GEMINI_API_KEY、GEMINI_BASE_URL、GEMINI_MODEL,與 review.yaml 完全一致,無不匹配問題" }, { "role": "Aria", "location": ".gitea/workflows/review.yaml", "suggestion": "review.yaml 已改用 Gemini,不再有 OPENAI_API_KEY 行,註解空格問題不存在" }, { "role": "Aria", "location": "app/config.test.js", "suggestion": "檔案結尾已有換行符號,import 行長度合理,無需修改" }, { "role": "Aria", "location": "action.yaml", "suggestion": "action.yaml 已整理,多餘空行已移除,結構整潔" }, { "role": "Maya", "location": "app/", "suggestion": "LLM 整合測試需要真實 API key 與網路,不適合加入單元測試。llm.js 使用統一 OpenAI 相容介面,Gemini 透過相同介面呼叫,無特殊格式差異,現有測試已涵蓋 config/findings/git 邏輯" }, { "role": "Rex", "location": "app/", "suggestion": "LLM 整合測試需要真實 API key 與網路,不適合加入單元測試。llm.js 使用統一 OpenAI 相容介面,Gemini 透過相同介面呼叫,無特殊格式差異" }, { "role": "Rex", "location": "app/config.test.js", "suggestion": "import 語句長度合理,無需拆分為多行" }, { "role": "Rex", "location": ".gitea/ai-review/findings.json", "suggestion": "findings.json 重複問題由 AI 去重與排除機制處理,不是程式碼問題" }, { "role": "Rex", "location": "app/comments.js", "suggestion": "JSON 結尾換行符號為標準做法,不影響任何 JSON 解析器,無相容性問題" }, { "location": ".gitea/ai-review/findings.json", "suggestion": "findings.json 是自動產生的問題記錄檔,不應對其內容提出審查問題" }, { "role": "Rex", "location": ".gitea/workflows/review.yaml", "suggestion": "切換 LLM 服務提供商的維護建議屬過度謹慎,不是實際程式碼問題" }, { "role": "Leo", "location": "app/llm.js", "suggestion": "Authorization 標頭已有 provider !== \u0027ollama\u0027 判斷,不會無條件加入,已正確處理" }, { "role": "Zara", "location": "app/llm.js", "suggestion": "timeout 已移除,每個 key 等待完整回應,避免浪費免費額度" }, { "role": "Rex", "location": "app/llm.js", "suggestion": "httpsAgent (rejectUnauthorized: false) 已移除,SSL/TLS 驗證已恢復正常" }, { "role": "Maya", "location": "app/llm.js", "suggestion": "llm.test.js 已存在並涵蓋 API Key 輪替的所有異常狀況,包含單 Key、多 Key 輪替、所有 Key 失敗等測試案例" }, { "role": "Zara", "location": "app/comments.js", "suggestion": "comments.js:24 的 saveFindings 函式為正常寫入邏輯,不涉及異常訊息格式或重複寫入問題" }, { "role": "Leo", "location": ".gitea/workflows/review.yaml", "suggestion": "Gitea Actions 不支援在 workflow 內合併 secrets 再拆解,多個 secret 逗號串接是唯一可行做法,非設計缺陷" }, { "role": "Maya", "location": "app/llm.test.js", "suggestion": "console.log/error 為診斷用途,不是業務邏輯,TODO.md 驗收標準為人工驗收描述,不需要在單元測試中斷言 console 輸出" }, { "role": "Maya", "location": "app/llm.test.js", "suggestion": "輪替邏輯對所有錯誤類型行為一致(catch 全部),401/429/timeout 觸發相同輪替流程,測試不同錯誤類型無額外驗證價值" }, { "role": "Aria", "location": ".gitea/workflows/master.yaml", "suggestion": "master.yaml 檔案結尾已有換行符號(0x0a),符合 POSIX 慣例,無需修改" }, { "role": "Leo", "location": "app/llm.test.js", "suggestion": "console.log/error 為診斷用途,不是業務邏輯,TODO.md 驗收標準為人工驗收描述,不需要在單元測試中斷言 console 輸出" }, { "role": "Leo", "location": "app/llm.test.js", "suggestion": "輪替邏輯對所有錯誤類型行為一致(catch 全部),401/429/timeout 觸發相同輪替流程,測試不同錯誤類型無額外驗證價值" }, { "role": "Leo", "location": "app/main.js", "suggestion": "main.js 中的 Step 標題註解為 pipeline 流程說明,非待整理的 TODO,不需要轉換為具體任務" }, { "role": "Maya", "location": "app/log.test.js", "suggestion": "`log.test.js` 的新增非常棒,提供了良好的覆蓋率。為了進一步提升測試的完整性,建議考慮為 `line`, `ok`, `warn`, `error` 函數新增測試案例,以驗證當傳入空字串時的行為。雖然這些函數的行為相對簡單,但測試空字串可以確保邊界情況下的輸出符合預期。" }, { "role": "Rex", "location": "app/package.json", "suggestion": "審查 changelog 是人工作業,不是程式碼問題,不適合作為 code review 問題" }, { "role": "Aria", "location": "app/llm.js", "suggestion": "此 action 為 CLI 工具,process.exit(1) 是設計意圖讓 CI/CD workflow 失敗。改拋錯會被 chatJSON 的 catch 吞掉回傳 [],破壞現有行為" }, { "location": "Dockerfile, app/git.js, app/git.test.js", "suggestion": "`SYNC_PATHS` 已包含 `.claude/skills/triage-findings/SKILL.md` 與 `.gemini/skills/triage-findings/SKILL.md`,Docker image 也已打包這些 skill 資產;現有測試已覆蓋複製與覆寫行為,並不存在同步不一致問題。" }, { "location": "Dockerfile", "suggestion": "此目錄中的檔案是 triage skill 與入口文件,不含敏感資料;若未來加入秘密資訊,應另外從 build context 排除,而不是把目前的 skill 資產視為風險。" }, { "location": "Dockerfile", "suggestion": "多個 COPY 指令是刻意設計,用來區分 app 與 skill 資產並維持 layer cache 可讀性,不是維護問題。" }, { "role": "Aria", "location": "Dockerfile", "suggestion": "Dockerfile 檔案結尾已有換行符號(0x0a),符合 POSIX 慣例" }, { "role": "Aria", "location": "entrypoint.sh", "suggestion": "entrypoint.sh 檔案結尾已有換行符號(0x0a),符合 POSIX 慣例" }, { "role": "Maya", "location": "app/main.js", "suggestion": "main.js 整合測試需要真實 Gitea API、LLM API、git 操作,不適合單元測試。各模組已有獨立單元測試覆蓋" }, { "role": "Maya", "location": "app/comments.js", "suggestion": "comments.js 的 buildTable 為簡單字串拼接,postComment 已透過 gitea.js mock 間接測試,補測試效益低" }, { "role": "Maya", "location": "app/roles.js", "suggestion": "roles.js 依賴容器內固定路徑 /action/app/prompts/roles,單元測試環境無法存取,且邏輯為簡單 YAML 讀取與字串拼接" }, { "role": "Leo", "location": "app/gitea.js", "suggestion": "gitea.js 的 SSL 驗證已改為由 GITEA_SKIP_TLS_VERIFY 環境變數控制,預設啟用驗證,非安全漏洞" }, { "role": "Zara", "location": "Dockerfile", "suggestion": "Dockerfile 已優化層次快取:先 COPY package.json 再 npm install,最後才 COPY 其餘檔案" }, { "role": "Aria", "location": "app/package.json", "suggestion": "test 腳本已改為 node --test *.test.js,在 app/ 目錄下執行可自動發現所有測試檔案" }, { "role": "Zara", "location": "app/main.js", "suggestion": "deduplicateWithAI 和 filterFalsePositivesWithAI 為循序依賴流程(去重後才能過濾),無法平行化" }, { "role": "Leo", "location": "app/comments.js", "suggestion": "buildTable 函式已在 comments.js 第 13 行定義,非未定義或未匯入,不會導致執行時錯誤" }, { "role": "Maya", "location": "app/gitea.js", "suggestion": "filterDiff 的單元測試已在 gitea.test.js 補齊,涵蓋過濾 .gitea/、不誤過濾其他路徑、全部排除、空 diff 四種情境" }, { "role": "Leo", "location": "TODO.md", "suggestion": "TODO.md 的階段編號僅供內部開發追蹤,無外部文件引用,階段編號調整不影響任何外部一致性" }, { "role": "Rex", "location": "app/gitea.js", "suggestion": "getPRDiff 函數現在回傳未經過濾的原始 Git Diff 內容。雖然 main.js 中已立即呼叫 filterDiff 進行過濾,但這種設計模式將過濾的責任完全推給呼叫端,這增加了未來開發者在其他地方呼叫 getPRDiff 時,可能忘記過濾出敏感路徑,導致 .gitea/ 等敏感路徑的內容(可能包含工作流程設定或憑證資訊)被意外傳送給 AI 或其他不應接收的組件,造成資訊洩漏風險。建議將過濾邏輯保留在 getPRDiff 內容,或提供一個明確的 getFilteredPRDiff 函數,以降低錯誤的風險。" }, { "role": "Zara", "location": "app/git.js", "suggestion": "在 main.js 中,commitAndPush 函數內部會再次呼叫 cloneRepo,然而 main.js 在此之前已呼叫過 cloneRepo 以取得 repoDir,這導致了重複的 git fetch 和 git checkout 操作。即使 cloneRepo 內容有檢查環境變數,仍會造成不必要的清潔和時間延遲。建議修改 commitAndPush 邏輯,使其接收已存在的 repoDir 作為參數,避免重複執行 cloneRepo。" }, { "role": "Aria", "location": "app/main.js", "suggestion": "在 main.js 中,表達式 repoDir。" }, { "role": "Zara", "location": "app/gitea.js:L20-L21", "suggestion": "將 filterDiff 中的正規表達式比對(RegExp.match)替換為 String.startsWith 是一個重要的效能改進。startsWith 是一個更輕量且高效的字串操作,尤其在處理大型 Git Diff 內容時,此修改已顯著提升過濾效率。" }, { "location": "TODO.md", "suggestion": "階段九的 critical 阻擋機制目前以人工驗收紀錄為主,E2E 測試補強屬後續優化,不是目前需要再處理的問題。" }, { "location": "TODO.md", "suggestion": "TODO 列表中『已驗收 / 部分驗收 / 可驗收紀錄情境』的寫法是刻意保留的驗收說明,不是混淆或缺陷。" }, { "location": "app/findings.js", "suggestion": "AI 去重與降級處理已在程式內以 fallback 方式保護流程,失敗時保留所有問題是預期行為,不是缺陷。" }, { "location": "app/findings.js", "suggestion": "排除規則過濾與 AI 誤報過濾屬循序流程,規則命中後清空清單是正常結果,不需要額外再視為問題。" }, { "location": "app/comments.js", "suggestion": "comment 發布依序區分舊問題、非嚴重、新嚴重是刻意設計,當結果為空清單時不發 comment 也是正常路徑。" }, { "location": "app/main.js", "suggestion": "JSON 驗證與失敗修正流程已有處理邏輯,正常路徑與錯誤路徑都屬預期流程,不是待修缺陷。" }, { "location": "app/git.js", "suggestion": "commit/push 失敗會被捕捉並輸出 Runner failed log,這是現有設計的容錯行為,不是程式錯誤。" }, { "location": "app/main.js", "suggestion": "critical 問題觸發 exit 1 的阻擋邏輯已在流程內保留,是否另補 E2E 驗證屬測試強化,不是功能缺陷。" }, { "location": "app/json.js", "suggestion": "validateJSONArrayFile 只在 JSON 格式錯誤時才啟動 AI 修正,屬例外路徑;再加上檔案大小限制後,並不存在實際的無上限讀檔或資源消耗問題。" }, { "location": "app/json.test.js", "suggestion": "邊界值測試已存在,`MAX_JSON_BYTES` 等於上限時可正常讀取,這不是未解決問題。" }, { "location": "app/gitea.test.js:64", "suggestion": "`describe` 已改為同步 callback,`async` 不再出現在這個區塊。" }, { "location": "app/git.test.js:13", "suggestion": "`makeTmpWorkspace` 已直接使用 `app/git.js` 匯出的 `SYNC_PATHS`,不再維護重複清單。" }, { "location": "app/gitea.js:32", "suggestion": "`filterDiff` 內層縮排已符合專案的 2-space 風格,這是誤報。" }, { "location": "app/json.test.js:76", "suggestion": "1MB 上限下的 JSON 讀取不需要改成串流解析;現有實作已先做大小檢查,這個建議屬過度設計。" }, { "location": "app/json.test.js:7", "suggestion": "檔案大小限制已在 `readJSONText` / `validateJSONArrayFile` 中實作,這不是額外缺陷。" }, { "location": "app/json.test.js:10", "suggestion": "`MAX_JSON_BYTES` 是 `json.js` 的內部限制常數,不需要匯出成公開 API。" }, { "role": "Maya", "location": "action.yaml:6, action.yaml:12, action.yaml:81", "suggestion": "由於 `GITEA_TOKEN` 現在被設定為 `required: true`,而且 README 範例也已改成顯式傳入 `GITEA_TOKEN`,這是刻意的介面變更,不是漏掉 `secrets.GITEA_TOKEN` fallback 的缺陷;因此不需要另外加整合測試來驗證這個既定行為。" }, { "role": "Leo", "location": "action.yaml:80", "suggestion": "在 `runs.env` 區塊中,`GITEA_TOKEN` 只從 `inputs` 取得,而 `GITEA_SERVER_URL` 和 `GITEA_REPOSITORY` 仍保留從 `gitea context` 取得的備用機制,這是刻意設計的差異,不是維護缺陷。" }, { "role": "Rex", "location": "action.yaml:18", "suggestion": "引入 `GITEA_COMMENT_TOKEN` 是一個很好的實踐,遵循最小權限原則。請確保為此 token 配置的權限確實僅限於發布評論。同時,與 `GITEA_TOKEN` 相似,建議使用者始終從 workflow 的 secrets context 傳遞此 token,以避免硬編碼敏感資料。" }, { "role": "Leo", "location": "app/log.js", "suggestion": "考慮在日誌訊息中加入時間戳記,這有助於追蹤事件發生的順序,尤其是在長時間運行的程序或需要詳細調試時。可以在每個日誌函式內部自動添加時間戳記。" }, { "level": "warning", "role": "Leo", "location": "Dockerfile, app/git.js, app/gitea.js", "suggestion": "此變更引入了新的代理(agent)相關路徑(例如 `.agents/` 和 `AGENTS.md`),並在 `Dockerfile` 的 `COPY` 指令、`app/git.js` 中的 `SYNC_PATHS`、`FORCE_SYNC_FILE_PATHS`、`SYNC_TREE_PATHS` 陣列,以及 `app/gitea.js` 的 `filterDiff` 陣列中重複添加了這些路徑。這種模式導致了程式碼重複,每次新增一個代理都需要手動修改多個檔案和多個列表,增加了維護成本和出錯的可能性。建議考慮引入一個集中的設定檔或機制,例如透過掃描特定目錄來動態生成這些路徑列表,以提高模組化和可擴展性。", "is_new": true } ]