# Plan: 商機工作收件匣與產品需求搜尋

> Status: `approved`
> Status note: 使用者 2026-08-11「請幫我做」；視為批准 plan 並要求進入實作拆解
> Source: `docs/product/opportunity-inbox/spec.md`（approved 2026-08-11）
> Requirements: `docs/product/opportunity-inbox/requirements.md`（approved 2026-08-11）
> Baseline: `docs/product/brand-product-radar/` T700–T762 done
> Last updated: `2026-08-11`

## 1. 交付策略

1. **保留已完成的產品雷達底座。** Opportunity、ProductMatch、主推產品、owner＋來源去重、每日巡邏、mock／live repository 均原地擴充，不另建一套商機或抓取系統。
2. **先簡化操作，再擴大後端。** Phase A 只固定新增資料語意、跨層契約與舊資料映射；Phase B 先用 mock 驗證單一收件匣、完成／移除／復原、時間排序、DemandMap 與成本確認。使用者未確認這套操作前，不進入 Phase C 大量業務後端。
3. **先把免費流程做實，再接付費判定。** ReviewState、tombstone、DemandMap baseline、前處理與輕量排序先獨立可測；搜尋與 AI 深判最後才接入 CostPreview、CreditCeiling、有效判定快取及既有 usage meter。
4. **分數不互相偷換。** 既有 `intent_score` 與 `product_fit_score` 保留，新 `priority_score` 專門服務推薦排序；所有分頁排序由後端穩定執行，前端不只重排當頁。
5. **用漏斗與點數對帳驗收。** 每次巡邏必須能對上 hit → dedupe → prefilter → cache → judge → created／merged／tombstone／deferred，以及 search／judge 實際點數；部分完成不能顯示完整成功。
6. **準確度與成本同時是上線閘門。** 前十至少八筆具有真實需求與產品適配證據，且同候選同版本重試不重扣；任一未達標，先修召回、前處理、排序或冪等，不擴 P1 自動學習。

正式 tasks 每題預估約 100–200 行 production change；測試或文件可較小，但不得把 API、repository、worker、頁面與品質閘塞成單一大題。

## 2. 里程碑

### M0 — Phase A：契約、相容與測試地基

- **目標：** 固定 ReviewState、DemandMap、CostPreview、PriorityScore、Sweep funnel 的跨層 shape 與舊資料讀法；尚未完成的能力明確回 not-ready，不回成功空資料。
- **完成定義：**
  - 盤點並鎖定現有 OpportunityStatus、accept／dismiss、today／opportunities、ProductMatch、Sweep 與 usage 行為，建立不可退化的 baseline tests。
  - 只從 `generate/api/radar.api` 擴充 review state、需求地圖、cost preview、列表 query、priority／evidence 與 Sweep funnel 契約，再執行 `make gen-api`；不手寫 handler／routes。
  - goctl 產生的新 logic 在業務尚未完成前回明確 not-ready；不得回 `102000` 空物件或空陣列。
  - 前端 domain types 與 RadarRepo 加入同一契約；mock／live 的方法簽名一致。
  - 建立舊資料 fixture：accepted→completed、dismissed→removed(legacy_unknown)、其他→pending；舊 `sort=score|posted` 映射新排序。
  - 定義 owner＋source＋product＋input/map version＋judge kind 的判定冪等測試鍵，但此里程碑不實作深判業務。
- **建議 task 區間：** **T800–T806**
- **驗證：** spec 驗收 #1、#11、#12、#24 的契約／fixture 層；generated code 無手改；前後端 compile／build。

### M1 — Phase B：單一商機收件匣（frontend + mock）

- **目標：** 先讓第一次使用者只在一個頁面完成「看證據 → 完成或移除 → 查看／復原」；不接真 API。
- **完成定義：**
  - `/app/radar/opportunities` 成為唯一商機入口，預設 pending＋today＋recommended；`/app/radar/today` redirect 並保留可理解的 query。
  - 導覽移除第二個商機結果入口；today／7d／all 只作為同頁時間範圍。
  - 卡片首層只顯示問題、主推產品、一段最強證據、分數／時間、原文、完成、移除；回覆、CRM、其他產品與完整理由放詳情層。
  - mock RadarRepo 支援 pending／completed／removed、移除原因、復原前狀態、legacy mapping、tombstone 再命中。
  - 支援 recommended／newest／oldest／product_fit／demand_intent 與穩定分頁；未知 posted_at 在 newest／oldest 均置底。
  - filter、sort、time scope、page 寫 URL；loading、error、各種 empty reason、操作失敗 rollback 均有明確狀態。
  - 完成、移除、復原的 mock 用量保持 0；加入 CRM 成功後自動 completed，但單純完成不建 Contact。
- **建議 task 區間：** **T810–T819**
- **驗證：** spec 驗收 #1–#5、#11、#12、#24；頁面 keyboard／mobile 走查；前端 test／build／lint／token check。

### M2 — Phase B：需求地圖、探索成本與巡邏漏斗（frontend + mock）

- **目標：** 在任何付費後端開發前，驗證使用者能看懂「系統準備找什麼、可能花幾點、結果為何被排除或延後」。
- **完成定義：**
  - `/app/radar/watches` 或其產品詳情提供 DemandMap 五類內容、來源 basis、完整度、AI／user／product origin 與 stale／待確認狀態。
  - 免費 baseline、人工新增／啟停 phrase 與版本衝突用 mock 完整走通；產品只有名稱時明確 incomplete 並阻擋新巡邏。
  - QueryPlan 摘要顯示 1–6 組使用者語言；純品牌／產品名不能單獨成查詢組。
  - AI enrichment、手動 sweep／explore 與回覆按鈕在需要的位置顯示固定點數或範圍；變動成本先開 CostPreview 確認。
  - mock CostPreview 覆蓋 platform／BYOK、過期、版本變動、餘額不足與 ceiling 小於固定成本。
  - mock Sweep 結果顯示 hit、dedupe、prefilter pass／review／reject、cache、judge、created／merged／tombstone、deferred 與 credit breakdown。
  - `complete / partial_budget / blocked_budget / failed` 的文案與下一步可辨識，不用成功空畫面代替。
- **建議 task 區間：** **T820–T829**
- **驗證：** spec 驗收 #6、#7、#13–#15、#19、#20、#22；人工走查能在執行前回答最多花幾點，完成後回答點數花在哪裡。
- **硬閘門：** M1＋M2 未取得使用者 UX 批准前，不得開始 M3–M6 的業務後端。

### M3 — Phase C：ReviewState、軟移除與穩定列表

- **目標：** 把 Phase B 已批准的收件匣工作狀態接到可信 domain、repository 與 live API，保持既有業務狀態相容。
- **完成定義：**
  - Opportunity domain 加 ReviewState、previous state、移除欄位與 review audit；OpportunityStatus 保留原語意。
  - memory／Mongo repository 實作 idempotent 狀態更新、append-only audit、duplicate target owner 驗證與 tombstone merge。
  - 缺 review 欄位的舊文件讀時映射，不要求全庫回填；首次更新才持久化。
  - accept 在 Contact binding 與 completed 之間維持一致成功邊界；舊 dismiss 保存 reason 或 legacy_unknown。
  - list 支援 review state、time scope／from-to、新五種 sort、priority band 與 server-side 穩定 pagination；未知時間置底。
  - `/today` 使用新列表語意提供相容結果；新 UI 不再依賴 today 專屬商業邏輯。
  - migration／索引只加入必要查詢與 audit 支援，保留 owner＋來源唯一性。
- **建議 task 區間：** **T830–T839**
- **驗證：** domain／memory／Mongo parity、owner isolation、race 與 API integration；spec 驗收 #1–#5、#11、#12、#24。

### M4 — Phase C：DemandMap、版本與 CostPreview

- **目標：** 以產品真相建立可追溯需求地圖，並在 provider 執行前完成成本預估、版本確認與 ceiling gate。
- **完成定義：**
  - DemandMap domain 定義五類 phrase、basis、origin、enabled、input／map version、ready／incomplete／stale 不變量。
  - baseline 只讀 Brand／Product 受眾、痛點、情境、能力、匹配與排除內容，免費建立；不得因開頁或排程自動呼叫 AI。
  - input version 只對實質搜尋內容變更；名稱／圖片等顯示修改不改版。產品內容變更後，AI phrases 失效、user phrases 標待確認。
  - memory／Mongo repository 支援 current map、舊版本解釋、optimistic version conflict 與 owner 隔離。
  - QueryPlan 產生 1–6 組合法短詞與排除訊號，保存使用版本與摘要。
  - CostPreview 對 enrichment、manual sweep／explore、批次判定與回覆回固定／範圍、key mode、餘額、期限及依據；取得 preview 不扣點。
  - preview 使用時驗證 owner、action、context version、expiry、remaining credits 與 ceiling；不合格時 provider 不被呼叫。
  - AI enrichment 走既有 meter 與新 `radar.demand_map` source，結構／basis 驗證成功才切 current map 並 commit；失敗保留舊 map 且不重複扣。
- **建議 task 區間：** **T840–T849**
- **驗證：** spec 驗收 #6、#7、#13、#17、#20–#22；usage event 可與 action 對帳；BYOK credits=0。

### M5 — Phase C：候選前處理、推薦排序與判定快取

- **目標：** 把昂貴 AI 判定縮到真正高價值或不確定候選，並讓推薦排序比產品名／關鍵字命中更貼近產品痛點。
- **完成定義：**
  - 建立可純測試的 candidate preprocessing：來源正規化／去重、基本有效性、產品排除、provider／廣告／公告／雜訊、DemandEvidence 抽取、產品證據與 pass／review／reject。
  - 每個 reject／review 保存 machine reason 與 Sweep counter；reject 不進 AI，不建立高／中／低商機。
  - 輕量 ranking 在 provider 呼叫前排序；review 只可低順位或在 ceiling 內抽樣深判，不能直接進 high。
  - AI judge 輸出可驗證原文 excerpt、DemandMap／產品 basis、風險與 priority factors；缺欄位、找不到引文或 basis 不存在即失敗。
  - `priority_score=45 pain fit + 25 demand intent + 15 evidence quality + 15 freshness`，並落實 high hard gates。
  - JudgmentResult 以 owner＋source＋product＋input/map version＋kind 唯一；有效 accepted／rejected 都可復用，失敗不進快取。
  - 逐筆 usage commit 與結果保存具冪等關聯；同候選跨 watch／query、HTTP 重試或 worker 重跑不重扣。
  - 建立口語改寫、純名稱、同業銷售、公告、泛討論、未知時間、重複來源與真正求助的固定 fixture。
- **建議 task 區間：** **T850–T859**
- **驗證：** table／fixture／AI structured output／concurrency tests；spec 驗收 #8–#12、#15–#18、#21、#23。

### M6 — Phase C：巡邏整合、續跑、live UI 與完整點數對帳

- **目標：** 把 DemandMap、前處理、快取、深判與新收件匣接回每日／手動巡邏，完成真實可操作的 P0。
- **完成定義：**
  - `radar_sweep` 保存 system preview、DemandMap／QueryPlan 版本，按搜尋→前處理→cache→judge→persist 執行。
  - API search 成功 0 hit 仍按既有規則計 search；前處理 reject 不計 AI；有效 rejected judge 正常計 AI；無效 AI 結果走失敗退點語意。
  - 每完成一筆保存 guarded checkpoint；中斷後只續未完成唯一鍵。Redis lock value 保持 workerID，晚到 worker 不覆寫終態。
  - CreditCeiling／方案額度用完後停止新 provider 呼叫，保留已完成結果並寫 deferred；watch 不刪除、不封存。
  - removed tombstone 再命中只合併來源，Sweep 增加 tombstone count；不新建、不恢復、不重判。
  - Sweep API 與 UI 可對帳所有 funnel counts、status、failed／deferred reason 與 search／demand map／judge／reply credits。
  - Phase B 頁面切 live repository，mock／live contract tests 使用同一組 fixtures；loading、not-ready、conflict、quota、partial、empty reason 行為一致。
  - 完成跨層流程：產品資料 → DemandMap → preview／ceiling → sweep → prefilter／judge → 收件匣排序 → 完成／移除／復原 → usage 對帳。
- **建議 task 區間：** **T860–T869**
- **驗證：** spec 驗收 #1–#22、#24 的 live path；integration、race、restart tests；前後端全套 build／test。

### M7 — Phase D：品質閘、成本回歸與安全上線

- **目標：** 以代表性資料證明搜尋更貼近產品且成本可控，再收斂相容路徑、文件與既有回歸。
- **完成定義：**
  - 建立人工標註集與可重跑 evaluation：有效需求、口語同義、產品不適合、provider、廣告、公告、泛討論、stale、unknown time、duplicate。
  - recommended 前十至少八筆同時具備 DemandEvidence 與產品證據；provider／公告／純名稱命中不得為 high。
  - 比較 baseline 關鍵字排序與新 pipeline 的 precision@10、AI judged ratio、每筆有效商機平均 credits；未達品質或成本閘不宣告完成。
  - 多 worker、provider retry、HTTP retry、跨 watch／query、產品僅改名與產品實質改版的防重扣回歸全部通過。
  - 回歸 generic radar、ProductMatch、主推產品、回覆、CRM、Scout、Outbox、usage soft caps、BYOK、通知與舊 Opportunity。
  - `/today` redirect 與舊 API 相容性保留一個版本週期；文件記錄移除條件，不在本周期直接刪 API。
  - 更新 testing／gaps／操作說明，明列 P1：批次操作、每產品 ceiling、移除原因調校、產品成本報表。
  - 與 `brand-product-radar` T773–T775 共用 accuracy／race／regression 驗收資產；tasks 階段交叉引用一次實作，不複製兩套 fixture 與指令。
- **建議 task 區間：** **T870–T879**
- **驗證：** spec 驗收 #16–#23、requirements 成功標準全表；全 repo test／build／lint／token check 綠。

## 3. 依賴與硬閘門

```text
brand-product-radar T700–T762（已完成底座）
  └─► M0 Phase A 契約／相容
       └─► M1 Phase B 收件匣 mock
            └─► M2 Phase B DemandMap／成本 mock ──【使用者 UX 批准】
                 ├─► M3 Phase C ReviewState／列表
                 └─► M4 Phase C DemandMap／CostPreview
                       M3 ─┐
                       M4 ─┴─► M5 Phase C 前處理／排名／快取
                                  └─► M6 Phase C sweep＋live ──【P0 live 驗收】
                                       └─► M7 Phase D 品質／成本／回歸
```

| 關係 | 原因 |
|------|------|
| baseline → M0 | 新能力沿用現有 ProductMatch、主推產品、去重與 usage，不重做底座 |
| M0 → M1／M2 | mock 與 UI 必須使用批准後的共用 shape、legacy mapping 與錯誤語意 |
| M1＋M2 → M3–M6 | **硬性：** 前端＋mock 操作未批准，不大寫業務後端 |
| M3 ↔ M4 | 可在 UX gate 後分開進行；兩者只透過已凍結契約相遇，不互相直接依賴 repository |
| M3＋M4 → M5 | 前處理結果需保存到商機／判定快取，且需要 DemandMap 版本與 basis |
| M5 → M6 | worker 不可自己重寫另一套前處理、排序或計費判斷 |
| M6 → M7 | accuracy 與成本只能用真 pipeline、真 funnel 語意驗證 |
| T773–T775 ↔ M7 | 舊待辦與本計畫最後閘門範圍重疊；正式 tasks 只建立一次共用輸出並同步兩邊狀態 |

## 4. 風險與緩解

| 風險 | 緩解 |
|------|------|
| ReviewState 與既有 accepted／dismissed 混義 | M0 鎖 legacy mapping；M1 先驗證用語；M3 保持兩個狀態機獨立，accept 只做明確同步 |
| 合併頁面反而塞更多資訊 | M1 首層固定七項資訊，其餘進詳情；以第一次使用者走查作 Phase B 硬閘 |
| removed 後相同貼文再次出現 | M3 tombstone 沿用 owner＋來源唯一鍵；M6 sweep 專測再命中只 merge |
| 移除一次就污染後續搜尋 | P0 只保存原因，不立即 AI 調校；P1 批次建議仍需人工確認 |
| DemandMap 成為第二份產品真相 | phrase 必須有 basis；baseline 由 Product 建立，AI／user 只作可辨識擴充；產品實質變更會改版 |
| 產品改名造成重判重扣 | DemandInputVersion 排除顯示欄位；M4／M7 專測名稱修改不改版 |
| 廣召回造成 AI 成本暴增 | M5 先免費前處理與 cache，再在 ceiling 內 shortlist；M7 比較 judged ratio 與每筆有效商機成本 |
| 前處理過嚴造成完全找不到 | reject 與 review 分開；review 可低順位保留或抽樣深判；Sweep 保存每階段原因供抽查 |
| CostPreview 顯示假精準 | 固定成本顯示確值，候選未知顯示 min/max 與依據；版本或餘額變動即失效重估 |
| provider 成功但後續中斷造成雙扣 | M5 有效判定唯一鍵＋usage 關聯；M6 逐筆 checkpoint，重跑只續未完成鍵 |
| ceiling 競態被多 watch 突破 | owner 當日額度在 provider 前 guarded 檢查；M6／M7 多 worker race 測試 |
| server 與前端排序不一致 | M0 共用 enum；M3 server-side stable sort；mock／live contract 使用相同 fixture |
| goctl 產生空 logic 被當成功 | M0 每個新 logic 先明確 not-ready；只改 `.api` 後 gen，業務只在 logic／module |
| 舊工作樹已有相關修改 | tasks 實作前逐檔比對現況，保留使用者變更；禁止用 reset／checkout 覆蓋，重疊無法安全拆分時停下回報 |

## 5. 刻意不做（本 plan 週期內）

- 不實作 P1 的批次完成／移除、每產品 CreditCeiling、移除原因自動調校與產品成本儀表板。
- 不因每次完成或移除即時呼叫 AI，也不自動修改 Product、Brand 或 DemandMap。
- 不提供 Opportunity 不可逆硬刪除，不封鎖作者，不刪除來源內容。
- 不新增 usage meter、不修改 Free／Starter／Pro 售價，不重做 Stripe／BYOK／provider 抽象。
- 不重做 Threads API／crawler、排程器、CRM、Outbox、通知或成果歸因。
- 不用 priority score 取代既有 intent／product fit 歷史欄位。
- 不保證公開發文者有預算、身分真實或一定購買；只判斷公開需求與產品證據。
- 不在本週期移除舊 `/api/v1/radar/today` 或 dismiss API；只提供相容映射與 UI redirect。

## 6. 驗證策略與指令

### 6.1 前端里程碑

```bash
cd apps/web
npm run test
npm run build
npm run lint
npm run check:tokens
```

### 6.2 API 契約或 generated code 變更

```bash
cd apps/backend
make gen-api
go test ./internal/logic/radar/... ./internal/module/radar/... -count=1
make build
```

### 6.3 Phase C 整合與競態

```bash
cd apps/backend
make test-integration
go test ./internal/module/radar/... -race -count=1
go test ./... -count=1
```

### 6.4 Migration 驗證

只使用明確的獨立測試資料庫，不對預設開發資料庫執行：

```bash
cd apps/backend
RADAR_INBOX_TEST_MONGO_URL=mongodb://127.0.0.1:27017/thread_master_opportunity_inbox_test
MONGO_URL="$RADAR_INBOX_TEST_MONGO_URL" make migrate-up
MONGO_URL="$RADAR_INBOX_TEST_MONGO_URL" make init
```

### 6.5 品質與成本閘

正式 task 需提供固定 evaluation 指令，至少輸出：

```text
precision_at_10
high_without_required_evidence
provider_or_announcement_in_high
ai_judged_ratio
credits_per_valid_opportunity
duplicate_judgment_or_charge_count
```

通過條件：`precision_at_10 >= 0.8`、兩種 high 違規數皆為 0、重複判定／扣點數為 0；AI judged ratio 與每筆有效商機 credits 先記 baseline，再由實際成本資料決定 P1 預算優化目標，不在 P0 捏造門檻。

### 6.6 里程碑驗收摘要

| M | 額外驗收 |
|---|----------|
| M0 | 舊 fixture 可讀；新 logic 未完成不回成功空資料；goctl 產出乾淨 |
| M1 | 一個頁面完成處理流程；today redirect；五種排序與 URL 狀態正確；操作不扣點 |
| M2 | DemandMap 與 QueryPlan 看得懂；執行前知道成本範圍；partial／blocked 不裝完整成功；取得 UX 批准 |
| M3 | review／business state 不混；tombstone 不重建；memory／Mongo／API 行為一致 |
| M4 | input／map version 正確；preview 免費且會過期；provider 前 ceiling gate；enrichment 失敗不覆蓋 |
| M5 | provider／公告／純名稱不進 high；reject 不問 AI；有效 accepted／rejected 都能防重復用 |
| M6 | 中斷可續、budget partial 可對帳；mock／live 行為一致；完整 P0 流程可展示 |
| M7 | precision@10 過 0.8；high 違規 0；duplicate charge 0；全 repo 回歸綠 |

## 7. Task 號段總表（供下一階段拆 task）

| 區間 | Phase / Milestone | 內容 |
|------|-------------------|------|
| T800–T806 | A / M0 | baseline tests、goctl 契約、not-ready、FE types／repo、legacy／sort fixtures、冪等鍵契約 |
| T810–T819 | B / M1 | 單一入口、收件匣 layout、review mock、完成／移除／復原、時間／排序、URL／empty／mobile tests |
| T820–T829 | B / M2 | DemandMap mock、phrase editor、QueryPlan、CostPreview、付費確認、Sweep funnel／點數、UX gate tests |
| T830–T839 | C / M3 | review domain／audit、memory／Mongo、migration、tombstone、accept／dismiss 相容、列表 API／排序 |
| T840–T849 | C / M4 | DemandMap domain／repo、版本、baseline／QueryPlan、CostPreview、enrichment、meter／BYOK |
| T850–T859 | C / M5 | preprocessor、evidence、disposition、priority scorer、AI judge、JudgmentResult cache、冪等計點、fixtures |
| T860–T869 | C / M6 | sweep pipeline、checkpoint／resume、budget partial、counters、live repo／pages、跨層 P0 integration |
| T870–T879 | D / M7 | 標註集、precision／成本 evaluation、race／retry、相容與全回歸、文件／rollout |

正式拆 tasks 時，每題必須包含 Goal、Depends on、Inputs、Outputs（實際路徑＋行為）、Out of scope、Acceptance commands、對應 spec 驗收編號。M1／M2 tasks 只能做 frontend＋mock；M3 之後才可寫業務後端。與 `brand-product-radar` T773–T775 重疊的測試輸出只能建立一份並由兩邊索引交叉引用。

## 8. 批准

- [x] Plan 已批准（2026-08-11／使用者）
- 批准後下一步：建立 `tasks/INDEX.md` 與各 T8xx task 文件；不在同一輪開始實作。
