ワンクリックで
commit
依功能分類變更並逐步完成 commit,遵循 commitlint 規範。Use when 使用者說「commit」「提交」「分批 commit」,或 working tree 有多組 unrelated 變更需要分門別類提交。
Codex または Claude でインストール この Prompt をコピーして Codex、Claude、または他のアシスタントに貼り付けると、Skill ページを確認してインストールできます。
メニュー
依功能分類變更並逐步完成 commit,遵循 commitlint 規範。Use when 使用者說「commit」「提交」「分批 commit」,或 working tree 有多組 unrelated 變更需要分門別類提交。
Codex または Claude でインストール この Prompt をコピーして Codex、Claude、または他のアシスタントに貼り付けると、Skill ページを確認してインストールできます。
SOC 職業分類に基づく
Use when 使用者說「自動推」「loop」「幫我把 change 推到 ready」「不在的時候繼續做」、或 routine fire 自動觸發(--unattended)。適用於推進既有 spectra change;NOT for 非 spectra 工作、一次性任務、interval 盲跑命令(用 /loop)、user 在場想逐項拍板(用 /goal)、或想設計新 loop(看 vendor/snippets/loop-engineering cookbook)。
依功能分類變更並逐步完成 commit,遵循 commitlint 規範。Use when 使用者說「commit」「提交」「分批 commit」,或 working tree 有多組 unrelated 變更需要分門別類提交。
Deprecated alias — 已改名 /change-loop(2026-07-05)。Use when 既有 routine 或舊指令仍呼叫 /loop-engineer 時轉發。
Analyze artifact consistency for a change
Implement or resume tasks from a Spectra change
Archive a completed change
| name | commit |
| description | 依功能分類變更並逐步完成 commit,遵循 commitlint 規範。Use when 使用者說「commit」「提交」「分批 commit」,或 working tree 有多組 unrelated 變更需要分門別類提交。 |
| effort | high |
$ARGUMENTS
政策、禁止事項、commit 類型表見 .claude/rules/commit.md。本檔只定義執行流程。
node .claude/scripts/commit-lock.mjs acquire
失敗(exit 1)代表另一個 session 正在跑 /commit → 停下,向使用者回報鎖資訊,不要自行 rm 清鎖或重試。
成功後此 session 取得獨占權,直到最後一步釋放。中斷處理:若 /commit 流程中途失敗 / 使用者中斷,仍必須在終止前呼叫 node .claude/scripts/commit-lock.mjs release;漏釋放的鎖會在 30 分鐘後被下次 acquire 自動清除(可用 COMMIT_LOCK_STALE_MINUTES 調整)。
commit-lock 只擋同時兩個 /commit;不擋「commit 跑時別 session 在跑 publish / propagate / wt-helper add / rescue-consumer」造成 staged 區意外污染(已實證 3 條 incident,見 docs/pitfalls/2026-05-{14,18,22}-*.md)。Step 0-Coord 跑 3 個 detection signal warn-only,命中再用 AskUserQuestion 讓 user 決定等候還是強制繼續。
.git/index.lock mtime < 60 秒別 session 正在 staging(git add / git commit / git checkout 過程中會建這個 lock,正常結束會自動移除)。
GIT_DIR=$(git rev-parse --git-dir)
LOCK="$GIT_DIR/index.lock"
if [[ -f "$LOCK" ]]; then
NOW=$(date +%s)
LOCK_MTIME=$(stat -f %m "$LOCK" 2>/dev/null || stat -c %Y "$LOCK" 2>/dev/null)
AGE=$((NOW - LOCK_MTIME))
if (( AGE < 60 )); then
echo "SIGNAL_1_HIT: index.lock age=${AGE}s path=$LOCK"
fi
fi
解讀:AGE < 60 → 別 session 大機率仍活著正在 staging;AGE >= 60 → stale lock(崩潰殘留,建議手動 rm "$LOCK" 但不在 Step 0-Coord 處理,留給 user 自決)。
scripts/publish.mjs 的 --stash-untracked flow 跑時會在 .spectra/stash-meta-<tag>.json 落 sidecar(含 pid / cwd / fileList),publish 完成才 cleanup。看到 sidecar 代表 publish 流程還在跑或崩潰未收尾。
REPO_ROOT=$(git rev-parse --show-toplevel)
SIDECARS=("$REPO_ROOT"/.spectra/stash-meta-*.json)
if [[ -f "${SIDECARS[0]}" ]]; then
for f in "${SIDECARS[@]}"; do
[[ -f "$f" ]] || continue
echo "SIGNAL_2_HIT: publish stash sidecar=$f"
done
fi
解讀:任一 sidecar 存在 → 別 session 的 publish flow 仍未收尾;commit 時若擴大 staging 範圍可能跟 publish 的 auto-stash pop 撞 conflict。
vendor/scripts/wt-helper.mjs cmdAdd --baseline-strategy stash 會建 wt-baseline/<slug>/<session-id>/<iso> stash entry,建完立刻 apply + drop。stash list 裡看到 wt-baseline/ 命名且 reflog timestamp < 60s → wt-helper add 可能還在跑。
git stash list --format='%gd %ct %gs' 2>/dev/null \
| awk -v now=$(date +%s) '
/wt-baseline\// {
age = now - $2
if (age < 60) {
printf "SIGNAL_3_HIT: wt-baseline stash age=%ds entry=%s\n", age, $1
}
}'
解讀:命中 → wt-helper add 流程未結束;此時 commit 跑下去可能撞 wt-helper 中段的 stash apply / index reset 序列。
全部 silent(三條 signal 都沒命中)→ 直接輸出 ✅ 0-Coord 通過(無 cross-session 污染信號),進入 Step 0-Scope。
任一 signal 命中 → stderr 印 warn block:
⚠️ 0-Coord: 偵測到 cross-session 活動信號
<列出命中的 SIGNAL_N_HIT 行>
可能後果:
- 別 session 正在 staging → 你的 git add 可能跟它的 index 寫入互踩
- publish flow 未收尾 → 你的 commit 可能跟 auto-stash pop 撞 conflict
- wt-helper add 中途 → baseline staged index 可能污染你的 selective stage
建議處置(mitigation hint):
1. 等 60 秒後重跑 /commit(最常見:別 session 馬上結束就乾淨了)
2. 跑 git status / git stash list / ls .spectra/ 確認別 session 真實狀態
3. 確認別 session 沒在跑後再繼續
接著用 AskUserQuestion 二擇一:
label: "等候重試", description: "退出 /commit,等 60 秒後重跑(推薦:避開 staged 污染風險)"label: "強制繼續", description: "接受 staged 污染風險繼續跑 Step 0-Scope(user 確認別 session 已結束時用)"選 A → 釋放 commit-lock 後 STOP;選 B → 輸出 ⚠️ 0-Coord 強制繼續(user 接受風險),進入 Step 0-Scope。
rm .git/index.lock 或清掉 sidecar — 那是別 session 的 SoT,誤刪比繼續跑風險更高同類 race 也存在於 ad-hoc commit(不走本 skill 的單檔 commit、HANDOFF 補一行就 commit、修 typo 就 commit 等)。預防規約見
rules/core/commit.md§ Ad-hoc commit 必走git commit --only -- <paths>。
主線從 commit SKILL 派 codex 跑 commit 工作時(例如 /wt worktree 內派 codex commit phase),MUST 走 rules/core/agent-routing.codex-watch-protocol.md § Codex 派工的標準流程 + Codex Watch Protocol。禁止 Agent tool with subagent_type: screenshot-review 派視覺 QA — sonnet wrapper 派工已多次驗證 self-rationalize(per [[pitfall-screenshot-review-sonnet-wrapper-self-rationalize]])。
預設行為:所有 git status 顯示的 uncommitted 變更(含與本次工作無關、其他 session 並行的 WIP、不認得的檔案)一律無條件列入本次 /commit 流程,照常跑 0-A review、在 Step 3 依功能分組成獨立 commit。
這是預設動作,不需要徵詢使用者意見。 Step 0-Scope 不是「決定要不要納入」的判斷步驟,而是「確認預設已生效」的紀錄步驟。看到 git status 任何輸出 → 直接進 0-A,NEVER 在這一步停下來問使用者「XX 看起來不在本次 scope,要不要排除?」。
理由:/commit 已付出品質閘門的完整成本。把 WIP 排除在外等於下次 /commit 要重跑一次閘門,浪費時間與 token。Step 3 分組階段就是設計來把「主線工作 + 並行 WIP」自然分到不同 commit,根本不需要在 Step 0 預先排除任何東西。
A. 使用者在 $ARGUMENTS 明確指名排除(白紙黑字、語意無歧義):
.env.local」reports/」app/ 底下」B. WIP 確實構成阻礙時的 stash + handoff 流程(見下節)
除 A、B 外一律全包。
預設行為是把所有 WIP 都靠 Step 3 分組成獨立 commit group,stash 是極少數例外。在啟動 stash 前MUST先排除下列「假阻礙」情境(這些情境一律走分組納入,NEVER stash):
stash 觸發條件(嚴格收斂為下列任一):
console.log、假資料、敏感資訊(且使用者尚未確認要保留)$ARGUMENTS 指名要 stash 某些檔案 / 變更確認觸發後執行(優先只 stash 阻礙檔,避免擴大連坐):
git stash push -u -- <具體檔案路徑> # 優先:只 stash 阻礙檔
# 確實必要時才整批:
git stash push -u -m "WIP: <簡述為何 stash> — see HANDOFF.md"
接著立即更新 HANDOFF.md(依 .claude/rules/handoff.md 格式),在 In Progress 或 Next Steps 寫入:
git stash list 能找到)git stash pop 後該怎麼收尾)寫完 HANDOFF 才繼續 0-A。
git restore / git restore --staged / git checkout -- / git checkout <path>git reset --hard / git clean -fd唯一例外:使用者在 $ARGUMENTS 明確、主動寫出 git restore / git checkout -- / revert <commit> 等指令或具體變更名稱,且語意完全無歧義時,才能執行。從模糊語氣(「不要這個」「這個怪怪的」)解讀為「使用者想丟棄」一律禁止 — 必須先確認是「排除本次 commit」(→ stash)還是「丟棄變更」(→ 拒絕,請使用者明確下指令)。
.claude/rules/commit.md 「人工檢查 Gate」hard rule 的執行點。MUST 在 Step 0 品質檢查之前 fail-fast,避免人工檢查未完的 change 浪費 5–15 min codex / screenshot review 時間。
確認當前 branch:
git rev-parse --abbrev-ref HEAD
輸出 ∉ {main, master} → 輸出 ⏭️ 0-MR 跳過(branch=<name>),進入 Step 0。
萃取本次 commit 觸及的 spectra change(含 staged + unstaged + untracked,排除 archive/ 子目錄):
{ git diff --name-only HEAD; git ls-files --others --exclude-standard; } \
| grep -oE '^openspec/changes/[^/]+' \
| grep -v '^openspec/changes/archive$' \
| sort -u
結果為空 → 輸出 ⏭️ 0-MR 跳過(本次變更未觸及任何 in-progress spectra change),進入 Step 0。
對每個 change 讀 <path>/tasks.md,同時判定「非 ## 人工檢查 段有 - [x]」與「## 人工檢查 段有 leaf - [ ]」(parent #N 有 scoped #N.M 子項時,parent 由子項 derive,MUST leaf-only 計,見 .claude/rules/manual-review.md 「Parent State Derivation」段):
awk '
/^## /{ in_mr = (/^## *人工檢查/) ? 1 : 0; next }
!in_mr && /^- \[x\]/ { has_impl = 1 }
in_mr && /^- \[[ x]\] #[0-9]+ / {
pid = $0; sub(/^- \[[ x]\] #/, "", pid); sub(/ .*/, "", pid)
parent_pending[pid] = (/^- \[ \]/); next
}
in_mr && /^ - \[[ x]\] #[0-9]+\.[0-9]+ / {
pid = $0; sub(/^ - \[[ x]\] #/, "", pid); sub(/\..*/, "", pid)
has_scoped_child[pid] = 1
if (/^ - \[ \]/) has_pending_leaf = 1
next
}
END {
for (p in parent_pending)
if (parent_pending[p] && !(p in has_scoped_child)) has_pending_leaf = 1
print (has_impl && has_pending_leaf) ? "BLOCK" : "OK"
}
' "<path>/tasks.md"
tasks.md 不存在 → 視為 OK(尚未進入實作階段的 change)BLOCK → 列入 blocker,順便用同樣 leaf-only 邏輯抓出未勾 leaf 數量(同 awk 改 END 累加 pending_count 並 print)blocker list 非空時 → auto-triage(per [[review-gui-surface]] MUST 9):
MUST NOT 直接停下叫 user 去 review-gui。改走 auto-triage:逐條讀 pending leaf item 的 annotation,判斷阻塞原因並自行推進 Claude 可處理的項目。
對每個 blocked change 的每個 pending leaf item,讀 tasks.md 該行判斷:
| Item 狀態 | 判斷方式 | Claude 動作 |
|---|---|---|
(fix-requested) | 行內含 (fix-requested) | dispatch /wt 修 code → wt-helper merge-back → 重拍截圖 → strip (fix-requested) + (claude-analyzed:) → 更新 (verified-*:) annotation |
| evidence missing | [verify:ui] / [verify:api] / [verify:e2e] 但無對應 (verified-*:) annotation | 走 [[agent-self-verification]] fallback chain 收 evidence |
(issue:) 無 (claude-analyzed:) | 行內含 (issue:) 但無 (claude-analyzed:) | triage issue → 走 (A)-(E) 路由 |
純 [review:ui] user 驗收 | 上述都不符,item 是 [review:ui] | 只有這類才引導 user 到 review-gui |
純 [discuss] | 上述都不符,item 是 [discuss] | 不在此處處理(archive walkthrough) |
Claude 可處理的項目全部推進完畢後,跑 mechanical readiness gate:
node ~/offline/clade/vendor/scripts/check-review-readiness.mjs \
--repo . --change <change-name>
✅ 0-MR auto-triage 完成,bucket=ready,釋放 lock,引導 user 到 review-guibucket + blocking 數據,繼續 auto-triage 或釋放 lock + 如實報告卡住原因,NEVER 只說「請去 review-gui」NEVER 跳過 readiness gate 自判 bucket — Claude 自判已 9 次證明不可靠(per [[review-gui-surface]] MUST 9)
NEVER 自動勾任何 [review:ui] 的 - [ ]、NEVER 提議跳過 gate、NEVER 提議 stash 走 tasks.md
blocker list 空 → 輸出 ✅ 0-MR 通過,進入 Step 0。
main / master 以外的 branch 判進 gate 範圍(feature branch 上後續有 /ship + PR review 擋)$ARGUMENTS 任何形式的「skip / ignore / override」旗標 — gate 無 overrideEdit tasks.md 勾掉 - [ ] 來通過 gate — 違反 .claude/rules/manual-review.md 核心規則tasks.md / change 目錄 stash / mv / rm 走讓 step 2 / 3 抓不到 — 等同繞過 hard rule.claude/rules/commit.md § Partial Archive Gate 的執行點。MUST 在 0-MR 之後、0-A/B/C 之前 fail-fast,避免 partial /spectra-archive state 默默 commit 進 main 導致 change artifact 永久遺失(per [[pitfall-spectra-archive-interrupted-leaves-partial-state]])。
確認當前 branch:
git rev-parse --abbrev-ref HEAD
輸出 ∉ {main, master} → 輸出 ⏭️ 0-Archive-Coupling 跳過(branch=<name>),進入 Step 0。
萃取本次 commit 有 staged-delete 的 spectra change(排除 archive 子目錄):
git diff --cached --name-only --diff-filter=D \
| grep -E '^openspec/changes/[^/]+/' \
| grep -v '^openspec/changes/archive/' \
| sed -E 's|^openspec/changes/([^/]+)/.*|\1|' \
| sort -u
結果為空 → 輸出 ⏭️ 0-Archive-Coupling 跳過(無 spectra change staged-delete),進入 Step 0。
對每個 change <X> 驗證兩條件:
條件 A — Archive directory 存在:
ARCH=$(find openspec/changes/archive -maxdepth 1 -type d -name "*${X}" 2>/dev/null | head -1)
[ -n "$ARCH" ] && [ -f "$ARCH/tasks.md" ] && [ -f "$ARCH/proposal.md" ]
失敗 → blocker MISSING_ARCHIVE_DIR,記下 <X>。
條件 B — Spec delta-sync 完整(僅對 HEAD 內 changes/<X>/specs/<cap>/ 存在的 cap 套用):
# 注意 trailing / — 沒加會回該目錄本身(一個 entry "specs"),加了才列子目錄
for cap_path in $(git ls-tree -d --name-only HEAD "openspec/changes/<X>/specs/" 2>/dev/null); do
cap=$(basename "$cap_path")
# 該 cap 的 spec.md 在 openspec/specs/ 必須有 staged modification
if ! git diff --cached --name-only -- "openspec/specs/$cap/spec.md" | grep -q . ; then
# 例外:若 openspec/specs/$cap/ 不存在於 HEAD(純新 cap),untracked staging 也算(git status --porcelain)
if ! git status --porcelain "openspec/specs/$cap/spec.md" 2>/dev/null | grep -qE '^A |^M |^\?\?'; then
echo "BLOCKER: $X cap=$cap spec delta-sync missing"
fi
fi
done
任一 cap 失敗 → blocker MISSING_SPEC_DELTA,記下 <X> + cap list。
trailing slash hard rule:git ls-tree -d --name-only HEAD <dir-path> 不加 trailing / 時返回該 dir 本身(一個 entry,等同 ls -ld);加 / 才會列出子目錄(等同 ls -d <dir>/*)。沒加 → cap_path="openspec/changes/<X>/specs" → cap="specs" → 查 openspec/specs/specs/spec.md 永遠 missing → 任何 change 永遠 BLOCK(false positive)。詳見 docs/pitfalls/2026-05-24-spectra-archive-interrupted-leaves-partial-state.md § Why slipped past tests。
blocker list 非空時:
MUST 立即釋放 lock:
node .claude/scripts/commit-lock.mjs release
印出 blocker 報告(每條 change 列 MISSING_ARCHIVE_DIR / MISSING_SPEC_DELTA <cap list>)+ recovery hint:
⛔ 0-Archive-Coupling 失敗 — partial /spectra-archive state detected
<X>: MISSING_ARCHIVE_DIR (archive/YYYY-MM-DD-<X>/ 不存在)
<Y>: MISSING_SPEC_DELTA (caps: burr-removal-workflow, focused-measurement-ui)
可能成因:
- /spectra-archive 跑到一半中斷(context out / shell bomb / user 切到別 task)
- wt-helper merge-back stash 把 spec delta 收進 wt-merge-block/* stash 後沒人 reconcile
Recovery(對每個失敗 change <X>):
DATE=$(date +%Y-%m-%d)
SRC="openspec/changes/<X>"
DEST="openspec/changes/archive/${DATE}-<X>"
mkdir -p "$DEST/specs"
git ls-tree -d --name-only HEAD "$SRC/specs/" 2>/dev/null \
| xargs -n1 basename \
| xargs -I{} mkdir -p "$DEST/specs/{}"
for f in $(git ls-tree -r --name-only HEAD "$SRC" | sed "s|^$SRC/||"); do
git show "HEAD:$SRC/$f" > "$DEST/$f"
done
若 spec delta 在 stash 內:
git stash list | grep wt-merge-block
git stash show 'stash@{N}' --name-only | grep '^openspec/specs/'
git checkout 'stash@{N}' -- openspec/specs/<cap>/spec.md
# 確認後 git stash drop 'stash@{N}'
Recovery 完成後重跑 /commit。
NEVER 自動修補(任何 mkdir / git show / stash extract 操作)— recovery 必須由 user 看完訊息決定(避免主線誤判 partial state、做出錯誤恢復)
blocker list 空 → 輸出 ✅ 0-Archive-Coupling 通過,進入 Step 0。
main / master 以外的 branch 判進 gate 範圍$ARGUMENTS skip / ignore / override 旗標git restore --staged 把 staged-deletes 退掉「敷衍 gate」— 那會掩蓋 in-flight archive statemkdir + git show > file 補建 archive dir — recovery 必由 user 決定(archive dir naming 含日期、是否該補 / partial 是否該 abort 都是判斷題)openspec/changes/<X>/** staged-deletes 用 git rm 重來 — 不解決問題,且會多一輪 staging churn0-A.0 simplify 必序跑且永遠第一(會刪死碼 / 精簡,否則後續 codex 白檢即將刪除 / 改寫的 code)。simplify 完成後,0-A.1 / 0-B / 0-C 三軸 MUST 並行(除非 fast-path 跳過 0-A.1),不可串行:
0-A.0 simplify(序跑、主線)
│
▼
[Fast-path: diff <20 行 + 限 doc/config + 無敏感路徑?]
│
├─ YES → skip 0-A.1/0-A.2,0-B/0-C 並行收尾
│
└─ NO ↓
┌─ 並行 fan-out(同一輪 tool call 內啟動) ─┐
├─ 軸 A:0-A.1 codex high(背景 bash,~5–15 min)
├─ 軸 B:0-B screenshot-review(subagent,條件觸發時派;~3–5 min)
└─ 軸 C:0-C pnpm check + pnpm test(主線 foreground;~2–5 min)
│
▼
匯合 → 合併所有修正 → 條件觸發 0-A.2 xhigh
│
▼
[大改動回扣:累計修正 >50 行 or >5 檔 → 重跑 codex high]
啟動順序(在同一個 assistant 回合內完成):
codex-review-safe.sh high(run_in_background: true)→ 拿到 background bash idscreenshot-review(若 0-B 觸發條件成立)pnpm check(foreground,主線同步跑)Fast-path 判定:
同時滿足下列三條件才能跳過 codex(任一不滿足都跑):
*.md、*.json(除 package.json 的 dependencies / devDependencies)、*.yml、*.yaml、.gitignore、HANDOFF.md、openspec/ROADMAP.mdreview-tiers.md Tier 3):**/migrations/**、**/auth/**、**/permission*、**/rls*、*.sql、**/*security*任何 .ts / .tsx / .vue / .mjs / .js / .sh 變更(即使單行)都不適用 fast-path —— 邏輯 bug 在小 diff 很常見,跨模型 review 仍有價值。
安全性保證:
codex-review-safe.sh high 確認新引入的程式碼也過 codex 眼睛禁止:
審查策略:
simplify skill —— 它看 reuse / 精簡 / 過度設計 / altitude 這條軸,codex exec review 不會抓。先處理掉避免後續 codex 重複指出已棄用:
code-review agent(Opus subagent)—— 職責與 codex exec review 高度重疊且同為 Anthropic 模型盲點,砍掉省一輪 subagent 成本對本次 working tree 變更跑 simplify review + 自動修 —— 聚焦 reuse / 精簡 / efficiency / altitude,codex exec review 不會抓這條軸。simplify 修完的版本才是下一步 codex exec review 應該看的對象。
執行方式:MUST 用 foreground Agent tool 開一個通用 subagent**(subagent_type: "general-purpose"、mode: "auto")跑下方 prompt 範本**,NEVER 用 Skill(simplify) 嵌套呼叫。
⚠️
subagent_typeMUST 是"general-purpose",NEVER 設成"simplify":/simplify是 Claude Code 內建 skill,不是 agent type,也不存在「simplify agent type」這種東西。這裡的意圖是「用通用 subagent 在隔離 context 跑下方那段 simplify review 工作」,不是去叫一個名為 simplify 的 agent。若誤把subagent_type設成"simplify"會得到Agent type 'simplify' not found並可能誤導你 fallback 去Skill(simplify)(本段明文 NEVER 的路徑)。
理由:Skill(simplify) inline 載入 simplify SKILL.md + 4-agent 編排 + 修正報告,大量 output 會把外層 commit flow 的 continuation 指令推出 working memory。用通用 subagent(general-purpose)跑這段 prompt 把 simplify 隔離在 subagent context,主線只收到 compact 結果,commit flow 的 fast-path 判斷指令仍在 working memory 頂端。
Agent prompt 範本(照搬,不自由發揮):
Review the current uncommitted changes (git diff HEAD) for reuse, simplification, efficiency, and altitude issues — not correctness bugs. Launch 4 parallel review agents (Reuse / Simplification / Efficiency / Altitude), dedup findings, fix each one directly. Skip findings that change behavior or require changes outside the diff. After applying all fixes, run `vp lint --deny-warnings` and revert any fix that introduces a lint violation. Report: what was fixed, what was reverted due to lint, what was skipped (or confirm clean). Keep the final summary under 200 words.
Agent 回傳後主線處理:
HANDOFF.md([simplify] prefix),立即 fast-path 判斷✅ 0-A.0 完成(simplify 無修正),立即 fast-path 判斷Deferred items → HANDOFF(自動,不停住):simplify 指出「現在不做但值得做」的改善項 MUST 自動寫入 HANDOFF.md 的 Next Steps 區塊(一行一項,前綴 [simplify]),然後立即繼續 fast-path 判斷。NEVER 停住等使用者確認。
0-A.0 完成 ≠ 停頓點(hard rule):simplify Agent 回傳後,主線 MUST 在同一個 assistant turn 內 輸出一行摘要 → 判斷 fast-path → 啟動 0-A.1/0-B/0-C。NEVER 在 simplify 完成後等 user 回應 — commit 流程是單一連續執行,中間不停。
跑完輸出 ✅ 0-A.0 完成(simplify 已 review + 修正{,N 項 deferred → HANDOFF}) 後判斷 fast-path:
⏭️ 0-A.1/0-A.2 跳過(fast-path: diff <20 行、限 doc/config、無敏感路徑),進入 0-B/0-C 並行codex exec review 在 high 推理下常需 5–15 分鐘。MUST 用 Bash run_in_background: true 啟動,並每 3 分鐘讀一次背景輸出確認進度(process 還活著、有沒有錯訊、跑到哪一檔)。建議用 ScheduleWakeup({delaySeconds: 180}) 排隔——3 分鐘穩穩落在 prompt cache 5 分鐘 TTL 內(300s 是 cache miss 最差解),又是使用者明定的上限,不可拉長。
啟動背景 process 後 MUST 立即進入並行階段(同一個 assistant 回合內),啟動 0-B(條件觸發)與 0-C —— 不要乾等 codex 完成才推進其他軸,那等同放棄並行收益。詳見上方「0-A/B/C 並行策略」。
.claude/scripts/codex-review-safe.sh high
ℹ️ codex-review-safe.sh 以
codex exec+內嵌 review prompt 執行——codex review子命令已禁用(硬編碼 workspace-write sandbox 會卡死 MCP,見 agent-routing.codex-watch-protocol.md)。MCP(含 codebase-memory)在 review 期間可用。已知 trade-off:--dangerously-bypass-approvals-and-sandbox下 prompt injection 理論上可繞過唯讀指示——本 script 僅用於 review 自家 fleet 的 diff,NEVER 拿去 review 不可信第三方 code。
讀完 codex 輸出後依 codex 自己輸出的 severity 標記分情境處理(此時 0-B / 0-C 應已並行完成或在收尾):
## Semantic Verdict 表且覆蓋 patterns.json semantic 全部 id——缺表或缺列=review 不完整,重跑 0-A.1,NEVER 當作通過✅ 0-A.1 通過(codex high 無 issue),跳過 0-A.2,進入「並行匯合」✅ 0-A.1 通過(codex high 僅 Minor/Info 已修),跳過 0-A.2,進入「並行匯合」Severity 來源:以 codex 自己輸出的 severity 標記為準(Critical / Major / Minor / Info)。NEVER 由主線自行判定降級「這個其實沒那麼嚴重」—— codex 標 Major 就照 Major 處理,否則 0-A.2 條件觸發機制等於形同虛設。
僅在 0-A.1 出現 Critical / Major 級 issue 時執行,其他情況一律跳過。
.claude/scripts/codex-review-safe.sh xhigh
讀完輸出後判斷:
✅ 0-A.2 通過(codex xhigh 無 issue),進入「並行匯合」三軸完成後合併狀態檢查 + 條件觸發 0-D:
0-D 執行時機:三軸匯合後、大改動回扣之前。0-D 條件觸發(見下方 § 0-D. Doc Alignment 檢查),觸發時在主線 foreground 跑,修完再評估大改動回扣。
大改動回扣:若 0-A / 0-B / 0-C / 0-D 累計的修正超過 50 行或跨 5 檔以上,MUST 在此處重跑一次 codex-review-safe.sh high 確認新引入的程式碼也過 codex 眼睛(codex 看的是啟動時 snapshot,後續大改動不在它覆蓋範圍)。小改動(< 50 行 / < 5 檔)視同安全跳過。
完成匯合後輸出:
✅ 0-A/B/C/D 並行匯合通過(codex {1|2} 輪、screenshot {pass|skip}、check 全綠、doc {aligned|skip})
禁止:
gpt-5.6-sol)high、0-A.2 必為 xhigh)code-review agent(職責已被 codex 兩輪取代)git diff --name-only
同時滿足才觸發:
.vue 檔的 <template> 區塊不觸發:純 <script> / <style> 微調、composable / store / API 純邏輯、測試、文件、設定檔、單純重構不影響視覺輸出。
並行啟動:觸發時 MUST 在 0-A.1 codex 背景 process 啟動的同一個 assistant 回合內派 screenshot-review agent —— NEVER 等 codex 跑完才派(會浪費 3–5 min 的並行收益)。subagent 跑完回收 finding,與 0-A.1 / 0-C 的 finding 一起匯合修正。
觸發時派 screenshot-review agent 截圖並評估。問題修正後輸出 ✅ 0-B 通過;不觸發則直接輸出 ⏭️ 0-B 跳過(無 UI 變更)。
並行啟動:MUST 在 0-A.1 codex 背景 process 啟動的同一個 assistant 回合內,主線 foreground 開跑 pnpm check —— 跟 codex 並行不阻塞。0-C 完成(含 fix loop 通過)後再 poll 0-A.1 與回收 0-B subagent。
跑下列指令確保 format / lint / typecheck / test / doctor 全部 0 errors + 0 warnings + 0 test failures:
pnpm check
檢查 pnpm check 是否真的包含 test(多數 consumer 的 check 只有 format/lint/typecheck,CI 才跑完整 test,本地不補跑就會在 push 後才看到測試失敗):
node -e "const s=require('./package.json').scripts.check||''; console.log(/test|vitest/.test(s)?'check-includes-test':'check-missing-test')"
若輸出 check-missing-test,必須額外跑:
pnpm test # 或 vp test run / pnpm test:unit,依 consumer 設定
檢查是否有 scripts.doctor(vite-doctor import graph 健康度檢查:cycles、broken imports/exports、phantom deps):
node -e "const s=require('./package.json').scripts; console.log(s.doctor?'has-doctor':'no-doctor')"
若輸出 no-doctor → MUST block commit,印出安裝指引後中止:
⛔ 0-C 失敗 — vite-doctor 未安裝
vite-doctor 是 commit 品質閘門的必要組件(import graph 健康度:cycles、broken imports/exports、phantom deps)。
安裝步驟:
1. pnpm add -D vite-doctor
2. 在 package.json scripts 加入:
"doctor": "vite-doctor scan . --max-warnings 0"
3. Nuxt 專案:在 nuxt.config.ts 加入 module:
import { doctorConfig } from './vendor/doctor-shared/preset.mjs'
modules: [['vite-doctor/nuxt', doctorConfig]]
4. 安裝完成後重跑 /commit
詳見 .claude/rules/vite-doctor.md
隨後 MUST 釋放 commit-lock(node .claude/scripts/commit-lock.mjs release)並 STOP。NEVER 跳過此 gate 繼續跑後續步驟。
若輸出 has-doctor,必須額外跑(MUST pnpm run doctor,NEVER 裸打 pnpm doctor — doctor 撞 pnpm 內建子命令,裸打跑的是 pnpm 自家 doctor 並 silent exit 0,scripts.doctor 的 vite-doctor scan 永遠不執行):
pnpm run doctor
Doctor health score < 100 或 exit code ≠ 0 → MUST block commit,修復後重跑直到 health score 100/100 + 0 warnings + exit 0。即使 warning 是既有、非本次 diff 引入也必須修——每次 /commit 順手把既有 doctor warning 修掉,保持零警告 baseline。典型修法:移除 dead imports、修正 re-export 路徑、打斷 import cycles、套用 readValidatedBody 取代 raw body read。NEVER 以「非我引入」「既有 debt」為由跳過 doctor warning — 0-C gate 不區分新舊,一律全綠。
失敗時進入 loop:修復 → pnpm format(裸打 vp fmt 必須加 --ignore-path .oxfmtignore) → 重跑上述步驟 → 直到全綠。loop 的執行者依下方「fix loop 的 codex offload」規則決定(預設背景 codex;例外才主線直修)。
⚠️ oxfmt batched false-positive(vite-plus 0.1.21 已知 bug):第一次
pnpm format:check紅但 single-filevp fmt --check <path>通過,是 batched bug 不是 format issue — 先跑一次pnpm format(vp fmt --write)再重跑 check 通常就過。NEVER 動.oxfmtignore或 LOCKED projection(.claude/rules//AGENTS.md/CLAUDE.md/ spectra change markdown)試圖讓 oxfmt 滿意 — 那是 governance violation。clade 中央倉 release flow 已在scripts/publish.mjs主流程加 stable fmt pre-stage(兩輪vp fmt --write+vp fmt --check),consumer 端 commit 流程不需再背 workaround SOP。詳見docs/pitfalls/2026-05-18-oxfmt-batched-check-false-positive.md。
Fix loop 的 codex offload(預設派背景 codex,主線不留在 foreground 修):
0-C 檢查發現失敗需要修補時,預設派背景 codex 跑 fix-verify loop,主線同回合繼續既有並行收尾(poll 軸 A、回收軸 B)— 三軸並行結構不變,軸 C 只是從「主線 foreground 修」換成「codex 背景修」:
node ~/offline/clade/vendor/scripts/codex-dispatch.mjs \
--template ~/offline/clade/vendor/snippets/codex-offload/templates/fix-verify-loop.template.md \
--var <key>=<value> ...(依 template 變數表填:check 命令、失敗摘要 / log 等) \
--label commit-0c-<slug> --effort high
(背景跑、stdout 單一 JSON;exit 0=全綠 / 2=修不到全綠(業務 fail)/ 3=機械故障 / 4=quota。exit 3/4 → 主線 fallback foreground 自跑 fix loop;exit 2 → 失敗摘要回主線判斷,不重派同一 brief。)
4.8-aware 範圍明寫:每一輪 0-C 失敗都先做 dispatch 評估(含匯合修正 / 大改動回扣後重跑 0-C 又紅的輪次),不是只有第一輪。
例外(主線直修,不派):
codex 完工後主線 MUST:
pnpm check(+ 條件觸發的 pnpm test / pnpm run doctor)確認全綠 — 不信 codex 自報git diff 確認 codex 改動 scope 只在修錯相關檔;scope 外 substantive change → revert 該段改動 + 主線自修(注意 working tree 含本次 commit 的 uncommitted 變更,NEVER git checkout HEAD -- <file> 整檔回退 — 會把本次 commit 的原始變更一起砍掉;用 Edit 撤掉 codex 引入的段落即可)禁止用 npx vitest run / npx eslint 等個別工具替代 pnpm check / pnpm test / pnpm run doctor。若 .claude/worktrees/ 干擾結果,先清理再跑。
通過後輸出 ✅ 0-C 通過(format/lint/typecheck/test/doctor 全綠)。
本次 diff 觸及的變更若涉及 docs/ 相關面向,MUST 在 0-C 完成後跑 doc alignment 檢查。0-D 不阻塞 0-A/0-B/0-C 並行(在三軸匯合後跑)。
以下任一成立即觸發(全不成立 → 輸出 ⏭️ 0-D 跳過(diff 無 doc-relevant 變更),進入匯合):
docs/** 本身rules/core/** / rules/modules/** / vendor/snippets/**(標準層有變 → docs 可能需同步)scripts/*-audit.mjs(audit signal 變更 → docs/rule-enforcement-matrix.md 或 docs/dev-guide.md 可能需更新)server/api/** / app/components/** / app/pages/** / composables/**(業務碼有變 → consumer docs/ 可能需對齊)fix type)→ pitfall 覆蓋檢查DIFF_FILES=$(git diff --name-only HEAD)
HAS_DOC=$(echo "$DIFF_FILES" | grep -E '^docs/' | head -1)
HAS_RULES=$(echo "$DIFF_FILES" | grep -E '^rules/(core|modules)/' | head -1)
HAS_SNIPPETS=$(echo "$DIFF_FILES" | grep -E '^vendor/snippets/' | head -1)
HAS_AUDIT=$(echo "$DIFF_FILES" | grep -E '^scripts/.*-audit\.mjs$' | head -1)
HAS_BIZ=$(echo "$DIFF_FILES" | grep -E '^(server/api|app/components|app/pages|composables)/' | head -1)
# fix type 在 Step 3 分組後才能判,0-D 先用 diff 中有無 pitfall-related file 近似
HAS_PITFALL_REF=$(echo "$DIFF_FILES" | grep -E '^docs/pitfalls/' | head -1)
if [[ -z "$HAS_DOC$HAS_RULES$HAS_SNIPPETS$HAS_AUDIT$HAS_BIZ$HAS_PITFALL_REF" ]]; then
echo "⏭️ 0-D 跳過(diff 無 doc-relevant 變更)"
else
echo "0-D 觸發:需要 doc alignment 檢查"
fi
掃 docs/ 中所有 [[...]] cross-ref,驗證 target 存在(rules/core/ 檔名、pitfall id、memory name):
grep -rn '\[\[' docs/ --include="*.md" 2>/dev/null \
| sed -E 's/.*\[\[([^]]+)\]\].*/\1/' \
| sort -u \
| while read ref; do
# 嘗試 match rules/core/<ref>.md、docs/pitfalls/*<ref>*.md、或 memory
found=0
[[ -f "rules/core/${ref}.md" ]] && found=1
[[ -f "rules/modules/${ref}.md" ]] && found=1
ls docs/pitfalls/*"${ref}"*.md 2>/dev/null | head -1 | grep -q . && found=1
[[ $found -eq 0 ]] && echo "BROKEN_REF: [[${ref}]]"
done
任何 BROKEN_REF → MUST 修復(更新引用或移除過時 cross-ref)。
掃 docs/ 中引用的檔案路徑(backtick 包裹的相對路徑),驗證 target 仍存在:
grep -rnoE '`[a-zA-Z][a-zA-Z0-9._/-]+\.(md|mjs|ts|mts|sh|json|yml|yaml)`' docs/ --include="*.md" 2>/dev/null \
| sed -E 's/.*`([^`]+)`.*/\1/' \
| sort -u \
| while read fpath; do
# 嘗試以 repo root 解析
[[ -f "$fpath" ]] || echo "STALE_PATH: $fpath"
done
STALE_PATH → 修正路徑(檔案已搬/改名)或移除引用。
若 diff 觸及了某 pitfall 的 prevention.ref 指向的檔案:
for pit in docs/pitfalls/*.md; do
refs=$(grep -A1 'ref:' "$pit" 2>/dev/null | grep -v '^--$' | sed -E 's/.*ref: *"?([^"]+)"?.*/\1/' | head -5)
for r in $refs; do
base=$(echo "$r" | sed 's/#.*//')
if echo "$DIFF_FILES" | grep -qF "$base"; then
echo "PITFALL_TOUCH: $(basename $pit) ref=$base — 檢查 prevention.status 是否需更新"
fi
done
done
命中 PITFALL_TOUCH → MUST 讀該 pitfall 的 prevention: 段,確認 status 是否因本次修改需更新(open → implemented、或 implemented 但行為已改需補 regression-evidence)。
適用場景:diff 觸及業務碼(server/api/、app/components/、app/pages/、composables/)、或新增 rules/snippets、或 docs/ 本身有大範圍改動。
三方受眾檢查清單(主線自行 review,不開 subagent):
| 受眾 | docs 位置(典型) | 檢查項 |
|---|---|---|
| 非技術人員(客戶 / PM) | docs/user-guide/、docs/business/、VitePress 首頁 hero | 新功能是否有使用說明?既有說明是否因 UI/流程變更過時?截圖是否對齊當前版本? |
| 開發者 | docs/solutions/、docs/decisions/、docs/guides/、docs/modules/、docs/dev-guide.md | API 改動 → 對應 solution/guide 是否更新?新模組 → 有沒有 module doc?架構決策 → decision record 是否需更新? |
| 維運者 | docs/operations/、docs/ops/、docs/runbooks/ | config/env 變更 → runbook 是否更新?deploy 流程變更 → ops doc 是否對齊?新 migration → rollback SOP 是否存在? |
VitePress 場景額外檢查:若專案有 docs/.vitepress/config.{ts,mts}:
執行方式:主線列出 diff 涉及的受眾面向 → 逐條對 docs/ 檢查 → 有缺失就當場補、修路徑、更新內容。
檢查 A/B 的 BROKEN_REF / STALE_PATH → 修 → 重跑驗證 → 直到 0 issues。
檢查 C 的 PITFALL_TOUCH → 更新 status/evidence → 不需重跑(人工判斷)。
檢查 D 的受眾缺口 → 補 doc → format(pnpm format)→ 確認。
通過後輸出 ✅ 0-D 通過(doc alignment: N ref OK, M path OK, pitfall K/K 對齊{, 受眾文件已補齊})。
觸發條件:types 檔或任一 migration 有變更(含 staged + unstaged)。
# 從 package.json 讀 types 路徑(若有自訂路徑);fallback 到 conventional locations
# 避開頂層 return(Node script 不允許)— 用 if/else 與 .find()
TYPES=$(node -e "
const fs = require('fs');
const pkg = require('./package.json');
const custom = pkg.config && pkg.config.dbTypesPath;
const candidates = [
'packages/core/app/types/database.types.ts',
'app/types/database.types.ts',
'shared/types/database.types.ts',
'src/types/database.types.ts',
];
const path = custom || candidates.find(function(p) { return fs.existsSync(p); }) || 'app/types/database.types.ts';
console.log(path);
")
# 檢查 types 或 migrations 是否變更(HEAD diff 含 staged)
git diff --name-only HEAD -- "$TYPES" supabase/migrations/ | grep -q . && echo HAS || echo NO
若 HAS(types 檔或 migrations 有變更):
# 1. 先把 working tree 的版本(含 staged + unstaged)拷一份備查
cp "$TYPES" /tmp/types-before-reset.ts
# 2. 重置 DB + 從 migrations 重新生成 types(自動偵測 LXC/Docker 模式)
if node -e "process.exit(require('./package.json').scripts?.['db:reset'] ? 0 : 1)" 2>/dev/null; then
# LXC / 遠端 Supabase 模式:consumer 提供 pnpm db:reset wrapper(會 reset DB + 跑 db:types 寫到 $TYPES)
pnpm db:reset
else
# 本機 Docker Supabase 模式
supabase db reset
supabase gen types typescript --local > "$TYPES"
fi
# 3. 比對:working tree 版本 vs migrations 推導版本
diff /tmp/types-before-reset.ts "$TYPES"
有差異 → 停止 commit,提示使用者依差異建立對應 migration 或還原 $TYPES。
遠端 LXC 模式注意:
pnpm db:types通常直接寫入$TYPES不輸出 stdout,所以不能用> /tmp/...重導向取值(一定要先cp備份再pnpm db:reset)。
git status --porcelain # 分組輸入的權威來源(含 tracked modified + untracked)
git diff --stat # 僅輔助看 tracked 改動規模;NEVER 當分組輸入唯一來源
分組輸入 MUST 用
git status --porcelain,不是git diff --stat:git diff --stat只列 tracked modified,會漏掉 untracked 非 ignored 檔(??開頭,如新建的tasks/todo.md/docs/<new>.md)。只憑git diff --stat分組 → untracked 檔被 silently 丟掉、never commit。每次分組前自問:「git status有沒有??開頭的行?沒被.gitignore覆蓋 = 必須納入分組。」
若 .gitignore 有變更:
.claude/.commit.lock、codex/)git stash push -- .gitignore 並寫入 HANDOFF.md,NEVER git checkout .gitignore 直接還原(會毀掉使用者的 WIP)依功能/目的分組並輸出:
### Group 1: [功能描述]
類型: ✨ feat
檔案:
- path/to/file.ts
分組輸入 = Step 2 的 git status --porcelain 完整輸出(tracked modified + untracked 非 ignored),NEVER 只用 git diff --stat。
??)一律納入分組,通常自成獨立 🧹 chore group(除非語義明確屬於某 feat / fix group)?? 開頭的檔想加 .gitignore 消掉時 STOP:先問「這本來就該 ignore(build artifact / runtime state),還是我在逃避 commit?」逃避 commit 而 gitignore = 把該入庫的東西藏掉,方向反了(詳見 [[wip-orphan-recovery]] § 反射性 gitignore 禁令)對每個分組(用 git commit --only -- <files> 強制 limit scope,防別 session staged race — 詳見 rules/core/commit.md § Ad-hoc commit 必走 git commit --only -- <paths>):
git commit --only -m "$(cat <<'EOF'
✨ feat: 功能描述
Co-Authored-By: Claude <noreply@anthropic.com>
EOF
)" -- <files>
git log -1 --oneline
git show --stat HEAD | tail -3 # MUST verify scope == expected files
Untracked file 例外:須先 git add <untracked> 再 git commit --only -- <both-paths> — scope 仍受 --only 過濾。
判斷升級類型:
✨ feat → pnpm version minor --no-git-tag-version🐛 fix 或其他 → pnpm version patch --no-git-tag-version建立 deploy commit:
git add package.json
git commit -m "$(cat <<'EOF'
🚀 deploy: 發布新版本 v{新版本號}
- 功能描述一
- 功能描述二
Co-Authored-By: Claude <noreply@anthropic.com>
EOF
)"
pnpm tag
git push origin main --tags
pnpm tag 建立 v{版本號} local tag。接著 git push origin main --tags 一步推 commits + tag。NEVER 分兩步 git push && git push --tags — 分步推時 GitHub 先收到 main commit SHA,再收到指向同 SHA 的 tag,有機率不觸發 push:tags workflow(2026-06-03 v1.185.1 實證)。
Completion evidence gate(輸出「✅ Commit 完成」前 MUST 逐格自查;每格附「實跑命令+輸出摘尾」,貼不出證據=該格未完成,不准宣告完成):
git show --stat <hash> | tail -3 輸出(changed files 數 vs 預期不符 → 回 Step 4 處置,不出報告)git push 輸出(本次不 push 則標明原因)三格證據放進完成報告的 Evidence 段。
✅ Commit 完成!
共建立 N 個 commit:
1. abc1234 ✨ feat: ...
2. def5678 🐛 fix: ...
3. ghi9012 🚀 deploy: 發布新版本 v1.8.0
版本:1.7.1 → 1.8.0 (minor)
Tag:v1.8.0 已建立並推送
Evidence:
- scope: <各 git show --stat 摘尾>
- checks: <0-A/0-C 結論行或未觸發原因>
- push: <git push 輸出摘尾>
遵守 .claude/rules/handoff.md:commit 完成後必須更新 HANDOFF.md,把所有可延續且尚未被接手的後續工作寫入 —— 不限於 spectra change。同時同步 Spectra ROADMAP。
檢查以下任一條件成立 → 需要 handoff:
openspec/changes/ 仍有非 archive 目錄(in-progress spectra change)git status 仍有 uncommitted 變更(刻意未入本次 commit 的 WIP)@followup[TD-NNN] marker、TODO 註解、scope 外發現)全部不成立(真正什麼都沒得做了)→ 跳到 7-D:若 HANDOFF.md 存在且內容已過時,清空或刪除。
從本次 session 脈絡、git log、docs/tech-debt.md、openspec/ROADMAP.md 的 Next Moves 萃取:
@followup[TD-NNN] 指向的 tech debtHANDOFF.md依 .claude/rules/handoff.md 格式覆寫:
# Handoff
## In Progress
- [ ] <任務描述(spectra change 名稱 / 自由任務 / WIP)>
- <做到哪、關鍵檔案或決策點>
## Blocked
- <blocker 描述;無則省略整個區塊>
## Next Steps
1. <下一步,按優先序>
2. <...>
禁止:
pnpm spectra:roadmap
重算 openspec/ROADMAP.md 的 AUTO 區塊(Active Changes / Active Claims / Parallel Tracks / Parked Changes)。
若 7-B 收集到的 Next Steps 中包含跨 session backlog(不只是「commit 後立刻要做」的驗證動作),依 .claude/rules/proactive-skills.md 的「Spectra Roadmap Maintenance」手動更新 MANUAL 區塊的 ## Next Moves,格式:
- [priority] 描述 — 依賴:xxx / 獨立 / 互斥:yyy
禁止:手編 <!-- SPECTRA-UX:ROADMAP-AUTO:* --> 區塊(會被下次 sync 覆寫)。
7-C/7-D 修改的是 tracked 檔(HANDOFF.md、openspec/ROADMAP.md),MUST 在 Step 8 /ship 之前 commit 進去,否則 working tree 會 dirty、/ship 開出的 PR 也不含這次的交接狀態。
# 只 stage 7-C/7-D 動到的檔,避免誤包其他 WIP(commit 流程預設不該再撿東西)
git add HANDOFF.md openspec/ROADMAP.md 2>/dev/null || true
# 若沒實際變動(HANDOFF 不需更新、ROADMAP 已 current),跳過 commit
if ! git diff --cached --quiet -- HANDOFF.md openspec/ROADMAP.md 2>/dev/null; then
git commit -m "$(cat <<'EOF'
📝 docs(handoff): 更新 commit 後交接狀態
Co-Authored-By: Claude <noreply@anthropic.com>
EOF
)"
git log -1 --oneline
git push origin main
fi
注意:這個 commit 不重新 bump 版本(不是 deploy),只是把 HANDOFF/ROADMAP 落入 history。Tag 仍指向 Step 5 的 deploy commit。因為 Step 5 已經 push 過 main,此處需要再 push 一次把 docs commit 送上去。
✅ HANDOFF.md 已更新(已入 commit / 無變更略過)
✅ ROADMAP 已同步(已入 commit / 無變更略過)
(或:無可延續工作,HANDOFF.md 已清空 / 未建立)
git branch --show-current
觸發條件:當前不在 main / master 分支,且 consumer 提供 /ship skill(會 push branch 並開 PR)。
🚀 Commit 完成!要繼續執行 /ship 推送並建立 PR 嗎?
/ship skill不觸發:在 main / master 分支,或 consumer 沒有 /ship skill。
node .claude/scripts/commit-lock.mjs release
必須執行,即使前面任何 step 失敗:
NEVER 讓鎖長期遺留;stale lock 雖然 30 分鐘後會自動清,但中間其他 session 要跑 /commit 會被卡住。