# Scout topic quality verification

> Verified: 2026-08-10 · UTC

本矩陣將 Spec §9 的 17 個 Given／When／Then 場景對到可重跑的測試。固定 fixture 不呼叫真實搜尋 provider；時間欄位以 unix nanoseconds 測試，UI 只格式化伺服器排序後的結果。

| # | 場景摘要 | 證據 | 結果 |
|---:|---|---|---|
| 1 | raw candidates 未達 eligible target 時繼續補量 | `TestSearchPipelineContinuesWhenRawHitsExceedEligibleHits` | PASS |
| 2 | shortfall 統計與原因，不以離題補數 | `TestSearchQualityShortfallIsExplainableAfterAllStages`, `TestRunPublishVisibilityBarrierAndCounters` | PASS |
| 3 | 多概念話題必須同時命中 | `TestTopicSignatureRequiresEveryConceptGroup`, `TestCandidateEvaluatorSeparatesIrrelevanceReasons` | PASS |
| 4 | 泛用推薦錨點不能繞過作品核心 | `TestTopicSignatureExcludesAnchorsAndKeepsNamedEntity`, `TestSearchQualityFixturesUseOneHardRelevanceGate` | PASS |
| 5 | Threads shortcode alias canonical dedupe | `TestCanonicalPostIdentityDedupesThreadsURLAliases`, `TestSearchHitsOnlyFansOutPerTermAndDedupes` | PASS |
| 6 | 歷史貼文不搬 run、不覆寫狀態 | `TestCandidateEvaluatorSkipsOwnerHistoricalCanonicalIdentity`, `TestSearchQualityHistoricalDedupKeepsOldRunUntouched` | PASS |
| 7 | score 主排序、同分以 posted_at 穩定決勝 | `TestPersistActivityHitsPrioritizesScore`, `TestSearchQualityResultsUseScoreThenPostedTimeAcrossPages`, `createMockScoutRepo` | PASS |
| 8 | 同分 unknown posted_at 置底並明示未知 | `TestSearchQualityResultsUseScoreThenPostedTimeAcrossPages`, `ScoutPage baseline harness` | PASS |
| 9 | 同分以時間／ID 穩定決勝、跨頁不重複 | `TestSearchQualityResultsUseScoreThenPostedTimeAcrossPages`, `ScoutRepo mock/live contract` | PASS |
| 10 | 同 theme 每次都是新 run | `TestRunScanCreatesFreshRunForSameTheme`, `createMockScoutRepo` independent-runs test | PASS |
| 11 | total／pending 來自完整 run，不是當頁 | `TestRunPostsLogicReturnsRunAndIndependentPage`, `TestRunPublishVisibilityBarrierAndCounters` | PASS |
| 12 | 新 run 完成只提示、不切換目前閱讀位置 | `ScoutPage keeps the selected run and result page when a scan completes` | PASS |
| 13 | 切換 run 時結果回第 1 頁、run page 不被改動 | `ScoutPage` run selection path、mock run pagination tests | PASS |
| 14 | 刪除結果最後一頁後回到有效頁 | `ScoutPage backs up from an emptied result page after deleting its last post` | PASS |
| 15 | running run 刪除回 409、資料保留 | `TestRunRemoveLogicGuardsNonTerminalAndOwner`, `ScoutPage keeps the batch visible when deleting a running batch conflicts` | PASS |
| 16 | mock/live 排序、pagination、unknown time 等價 | `scoutRepo.contract.test.ts`, `m5Repos.test.ts`, `scoutRepo.test.ts` | PASS |
| 17 | 至少 30 組 precision@10 與 target gate | `TestTopicQualityGate`（31 組；平均 precision@10 1.00、target rate 1.00） | PASS |
| 18 | 低品質同 identity 不鎖死後續完整候選 | `TestCandidateEvaluatorAllowsRicherRetryAfterRejectedIdentity` | PASS |
| 19 | 單一批准詞遇 provider 第一頁重複時有限補查 | `TestSearchPipelineExpandsOneApprovedTermWhenProviderRepeatsFirstPage` | PASS |
| 20 | 多概念 query token 不被誤當 alias、anchor-only 不入選 | `TestTopicSignatureDoesNotTurnConjunctionFragmentsIntoAliases`, `TestCandidateEvaluatorRejectsAnchorOnlyActivityBrief` | PASS |
| 21 | 自然句抽出主題核、工作同義詞受限、provider 缺 snippet 使用 title、批次顯示建立時間 | `TestTopicSignatureDecomposesNaturalIntentWithoutRequiringWholeSentence`, `TestCandidateEvaluatorAcceptsNaturalActivityIntentFromApprovedVariants`, `TestCandidateEvaluatorUsesTitleWhenProviderOmitsSnippet`, `ScoutPage` run list render；web build | PASS |

## Command evidence

| Command | Result |
|---|---|
| `cd apps/backend && make gen-api` | PASS；goctl 生成檢查完成 |
| `cd apps/backend && go test ./...` | PASS |
| `cd apps/backend && make test-integration` | PASS |
| `cd apps/backend && go test ./internal/module/scout/usecase -run TopicQualityGate -count=1` | PASS；31/31 cases |
| `cd apps/web && npm test` | PASS；全套 web tests |
| `cd apps/web && npm run lint` | PASS；僅既有 Fast Refresh warnings |
| `cd apps/web && npm run build` | PASS |
| `git diff --check` | PASS |

補強後重新執行：backend `go test ./...` PASS；web `npm test`（22 files／136 tests）、`npm run lint`（僅既有 Fast Refresh warnings）、`npm run build` PASS。

Mongo-backed tests remain environment-aware and skip when no Mongo test server is configured; the memory repository and deterministic fake-provider gates above are the CI quality contract.
