# Plan: 話題海巡品質、分數排序與批次分頁 > Status: `approved` > Source: `docs/product/scout-topic-quality/spec.md` > Last updated: `2026-08-10` ## 1. 交付策略 先以自動化案例鎖住現有的話題產詞、網址正規化與其他 Scout 模式,避免新工作覆蓋目前工作樹中的有效修正。接著遵守 Phase B → Phase C 順序,先讓前端與 mock 具備獨立 run、雙層分頁及貼文時間語意,再由 goctl 產生後端契約,逐層完成 run persistence、搜尋管線與 worker 狀態。 後端不在原始候選階段截斷,而是每輪完成 canonical 去重、歷史去重與關聯性判定後才計算 target。完成結果以 run 為不可混同的邊界,由後端固定 score 排序並分頁;前端不再載入全量 posts 自行組批次。最後接上 live repository,使用同一組 mock/live 契約案例、品質題組及全專案測試完成驗收。 實作期間保留目前未提交的 Scout 改動,不回復、不覆寫與本功能無關的使用者變更。所有後端 HTTP 變更一律先改 `generate/api/*.api` 再執行 `make gen-api`;生成的 handler/routes 僅由 goctl 維護,業務實作只落在 `internal/logic/**` 與 `internal/module/**`。 ## 2. 里程碑 ### M0 — 基線與可重現品質題組 - **目標:**把目前已存在的產詞、關聯性與 shortcode 去重行為固定成回歸基線,並建立本次規格所需的代表題組與分頁 fixtures。 - **完成定義:** - 現有「鬼滅之刃」、「外包/後端工程師」與網址 alias 案例持續通過。 - 加入至少 30 組人工標註題組的資料格式與首批固定 fixtures;題組能標出必要概念群、直接相關與離題候選。 - 建立 score/同分時間排序 fixtures,涵蓋已知時間、未知時間、相同時間與相同貼文網址別名。 - 記錄開始實作前的 backend Scout 測試、web 單元測試與 build 結果;若既有失敗,與本工作造成的回歸分開列示。 - **建議 task 區間:** T001–T004 - **驗證:** ```bash cd apps/backend go test ./internal/module/scout/... -count=1 cd ../web npm test -- src/lib/threadsTerm.test.ts npm run build ``` ### M1 — Phase B:前端 repository 契約、mock 與操作行為 - **目標:**先在 mock 模式完成使用者能驗收的 run 清單、批次結果、時間顯示與獨立分頁,不依賴尚未存在的 live API。 - **完成定義:** - 前端 domain 定義 `ScoutRun`、不足原因與通用 page response;`ScoutPost` 增加 run ID。 - Scout repository 增加 `listRuns`、`listRunPosts`、`removeRun`,mock 與 live 介面形狀一致;舊 `listPosts` 暫時保留。 - mock 每次執行建立新 run;相同 theme 的兩次執行不合併、不搬動舊結果。 - Scout 頁面先由新 repository 讀取批次與單批結果,不再使用全量 `listPosts`+`runGroups`+`pageSlice` 作主路徑。 - 批次 pager 預設 10、結果 pager 預設 8;兩個頁碼獨立,切換批次只重設結果頁。 - 結果列與明細顯示貼文時間/未知,掃入時間另行標示;score 主排序,pending 不影響顯示順序。 - 新批次完成只顯示提示,不自動切換目前選取或頁碼;刪除後頁碼收斂到有效頁。 - **建議 task 區間:** T010–T018 - **驗證:** ```bash cd apps/web npm test -- src/pages/ScoutPage.test.tsx npm test -- src/data npm run lint npm run build ``` ### M2 — Phase C 契約地基:goctl API 與 domain - **目標:**依已批准 Spec 建立 run、分頁結果與 scan response 契約,並產生可編譯的 go-zero 程式骨架。 - **完成定義:** - 在 Scout API 定義 `ScoutRunPublic`、run list、run posts page、run delete、pagination 與 `run_id`。 - scan response 同時含 job 與新建 run;舊 posts/theme 端點保留相容。 - domain 加入 Run、狀態、計數、不足原因與 guarded transition 所需輸入;所有時間為 UTC unix nanoseconds。 - API 經 `make gen-api` 生成;不手寫 handler 或 routes。 - 生成後 gateway、worker 及既有 Scout logic 可編譯,新增 logic 尚未實作時不得回成功空資料。 - **建議 task 區間:** T020–T025 - **驗證:** ```bash cd apps/backend make gen-api go test ./internal/logic/scout/... ./internal/module/scout/... -count=1 go build . go build ./cmd/worker ``` ### M3 — Run persistence、穩定查詢與 legacy 相容 - **目標:**讓 memory 與 Mongo repository 都能保存獨立 run、穩定分頁結果及完整批次統計。 - **完成定義:** - repository 支援建立 run、guarded 更新狀態/統計、owner-scoped run 分頁、單 run posts 分頁及終態 run 刪除。 - run 排序固定為 `created_at DESC, id DESC`。 - run posts 依 `score DESC`;同分已知時間優先,再依 `posted_at DESC, created_at DESC, id DESC`。 - owner+canonical identity 維持唯一;新掃描遇到歷史貼文不覆寫其 run、時間或 outreach 狀態。 - 批次 total、eligible 與 pending 從完整批次維護,不從目前頁推算。 - legacy posts 以穩定 synthetic run 呈現、分頁及整組刪除;不虛構無法還原的多次執行。 - Mongo 查詢具備符合 owner、run、時間與唯一性的索引;memory 與 Mongo contract tests 使用相同案例。 - **建議 task 區間:** T030–T037 - **驗證:** ```bash cd apps/backend go test ./internal/module/scout/repository/... ./internal/module/scout/usecase/... -count=1 go test ./internal/logic/scout/... -count=1 ``` ### M4 — 合格數驅動的搜尋、關聯性與原子發佈 - **目標:**把「找得夠多」改成以合格結果計數,同時維持關聯性硬門檻與可診斷的不足原因。 - **完成定義:** - 候選管線拆成可測的 normalize → canonical dedupe → historical dedupe → relevance/classification → eligible 階段。 - 話題簽章從原始 intent 與批准搜尋詞建立;泛用錨點不算主題證據,必要實體及多概念群不能被廣義詞繞過。 - target 在每階段過濾後計算;不得在 raw hit 數達標時提前 cap。 - 依 initial fan-out、同源加量、單一批准詞的有限 intent 擴展、既有次來源補量;達標立即停止,最多檢視 320 個原始候選。 - 計算 searched、duplicate、irrelevant、eligible、shortfall 與原因;單一來源失敗可降級,全來源失敗則整批失敗。 - 所有模式結果依 score 排序;posted_at 僅作同分 tie-breaker,軟時效只降分、不淘汰。 - 結果先暫存為該 run 不可見集合,完整成功後才發佈;失敗 run 不暴露半套 results。 - **建議 task 區間:** T040–T049 - **驗證:** ```bash cd apps/backend go test ./internal/module/scout/usecase/... -count=1 go test ./internal/module/scout/... -run 'Target|Relevance|Dedupe|PostedAt|Run' -count=1 ``` ### M4 補強 — 真實稽核後的召回與去重修正 首輪品質 gate 通過後,針對「實際結果仍偏少、單一核命中不夠貼題」再做一次資料流稽核: - 候選只有通過完整關聯性與分類門檻後才佔用 local identity;低品質的先到摘要不會鎖死後續較完整摘要。 - 自然語句意圖先抽出真正主題核,不把產生器的所有變體(外包/接案/徵人等)錯誤合併成同時必要條件。 - activity 只有泛用錨點(例如「推薦」)時全部拒絕,不再把錨點當成主題證據。 - 多概念批准詞的 token 不會被誤當成另一個核心的 alias;只有單一批准詞才能明確授予短 alias。 - API provider 沒有 offset 時,單一批准詞在同源加量仍重複,會依原始 intent 追加最多四組短查詢;每筆仍經同一個 topic signature gate。 - 批次清單顯示 `created_at`,讓跨頁批次辨識不依賴狀態或結果數。 這些補強不降低關聯性門檻,只改善被錯誤去重與單一 query 第一頁限制造成的召回損失。 ### M5 — Job/worker 生命週期與進度 - **目標:**讓 run 與 `scout_scan` job 一對一、狀態 guarded 且失敗可收斂。 - **完成定義:** - POST scan 先建立 run,再排程 job;job `RefID` 與 immutable payload 都帶同一 run ID。 - 排程失敗時 run guarded 轉 failed;不得留下永遠 queued 的批次。 - worker 驗證 RefID/payload/owner 一致後才執行,依序 guarded 更新 running、succeeded/failed/cancelled。 - 進度摘要包含搜尋階段與目前 eligible/target,不記錄 token 或敏感 payload。 - job 更新沿用既有 guarded 語意,Redis lock value 維持 workerID。 - retry 或重複 claim 不會重複發佈結果、搬動舊貼文或讓終態倒退。 - **建議 task 區間:** T050–T055 - **驗證:** ```bash cd apps/backend go test ./internal/module/job/... ./internal/module/scout/... -count=1 go test ./cmd/worker -count=1 make test-integration ``` ### M6 — Live repository 串接與前後端整合 - **目標:**讓 Phase B 已驗收的操作改用真 API,mock 與 live 保持同一契約。 - **完成定義:** - live repository 正確送出 `page`/`pageSize`、run ID 與 filters,映射 `pagination`、run counters、shortfall reasons、posted_at 與 created_at。 - Scout 主頁只取得目前 run page 與目前 result page;不再因 job revision 下載全部 posts。 - 其他仍依賴舊 `listPosts` 的頁面維持相容並通過測試。 - 401/404/409/503 使用既有錯誤顯示,不將失敗轉成成功空列表。 - live contract tests 驗證 URL、query、envelope mapping 與 unix ns 欄位;前端不以 JavaScript number 精度重新排序 ns 時間。 - **建議 task 區間:** T060–T066 - **驗證:** ```bash cd apps/web npm test -- src/data/live/m5Repos.test.ts src/pages/ScoutPage.test.tsx npm run lint npm run build ``` ### M7 — 品質門檻、全域回歸與文件收尾 - **目標:**以需求成功標準證明「相關、足量、按時間、可分頁、不重複」均成立。 - **完成定義:** - 30 組人工標註題組的 precision@10 平均至少 0.8;有足量合格候選的題組至少 90% 達成 target。 - 規格中的 17 個 Given/When/Then 場景都有自動化證據,或明確對應的人工驗收記錄。 - backend 全測試、integration gate、web 全測試、lint 與 build 通過。 - 檢查 goctl 生成差異,確認沒有手寫 handler/routes,也沒有改動 `old/`。 - 更新相關產品文件中的現況描述,tasks INDEX 全部反映實際狀態;不把尚未達標項目標成 done。 - **建議 task 區間:** T070–T075 - **驗證:** ```bash cd apps/backend make test make test-integration make build cd ../web npm test npm run lint npm run build cd ../.. git diff --check git status --short ``` ## 3. 依賴 ```text M0 → M1 → M2 → M3 → M4 → M5 → M6 → M7 └──────────────→ M6 ``` - M0 先建立保護網,所有後續里程碑都依賴它。 - M1 先固定 Phase B 操作與 mock 契約;M2 的 API 形狀必須與其一致。 - M3 提供 M4 的歷史去重、暫存/發佈與分頁能力。 - M4 完成後,M5 才能正確把搜尋統計與終態寫回 run。 - M6 同時依賴 M2 的 API 契約與 M5 的完整 live 行為。 - M7 只能在所有前後端路徑接通後開始宣告成功標準。 ## 4. 風險與緩解 | 風險 | 緩解 | |------|------| | 目前工作樹已有未提交的 Scout 修正,生成或重構時可能被覆蓋 | M0 先記錄基線;每個 task 只碰輸出範圍,執行 goctl 後逐檔檢查差異,不回復使用者既有變更。 | | `theme_key` 曾同時扮演話題與批次,舊資料無法還原每次執行 | 新資料強制 run ID;舊資料只建立一個穩定 synthetic run,不虛構歷史。 | | owner+canonical identity 唯一規則可能與既有 permalink ID/upsert 行為衝突 | persistence contract tests 先覆蓋「新 run 不覆寫舊 post」;在搜尋階段查 seen identity,保存端再以唯一約束防競爭。 | | 多文件結果寫入中途失敗會暴露半套批次 | 以 run 可見狀態隔離暫存結果;列表只讀成功發佈的 membership,失敗路徑清理或保留不可見資料供重試。 | | offset pagination 遇到背景新增資料會漂移 | 完成 run 的 membership 與排序鍵固定;批次列表不自動刷新,提示使用者主動回到最新頁。 | | unix nanoseconds 超過 JavaScript safe integer,前端重排可能不穩 | score/同分時間排序在後端完成,使用唯一 ID 決勝;前端只格式化與顯示,不再按 ns 數值重排分頁結果。 | | 高關聯門檻使窄題目無法湊足 target | 三階段有限補量並呈現 shortfall;品質門檻優先,不降低核心概念要求。 | | 人工題組過度貼合既有範例 | 30 組涵蓋不同領域並分離固定回歸 fixture 與驗收 fixture,避免只對兩個已知查詢最佳化。 | | 新 run API 破壞 Today 或其他舊呼叫者 | 保留舊 posts endpoint 與 repository 方法直到相依頁面另有批准的遷移計畫;M6 明確跑全前端測試。 | | goctl 生成大量無關差異 | 固定使用專案內 goctl home 與 make target;生成後只接受契約導致的差異,業務不寫 handler/routes。 | ## 5. 刻意不做(本 plan 週期內) - 不改造 Radar 商機判定、CRM 狀態或 Radar 列表。 - 不新增第三個外部搜尋/爬取來源。 - 不降低主題關聯性門檻來湊 target,也不做無限補抓。 - 不自動發送 Threads 回覆。 - 不刪除所有歷史 Mongo 重複資料;只確保讀取與新寫入不再重複呈現。 - 不移除舊 `GET /scout/posts` 或強迫其他頁面同時遷移。 - 不重新設計 Harbor Desk 全站視覺或導覽。 - 不在本週期加入個人化學習、互動量趨勢或跨批次分析。 ## 6. 全域驗證指令 ```bash cd apps/backend make gen-api make test make test-integration make build cd ../web npm test npm run lint npm run build cd ../.. git diff --check git status --short ``` 需要 Mongo 行為的 repository contract 以可控 test database/既有測試容器執行;外部搜尋來源一律使用 fake provider,不以真實 provider token 或不穩定網路作為 CI 成功條件。 ## 7. 批准 - [x] Plan 已批准:2026-08-09/使用者