# Post Project 計劃（文案任務 Flow A 砍掉重做）

> 取代 `backend/docs/threads-automation-gap-plan.md` 中文案自動營運閉環的舊版 Flow A。Flow B（海巡獲客：brand / placement_topic / outreach_drafts / PatrolPage）不在本計劃範圍。

## 1. 動機

舊版文案任務（Flow A）流程過重：

- 一個任務跑 5 個 worker（analyze → expand-graph → scan-viral → matrix → copy-draft），加上 refill-publish-inventory 自動補庫存旁路。
- 中間產 research map、knowledge graph、scan_posts、相似帳號 / 受眾樣本等中介資料，前端被 pipeline 狀態綁住。
- 使用者真正要的是：

  > 單純 po 文、相似貼文模仿、發想主題、AI 找網路資料、矩陣產 N 篇、定時發送、可選人設、可貼參考貼文讓 AI 學風格。

本計劃把 Flow A 整包砍掉重做，換成線性可操作的「發文計畫（Post Project）」。

## 2. 範圍

### 砍

- Persona 文案流（copy_mission / copy_draft 全部）
- refill-publish-inventory worker 與其 publish_inventory policy 接面
- 5 個文案 worker（analyze-copy-mission / expand-copy-mission-graph / generate-copy-matrix / generate-copy-draft / refill-publish-inventory）
- `library/copymission/`、`library/matrix/copy_generate.go`、`library/viral/mission_*.go`、`library/viral/research_map.go`
- prompt files `matrix_copy.*`
- 前端 MissionsPage + `pages/missions/`
- `copy_missions` / `copy_drafts` 兩個 MongoDB collection（drop）

### 不動

- Flow B（brand / placement_topic outreach_drafts、PatrolPage 海巡獲客）
- `outreach_drafts` model / library/outreach / `generate-outreach-draft` worker
- `knowledge_graph` model（brand / placement_topic 共用）
- `scan_post` 通用 viral 採樓能力（移除 `CopyMissionID` 欄位即可）
- `publish_queue` / `publish_analytics` 通用發布與量測
- scheduler / reaper / sweeper / token-sweeper / refresh-threads-token 通用 worker
- Persona model / API（沿用）

### 新增

- 兩個 collection：`post_projects` / `post_drafts`
- 一個 worker：`go-generate-post-matrix`
- 三個同步 LLM endpoint：research-topics / analyze-style / generate-matrix
- 一條線性的前端 wizard 頁面
- 三份 prompt files（post_matrix_generate / analyze_style / research_topics）

## 3. 資料模型

### post_projects

| 欄位 | 型態 | 說明 |
|------|------|------|
| `_id` | ObjectID | |
| `tenant_id` | string | |
| `owner_uid` | string | |
| `persona_id` | ObjectID | 關聯 personas |
| `label` | string | 使用者自訂專案名 |
| `topic` | string | 使用者選定 / 編輯後的主題描述 |
| `web_research_summary` | string | research-topics 回傳的摘要 |
| `web_research_refs` | []object | `[{url, title, snippet}]` |
| `style_profile` | object | analyze-style 回的結構化風格 |
| `style_summary` | string | 給前端顯示的風格短述 |
| `style_references` | []string | 使用者貼進來的參考貼文原文 |
| `status` | string | `topic_set` / `style_set` / `matrix_generating` / `drafts_ready` / `archived` |
| `matrix_draft_count` | int | 矩陣產生的草稿數 |
| `created_at` / `updated_at` | int64 | unix nanoseconds（UTC+0） |

`style_profile` 建議結構：

```json
{
  "tone": "親切、口語、像跟朋友聊天",
  "length": "短至中（80-180 字）",
  "structure": ["hook", "故事", "takeaway", "互動問句"],
  "emoji_usage": "少量，平均 1-2 顆，放結尾",
  "hook_style": "提問或反差句",
  "avoid": ["過度口號", "廉價 emoji 疊字"]
}
```

### post_drafts

| 欄位 | 型態 | 說明 |
|------|------|------|
| `_id` | ObjectID | |
| `tenant_id` | string | |
| `project_id` | ObjectID | 關聯 post_project |
| `persona_id` | ObjectID | |
| `topic` | string | snapshot 主題（生成時） |
| `angle` | string | AI 給該篇的角度標籤 |
| `hook` | string | AI 給的 hook |
| `text` | string | 草稿本文 |
| `rationale` | string | AI 說為何這樣寫 |
| `sources` | []object | `[{url, title}]` 引用網路資料 |
| `status` | string | `draft` / `scheduled` / `published` |
| `publish_queue_id` | ObjectID? | 排入佇列後回填 |
| `scheduled_at` | int64? | 排程時間 |
| `published_post_id` | string? | 發布後 Threads post id |
| `created_at` / `updated_at` | int64 | unix nanoseconds |

### publish_queue.queue_item 改欄位

- `copy_mission_id` / `copy_draft_id` → `post_project_id` / `post_draft_id`（可空，舊資料不再解析）
- 既有 outreach 排程不受影響（Flow B 不靠這兩欄）

### publish_inventory policy

- 移除 `copy_mission_id` 欄位（refill worker 已砍）
- publish_inventory 對 outreach 是否仍有用需確認；若只服務文案 refill 則整個 model 一併砍。階段 1 解耦時若發現 inventory 僅供 refill，連 collection 一起 drop。

## 4. API

新 `generate/api/post_project.api`，import 至 `gateway.api`。

```
Prefix: /api/v1/personas (scope) 與 /api/v1/post-projects (資源)

POST   /api/v1/personas/:personaId/post-projects
GET    /api/v1/personas/:personaId/post-projects
GET    /api/v1/post-projects/:projectId
PATCH  /api/v1/post-projects/:projectId          (label / topic / status)
DELETE /api/v1/post-projects/:projectId

POST   /api/v1/post-projects/:projectId/research-topics    sync:  { seed } -> { suggestions[], refs[] }
POST   /api/v1/post-projects/:projectId/analyze-style      sync:  { references:[{text}] } -> { style_profile, style_summary }
POST   /api/v1/post-projects/:projectId/generate-matrix    async job: enqueue generate-post-matrix
POST   /api/v1/post-projects/:projectId/drafts/:draftId/regenerate  async job: 重生單篇

GET    /api/v1/post-projects/:projectId/drafts
PATCH  /api/v1/post-projects/:projectId/drafts/:draftId   (text / angle / sources)
DELETE /api/v1/post-projects/:projectId/drafts/:draftId

POST   /api/v1/post-projects/:projectId/drafts/schedule   body: [{draft_id, send_at}] -> publish_queue.Create
```

Envelope：`code/message/data/error`；列表用 `page/pageSize` + `pagination/list`。

## 5. Worker

`internal/worker/job/generate_post_matrix.go`

- `workerType = "go-generate-post-matrix"`
- `step = "post_matrix_generate"`
- 讀 `project.topic` + `project.style_profile` + `persona` + `project.web_research_refs`
- 組 prompt → LLM 一次吐 N 篇（N=5 預設）
- 寫 N 個 `post_drafts` doc
- 更新 `project.status = drafts_ready`、`matrix_draft_count = N`
- 過程中 `RefreshRunLock` + `Heartbeat`，不做 cancel 自行設狀態

regenerate 走同一個 worker 同一 template，payload 帶 `target_draft_id`：
- 有 → 刪舊 draft / 重生一篇覆蓋
- 無 → 全新矩陣

## 6. Library

新 `internal/library/post_matrix/`（取代 `library/copymission/`）：

- `analyze_style.go`：貼文 → LLM → 吐 `style_profile` JSON + `style_summary`
- `research_topics.go`：seed → 內部 websearch（`node-tool-keyword-search` 或 `library/viral/crawler_search`） → LLM 摘要 + 主題候選
- `generate.go`：組 prompt → LLM → N 篇 post_drafts

prompt files（`internal/library/prompt/files/`）：

- `post_matrix_generate.system.md` / `.user.md`
- `analyze_style.system.md` / `.user.md`
- `research_topics.system.md` / `.user.md`

## 7. 前端

### 7.1 新頁面 `web/src/pages/PostProjectsPage.tsx`

取代 MissionsPage，5-step wizard（沿用 `PageTitle` / `Card` / `Field` / `Input` / `Textarea` / `Button` / `Badge`）：

1. **人設**：下拉選 Persona
2. **主題**：
   - Textarea 輸入 seed 或想法
   - 按鈕「給我主題靈感」→ `research-topics`（同步） → 顯示 3-5 張主題卡（含摘要 + 來源）
   - 點卡片自動填 Textarea，或手動編輯
   - 「再查一次」換一批
3. **風格**（可選）：
   - Textarea 貼 1~N 篇參考貼文（空行分隔）
   - 按鈕「分析風格」→ `analyze-style`（同步） → 顯示 `style_profile` 條列
   - 可重貼、重新分析
4. **矩陣**：
   - 按鈕「產生 5 篇草稿」→ `generate-matrix` job → polling 進度
   - 顯示 N 張草稿卡（Hook / Text / Angle / Sources）
   - 每張可：編輯 / 刪除 / 重生單篇
5. **排程**：
   - 勾選草稿 → 展開 datetime-local 輸入
   - 「排入佇列」→ `drafts/schedule`
   - 顯示排程清單（沿用 publish_queue 顯示元件）

### 7.2 路由 / 導覽

- `App.tsx`：`case "posts": return <PostProjectsPage />`，加進 `validPages` / `accountRequiredPages`
- `acAssets.ts` `navApps`：把原 `missions` 換成 `posts`（label「發文計畫」，sublabel `POST`）
- `mobileNavKeys` 同步替換
- `AcIcon`：`mission` 換 `posts` SVG（鉛筆 + 對話框 stack 線條稿）
- `validPages` / `accountRequiredPages` 移除 `"missions"`

### 7.3 API client

`api/haixun.ts`：

- 刪 types `CopyMission`、`ContentInboxItem`、`CopyMissionID` 等相關
- 刪 endpoints `copyMissions` / `createCopyMission` / `inspireCopyMission` / `startCopyMissionAnalyze` / `startCopyMissionMatrix` / `scheduleMissionDrafts` / persona copy-drafts 系列
- 新 types `PostProject` / `PostDraft` / `StyleProfile`
- 新 endpoints `createPostProject` / `listPostProjects` / `researchTopics` / `analyzeStyle` / `generateMatrix` / `regenerateDraft` / `listPostDrafts` / `updatePostDraft` / `deletePostDraft` / `schedulePostDrafts`
- 保留 outreach_drafts endpoints（Flow B）

## 8. Infra / deploy

`deploy/docker-compose.yml`：

- 刪 `worker-copy-mission-analyze` / `worker-copy-mission-expand-graph` / `worker-copy-matrix` / `worker-copy-draft` / `worker-refill-publish-inventory` 五個 service
- 新 `worker-post-matrix`：`HAIXUN_JOB_WORKER_TYPE=go-generate-post-matrix` / `HAIXUN_JOB_WORKER_ID=go-generate-post-matrix`
- 保留：`worker-scan-viral`、`worker-outreach-draft`、其餘通用 worker

## 9. Migration

`internal/bootstrap/init.go` 加 idempotent migration block：

- `db.RunCommand({dropIndexes: "publish_queue"})` 必要時重 EnsureIndexes
- 寫一個 `tool` subcommand（沿用 `backend/cmd/tool`）或 init runtime 做一次性：
  - drop `copy_missions` collection（忽略 not exist）
  - drop `copy_drafts` collection（忽略 not exist）
  - rename `publish_queue.copy_mission_id` → `post_project_id`、`copy_draft_id` → `post_draft_id`（對存量資料，若有人 queue 還沒發）
  - 移除 `publish_inventory.policy.copy_mission_id` 欄位（或整 drop `publish_inventories` collection，階段 1 確認後決定）
- 新 EnsureIndexes `post_projects`（tenant+owner+status）、`post_drafts`（project_id+status）

執行：首次 `make prod-install` 跑 init，後續 `make prod-rebuild` 即可。

## 10. 執行階段

| 階段 | 動作 | 驗證 |
|------|------|------|
| **1. 後端解耦** | scan_viral 拔 CopyMission/CopyDraft 依賴；刪 refill_publish_inventory.go；service_context 對應移除；publish_queue / publish_inventory entity 改欄位 | `go test ./...` Flow B 仍綠 |
| **2. 刪除 Flow A 後端** | api / handler / logic / model / worker template / library / prompt 全砍；`make gen-api` 重產 | `go test ./...` 通過 |
| **3. 前端清 Flow A** | MissionsPage / missions 目錄 / App.tsx / Layout / acAssets / haixun.ts 清 off | `make web-build` 通過 |
| **4. Infra 清理** | docker-compose.yml 刪 5 個 worker service | `docker compose config` 過 |
| **5. 新後端實作** | post_project / post_draft model + usecase + repository；post_project.api + generate_post_matrix.go worker + library/post_matrix + prompt files；job template 註冊 | `go test ./...` 通過 |
| **6. 新前端** | PostProjectsPage + 5-step wizard；AcIcon 新 icon | `make web-build` 通過 |
| **7. DB migration** | bootstrap init drop / rename / EnsureIndexes | `docker run init` 過 |
| **8. 煙霧測試** | 開 project → 靈感 → 風格 → 矩陣 → 排程 → sweeper 發；Flow B outreach 仍可；JobsPage 顯示新 type | 部署在 dev 環境實測 |

每階段結束跑 `go mod tidy && make fmt && go test ./... && make web-build`。

## 11. 風險 / 開放問題

1. **websearch 串接**：`library/viral/crawler_search.go` 與 `node-tool-keyword-search` 服務是否適合 research-topics 用？實作時驗證呼叫 path；不順則直接打內部 node-tool-keyword-search HTTP。
2. **publish_queue rename 對存量資料**：先做 null-tolerant（舊資料視為已發 / 忽略），降低遷移負擔；正式上線前可一次性 rename script。
3. **publish_inventory 是否完全沒用**：階段 1 確認；若 Flow B 不依賴，整包砍。
4. **style_profile 形態**：採 JSON 結構化（語氣 / 長度 / 結構 / emoji / hook_style / avoid），方便前端顯示；prompt 需校調測試。
5. **矩陣批次數**：MVP 固定 N=5；後續可加 `count` query 參數，受 persona 配額上限保護。
6. **regenerate 是否保留 sources**：MVP 整篇重來，不保留；後續可加「保留 sources」選項。

## 12. 不在本計劃範圍

- Flow B 重做（若日後需要另開計劃）
- Persona model 改造（沿用）
- publish_queue_sweeper 行為修改（沿用）
- A/B 測試、自動成效優化（屬另一個閉環計劃）