實戰背景
當一個功能同時要動後端路由、前端元件與測試套件時,循序交給單一個 Codex 工作階段會很慢,因為這三塊彼此獨立的區域其實可以同時開工。
痛點與問題描述: 要做「使用者頭像上傳」功能,拆開來看是三條互不擋的線:
- 後端:新增
POST /api/users/:id/avatar,接到 S3 相容的儲存後端。 - 前端:在設定頁加上
<PhotoUpload>元件,呼叫新端點。 - 測試:針對完整上傳流程補整合測試。
三條線各自可以獨立展開,唯一的匯合點是最終的合併與全測試。OMX 的 $team 正是為這類情境設計:在 tmux 與 worktree 上協調多個 Codex 工作階段平行跑。本範例只使用 theme.yaml 列出的能力:$team、worktree 啟動參數、HUD 多窗格監控、.omx/ 共享狀態,以及 $ultragoal 驗收迴圈。
平台前提:
$team的 tmux 協調在 macOS 與 Linux 原生支援。Windows 屬 secondary path,需以psmux替代,或直接在 WSL2 中執行。
第 1 步:規劃三條平行線並啟動 worktree
先把功能拆成三個角色,連同收斂條件一起寫進 OMX 的目標敘述:
$ultragoal 實作使用者頭像上傳功能,以 $team 拆成三條平行線:
- backend: 新增 POST /api/users/:id/avatar 串接 S3
- frontend: 新增 <PhotoUpload> 元件呼叫該端點
- tests: 補上傳流程整合測試
收斂條件:三條線全綠後由 $ultragoal 合併並跑全測試。
OMX 先以 $deep-interview 確認儲存後端的選型,接著以 worktree 模式啟動整個 session:
# theme.yaml 中 launchCommand 的形式:worktree + madmax + xhigh
omx --worktree=feat/photo-upload --madmax --xhigh
第 2 步:$team 在 tmux 開出三個窗格
$team 讀取設定後,在 tmux session 開出三個窗格,每個窗格是一個獨立的 Codex 工作階段,各自掛在自己的 worktree 分支上:
tmux session: omx-photo-upload
pane 0 [backend] worktree feat/photo-upload-backend
pane 1 [frontend] worktree feat/photo-upload-frontend
pane 2 [tests] worktree feat/photo-upload-tests
三條任務佇列同時寫入共享的 .omx/queue.json,HUD 也切換成多窗格模式,同時追蹤三條進度線:
┌─ HUD ─────────────────────────────────────────┐
│ backend Queue: 3 | Active: 1 | Done: 1 │
│ frontend Queue: 2 | Active: 1 | Done: 0 │
│ tests Queue: 2 | Active: 0 | Done: 0 │
│ Session cost: $0.34 Shared state: .omx/ │
└───────────────────────────────────────────────┘
這就是 $team 與單一 Autopilot 的差別:HUD 同時追蹤多條進度線,而不是只有一條。
第 3 步:平行推進,backend 與 frontend 同時落地
backend 窗格在 routes/avatar.ts 落地端點:
router.post("/users/:id/avatar", upload.single("file"), async (req, res) => {
await storage.put(`avatars/${req.params.id}`, req.file.buffer);
return res.json({ ok: true });
});
同一時間,frontend 窗格在 components/PhotoUpload.tsx 落地元件:
export function PhotoUpload({ userId }: { userId: string }) {
const [uploading, setUploading] = useState(false);
const onPick = async (file: File) => {
setUploading(true);
const form = new FormData();
form.append("file", file);
await fetch(`/api/users/${userId}/avatar`, { method: "POST", body: form });
setUploading(false);
};
return (
<input
type="file"
disabled={uploading}
onChange={(e) => e.target.files && onPick(e.target.files[0])}
/>
);
}
兩條線互不等待,HUD 同步推進為 backend Done: 2 | frontend Done: 1。
第 4 步:tests 窗格卡關,$team 重派相依
tests 窗格想先寫上傳的整合測試,但需要 backend 的儲存介面才 mock 得起來,於是它在共享狀態上標記自己 blocked:
[tests] blocked on backend: needs IStorage signature
$team 偵測到這條相依,把 backend 的「定義 IStorage 介面」任務提到最前面優先完成,再通知 tests 窗格解除阻塞。這就是平行協調的價值:相依交給排程器處理,你不必手動排順序。
第 5 步:三條線收齊,$ultragoal 進入驗收迴圈
三條 worktree 都自我宣告完成後,OMX 自動啟動 $ultragoal 的持久化完成與驗證迴圈:合併三條分支、跑全測試、失敗就把任務回派修補、再跑一次,直到全綠。
第一輪全測試有兩個前端測試失敗(上傳中狀態未覆蓋)。$ultragoal 把失敗任務回派給 frontend 窗格補修,第二輪全綠。HUD 收斂為三條線全 Done,session 成本停在可預期的範圍。
第 6 步:結果盤點
| 評估維度 | 循序單一工作階段(估計) | $team 平行(實際) |
|---|---|---|
| 三條線完成時間 | 約 90 分鐘(序列等待) | 約 35 分鐘(重疊) |
| 相依排程 | 人工排序 | $team 自動偵測並重派 |
| 驗收迴圈 | 手動跑測試 | $ultragoal 合併即驗證 |
| 進度可見度 | 單一指標 | HUD 多窗格同步 |
關鍵學習點
- 平行是結構性的:
$team把功能拆成角色後,在獨立 worktree 平行跑,前後端不再互相等待,適合介面已經談定、實作可以獨立展開的功能。 - 相依交給排程器:當一條線需要另一條線的產出(例如測試需要介面定義),
$team會在共享的.omx/queue.json上偵測阻塞並重派,不必人工排順序。 - worktree 是平行的基礎:啟動參數
--worktree=feat/...讓每條線有自己的分支,合併交給最後的$ultragoal,避免中途互相覆寫。 - 驗收仍是一個迴圈:不論跑了幾條平行線,最後都收斂到
$ultragoal的合併加全測試迴圈,確保三條線合起來真的能跑。
v0.21.3~v0.21.4:長 Team Session 的兩個注意事項
1. tmux scrollback
Detached leader session 的預設 history ceiling 已從 500 提高到 5000 行,也可在建立新 pane 前設定:
export OMX_TMUX_HISTORY_LIMIT=5000
既有 pane 的 history-limit 不會動態改變;如果長 session 已經因舊 ceiling 丟失輸出,要建立新的 tmux pane / session 才能套用新值。
2. Astra 是 default,不是 override
v0.21.4 在未指定模型時會對多數 agent tiers 使用 gpt-6-astra;若你的 Team 已經在 profile、per-agent config 或 CLI 中 pin model,explicit choice 仍然優先。升級後不要看到 default 改名就誤以為所有 worker 都會被強制換模型。