Bullinv Forge:MVP 開發規格書
本文件供工程團隊實作使用,與 MVP 完整定義 對齊。若兩者有衝突,以本文件「實作決策」章節為準並回寫產品文件。
0. 文件目的與讀者
- 讀者:後端、前端、DevOps、負責 Worker 的工程師。
- 目標:在約 4~8 週(依人力)內交付可內部試用的 MVP(見驗收清單)。
1. 技術選型(建議預設)
| 層級 |
建議 |
說明 |
| 前端 |
Next.js(App Router)+ TypeScript |
與業界一致,SSR/CSR 皆可。 |
| 後端 API |
Python 3.11+ + FastAPI(或等價框架) |
AI 生態與迭代速度友善。 |
| Worker |
Python 獨立進程(同 repo 或同套件 worker 模組) |
與 API 共用 domain 型別與 client。 |
| 資料庫 |
PostgreSQL 15+ |
單一主庫;MVP 不使用 pgvector。 |
| ORM / Migration |
SQLAlchemy 2 + Alembic(或 SQLModel) |
依團隊習慣二選一。 |
| 快取 / 佇列 |
可不使用;若需要佇列可用 DB job 表或 Redis(選配) |
MVP 優先簡單。 |
| 容器 |
Docker Compose(api + web + db + worker) |
內部部署一致。 |
Go / Node 後端:若團隊全棧熟 Go,可替換 API 層,但需自行補齊本規格中的 API 契約與遷移;預設以 Python 為準。
2. 系統架構與服務邊界
2.1 進程清單(MVP)
web:Next.js,對使用者瀏覽器。
api:REST(+ 可選 WebSocket/SSE 給 run 串流,選配)。
worker:輪詢 api、執行 CLI、回報 run 事件。
postgres:主資料庫。
flowchart LR
Browser --> web
web --> api
api --> postgres
worker --> api
worker --> LocalCLI[codex_or_claude]
api --> LinearAPI[Linear_API_optional]
2.2 責任邊界
| 服務 |
負責 |
不負責 |
web |
UI、表單驗證、呼叫 api、顯示 run 時間線 |
不直接呼叫 Linear;不持有 service token(除使用者 OAuth 若未來做) |
api |
Auth、授權、Task/Run CRUD、狀態機、Webhook、Linear adapter、發佈給 worker 的「可執行任務」 |
不在 request 內長跑 CLI |
worker |
認領/執行、spawn CLI、寫 run events、回寫狀態 |
不對外暴露 HTTP(可僅出站) |
3. 認證與授權
3.1 使用者(瀏覽器)
- MVP 建議:
JWT(access + refresh)或 session cookie,擇一。
- 所有
/api/v1/*(除 login/health)需驗證。
- 角色:
workspace_admin、workspace_member;review 可由 workspace_admin 兼任或獨立 flag(MVP 可簡化為 admin 可審核)。
3.2 Worker
- 使用 長效 API Token(hash 存 DB,明文僅建立時顯示一次)。
- Header:
Authorization: Bearer <worker_token>。
- Token 綁定:
workspace_id + 可選 agent_id 列表(或 worker 註冊時動態 claim agent)。
3.3 Linear(若整合)
- OAuth 或 API Key 僅存於 server(環境變數或加密欄位);永不下發給前端。
4. 資料模型(資料表草案)
命名採 snake_case。以下為 MVP 最小集;可增索引與 audit 欄位。
4.1 users
| 欄位 |
型別 |
說明 |
| id |
uuid PK |
|
| email |
text unique |
|
| password_hash |
text nullable |
若走 OIDC 可空 |
| created_at |
timestamptz |
|
4.2 workspaces
| 欄位 |
型別 |
說明 |
| id |
uuid PK |
|
| name |
text |
|
| created_at |
timestamptz |
|
4.3 workspace_members
| 欄位 |
型別 |
說明 |
| workspace_id |
uuid FK |
|
| user_id |
uuid FK |
|
| role |
text |
admin | member |
| PK |
(workspace_id, user_id) |
|
4.4 agents
| 欄位 |
型別 |
說明 |
| id |
uuid PK |
|
| workspace_id |
uuid FK |
|
| display_name |
text |
|
| runtime_type |
text |
MVP 固定 cli |
| cli_command |
text |
例如 codex |
| is_active |
boolean |
|
| created_at |
timestamptz |
|
4.5 tasks
| 欄位 |
型別 |
說明 |
| id |
uuid PK |
|
| workspace_id |
uuid FK |
|
| title |
text |
|
| description |
text nullable |
|
| status |
text |
見 MVP 狀態機 |
| priority |
text |
low/medium/high/urgent |
| assignee_type |
text |
human/agent/unassigned |
| assignee_user_id |
uuid nullable |
|
| assignee_agent_id |
uuid nullable |
|
| source_system |
text nullable |
如 linear |
| source_ref_id |
text nullable |
|
| source_url |
text nullable |
|
| created_by_user_id |
uuid nullable |
|
| created_at / updated_at |
timestamptz |
|
索引:(workspace_id, status)、(workspace_id, updated_at desc)、(source_system, source_ref_id) unique partial where both non-null。
| 欄位 |
型別 |
說明 |
| id |
uuid PK |
|
| task_id |
uuid FK |
|
| author_type |
text |
user/system |
| author_user_id |
uuid nullable |
|
| body |
text |
|
| created_at |
timestamptz |
|
4.7 runs
| 欄位 |
型別 |
說明 |
| id |
uuid PK |
|
| task_id |
uuid FK |
|
| agent_id |
uuid FK |
|
| status |
text |
pending/running/completed/failed/cancelled |
| started_at |
timestamptz |
|
| ended_at |
timestamptz nullable |
|
| exit_code |
int nullable |
|
| error_summary |
text nullable |
|
| workdir_path |
text nullable |
除錯用,可僅 server 可見 |
4.8 run_events
| 欄位 |
型別 |
說明 |
| id |
bigserial PK |
|
| run_id |
uuid FK |
|
| seq |
int |
每 run 內遞增 |
| occurred_at |
timestamptz |
|
| type |
text |
run_started/log/run_completed/run_failed/… |
| payload |
jsonb |
|
約束:(run_id, seq) unique。
4.9 worker_tokens(或 api_tokens)
| 欄位 |
型別 |
說明 |
| id |
uuid PK |
|
| workspace_id |
uuid FK |
|
| token_hash |
text |
|
| label |
text |
|
| created_at |
timestamptz |
|
| revoked_at |
timestamptz nullable |
|
5. API 契約(REST 草案)
前綴:/api/v1。回應 JSON。錯誤格式:{ "error": { "code", "message", "details?" } }。
5.1 Auth
POST /auth/register(內部用可關閉,僅 admin 建帳)
POST /auth/login → tokens
POST /auth/refresh
GET /auth/me
5.2 Workspaces
GET /workspaces
POST /workspaces
GET /workspaces/{id}
POST /workspaces/{id}/members(admin)
5.3 Tasks
GET /workspaces/{ws_id}/tasks?status=&assignee_type=&q=
POST /workspaces/{ws_id}/tasks
GET /tasks/{id}
PATCH /tasks/{id}(狀態、指派、優先級)
POST /tasks/{id}/comments
POST /tasks/{id}/claim(human 或 worker 流程擇一)
POST /tasks/{id}/submit-review → in_review
POST /tasks/{id}/review-decision body: { "decision": "approve"|"reject" }
5.4 Agents
GET /workspaces/{ws_id}/agents
POST /workspaces/{ws_id}/agents
PATCH /agents/{id}
5.5 Runs
GET /tasks/{task_id}/runs
GET /runs/{run_id}
GET /runs/{run_id}/events?after_seq=(分頁)
5.6 Worker 專用(可掛在 /worker/v1 以免與使用者 API 混淆)
POST /worker/v1/heartbeat body: { "hostname", "agent_ids": [] }
GET /worker/v1/assignments?workspace_id=&agent_id=
回傳:下一筆待執行 task + run 草稿 id 或建立指令。
POST /worker/v1/runs/{run_id}/events batch append
PATCH /worker/v1/runs/{run_id} 完成/失敗
PATCH /worker/v1/tasks/{task_id} 同步 task 狀態(或由 run 完成時後端一併更新)
實作決策(MVP 建議):Worker 輪詢 GET /worker/v1/assignments 每 3~5 秒;認領時由 API transaction 將 task 從 queued→claimed 並建立 run,避免雙執行。
6. Worker 執行規格
6.1 環境變數(建議)
| 變數 |
說明 |
FORGE_API_URL |
API base,如 https://api.internal |
FORGE_WORKER_TOKEN |
|
FORGE_WORKSPACE_ID |
預設 workspace |
FORGE_AGENT_ID |
本機對應的 agent |
FORGE_WORK_ROOT |
工作目錄根,預設 ~/forge_workspaces |
FORGE_POLL_INTERVAL_SEC |
預設 3 |
FORGE_RUN_TIMEOUT_SEC |
預設 7200 |
6.2 執行步驟
- Heartbeat(可選)。
- Poll assignments。
- 若取得 task:建立
run(若 API 尚未建則先 POST 建立 run)。
- 建立
workdir = FORGE_WORK_ROOT/{task_id}/{run_id}。
- 將
title、description、上下文寫入 task.md 或透過 stdin 餵給 CLI(實作選一,全專案統一)。
subprocess.run([cli_command, ...], cwd=workdir, timeout=...),串流 stdout/stderr → run_events type=log。
- 結束:
run_completed / run_failed,更新 task 為 completed/failed/in_review(規則見下)。
6.3 完成規則(MVP 建議寫死)
exit_code == 0 且無致命錯誤 → task → in_review(若開啟審核)或直接 completed(可由 workspace 設定 require_review boolean)。
exit_code != 0 或 timeout → task → failed,error_summary 寫入最後 500 字內摘要。
7. Linear 整合規格(MVP)
7.1 目標
- Forge
tasks.source_* 與 Linear issue 對齊。
- 至少一種同步路徑(擇一實作):
選項 1 — Webhook:Linear → Forge POST /integrations/linear/webhook,驗簽後 upsert task。
選項 2 — 週期同步:Cron 或 api 內 scheduler 每 N 分鐘拉取指定 team 的 issues,upsert task。
7.2 對應欄位(建議)
| Linear |
Forge |
| issue id |
source_ref_id |
| url |
source_url |
| title |
title |
| description |
description |
| state |
映射到 status(需建映射表或簡化對照) |
7.3 回寫(P1 可選)
- Run 完成後呼叫 Linear API 新增 comment:Forge run 連結 + 一行摘要。
8. 前端頁面清單(MVP)
| 路由 |
功能 |
/login |
登入 |
/w/[wsId]/tasks |
任務列表 + 篩選 |
/w/[wsId]/tasks/[taskId] |
詳情、留言、狀態操作、runs、events |
/w/[wsId]/agents |
Agent CRUD、顯示最後心跳 |
/w/[wsId]/settings |
workspace 名稱、require_review、token 管理(admin) |
9. 非功能需求
- Log:結構化 JSON log,
X-Request-Id。
- CORS:僅允許
web origin。
- Secret:12 factor,禁止 commit
.env。
- DB 遷移:所有 schema 必須有 Alembic revision。
10. 測試與驗收
10.1 自動化(最低)
- API:
pytest 覆蓋 auth、task 狀態轉移、worker assignment 併發(單測模擬兩 worker)。
- 前端:關鍵路徑 e2e 可選(Playwright)。
10.2 MVP 驗收清單(工程)
- [ ]
docker compose up 可啟動 web + api + db + worker。
- [ ] 使用者可登入、建立 workspace、建立 task、指派 agent。
- [ ] Worker 可執行 mock CLI(測試用
echo script)並寫入 run events。
- [ ] UI 可看到 run 時間線。
- [ ] Review:可演示 approve/reject。
- [ ] Linear:至少完成「建立 Forge task 帶 source 連結」或「從 Linear 同步一筆」其中一條(與產品對齊)。
11. 建議迭代順序(給排程)
| 週次 |
交付 |
| 1 |
DB schema、Auth、Workspace、Task CRUD API、最小 web 列表 |
| 2 |
Task 詳情、Comment、Agent CRUD、狀態機 |
| 3 |
Worker PoC + run/run_events + UI 時間線 |
| 4 |
Review、錯誤處理、token、部署 compose、Linear 整合 alpha |
| 5~6 |
打磨、權限、文件、optional 回寫 Linear / e2e |
12. 與其他文件關係