統一 dep 升級入口 — 依輸入分兩個 mode。(1) 在 consumer cwd 無參數觸發 → § Outdated batch mode:跑 pnpm outdated 後逐 pkg 派 codex 在 worktree 升版 + 驗證 + commit + 失敗升 high research。(2) 在 clade home 給 release URL / `<pkg>@<ver>` / 純 pkg name → § Fleet mode:跨 consumer 對單一 pkg sweep,自動 fetch + 解析 BC、用 dep-fleet-scan 掃命中 consumer、用 codebase-memory-mcp 定位 callsites、並行 fan-out 長駐 subagent。Use when 使用者說「升 deps」、「pnpm outdated 處理」、「處理過期套件」、「跟上 release」、「跨 consumer 升 X」、「全部 consumer 升 @xxx」、「無腦升 @xxx」、「升級套件」。**不適用於**單套件 ad-hoc ...
Scanned 6/6/2026
Install via CLI
openskills install YuDefine/nuxt-supabase-starter---
name: dep-upgrade
description: 統一 dep 升級入口 — 依輸入分兩個 mode。(1) 在 consumer cwd 無參數觸發 → § Outdated batch mode:跑 pnpm outdated 後逐 pkg 派 codex 在 worktree 升版 + 驗證 + commit + 失敗升 high research。(2) 在 clade home 給 release URL / `<pkg>@<ver>` / 純 pkg name → § Fleet mode:跨 consumer 對單一 pkg sweep,自動 fetch + 解析 BC、用 dep-fleet-scan 掃命中 consumer、用 codebase-memory-mcp 定位 callsites、並行 fan-out 長駐 subagent。Use when 使用者說「升 deps」、「pnpm outdated 處理」、「處理過期套件」、「跟上 release」、「跨 consumer 升 X」、「全部 consumer 升 @xxx」、「無腦升 @xxx」、「升級套件」。**不適用於**單套件 ad-hoc 升(`pnpm add <pkg>@latest` 更快)、framework major migration(Nuxt 3→4 / Next 14→15 需專屬 plan)。Fleet mode **MUST run from clade home (`~/offline/clade`)**,受 [[clade-role-and-todo-discipline]] § upstream-driven dep migration carve-out 約束。
---
<!-- 🔒 LOCKED — managed by clade · auto-generated by sync-to-agents; edit source in .claude/ then re-run sync -->
# dep-upgrade — 統一 dep 升級入口
兩種升級需求一個 skill 涵蓋:
- **Outdated batch**:一個 consumer 累積很久沒升、`pnpm outdated` 一坨要批升
- **Fleet sweep**:看到 upstream release、想跨 consumer 跟上同一個版本
skill 開頭依輸入分流,**不要記兩個 skill 名**。
## Step 0 — Mode dispatcher(最先讀)
依**輸入**跟 **cwd** 分流:
| 觸發 | cwd 預期 | Mode | 跳到 |
| --- | --- | --- | --- |
| 純命令 `/dep-upgrade`(無參數) | consumer root(有 `package.json` + lockfile) | **Outdated batch** | § Outdated · Step O.1 |
| 給 GitHub release URL(`https://github.com/.../releases/tag/v<ver>`) | clade home | **Fleet** | § Fleet · Step F.1 |
| 給 `<pkg>@<ver>` / `<pkg> v<ver>` / `<pkg> <ver>` | clade home | **Fleet** | § Fleet · Step F.1 |
| 給純 pkg name(「升 @nuxt/ui」、「無腦升 X」) | clade home | **Fleet + Discovery** | § Fleet · Step F.1 |
| 無參數但 cwd = clade home | — | STOP + 問意圖 | 見下方 § Disambiguation |
### Disambiguation(cwd 與 input 不對)
- 在 consumer 給 pkg name → 問「你想 (A) 只對這個 consumer 升 (走 Outdated batch 但鎖單套件)、還是 (B) 跨 6 consumer sweep (要 `cd ~/offline/clade` 再跑)?」
- 在 clade 無參數 → 問「你想 (A) sweep 哪個 pkg?或 (B) 你其實要進某個 consumer 跑 `pnpm outdated`?」
- 在 worktree(cwd 含 `-wt/`) + 無參數 → 視為已在 Outdated batch 中段,跳 Step O.0、直接續跑 Step O.2
**MUST 等 user 拍板**,**NEVER** 主線自選 mode。
## 共用基礎(兩個 mode 都用到)
| 基礎 | 出處 |
| --- | --- |
| Worktree gate | [[worktree-default]] §1,wt-helper 開 / merge-back |
| Codex 派工模板 | `vendor/snippets/codex-upgrade-prompts/{medium,high}.md`(authoring source),SKILL.md § Codex prompt templates 是 plugin cache 副本 |
| Codex watch protocol | [[agent-routing.codex-watch-protocol]] |
| Selective stage on main | 一律 `git add package.json <lockfile>` + 額外指定檔,**NEVER** `git add -A` |
| Commit msg(commitlint-aware) | subagent / codex 端讀 consumer commitlint config 後生 compliant msg |
---
# § Outdated mode — Single consumer, multi-pkg batch
把「升 deps」這個機械活全程委派給 codex 跑:主線只負責 plan / dispatch / watch / 摘要,**不**自己改 `package.json`、不自己跑 `pnpm install`。每個 package 一個 codex 派工、一個 commit,失敗自動升 reasoning effort + 加上 web search / GitHub Issues 研究。
## 何時用 / 不適用(Outdated mode)
**適用**:
- `pnpm outdated` / `npm outdated` 跑出一堆 patch + minor + major,想逐套件穩穩升
- 不確定哪些套件會 break,想要明確的 per-package commit boundary 方便 bisect
- 大版號跳躍想讓 codex 自己去查 release notes / migration guide
**不適用**:
- 單一套件、單行升版(`pnpm add foo@latest` 自己跑更快)
- 整個 framework migration(Nuxt / Next / React major bump 需要專屬的 migration plan,不是逐套件 loop 能處理)
- monorepo 跨 workspace catalog 升版(先用 [[evlog-catalogs]] 派工方式處理 catalog)
**Changelog-aware sub-mode**:被 § Fleet mode subagent 呼叫時,會跳過 Step O.1(target / version 由 fleet brief 指定)、Step O.2.1 的 prompt 內嵌 BC clauses + callsites。詳見 § Codex prompt templates · Changelog-block 填充。
## Step O.0 — Worktree gate([[worktree-default]] §1)
升 deps 會改 tracked code(`package.json` / lockfile / 必要時 source code),**MUST** 在 session worktree 內跑,不在 main working tree 直接動。
**進入 worktree 的兩條路**:
1. 主線目前已在 worktree(cwd 名含 `-wt/`)→ 跳過 Step O.0、繼續 Step O.1
2. 主線在 main → 跑 `/wt upgrade-deps-<YYYYMMDD>` ad-hoc Form-1(不對應 spectra change)。`wt-helper add` 會走 `--baseline-strategy stash` 把 main dirty 保留,fork 出 worktree 後主線 `cd` 進去
**禁止**直接在 main working tree 跑這個 mode — 升爆掉一條 package 整個 main 都會卡,bisect / rollback 成本爆增。
## Step O.1 — Detect PM + 收 outdated + 讀 deps 分類
1. **偵測 package manager**(看 lockfile):
```bash
ls -1 pnpm-lock.yaml package-lock.json yarn.lock bun.lockb 2>/dev/null | head -1
```
找到的對應 `pnpm` / `npm` / `yarn` / `bun`。**沒有 lockfile** → STOP,回覆「找不到 lockfile,無法判定 package manager」。
2. **拉 outdated**:
```bash
pnpm outdated # 或 npm outdated / yarn outdated
```
> pnpm `--format=json` 在 monorepo workspace 會回多行 NDJSON 不是純 JSON,直接讀人類格式 + 主線自己 parse 比較穩。
3. **讀 package.json 把每個 outdated 套件分類為 deps / devDeps**(**P0,下 prompt 必用**):
```bash
python3 -c "
import json
p = json.load(open('package.json'))
for pkg in <outdated_pkg_list>:
if pkg in p.get('dependencies', {}): print(f'{pkg} dep')
elif pkg in p.get('devDependencies', {}): print(f'{pkg} devDep')
"
```
後續 prompt builder 依此結果決定 `pnpm add <pkg>` 還是 `pnpm add -D <pkg>` — **NEVER** 預設 `-D`,否則會把 dependencies 套件靜默搬到 devDependencies(實證踩坑:TDMS 第一次 run wrangler 被誤搬,commit 後才發現)。
4. **分類版號差距**(從低風險到高風險升):
- **patch**(`1.2.3 → 1.2.4`):通常安全
- **minor**(`1.2.3 → 1.3.0`):should be safe 但偶爾有 regression
- **major**(`1.x → 2.x`):高機率 breaking change
5. **`@types/*` 特殊封頂(MUST 在分類後立刻套用)**:
`@types/<lib>` 的 major 版號**跟著對應 runtime / lib 版本走**,不是越新越好。**MUST** 把每個 `@types/*` 套件的 target version 上限封頂為 project 當前對應版本的 major:
- `@types/node` → ≤ project Node major(讀 `.nvmrc` / `.tool-versions` / `package.json` `engines.node` / `Dockerfile` 任一可靠來源;都查不到才問 user)
- `@types/react` → ≤ `dependencies.react` 的 major
- 其餘 `@types/<lib>` 同理跟 `<lib>` 自身 major
若 `outdated` 給的 Latest 超過上限,target 改成「上限 major 內最新」(例:Node 20 環境 + `@types/node` Latest 是 22.x → target 鎖 `^20.x` 最新 minor/patch,**不**升 22)。Step O.1.6 回報給 user 的計畫表 **MUST** 在這些 `@types/*` 條目後標註「(capped to Node N / react N)」讓 user 看到封頂邏輯有作用。
**NEVER** 為了「升到最新」把 `@types/*` 推超過對應 runtime / lib — 型別會漂移:typecheck 用的是 newer types,但 runtime 跑的是舊版 API,bug 表現是「TS 說沒問題、prod 卻炸」這種最難 debug 的型別。
6. **回報計畫給使用者**並等確認:
```
偵測到 <PM>,outdated 共 <N> 個:
- patch <a> 個:foo, bar, ...
- minor <b> 個:baz, qux, ...
- major <c> 個:quux, corge, ...
建議升級順序:patch → minor → major(風險遞增、容易在崩潰點停下 bisect)
要排除任何套件不升嗎?
```
使用者可指定排除清單(如 `vue` lockstep with `nuxt`,先不動)。**MUST 等使用者確認**才進 Step O.2 — 不要自決定。
> Smoke test 建議:第一次跑這個 mode on 一個新 consumer 時,先挑 1 個最低風險 patch 跑 smoke,驗證 worktree fork + codex dispatch + commit boundary 都對,再批次推剩下的(實證:TDMS 第一次 run 就用這方式發現 prompt builder `-D` bug)。
## Step O.2 — 逐套件 codex 派工 loop
每個 package 走以下子流程,**一個 package 一個 codex 派工**、**一個 commit**:
### O.2.1 寫 prompt 到 `/tmp/codex-upgrade-<pkg>-prompt.md`
用 § Codex prompt templates · § A medium 模板。**MUST** 內含:
- `[DELEGATED-BY-CLAUDE-CODE]` marker(第一行,per [[agent-routing.codex-watch-protocol]] § Runtime Gate)
- 目標 package 名 + current version → target version + **正確的 install flag**
- Git Baseline 段(per codex-watch-protocol § Git Baseline;列當前 worktree 內所有 main fork 過來的 in-flight 變更 path,**不要列死**——每個 consumer / 每次 fork 都不同,主線跑 `git status --porcelain` 動態抓)
- Commit Authorization 段(per codex-watch-protocol § Commit Authorization;message format `🧹 chore: wt upgrade-<pkg>-<from>→<to>`,subagent 端需讀 commitlint config 調整)
- 失敗時的回報格式
**Plan-first 條件化**(DRY + 降 token):
- Patch 升版:**MAY 省略** Plan-first 硬指令,prompt 直接列「install → typecheck → commit」固定三步
- Minor / Major:**MUST** 加 Plan-first
**驗證步驟**(依升版類型):
- Patch:`pnpm install`(隱式跑) + `pnpm typecheck` 0 errors
- Minor:typecheck + 如有 build script 跑一次 + 相關 test
- Major:typecheck + build + 全 test + codex 自己決定要不要 smoke test
### O.2.2 Dispatch background bash(medium)
```bash
cd <worktree-path> && codex exec \
--model gpt-5.5 \
--dangerously-bypass-approvals-and-sandbox \
--skip-git-repo-check \
-c model_reasoning_effort=medium \
< /tmp/codex-upgrade-<pkg>-prompt.md 2>&1
```
`run_in_background=true`、立刻 `ScheduleWakeup(180, ...)` 啟動 [[agent-routing.codex-watch-protocol]] § Codex Watch Protocol。
**Wakeup 雜訊接受限制**:ScheduleWakeup 沒有 cancel API;package 已完成 + task-notification 已到後,先前排的 wakeup 仍會觸發 1–2 次「請 watch jobId X」prompt。主線收到時做兩件事:(1) 用 jobId 對齊當前 package(已完成的 jobId 直接忽略);(2) 不為已完成的 package 再次 reschedule。
### O.2.3 收到 `<task-notification status=completed>` 後判定
| 訊號 | 判定 | 下一步 |
| --- | --- | --- |
| `PHASE_RESULT: SUCCESS` + worktree 多了一個 `🧹 chore: wt upgrade-<pkg>-...` commit | 成功 | 記錄到摘要、進下一 package |
| `PHASE_RESULT: FAILURE` + codex 自報原因 | 失敗 → 進 O.2.4 升 high research | 不馬上問使用者;先讓 codex high 自己研究 |
| Plan section 缺 / commit message format 不符 / scope drift | codex 漏跑硬指令 | 不升 high;直接 request_user_input [重派 medium / 升 high / 跳過 / 中止] |
| `fetch failed` / sandbox 拒絕 / 互動 prompt 卡住 | 環境問題 | per watch protocol 「介入觸發」,request_user_input |
**絕不**在 O.2.3 替 codex 修檔(會破壞 per-package commit boundary)。要修就 reset worktree commit 後重派。
### O.2.4 升 high research(medium 失敗自動觸發)
寫 prompt 到 `/tmp/codex-upgrade-<pkg>-research-prompt.md`,用 § Codex prompt templates · § B high 模板。**MUST** 內含:
- `[DELEGATED-BY-CLAUDE-CODE]` marker
- Medium 派工的失敗 tail(≤ 50 行)+ codex 自報的失敗原因
- 明確指示走研究模式:先 **github** plugin 查 `<pkg>` repo 的 issues / releases / changelog,再 **browser-use** / web search 查官方 migration guide
- 研究完才動手改檔
- 一樣的 Git Baseline / Commit Authorization 硬指令
- Commit message format `🧹 chore: wt upgrade-<pkg>-<from>→<to> (researched <issue-url-slug>)`
Dispatch(同 O.2.2 但 `model_reasoning_effort=high`)+ watch(high 跑得更久,wakeup 間隔仍守 ≤ 180s)。
### O.2.5 high 仍失敗 → request_user_input
```
<pkg> medium + high research 都失敗。
Medium 失敗原因:<一句話>
High research 失敗原因 + 已查到的線索:<一句話>
完整 codex 輸出在 /tmp/codex-upgrade-<pkg>-research-prompt.md
要怎麼處理?
[1] 跳過 <pkg>(記錄到摘要的 SKIPPED 區,繼續下個 package)
[2] 中止整個 upgrade loop(保留 worktree 供手動處理)
[3] 主線接手手動升(會破壞 per-package commit boundary,僅在套件很小時建議)
```
**禁止**主線自己決定跳過或中止 — 必須使用者選。
## Step O.3 — Auto-merge-back + selective stage(主線自主完成)
所有 package codex 派工跑完(成功 / 跳過 / 中止皆同)後,**MUST** 自主把 worktree merge-back 進 main,**NEVER** 把這步當「下一步」丟給 user 自己跑(per [[worktree-default]] §5「Skill-owned worktree lifecycle」)。
### O.3.1 主線 cd 回 main consumer root
```bash
MAIN_PATH=$(git worktree list --porcelain | head -1 | awk '{print $2}')
cd "$MAIN_PATH"
```
從此往後所有 O.3 命令都在 main 跑。
### O.3.2 Merge-back(含 baseline blocker 自動處理)
```bash
node scripts/wt-helper.mjs merge-back <slug> --auto-stash
```
| 訊號 | 處理 |
| --- | --- |
| `merge-back: <slug> absorbed into main` | 成功 → 進 O.3.3 |
| `merge-back: <slug> absorbed into main (blockers stashed as wt-merge-block/<slug>/<ISO>)` | 成功 + main 端有 stash 紀錄 → 進 O.3.3,O.3.4 摘要末段提醒 user 用 `node scripts/stash-reconcile.mjs --slug <slug> --interactive` 收尾 |
| `merge-back blocked: worktree '<wt-path>' has N uncommitted edit(s)` | Pre-fork baseline 殘留 → 進 O.3.2.a 自動清理 |
| `merge-back blocked: <N> file(s) in main's working tree would be overwritten` | 上面 `--auto-stash` 應已涵蓋;若仍出現是 race condition → STOP + 報 user |
| `error: merge conflict in <files>` / `pre-sync` / `squash` conflict | Worktree 保留,wt-helper 內部已 abort + 救 stash → STOP + 報 user(不主線自決) |
#### O.3.2.a Pre-fork baseline blocker 自動清理
```bash
WT_PATH="$(dirname "$MAIN_PATH")/$(basename "$MAIN_PATH")-wt/<slug>"
for path in <parsed-blocker-paths>; do
git -C "$WT_PATH" checkout HEAD -- "$path"
done
node scripts/wt-helper.mjs merge-back <slug> --auto-stash
```
**安全性論證**:IDENTICAL baseline → data 在 main HEAD 還在;DIVERGED baseline → `refs/wt-baseline/<slug>/<ISO>` pinned ref 是安全網(per [[worktree-default]] §1);merge-back 只搬「branch commits 的 diff」,main 上別 session 的 dirty 完全不會被踩。
**NEVER**:`git stash` 在 worktree 內收 baseline / `git add` baseline 進 worktree branch / 用 `--include-worktree-wip`。
#### O.3.2.b 真衝突(pre-sync / squash conflict)
wt-helper 內部已 abort + 救 stash + 保留 worktree。主線 STOP 整個流程 + request_user_input 三選項:手動解 conflict / 放棄 upgrade / user 自己看狀態。
### O.3.2.c Sync node_modules on main
merge-back 只搬 tracked 檔(`package.json` / lockfile / `pnpm-workspace.yaml`);`node_modules/` 是 gitignored,main 端仍停留在升版前的舊狀態。**MUST** 跑 `pnpm install`(或對應 PM 的 install)同步,否則後續 `pnpm outdated` / typecheck / test 都看到 stale 版本。
```bash
<PM> install
```
這不違反「NEVER 主線自己跑 `pnpm install`」禁令 — 該禁令指的是升版階段(Step O.2)不該由主線做 `pnpm add`;這裡是 post-merge-back setup chore(per [[worktree-default]] §1.x 的 `pnpm install` 自動代勞清單)。
### O.3.3 Selective stage on main
```bash
git reset HEAD # 清掉 index(並行 session 的 WIP 退回 unstaged)
git add package.json <lockfile-path> # 只 stage upgrade 真的動的檔
```
`<lockfile-path>` 對應 PM:`pnpm-lock.yaml` / `package-lock.json` / `yarn.lock` / `bun.lockb`。
**罕見情況**:codex high-research 派工有 commit 過非 dep 檔(import path 重寫),那些檔也在 squash 後落在 main working tree。摘要 O.3.4 ⚠️ 區應列出,**MUST** 也加進 `git add`,但 stage 前 `git status` 印給 user。
**NEVER** `git add -A` / `git add .`。
### O.3.4 摘要彙報
```markdown
## dep-upgrade · outdated 摘要(<YYYY-MM-DD HH:MM>)
**Worktree**:`<wt-path>` → ✅ absorbed into main + cleaned
**Branch**:`session/<date>-upgrade-deps-<slug>` → ✅ removed
**Package manager**:`<PM>`
**總計**:N 套件(M 成功、K 升 high 後成功、S 跳過、F 失敗)
### ✅ Medium 一次成功(M)
| Package | <from> | <to> | Commit |
| --- | --- | --- | --- |
### ⚠️ 升 high research 後成功(K)
| Package | <from> | <to> | BC 摘要 | Commit |
| --- | --- | --- | --- | --- |
### ⏭️ 跳過(S)
| Package | <from> | <to> | 跳過原因 |
| --- | --- | --- | --- |
### 📦 Main 端狀態
- Staged:`package.json`、`<lockfile-path>`
- Unstaged:並行 session 原有 WIP 保留
- 並行 session 的 staged WIP(若有)已退回 unstaged
### 下一步(user 拍板)
走 `/commit` 收尾。**NEVER** 主線自動跑 `/commit` — 留 commit 時機 / message / sign-off 給 user。
```
### 例外:user 在 Step O.0 之前明確說「不要 land」/「先看一下」
跳過 O.3.1–O.3.3,只跑 O.3.4 摘要 + 把「下一步」改寫成手動指令。判定詞例:「先別 merge-back」「保留 worktree」。**NEVER** 主動延遲 — 預設一律自動 land。
---
# § Fleet mode — Single pkg, multi-consumer sweep
把「user 貼出一個 release URL → 6 個 consumer 全部升版 + 套用 BC 修正 + 各自 commit」這個工作流全程編排化。Clade 主線負責 fetch / 解析 / 掃描 / 編排 / 聚合;每個命中 consumer 一個長駐 subagent 在自己 worktree 內呼叫 § Outdated mode 的 changelog-aware 子流程。
## 何時用 / 不適用(Fleet mode)
**適用**:
- 使用者貼出 GitHub release URL(例:`https://github.com/nuxt/ui/releases/tag/v4.8.0`),想跨 consumer 升版
- 想用同一個 changelog 在多個 consumer 套用同樣的 BC 修正(rename / removal / signature / config schema)
- 想要「全部 OK 才一起 land」的統一 push gate
**不適用**:
- **單 consumer** ad-hoc 升版(直接在該 consumer 走 § Outdated mode 或 `pnpm add` 更快)
- **Framework major migration**(Nuxt 3→4、Next 14→15)— 需專屬 migration plan
- **無 changelog / changelog 不公開**的內部套件升版
- 一次 sweep 想升**多個套件**(per [[clade-role-and-todo-discipline]] § carve-out「一個套件 × 一個 target version」)
## Step F.1 — Preflight clade-only + 解析觸發 input
### F.1.1 確認 cwd 是 clade home
```bash
pwd # MUST end with /offline/clade
test -f registry/consumers.json -a -f consumers.local
```
- cwd 不是 clade home → STOP,回 user「Fleet mode 必須從 clade home 跑 (`cd ~/offline/clade`),因為要讀 `consumers.local` + 跨 consumer 編排」
- `consumers.local` 缺 → STOP,回 user「`consumers.local` 不存在,請先建好本機 consumer 路徑清單再跑」
**為什麼 clade-only**:Fleet mode 對「**所有** consumer」做改動,需要 `consumers.local` 的 ground truth;在某個 consumer 端跑會把當前 consumer 自己也納入 sweep 範圍但又無法掃其他 consumer,編排錯亂。
### F.1.2 解析觸發 input
使用者可能用以下四種形式觸發:
| 觸發形式 | 解析方法 |
| --- | --- |
| GitHub release URL(`https://github.com/<owner>/<repo>/releases/tag/v<ver>`) | 直接記 release_url + 從 release notes 找 `pnpm add` / `npm install` 範例驗證 npm pkg name(repo name 跟 npm pkg 可能不同,例:`nuxt/ui` repo → `@nuxt/ui` pkg) |
| `<pkg>@<ver>` 形式(例:`@nuxt/ui@4.8.0`) | pkg + version 直接拆;release_url 從 npm registry discover(見下方) |
| `<pkg> v<ver>` / `<pkg> <ver>` 空白分隔(例:「升 @nuxt/ui v4.8.0」) | 同上 |
| **純套件名(無腦升級,例:「升 @nuxt/ui」、「全 consumer 升 @nuxt/ui 到最新」)** | discover latest + release URL(見下方) |
### F.1.3 Discovery — 無腦升級自動補欄位
當缺 `target_version` 或缺 `release_url` 時,跑:
```bash
node vendor/scripts/dep-fleet-discover.mjs --pkg "<pkg>" [--version <ver>]
```
輸出 JSON:
```json
{
"pkg": "@nuxt/ui",
"latest_version": "4.8.0",
"target_version": "4.8.0",
"release_url": "https://github.com/nuxt/ui/releases/tag/v4.8.0",
"repo_url": "https://github.com/nuxt/ui",
"homepage": "https://ui.nuxt.com",
"source": "github_release"
}
```
Discovery 內部 fallback chain:
1. `npm view <pkg> dist-tags.latest` 拿 latest version(無 --version 時)
2. `npm view <pkg> repository.url` 拿 repo URL
3. 試 `https://github.com/<owner>/<repo>/releases/tag/v<ver>` 200 OK
4. 失敗 → 試 `releases/tag/<pkg>@<ver>`(monorepo 慣例)
5. 仍失敗 → fallback raw `CHANGELOG.md`
6. 全失敗 → STOP,回 user「找不到 changelog,請手動貼 release URL」
**無腦升級觸發判定**:user 訊息含 pkg name 但沒帶 `@<ver>` / `v<ver>` / 數字版號 → 直接走 discovery,**NEVER** 反問「target 版本是?」(user 已經明說要升、且預期 latest)。
**MUST** 在進入 Step F.2 之前把以下五個值都拍板:`<pkg>` / `<target_version>` / `<release_url>` / `<pkg-slug>`(檔名安全字串,例:`@nuxt/ui` → `nuxt-ui`) / 觸發語境(user 原話)。
## Step F.2 — Fetch + 解析 changelog
### F.2.1 Fetch release notes
```bash
gh release view v<target_version> --repo <owner>/<repo> --json body,name,tagName,publishedAt > /tmp/dep-fleet-release-<pkg-slug>.json
```
`gh` 認證已配置時 quota 寬鬆;不行才退到 WebFetch。**禁止** scrape release notes 之外的東西(issues / discussions / PRs 是 Step F.5 high-research subagent 的工作)。
### F.2.2 LLM 解析 release notes → 結構化
把 release body 丟給 LLM 解析(這個 mode 本身就是 LLM 跑,直接讀檔做解析即可),輸出**統一格式**:
```jsonc
{
"pkg": "@nuxt/ui",
"to_version": "4.8.0",
"release_url": "https://github.com/nuxt/ui/releases/tag/v4.8.0",
"breaking_changes": [
{
"category": "rename | removal | signature | config-schema | peer-bump",
"description": "<一句話人話>",
"affected_apis": ["<symbol1>"],
"before": "<code snippet>",
"after": "<code snippet>"
}
],
"features": [{ "description": "...", "opt_in": true }],
"deprecations": [{ "api": "<name>", "replacement": "<name>" }]
}
```
**解析守則**:
- `category` 必填、必須是上面列的五個之一;不確定就用 `signature`
- `affected_apis` 是要拿去 codebase-memory-mcp `search_graph` 的字串 — 必須是**真實的可搜尋符號**,不要寫人話
- `before` / `after` 給 codex 看的範例,**MUST** 是可貼上的 code snippet
- 找不到任何 BC → 全部 `breaking_changes: []`,跳到 Step F.3.5「無 BC fast path」
寫到 `/tmp/dep-fleet-changelog-<pkg-slug>.json`。
### F.2.3 Carve-out 條件自查
**MUST** 自查 [[clade-role-and-todo-discipline]] § upstream-driven dep migration 的 5 條允許條件:
- ✅ 每條 BC 在 release notes 找得到對應 clause
- ✅ Sweep 範圍只有「一個套件 × 一個 target version」
- ✅ Sweep 不夾帶 clade 自行發想的 refactor
- ✅ Sweep 不夾帶 unrelated 套件升版
- ✅ Worktree gate 由 § Outdated mode Step O.0 保證
任一條不滿足 → STOP,回 user 解釋哪條沒過。
## Step F.3 — Fleet scan 命中 consumer
```bash
node vendor/scripts/dep-fleet-scan.mjs --pkg "<pkg>" --target "<target_version>" > /tmp/dep-fleet-scan-<pkg-slug>.json
```
挑出 `found: true` 的當作 hit consumer 名單。
**Skip 條件**(hit 但不該 sweep 的 consumer):
- `business_activity: "paused"` → skip + 列入 plan 顯示為「skipped: paused」
- `version_gap: "same"` → skip + 列入 plan 顯示為「already at target」
- `version_gap: "unknown"` 且 manifest 是 `catalog:` 但 catalog 解析不到 → request_user_input 問是否 skip
### F.3.x — 0-sweep fast path(無事可做就早早結束)
算完 `hit_consumers - skipped`:
- **= 0**:所有 hit consumer 都符合 skip 條件 → **跳過 Step F.4-F.8**,直接輸出「No-op summary」並結束:
```
## dep-upgrade · fleet no-op(<YYYY-MM-DD HH:MM>)
Target: <pkg>@<target_version>
Release: <release_url>
Sweep 命中:0 個 consumer 需要動
| consumer | manifest | skip reason |
| --- | --- | --- |
| <id> | <spec> | already at target | paused | catalog unresolved |
無事可做。
```
**NEVER** 在這條 fast path 跑 changelog 解析 / callsite scan / plan gate — 這些只在有 sweep 需求時才有意義。
- **≥ 1**:照原流程進 Step F.4 callsite 預掃。
**Idempotency 設計**:「無腦升級」連跑兩次第二次必走 0-sweep fast path(第一次升完所有 active consumer 都到 latest,第二次掃出 gap=same)。
## Step F.4 — Callsite 預掃(codebase-memory-mcp)
對每個 hit consumer × 每條 BC 的 `affected_apis[]`:
### F.4.1 確認 consumer 已 indexed
**MCP project name 必須 normalize**:codebase-memory-mcp 用 dash-separated 形式,**不是** absolute path。轉換規則:去掉開頭 `/`、把剩下的 `/` 換 `-`。
```
/Users/charles/offline/perno → Users-charles-offline-perno
/Users/charles/offline/nuxt-edge-agentic-rag → Users-charles-offline-nuxt-edge-agentic-rag
```
每個 consumer 查 index 狀態:
```
mcp__codebase-memory-mcp__index_status(project="Users-charles-offline-<consumer-basename>")
```
若 not indexed → 跑 `mcp__codebase-memory-mcp__index_repository(repo_path=<consumer_path>, mode="fast")`(這個 tool 收 absolute path,**輸出** 才是 normalize 後的 project name)。
### F.4.2 搜 callsite
對每個 `<symbol>` 跑(**project 用 normalize 後形式**):
```
mcp__codebase-memory-mcp__search_graph(name_pattern=<symbol>, project="Users-charles-offline-<consumer-basename>", path_filter="^(app|server|components|composables|utils|pages|layouts)/")
mcp__codebase-memory-mcp__search_code(pattern=<symbol>, project="Users-charles-offline-<consumer-basename>", path_filter="\\.(vue|ts|tsx|js)$")
```
**過濾 docs-only 命中**:MCP 可能命中 `.agents/skills/<name>/references/*.md`、`docs/`、`README.md` 等檔。這些**不是 runtime callsite**,歸到「docs 提及」分類,不進 BC code mod 範圍。
**Fallback**:MCP 沒找到 → grep `<consumer_path>` 一次(`rg -n "<symbol>" --type vue --type ts <consumer_path>`)。
**Callsite 0 個的 BC**:不刪該條 BC(仍可能影響 transitive 用法),但 brief 內 callsites 為空、prompt 內提示「callsites: 0(請 codex 自行 grep 確認)」。
### F.4.3 寫 per-consumer brief 到 `/tmp/dep-fleet-brief-<pkg-slug>-<consumer-id>.json`
每個 hit consumer 一份 brief,schema:
```json
{
"pkg": "@nuxt/ui",
"from_version": "^4.7.1",
"to_version": "4.8.0",
"consumer_path": "/Users/charles/offline/perno",
"consumer_id": "perno",
"workflow_model": "trunk-based",
"release_url": "https://github.com/nuxt/ui/releases/tag/v4.8.0",
"field": "dependencies",
"dep_or_devdep": "dep",
"version_gap": "minor",
"breaking_changes": [...],
"features": [...],
"deprecations": [...],
"callsites": [{ "file": "app/components/Foo.vue", "line": 42, "symbol": "UInputMenu" }]
}
```
### F.4.5 無 BC fast path
若 Step F.2.2 拿到 `breaking_changes: []`:brief 仍寫,但 `breaking_changes` / `callsites` 都為空、subagent 端等同跑 Outdated mode Step O.2 minor / patch 流程。
## Step F.5 — 一次性 plan gate(request_user_input)
把 Step F.2-F.4 的結果整理成一張 plan table 給 user 拍板:
```
## dep-upgrade · fleet plan
Target: <pkg>@<target_version>
Release: <release_url>
BC clauses: <N> 條
Features: <M> 條
Deprecations: <K> 條
### Hit consumers (將 sweep)
| consumer | from | gap | manifest | callsites |
| --- | --- | --- | --- | --- |
### Skipped consumers
| consumer | reason |
| --- | --- |
```
request_user_input 提供四個選項:
```
[1] 全部 sweep(並行 fan-out N 個 subagent)
[2] 先挑一個試(指定 consumer_id 跑 smoke,回報後再決定剩下)
[3] 改 BC 解析(user 想增刪某條 BC clause)→ 回 Step F.2.2
[4] 中止
```
**MUST 等 user 選**,**NEVER** 主線自決定全部 sweep(即使 plan 看起來很安全)。
## Step F.6 — 並行 fan-out 長駐 subagent
per [[parallel-fanout]],每個 hit consumer 一個**長駐**(name 參數)subagent,**thin brief**(3–5K)只包含:
- Brief JSON 檔路徑:`/tmp/dep-fleet-brief-<pkg-slug>-<consumer-id>.json`(subagent 自己 Read)
- Consumer worktree 還沒開時的指令:`cd <consumer_path> && node scripts/wt-helper.mjs add upgrade-<pkg-slug>-<YYYYMMDD> --baseline-strategy stash`
- 跑 § Outdated mode changelog-aware 子流程的指示
- 回報格式
### F.6.1 Subagent brief template
```markdown
# Task: 跑 dep-upgrade § Outdated mode changelog-aware 對 <consumer_id> 升級 <pkg>
你是 dep-upgrade orchestrator 派的長駐 subagent,負責 **<consumer_id>** 這一個 consumer 的升版。
## Brief
Brief JSON:`/tmp/dep-fleet-brief-<pkg-slug>-<consumer-id>.json`
請 Read 這個檔。
## 工作流程
1. `cd <consumer_path>`
2. 開 worktree:`node scripts/wt-helper.mjs add upgrade-<pkg-slug>-<YYYYMMDD> --baseline-strategy stash`
3. 跑 dep-upgrade § Outdated mode changelog-aware 子流程:
- 讀 `~/offline/clade/plugins/hub-core/skills/dep-upgrade/SKILL.md` § Outdated mode + § Codex prompt templates
- 跳過 Step O.1(target / version 由 brief 取)
- 跑 Step O.2.1:用 § A medium 模板 + brief 內 BC 渲染 `<changelog-block>` + brief 內 callsites
- 跑 Step O.2.2:codex dispatch(medium)
- Watch codex per [[agent-routing.codex-watch-protocol]]
- 失敗 → 升 high(用 § B 模板)
- 高失敗 → 不要 request_user_input,直接 STOP + 回報 orchestrator
4. 跑 Step O.3 merge-back + selective stage(`package.json` + lockfile + callsite 改動檔)
5. **NEVER push、NEVER /commit**:worktree 已 merge-back 後 main 端 staged 狀態保留,回報 orchestrator
6. **產生 commit msg(commitlint-aware)**:
- 先 read consumer 的 commitlint 設定(`commitlint.config.{js,ts,mjs,cjs}` / `.commitlintrc.*` / `package.json` 內 `commitlint`)
- 偵測限制:`type-enum` 允許清單、自定 `subject-has-chinese` plugin、`body-max-line-length` / `header-max-length`
- 生 commit msg 必 **同時通過** worktree branch 跟 consumer main 的 commit-msg hook
- 範例:agentic-rag 用 `🧹 chore: 升級 @nuxt/ui ^4.7.0 → 4.8.0`;perno 可用 `⬆️ chore: upgrade @nuxt/ui ^4.7.1 → 4.8.0`
7. 回報 stdout 結尾:
\`\`\`
FLEET_SUBAGENT_RESULT: SUCCESS | PARTIAL | FAILURE
CONSUMER_ID: <consumer_id>
STAGED_FILES: <comma-separated paths>
COMMIT_PLAN_MSG: <你在 step 6 生的、worktree 已驗證過 hook 的 msg verbatim>
CODEX_FINDINGS: <codex high-research 找到的關鍵 URL / issue,若有>
FAILURE_DETAIL: <若 FAILURE / PARTIAL,一段 ≤ 10 行的失敗描述>
\`\`\`
**MUST** `COMMIT_PLAN_MSG` 是你在 worktree 上 **真正用過** 的 msg(hook 已驗證),orchestrator 會 verbatim 用這條去 commit main。
## Git Baseline / 禁止
- worktree 內 main fork 過來的 in-flight 變更:**不要動**
- 工作範圍:`package.json` + lockfile + brief.callsites 列到的檔案
- ❌ `git push` / `git commit` on main / `/commit` skill
- ❌ 動 brief.callsites 範圍外的 source code
- ❌ request_user_input(subagent 不直接跟 user 對話)
## Long-running
orchestrator 會用 SendMessage 跟你續跑 phase。請保持 session 狀態、不要主動結束。
```
### F.6.2 同時送多個 spawn_agent 工具 call
**MUST** 一個 message 內塞所有 hit consumer 的 spawn_agent 工具 call(per [[parallel-fanout]]);每個有獨立 `name`(建議:`fleet-<consumer-id>`)方便後續 SendMessage。
## Step F.7 — 主線 watch + 聚合
### F.7.1 收到 subagent 完成通知
每個 subagent 完成會送 `<task-notification>`。依 `FLEET_SUBAGENT_RESULT` 分流:
| 結果 | 處理 |
| --- | --- |
| SUCCESS | 記錄到 summary、worktree 已 merge-back、main staged |
| PARTIAL | 升版成功但有部分 callsite 沒套用 BC → 列入「需 user 確認」區 |
| FAILURE | 升版失敗 → 列入「未 land」區,worktree 保留供 user 手動處理 |
### F.7.2 等所有 subagent 完成
不要在中間 push 任何 consumer。**MUST** 等到全部 subagent 回報才進 Step F.8。
## Step F.8 — Fleet push gate + 摘要
### F.8.1 顯示聚合 plan
```
## dep-upgrade · fleet 聚合結果
| consumer | 狀態 | staged files | commit msg | wt path |
```
### F.8.2 request_user_input fleet push gate
```
[1] 全部 OK 的 consumer 一起 commit + push
[2] 我要先 review 一兩個 consumer 的 diff
[3] 只 push 某幾個 consumer(指定 consumer_id)
[4] 都先停下,我手動 review 後再決定(保留 staged 狀態)
```
**MUST 等 user 選**。**NEVER** 主線自決定全部 push。
### F.8.3 按 user 選擇執行
**Race 警示**:subagent 在 Step F.6 結束時把 upgrade staged-on-main,到 orchestrator 在 Step F.8 跑 commit 之間的時間窗,consumer 端**並行 session** 可能跑 `git add -A` / `git reset` 把 staged 區掃掉、或把 upgrade staged 內容夾帶進別人的 commit。**MUST** 在 commit 前 verify staged 集合:
```bash
# 預期 staged:subagent 回報的 STAGED_FILES
git -C <consumer_path> diff --cached --name-only > /tmp/dep-fleet-actual-staged-<consumer-id>.txt
diff <expected-staged-list> /tmp/dep-fleet-actual-staged-<consumer-id>.txt
```
- 若一致 → 正常 commit + push
- 若 staged 被擴大(多了不是 subagent 動的檔)→ STOP 該 consumer,列入摘要 ⚠️ 區「staged 被並行 session 擴大」+ 不 push
- 若 staged 被縮小或消失 → 查 `git log -p package.json` 確認升版是否已落地:已落地 → 列入摘要 ✅ Pushed 區,標記「by-other-session race」;未落地 → ❌ Failed 區、需重跑
**選 [1]**:對每個 SUCCESS consumer:
```bash
cd <consumer_path>
git status --short # race verify
git commit -m "<COMMIT_PLAN_MSG>" # **MUST** 用 subagent 回報的 msg verbatim
git push origin <current_branch> # trunk-based 都是 push main
```
- **選 [2]/[4]**:列出 `git diff --staged` 指令給 user,**NEVER** 替 user 跑 commit
- **選 [3]**:對指定 consumer_id 跑 commit + push,其餘留 staged
### F.8.4 摘要彙報
```markdown
## dep-upgrade · fleet 摘要(<YYYY-MM-DD HH:MM>)
**Sweep**:`<pkg>` `<from>` → `<target_version>`
**Release**:<release_url>
**BC clauses**:<N> 條套用、<M> 條無 callsite
### ✅ Pushed (P) / ⏸️ Staged but not pushed (S) / ❌ Failed (F) / ⏭️ Skipped (K)
[tables...]
```
---
# § Codex prompt templates(兩 mode 共享)
> **Authoring source**:`~/offline/clade/vendor/snippets/codex-upgrade-prompts/{medium,high}.md`(clade-only,不散播)。下方 § A § B inline 是 plugin cache 副本,**改其中一處時兩邊都要同步**。未來會由 TD-129 dispatch script 機械化渲染。
## § A — Medium 派工 prompt(per-package)
主線 / subagent 在生 prompt 時用 substitution:
- `<pkg>` / `<from>` / `<to>` / `<wt-path>` / `<branch>` / `<PM>` / `<lockfile>`:每個 package 不同
- `<install-flag>`:依 deps/devDeps 偵測(Outdated mode Step O.1.3)或 brief 的 `dep_or_devdep`(Fleet mode),`dep` 用空字串、`devDep` 用 `-D`
- `<baseline-paths>`:跑 `cd <wt-path> && git status --porcelain` 動態抓 unstaged + untracked
- `<plan-first-block>`:patch 升版時填空字串、minor/major 時填下方 Plan-first 區段
- `<verification-steps>`:依升版類型填 typecheck(patch)/ typecheck + build(minor)/ typecheck + build + test(major)
- `<changelog-block>`:**changelog-aware mode 才填**(Fleet mode 從 brief 渲染),非 changelog 模式留空白
模板:
```markdown
[DELEGATED-BY-CLAUDE-CODE]
# Task: 升級 <pkg> 從 <from> 到 <to>
你在 worktree `<wt-path>`(branch `<branch>`)跑。Package manager 是 `<PM>`。
<changelog-block>
<plan-first-block>
## Git Baseline
worktree 內這些 path 是 main fork 過來的 in-flight 變更,**不要動**:
<baseline-paths>
你的工作範圍**只動**:`package.json` + `<lockfile>`。
## 升版步驟
1. `<PM> add <install-flag> <pkg>@<to>`
<verification-steps>
N. 全綠後 commit
## Commit Authorization
**允許**:
- Selective stage:`git add package.json <lockfile>`
- Commit:`git commit -m "🧹 chore: wt upgrade-<pkg>-<from>→<to>"`(emoji-conventional commitlint 合規)
**禁止**:
- `git add -A` / `git add .`
- `--no-verify`(per `rules/core/commit.md` hard rule)
- `git push` / `git stash` / `git commit --amend`
- 修改 view 層檔(`.vue` / `.tsx` / `.jsx` / `app/pages/` 等)— 升 deps 不該動 view
- 動 Git Baseline 列的 in-flight 檔案
## 回報格式(MUST,stdout 結尾輸出)
成功:
```
PHASE_RESULT: SUCCESS
COMMIT: <sha>
FILES_CHANGED: package.json, <lockfile>
VERIFICATION: <依驗證步驟回報>
```
失敗:
```
PHASE_RESULT: FAILURE
STAGE_FAILED: <install | typecheck | build | test>
ERROR_TAIL:
<≤ 30 行 error message>
HYPOTHESIS: <一句話猜為什麼炸>
SUGGESTED_NEXT: <要不要升 high research / 要查什麼 issue / changelog>
```
失敗時**不要**自己 commit、不要強過 fail、不要刪 / revert lockfile。
```
**`<plan-first-block>` 填充**(minor / major 才填,patch 留空):
```markdown
## Plan-first(MUST)
在動任何 Edit / Write / Bash 寫入動作之前,先在 stdout 輸出 `## Plan` section:
- 預期要改的檔案
- 預期的驗證指令
- 預期影響範圍
Plan 寫完**立刻**繼續執行,不要等確認。
```
**`<verification-steps>` 填充**:
- Patch:`2. <PM> typecheck → 0 errors`
- Minor:`2. <PM> typecheck → 0 errors\n3. <PM> build → 成功(若有 build script)`
- Major:`2. <PM> typecheck → 0 errors\n3. <PM> build → 成功\n4. <PM> test 相關測試 → 全綠`
**`<changelog-block>` 填充**(Changelog-aware mode 才填,非 changelog 模式留空白):
```markdown
## Changelog(orchestrator 預先研究,不用再 web search)
Release: <release_url>
### Breaking changes
- **<category>**: <description>
Before: `<before>`
After: `<after>`
Affected APIs: <affected_apis joined>
### Callsites in this consumer(orchestrator 預先掃過)
- `<file>:<line>` 使用 `<symbol>`
### 動手範圍
除了 `package.json` + `<lockfile>` 之外,**可以**改上面 callsites 列到的檔案來套用 BC 修正。**NEVER** 改 callsites 清單外的其他 source code(即使「順手很合理」也不行 — 那是 unrelated refactor)。
```
## § B — High research 派工 prompt(escalation)
```markdown
[DELEGATED-BY-CLAUDE-CODE]
# Task: 升級 <pkg> 從 <from> 到 <to>(research mode)
Medium 已經失敗一次。失敗 tail:
\`\`\`
<medium-failure-tail>
\`\`\`
Codex 自報原因:<medium-hypothesis>
## 你的工作流程
**Phase R(Research,MUST 先做)**:
1. 用 **github** plugin 查 `<pkg>` 的 GitHub repo:
- releases / tags / changelog → 找 `<from>` → `<to>` 之間的 breaking changes
- issues 用關鍵字搜失敗的 error message
- migration guide / upgrade guide pull request
2. 用 **browser-use** 或 web search:
- `<pkg> migration guide <to>` / `<pkg> breaking changes <to>`
- 套件官方 docs site
3. 把研究結果濃縮成 `## Research Findings` section 輸出(≤ 20 行)
**Phase P(Plan,研究完才寫)**:
依 Research Findings 寫 `## Plan` section:要改哪些 source code 檔、預期驗證步驟、預期影響範圍。
**Phase I(Implement)**:
跟 medium 派工一樣(install → typecheck → build → test → commit),但 commit message 改成:
\`\`\`
🧹 chore: wt upgrade-<pkg>-<from>→<to> (researched <最關鍵的 issue/release URL slug>)
\`\`\`
## Git Baseline / Commit Authorization
同 medium 派工,不重述。
## 回報格式
成功時 stdout 結尾:
\`\`\`
PHASE_RESULT: SUCCESS
COMMIT: <sha>
RESEARCH_KEY_FINDINGS:
- <一行 breaking change 摘要>
RESEARCH_URLS:
- <release URL>
VERIFICATION: typecheck PASS, build PASS, test PASS
\`\`\`
失敗時:
\`\`\`
PHASE_RESULT: FAILURE
STAGE_FAILED: <stage>
ERROR_TAIL:
<≤ 30 行>
RESEARCH_FINDINGS_SO_FAR:
<線索>
WHY_STUCK: <一句話為什麼即使查到資訊也卡住>
\`\`\`
```
---
# 禁止事項(兩個 mode)
**通用**:
- **NEVER** 在 main working tree 跑(無 worktree gate)— 兩個 mode 都受此規約
- **NEVER** 主線自己改 `package.json` 或在升版階段(Step O.2)跑 `pnpm add` / `pnpm install`(升版全程委派給 codex / subagent)。**例外**:Step O.3.2.c post-merge-back `pnpm install` 是 setup chore,不是升版動作
- **NEVER** medium 失敗就直接問使用者 — 必須先自動升 high research
- **NEVER** high 也失敗就主線自己接手 — 必須 request_user_input 讓使用者選
- **NEVER** 把 merge-back 當「下一步」丟給 user 自己跑(per [[worktree-default]] §5)
- **NEVER** 把 `[DELEGATED-BY-CLAUDE-CODE]` marker 漏掉 — codex 端有 Runtime Gate 會擋
- **NEVER** 派 codex 時把 sandbox 換成 `read-only` / `workspace-write`(會擋 MCP)
- **NEVER** `git add -A` / `git add .` 在 main — 一律 selective stage
**Outdated mode 限定**:
- **NEVER** 一次派多 package 並行(破壞 per-package commit boundary,bisect 失效)
- **NEVER** 自動跑 `/commit` 收尾 — 留 commit 時機 / message / sign-off 給 user
**Fleet mode 限定**:
- **NEVER** 不在 clade home 跑(Step F.1.1 preflight)
- **NEVER** 一次 sweep 多個套件 / 多個 release(per carve-out「一個套件 × 一個 target version」)
- **NEVER** 在 BC 解析階段帶入 release notes 沒提到的修正(要找得到對應 clause 才能進 brief)
- **NEVER** subagent 直接 push 或 /commit — 集中 gate 在 Step F.8
- **NEVER** 跳過 Step F.5 user gate / Step F.8.2 fleet push gate(即使 plan / 聚合結果看起來很安全)
- **NEVER** 在 Step F.6 fan-out 後忘了等所有 subagent 完成就進 Step F.8
- **NEVER** 對 `business_activity: "paused"` 的 consumer 動手(即使 found: true)
- **NEVER** 主線替 subagent 改 worktree 內檔
- **NEVER** 把 unrelated 套件 / refactor / cleanup 搭便車進來(per carve-out 紅線)
# 相關規約
- [[clade-role-and-todo-discipline]] § upstream-driven dep migration — Fleet mode carve-out 條件
- [[worktree-default]] §1, §5 — worktree gate + skill-owned lifecycle
- [[parallel-fanout]] — Fleet mode 長駐 subagent + thin brief 規約
- [[agent-routing.codex-watch-protocol]] — codex 派工 + watch + Runtime Gate marker
- [[commit]] — Outdated mode main 端 selective stage 後的 `/commit` 收尾流程
No comments yet. Be the first to comment!