| 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@version / 純 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 約束。 |
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) 跨 registry 全命中 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:兩種觸發路徑:
- Outdated pre-scan(Step O.1.5):主線用
dep-fleet-discover.mjs + gh release view 拿 changelog → 分類為 bugfix / adaptation / feature → 依分類決定 codex prompt 是否帶 <changelog-block>。
- Fleet brief:被 § 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 的兩條路:
- 主線目前已在 worktree(cwd 名含
-wt/)→ 跳過 Step O.0、繼續 Step O.1
- 主線在 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 分類
-
偵測 package manager(看 lockfile):
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」。
-
拉 outdated:
pnpm outdated
pnpm --format=json 在 monorepo workspace 會回多行 NDJSON 不是純 JSON,直接讀人類格式 + 主線自己 parse 比較穩。
-
讀 package.json 把每個 outdated 套件分類為 deps / devDeps(P0,下 prompt 必用):
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 後才發現)。
-
分類版號差距(從低風險到高風險升):
- 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
-
@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.5.5 回報給 user 的計畫表 MUST 在這些 @types/* 條目後標註「(capped to Node N / react N)」讓 user 看到封頂邏輯有作用。
NEVER 為了「升到最新」把 @types/* 推超過對應 runtime / lib — 型別會漂移:typecheck 用的是 newer types,但 runtime 跑的是舊版 API,bug 表現是「TS 說沒問題、prod 卻炸」這種最難 debug 的型別。
-
進入 Step O.1.5 Changelog pre-scan(見下方)。pre-scan 完成後才回報增強版計畫給使用者。
Step O.1.5 — Changelog pre-scan(主線直接做)
在 O.1 收完 outdated 清單 + deps 分類 + 版號差距 + @types/* 封頂後,升版前先理解每個 package 的 release 性質,依此決定升版策略。主線直接完成(不派 codex),零額外 overhead。
O.1.5.1 — Batch discover release URLs
對每個 outdated package,跑 dep-fleet-discover.mjs 拿 release URL:
node ~/offline/clade/vendor/scripts/dep-fleet-discover.mjs --pkg "<pkg>" --version "<to>"
- 可並行跑(獨立、唯讀)— 多個 Bash tool call 同一 message
- 記錄每個 package 的
release_url + source + repo_url
source: "none" 的 package 標記分類為 unknown,skip O.1.5.2–O.1.5.4
- Skip
@types/*:型別定義的 changelog 對分類無意義 → 直接標 bugfix
O.1.5.2 — Batch fetch release bodies
對 source != "none" 的 package,fetch release body:
gh release view v<to> --repo <owner>/<repo> --json body -q .body > /tmp/dep-prescan-<pkg-slug>.md
gh 失敗(private repo / rate limit / 無 release)→ 若 source: "changelog_md",用 WebFetch 拿 changelog URL → 否則標 unknown
- 寫出的檔可在 O.2.1 codex prompt builder 復用
O.1.5.3 — 主線分類(三類 + 混合)
主線讀所有 fetch 到的 release body,對每個 package 判定分類標籤:
| 訊號 | 標籤 |
|---|
| release body 全是 bug fix / patch notes / perf / internal refactor,無 user-facing API 變更 | bugfix |
| 有 breaking changes / deprecations / renamed APIs / 移除的 API / signature 變更 / config schema 變更 | adaptation |
| 有重大新 feature(新 component / 新 API / 新 capability),consumer 可能受益 | feature |
changelog 不可得(O.1.5.1 source: "none" 或 O.1.5.2 fetch 失敗) | unknown |
混合判定(一個 release 可有多個標籤):
- BC + feature → 同時標
adaptation + feature(升版時修 BC,完成後 HANDOFF 記 feature)
- 只有 deprecation(尚未 remove)→
adaptation(趁這次改掉,不等到 remove 才爆)
- 小 feature(如新增一個 option、default off、不影響現有行為)→ 仍歸
bugfix(不值得 HANDOFF entry)
- 重大 feature 但無 BC → 純
feature(升版走 bugfix 路徑,HANDOFF 記 feature)
adaptation 時提取 BC 結構化資料(復用 Fleet mode F.2.2 schema):
{
"breaking_changes": [
{
"category": "rename | removal | signature | config-schema | peer-bump",
"description": "<一句話人話>",
"affected_apis": ["<symbol>"],
"before": "<code snippet>",
"after": "<code snippet>"
}
],
"deprecations": [{ "api": "<name>", "replacement": "<name>" }]
}
affected_apis 必須是可搜尋符號(函式名 / component 名 / config key),不要寫人話
before / after 是可貼上的 code snippet,給 codex 看
feature 時提取 feature 摘要:
{
"notable_features": [
{ "description": "<feature 描述>", "opt_in": true }
]
}
O.1.5.4 — Callsite quick-scan(adaptation only)
對標記 adaptation 的 package,用 rg 快掃每個 BC 的 affected_apis:
rg -n "<symbol>" --type-add 'vue:*.vue' --type vue --type ts .
- 記錄
file:line 組合,後續 O.2.1 填入 <changelog-block> 的 callsites 段
- 過濾 docs-only 命中(
.md / README / docs/)— 不是 runtime callsite
- 比 Fleet mode 的 codebase-memory-mcp 輕量(單 consumer、不需 index)
- callsite = 0:不刪該 BC(仍可能 transitive 影響),prompt 內提示
callsites: 0(codex 自行 grep 確認)
O.1.5.5 — 增強版計畫回報
偵測到 <PM>,outdated 共 <N> 個(changelog pre-scan 完成):
### 🟢 Bug fix only — <a> 個(順升)
| Package | from → to | 摘要 |
| --- | --- | --- |
### 🟡 需要適配 — <b> 個(有 BC / deprecation)
| Package | from → to | BC 摘要 | callsite 數 |
| --- | --- | --- | --- |
### 🔵 有新 Feature — <c> 個(升版 + HANDOFF 記 feature 適配計畫)
| Package | from → to | Feature 摘要 |
| --- | --- | --- |
### ⚪ Changelog 不可得 — <d> 個(走現有 medium → high fallback)
| Package | from → to |
| --- | --- |
建議升級順序:🟢 → ⚪ → 🟡 → 🔵(先簡單的、再需要適配的、最後有 feature 的)
要排除任何套件不升嗎?要調整任何分類嗎?
使用者可:
- 指定排除清單(如
vue lockstep with nuxt,先不動)
- 調整分類(如「bar 不用 adaptation,降成 bugfix」)
- MUST 等使用者確認才進 Step O.2 — 不要自決定
Smoke test 建議:第一次跑這個 mode on 一個新 consumer 時,先挑 1 個最低風險 🟢 patch 跑 smoke,驗證 worktree fork + codex dispatch + commit boundary 都對,再批次推剩下的。
Step O.2 — 逐套件 codex 派工 loop(category-aware)
每個 package 走以下子流程,一個 package 一個 codex 派工、一個 commit。Step O.1.5 的分類決定 prompt 填充策略:
| 分類 | <changelog-block> | <plan-first-block> | 工作範圍 | 驗證 |
|---|
bugfix | 空(現有行為) | patch 空 / minor 填 | package.json + lockfile only | typecheck(patch)/ +build(minor) |
adaptation | 填入 BC + callsites(復用 Fleet 的 changelog-block 格式) | 必填 | package.json + lockfile + callsite 檔 | typecheck + build + 相關 test |
feature(無 BC) | 填入 feature 摘要(informational) | 依版號差距 | package.json + lockfile only | 同 bugfix |
feature + adaptation | 填入 BC + callsites + feature 摘要 | 必填 | 同 adaptation | 同 adaptation |
unknown | 空(現有行為) | 依版號差距 | package.json + lockfile | 同 bugfix |
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 調整)
- 失敗時的回報格式
<changelog-block> 填充(adaptation / feature+adaptation 才填,其他留空):完全復用 § Codex prompt templates · Changelog-block 填充格式,callsites 來源為 O.1.5.4 的 rg 結果(而非 Fleet mode 的 codebase-memory-mcp)。adaptation 的 codex 工作範圍擴大到 callsite 檔:Commit Authorization 加 git add <callsite-files>。
feature(無 BC)的 <changelog-block> 填充:只含 feature 摘要段(informational),不含 callsites 或「動手範圍」段 — 告知 codex 這個版本有新功能但升版只需 bump,不必改 source code。
Plan-first 條件化(DRY + 降 token):
bugfix + patch:MAY 省略 Plan-first 硬指令,prompt 直接列「install → typecheck → commit」固定三步
bugfix + minor / unknown + minor:MUST 加 Plan-first
adaptation / feature+adaptation / major(任何分類):MUST 加 Plan-first
驗證步驟(依分類 + 升版類型,取嚴格者):
bugfix + patch:pnpm install(隱式跑) + pnpm typecheck 0 errors
bugfix + minor / unknown:typecheck + 如有 build script 跑一次 + 相關 test
adaptation(任何版號差距):typecheck + build + 相關 test
- major(任何分類):typecheck + build + 全 test + codex 自己決定要不要 smoke test
O.2.2 Dispatch background bash(low)
cd <worktree-path> && codex exec \
--model gpt-5.6-sol \
--dangerously-bypass-approvals-and-sandbox \
--skip-git-repo-check \
-c model_reasoning_effort=low \
< /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,再 agent-browser / 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
MAIN_PATH=$(git worktree list --porcelain | head -1 | awk '{print $2}')
cd "$MAIN_PATH"
從此往後所有 O.3 命令都在 main 跑。
O.3.2 Merge-back(含 baseline blocker 自動處理)
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 自動清理
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 版本。
<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
git reset HEAD
git add package.json <lockfile-path>
<lockfile-path> 對應 PM:pnpm-lock.yaml / package-lock.json / yarn.lock / bun.lockb。
NEVER git add -A / git add .。
adaptation 額外 stage:codex 有改 callsite 檔時(import path 重寫 / API rename),那些檔也在 squash 後落在 main working tree。MUST 也加進 git add,但 stage 前 git status 印給 user。
O.3.5 — Feature HANDOFF entries(feature 分類 only)
對每個在 Step O.1.5 分類含 feature 標籤的 package,append entry 到 consumer 的 HANDOFF.md:
## <pkg> <to> 新 feature 適配
**Discovered**: <YYYY-MM-DD>
**Source**: <release_url>
**Upgraded in**: dep-upgrade session <date>
新增功能:
- <feature description 1>
- <feature description 2>
建議適配方式:(待規劃)
- MUST 把
HANDOFF.md 加進 selective stage:git add HANDOFF.md
- 若 consumer 沒有
HANDOFF.md → skip(不替 consumer 建檔)
- O.3.6 摘要的 🔵 區段列出寫了哪些 HANDOFF entries
O.3.6 摘要彙報
## 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 失敗)
### 🟢 Bug fix — 一次成功
| Package | <from> | <to> | Commit |
| --- | --- | --- | --- |
### 🟡 Adaptation — 成功套用 BC
| Package | <from> | <to> | BC 摘要 | callsite 改動 | Commit |
| --- | --- | --- | --- | --- | --- |
### 🔵 Feature — 已升版 + HANDOFF 已記
| Package | <from> | <to> | Feature 摘要 | HANDOFF entry |
| --- | --- | --- | --- | --- |
### ⚠️ 升 high research 後成功(K)
| Package | <from> | <to> | BC 摘要 | Commit |
| --- | --- | --- | --- | --- |
### ⏭️ 跳過(S)
| Package | <from> | <to> | 跳過原因 |
| --- | --- | --- | --- |
### 📦 Main 端狀態
- Staged:`package.json`、`<lockfile-path>`(+ callsite 檔 + `HANDOFF.md` 若有)
- 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 → 命中的 consumer 全部升版(以 registry 為準)+ 套用 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
pwd
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 時,跑:
node vendor/scripts/dep-fleet-discover.mjs --pkg "<pkg>" [--version <ver>]
輸出 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:
npm view <pkg> dist-tags.latest 拿 latest version(無 --version 時)
npm view <pkg> repository.url 拿 repo URL
- 試
https://github.com/<owner>/<repo>/releases/tag/v<ver> 200 OK
- 失敗 → 試
releases/tag/<pkg>@<ver>(monorepo 慣例)
- 仍失敗 → fallback raw
CHANGELOG.md
- 全失敗 → 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
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 跑,直接讀檔做解析即可),輸出統一格式:
{
"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 保證
- ✅ 若 sweep 會重寫 hit consumer
package.json 的 test / lint / typecheck script(換工具 / 改命令):原本有 .clade/bin/clade-gate run <gate> -- 前綴的,MUST 只換 -- 後面的內層命令、保留前綴(per [[test-scripts]] § MUST:重寫已包 clade-gate 的 script 時保留前綴)。主線在 land 前 git -C <consumer-path> show <commit_sha> -- package.json 抽查前綴沒被整行覆蓋
任一條不滿足 → STOP,回 user 解釋哪條沒過。
實證:2026-06-09 一次 migrate lint from oxlint to vp lint across consumers sweep 對 6 consumer 整行覆蓋 lint script,把 clade-gate 前綴全弄掉(TD-152 5 天前才採用)→ instrumentation 退化。此條 + [[test-scripts]] 的 MUST 是補這個缺口。
Step F.3 — Fleet scan 命中 consumer
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:
{
"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 Subagent Fan-out 紀律(user-global AGENTS.md;回報契約見 [[agent-routing]] § Subagent 回報契約),每個 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
# 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 Subagent Fan-out 紀律);每個有獨立 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 集合:
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:
cd <consumer_path>
git status --short
git commit -m "<COMMIT_PLAN_MSG>"
git push origin <current_branch>
- 選 [2]/[4]:列出
git diff --staged 指令給 user,NEVER 替 user 跑 commit
- 選 [3]:對指定 consumer_id 跑 commit + push,其餘留 staged
F.8.4 摘要彙報
## 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 模式留空白
模板:
[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 留空):
## 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 模式留空白):
## 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)
[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. 用 **agent-browser** 或 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 Subagent Fan-out(user-global AGENTS.md)+ [[agent-routing]] § Subagent 回報契約 — Fleet mode 長駐 subagent + thin brief + 4-status 規約
- [[agent-routing.codex-watch-protocol]] — codex 派工 + watch + Runtime Gate marker
- [[commit]] — Outdated mode main 端 selective stage 後的
/commit 收尾流程