# 前後端改進建議 依「影響使用者 / 維護成本 / 風險」排序。細節背景見先前健康檢查;**架構導覽**見 `docs/architecture.md`。 --- ## 後端:立刻值得修的問題 ### 1. Job dedupe:改寫草稿全域互擋 `generate-rewrite-draft` 的 `DedupeKeys` 為空,但 `buildDedupeKey` 仍用 `type|scope` 產 key → 所有 `scope=persona` 的改寫 job 共用一把 lease。 - 檔案:`internal/model/job/usecase/usecase.go`、`helpers.go` - 建議:`DedupeKeys: []string{"scope_id"}`,或空 keys 時回傳 `""` 不啟用 lease - 補 test ### 2. CreateRun 失敗路徑用裸 `Update` lease 失敗時 `runs.Update` 直接改 status,違反 guarded 規則。 - 改 `UpdateIfStatus(pending → cancelled)` ### 3. ClaimNext lock 失敗立刻 re-enqueue 可能 tight loop 打 Redis。應 delay / 檢查狀態後再入列,FindByID 失敗不要 silent drop。 ### 4. Worker 忽略 Heartbeat 錯誤 `_ = step.Heartbeat(ctx)` 在 lock 過期後仍繼續跑。失敗應中止 step。 ### 5. Refresh token 並發 先發新 token 再 blacklist 舊的 → 並發 refresh 可能雙活。應用原子輪替(Lua / 先 blacklist 再 issue,或 refresh family)。 --- ## 後端:結構與可維護性 | 議題 | 建議 | |------|------| | ServiceContext 過胖 | 依域拆 bootstrap(auth/job/content/publish),減少單檔 400+ 行 DI | | 每個 worker process 註冊全部 handler | 只 register 該 `workerType` 需要的 step;啟動時 assert template↔handler | | ghost worker 在 compose | 已清理一輪;新增 template 時同步 `dev-all` / `docker-compose` | | 列表 API 用 `limit` | 新 API 一律 `page/pageSize`;舊的雙讀後 deprecate | | `time.Now().UnixNano()` 散落 | 統一 `clock.NowUnixNano()` 利於測試 | | Secrets key 可空 | prod config validate:`HAIXUN_SECRETS_KEY` 必填 | | copy_mission 半殘 | 見 `post-project-plan`:過渡期只留 inspire;完整 pipeline 勿再接,走 Post Project | | 測試 | Job 較完整;auth refresh、ClaimNext race、logic 層幾乎無測 — 優先補 P0 | ### 錯誤處理慣例 - 好:`errs.For(code.X).…` + `response.Write` - 差:`_, _ = FailRun` / `UpdateProgress` 吞錯誤 → 可觀測性差,長任務尤其危險 ### 不建議做的事 - 為了 template-monorepo 相容引入 OAuth/OTP/Zitadel 重依賴 - 在 provider adapter 裡讀 Mongo / setting - 裸 `Update` 蓋 job / publish_queue 狀態 --- ## 前端:可明顯變好的地方 ### 1. 登入態對 5xx 過於暴力 `AuthContext.reloadMember` 任何錯誤都 `clearTokens()`。gateway 短暫 502 會踢登出。 建議:僅 401 / refresh 失敗清 token;5xx 顯示「後端暫時無法連線」。 ### 2. 巨型頁面 | 檔案 | 約略行數 | 建議 | |------|----------|------| | `PatrolPage.tsx` | ~1900 | 拆 list / graph / scan / outreach 子元件 + hooks | | `MissionsPage.tsx` | ~1200 | 若走 Post Project 直接換 wizard,勿再膨脹 | | `OwnPostsHub.tsx` | ~750 | 拆 formula / reply / list | 好處:可測、可讀、島民/自動化也較好掛 `data-islander-*`。 ### 3. Hash 路由的限制 - 無巢狀 path、難做 deep link(例如 `#patrol/topic/xyz`) - 中長期可考慮 React Router,或至少支援 `page + query` 狀態 ### 4. 過時 UI / 死 client | 項目 | 建議 | |------|------| | `GapsPage` | 對齊 `threads-automation-gap-plan` 真實進度,或改「已完成 checklist」 | | `haixun.ts` 未呼叫方法 | 定期刪(inventory API client 等) | | `extensionSync` 固定 reject | 文件與 UI 標「已停用」;重啟時勿再 postMessage JWT | | jobLabels 幽靈 type | 隨 ghost template 刪 | ### 5. 設計系統(已做得不錯) - 繼續只用 `--hx-*` / semantic Tailwind;勿引入 MUI/Ant - 共用 `ui.tsx`(Button 必須有 children) - 大頁面新功能優先組 Card / Field / Badge,不要另寫色票 ### 6. API 層 `client.ts` 的 refresh single-flight、502 retry 是好的。可再: - 對 `ApiError` 做 `isUnauthorized` / `isUnavailable` helper,頁面統一處理 - 長輪詢(jobs 3s)考慮 visibility API:分頁隱藏時降頻 ### 7. 測試 目前幾乎無前端 test。最低投資: - `client.ts` refresh + 502 retry - `AuthContext` 5xx 不登出 - 純函式:`jobStatus`、`format` nano 轉換 ### 8. AGENTS 寫了但未存在的功能 - **Islander agent runtime**(`web/src/lib/islander`)文件超前,程式沒有 → 實作或從 AGENTS 刪除描述,避免後續 agent 按空文件改碼 --- ## 產品債(架構外但影響體感) 1. **Flow A 過渡態**:Missions 用 inspire + content_ops + drafts,舊 copy_mission CRUD 已移除/不可用;應盡快 Post Project 或明確標「精簡模式」。 2. **Extension 同步關閉**:海巡 dev crawl 若依賴 browser session,需新安全協議(short-lived code,勿 JWT postMessage)。 3. **Publish inventory refill job** 無 handler:policy API 可留,refill job 勿再 enqueue(應已清)。 --- ## 建議迭代順序(若只做改進、不做大功能) ```text Sprint A 正確性 rewrite dedupe、CreateRun guarded cancel、ClaimNext、Heartbeat、AuthContext 5xx Sprint B 可理解性 拆 PatrolPage / Missions 邊界、GapsPage 對齊、AGENTS 修路徑與 auth matrix Sprint C 可觀測 worker 錯誤上報、job stranded reaper、prod secrets validate Sprint D 產品 Post Project(取代 Flow A)或凍結 Missions 只修 bug ``` --- ## 自我檢查清單(PR 前) 後端: - [ ] 列表是否 `page/pageSize` + pagination - [ ] Job 狀態是否 guarded - [ ] 長任務是否處理 Heartbeat 錯誤 - [ ] 錯誤是否 `errs.For(code.X)` - [ ] 時間是否 unix nano UTC 前端: - [ ] 是否只用 semantic token - [ ] Button 是否有中文動詞 children - [ ] 是否經 `api/client` envelope - [ ] 錯誤是否區分 401 vs 5xx - [ ] `make web-build` 通過