[ { "level": "warning", "role": "Mage", "location": "app/releases.js:46", "problem": "在 `cleanupReleases` 迴圈中執行 DELETE 請求時,未針對網路不穩定或暫時性服務錯誤(如 502, 503, 504)實作重試機制。若刪除過程中發生瞬間網路中斷,該 release 將不會被刪除。", "suggestion": "對於特定的 HTTP 狀態碼(502, 503, 504),建議引入簡單的指數退避重試機制(Exponential Backoff)。", "status": "deferred", "defer_reason": "屬功能性增強與設計取捨。此清理 Action 通常以排程執行,單次失敗可於下次執行補刪;是否引入重試/退避涉及重試次數、間隔與冪等性等設計決策,保留待人工評估。" }, { "level": "warning", "role": "Mage", "location": "app/tags.js:56", "problem": "`cleanupReleases` 與 `cleanupOrphanTags` 非同步先後執行,若兩者之間 Gitea 上有新的 release 被建立,releaseTagNames 快照會過時,可能導致正在使用的 tag 被錯誤刪除。", "suggestion": "在兩個 cleanup 步驟之間確保 API 狀態一致性,或在刪除 tag 前再次檢查該 tag 是否仍未被任何現存 release 使用。", "status": "deferred", "defer_reason": "現行程式已在 cleanupOrphanTags 開頭重新抓取最新 release 清單作為主要緩解;殘餘競態窗極小且屬排程任務可接受範圍。是否再加每筆刪除前複查屬一致性/成本的設計取捨,保留待人工評估。" }, { "level": "warning", "role": "Rogue", "location": "app/releases.js:43", "problem": "刪除舊成品時使用序列化的 `for...of` + `await`,刪除請求逐一排隊等待 API 回應。", "suggestion": "改用 `Promise.all` 搭配 `map` 將刪除請求並行化以縮短總執行時間。", "status": "deferred", "defer_reason": "刻意保留序列化:可避免對 Gitea API 造成併發壓力與觸發速率限制,並維持可預期的記錄輸出順序,且與原 bash 版本行為一致。無上限並行化非等價變更,保留待人工評估(可日後改為有上限的並行)。" }, { "level": "warning", "role": "Rogue", "location": "app/tags.js:46", "problem": "刪除孤立 tag 時同樣使用序列化迴圈,造成總執行時間拉長。", "suggestion": "改用 `Promise.all` 將刪除請求並行化。", "status": "deferred", "defer_reason": "與 app/releases.js:43 同理,刻意保留序列化以避免併發壓力與速率限制並維持記錄順序,屬設計取捨,保留待人工評估。" }, { "level": "warning", "role": "Leo", "location": "app/releases.js:37", "problem": "`cleanupReleases` 同時負責取得資料、判斷邏輯與執行刪除副作用,違反單一職責原則,未來不易單獨測試刪除邏輯。", "suggestion": "將取得/過濾的純邏輯與執行刪除的副作用層拆開(目前 `selectReleasesToDelete` 已做了一部分)。", "status": "deferred", "defer_reason": "純邏輯(selectReleasesToDelete)已抽離且函式短小、已具失敗路徑測試覆蓋;進一步拆出刪除迴圈為設計偏好,效益有限且增加表面積,保留待人工評估。" }, { "level": "warning", "role": "Leo", "location": "app/tags.js:46", "problem": "`cleanupOrphanTags` 直接在主流程遍歷並刪除,未來若需更複雜的失敗處理(重試、批次)會難以維護。", "suggestion": "參考 cleanupReleases 結構,將分類過濾與執行刪除進一步解耦。", "status": "deferred", "defer_reason": "分類邏輯(categorizeTags)已抽為純函式並具測試;是否進一步拆出刪除執行層為設計偏好,與 app/releases.js:37 一併保留待人工評估。" }, { "level": "info", "role": "Rogue", "location": "app/gitea-client.js:33", "problem": "`fetchAllPages` 採線性逐頁請求,資料量龐大時總請求時間較長。", "suggestion": "若 API 支援,先取得總頁數再並行發出請求。", "status": "deferred", "defer_reason": "Gitea 分頁未可靠提供總頁數,需額外解析 Link 標頭且各端點支援度不一;線性逐頁搭配逾時已足夠穩健且簡單,並行化屬最佳化取捨,保留待人工評估。" } ]