Crowi Kickoff (spec → worktree 実装開始)
承認済み spec の実装着手を 1 コマンドにする skill。これまで手動だった
「gw start → tmux window → エージェント起動 → /crowi-feature 手打ち」を自動化し、
開発ループの入口を閉じる(出口は /crowi-complete-feature + orchestrate A)。
起動例
/crowi-kickoff feature-attachment-thumbnail
/crowi-kickoff attachment-thumbnail # feature- prefix は省略可
/crowi-kickoff feature-foo --no-launch # worktree 作成まで。指示投入はしない
/crowi-kickoff feat-a feat-b feat-c # 直列チェーン: 先頭だけ起動し、integrate 完了ごとに次を自動 kickoff
直列チェーン(複数 spec を順番に)
複数の spec id を渡すと、1 本ずつ直列に流す(並列に全部起動しない)。先頭だけを
今 kickoff し、/integrate-worktree がそれを main に統合し終えた時点で次を自動 kickoff
する。整合の要る feature を順に着地させたい / 並列 worktree を増やしたくないときに使う。
- 前提チェックは全 spec ぶん先にやる(Step 1-3 を渡された全 id に対して実行)。1 つでも not-ready / 重複なら、チェーンを一切開始せず欠落を列挙して中止する(途中で詰まる 連鎖を作らない)。全 id が ready のときだけ進む。
- 先頭 id だけ Step 4-5(worktree 作成・起動・指示投入)を行う。残りは起動しない。
- チェーン状態を
<stateDir>/kickoff-chain.jsonに atomic (tmp+rename) で書く:{ "after": "feature-<先頭id>", "then": ["feature-<2番目>", "feature-<3番目>"], "createdAt": "<UTC>" }after= 「これが integrate されたら次へ」の現在待ち id、then= 以降のキュー。thenが空になる単一 id 指定のときはこのファイルを作らない(通常の単発 kickoff と同じ)。 - Step 5.7 の watch は先頭 1 回だけ張ればよい(以降のチェーン前進は integrate 側が担う)。
- チェーンの前進は
/integrate-worktreeの最終ステップ (Step 9) が行う — integrate がafterに一致する id を統合し終えたら、then[0]を次として/crowi-kickoffし、 ファイルを更新/削除する。詳細は integrate-worktree Step 9。 - 途中の spec が NEEDS_WORK/ESCALATE で READY にならなければ、そこで連鎖は自然に一時停止 する(次に進まない)。再開したい場合は当該 spec を仕上げて integrate すれば連鎖が続く。
- 報告(Step 6)には「チェーン: 先頭 起動、以降 を integrate ごとに自動着手」 と 1 行含める。
前提(実測済みの環境)
gw start <id>は post_start_hook で.gw/feature-state-link.sh— worktree 側.feature-state/を作成し specs/ tasks/ config.json を main store へ symlink、queue.jsonを{ "currentTask": null }で seed(worktree ローカル).gw/tmux-claude.shでtmux new-window -n <window 名>に claude セッションを自動起動 を実行する。したがって kickoff がセッションを spawn する必要はない — gw が開いた window に指示を send-keys で投入するだけでよい (agmsg spawn で別セッションを立てると二重になる。やらない)。
- 起動フラグ・hook は crowi の project-local
.gwrc(repo root・gitignore 済み)で overrideしている。hook スクリプトは./.gw/(repo-local・home 非依存) に置き、.gwrcは$GW_WORKTREE_PATHから main worktree root を導出して$MAIN/.gw/*.shを 呼ぶ(CWD にも~にも依存しない)。.gw/tmux-claude.shはclaude --remote-control '<repo>:<id>' --name '<repo>:<id>'で起動し、RC 有効 + session/terminal title =<repo>:<id>(例crowi:live-page-sync-reconcile)にする。 kickoff 後に質問で止まった session を picker/title で見つけて remote で動かすため。 crowi の.gwrcだけの override で~/.gwrc(global)は触らない。 前提: gw が project-local.gwrcを読むビルドであること(gw のfeature-project-local-config機能。未対応バイナリでは global の素claude起動に fall back = RC/name 無し)。.gwrcを編集すると direnv 式 trust hash が変わるので、 次のgw startで trust 再承認プロンプトが出る(承認まで global 値に fall back)。 - なお claude は RC/name 付きでも
pane_current_commandは version 文字列(2.x.x)の ままなので、Step 5 の pane 検出は不変。
- hook 構成によってはさらに 右 pane に
pnpm devが自動分割起動される (split-window -d— dev-portal の anchor 自動採番で port 衝突しない。GW_NO_DEV=1 gw start <id>でスキップ可)。このため指示の投入先は window ではなく claude の pane_id を明示指定する(Step 5)。 - worktree dir は
../crowi-<id>、branch は<id>/impl形式、window 名は branch から/implを落としたもの(=<id>)。全 worktree branch が/implで終わる以上この suffix は情報量ゼロで、ただでさえ切り詰められる tab bar を 5 桁食うだけなので落とす。 window 名に依存する処理は無い — kickoff も integrate-worktree もpane_current_pathで window を引くので、名前は表示専用と考えてよい。
ワークフロー
Step 1: 引数解決 + 実行コンテキスト確認
spec-id は
feature-prefix の有無どちらも受ける:ls .feature-state/specs/*.md 2>/dev/null | grep -E "(^|/)(feature-)?<入力>\.md$"複数一致なら列挙して中止。
0 件なら wiki を確認(pull・一方向): crowi CLI の直リダイレクトで materialize する (取得本文をモデルが Write で書き起こす転記経路を、実装の入口に置かない):
mkdir -p .feature-state/specs # bash redirect は親ディレクトリを作らない crowi -p local get "/crowi/spec/<入力>" > ".feature-state/specs/<id>.md" \ || crowi -p local get "/crowi/spec/feature-<入力>" > ".feature-state/specs/<id>.md" # 検証: live と一致するか (末尾改行 1 行だけの差は一致とみなす) crowi -p local get "/crowi/spec/<id>" | diff - ".feature-state/specs/<id>.md"見つかったら「wiki から pull した」と報告して続行。pull した spec も Step 2 の ready 判定は通常どおり行う(wiki にあるからと言って ready とは限らない)。CLI が使えない環境に限り
crowi_get_page(MCP) を fallback にしてよいが、その場合も書き出した内容を live と diff で突合してから進む。wiki にも無ければ「spec が無い」で中止。wiki との正本ルール(crowi-design と共通): 作業中の正本は
.feature-state/specs/、 wiki/crowi/spec/<id>は耐久スナップショット。同期は一方向のみ(design → wiki の publish / wiki → specs/ のこの pull)。食い違ったら.feature-state/specs/が勝つ。main worktree で実行していること(
git worktree listの先頭パスとgit rev-parse --show-toplevelが一致)。worktree 内からの実行は中止 (「kickoff は main セッションから」)。main が dirty でも kickoff 自体は可 (worktree を切るだけで main を触らない)。
Step 2: implementation-ready 判定(orchestrate B と同一基準)
.claude/skills/_shared/spec-contract.md が正本。repo root で validator を実行する:
bash .claude/skills/_shared/validate-implementation-spec.sh ".feature-state/specs/<id>.md"
exit 0 のときだけ続行する(WARN: が stderr に出ていても exit 0 なら着手を止めない — symbol 粒度の freshness 判定が「共有ファイルへの無関係な変更」を soft に落としたもので、hard stale ではない)。validator は次をまとめて検証する:
- contract v2 marker(
spec_contract: 2/status: approved/implementation_ready: true) scope/ AC / blocking open question 無し- path + symbol 単位の実装マップ、処理フロー、契約・不変条件、実装順序
- stable AC ID と test file/case/level の対応
grounded_atが有効で、参照 path が以後の commit / working tree で変化していない(生成物 — lockfile / OpenAPI /**/generated/**— は staleness 判定から除外される。参照 path が symbol 単位で保守的な 5 条件を満たせば、シンボル行が変わっていない限りWARN:に落ちる — 詳細は.claude/skills/_shared/spec-contract.mdの freshness の symbol 粒度節)
複数 spec を preflight する場合は、各 spec の validator 実行について stdout・stderr・exit status を spec id と対応づけてリクエスト内に保持する(spec をまたいで混ざらないようにするため)。exit 0 の全 spec について、保持した stderr に WARN: 行があれば spec id ごとに原文のまま保持しておく(Step 6 で報告するため)。
umbrella spec(kind: umbrella)はこの経路に入らない。umbrella は運用契約とフェーズ表だけを持ち AC も実装マップも持たないのが正しい形なので、validator は代わりに phases: の各 sub-spec が実在しそれ自体が v2 を通ることを検証する(厳しさは sub-spec へ委譲される)。kickoff 側の手順は変わらず、worktree 名も umbrella の id を使う — 単一 worktree で全フェーズを回す運用契約は umbrella が持っており、sub-spec を個別に kickoff するとその契約が失われるため。
失敗時は validator の ERROR: を欠落・stale 理由として列挙して中止する。
kickoff 側で spec を補完・書き換えない。 legacy spec は直接 /crowi-feature の
planner fallback で実装できるが、安価なモデルへ設計判断を残さない kickoff 経路には入れない。
/crowi-design spec <topic> で v2 に作り直すか、既存 spec を強いモデルで v2 へ更新して
再レビューする。
Step 3: 重複ガード
git worktree listにcrowi-<id>を含む行があれば中止(「着手済み。続きはその worktree で」)。.feature-state/tasks/<id>.jsonが存在し status がPLANNED / IN_PROGRESS / REVIEW / NEEDS_WORK / READY_TO_INTEGRATEなら中止(進行中 or 統合待ち)。COMMITTED/INTEGRATEDの化石なら警告つきで続行可(再着手のケース)。
Step 4: worktree 作成 + queue 初期化
gw start <id> # hook が .feature-state 配線 + tmux window + claude 起動までやる
- gw が無い環境では中止(
git worktree add直呼びはしない — 既存規約)。 - hook が seed した worktree 側
queue.jsonのcurrentTaskを上書き。queue.jsonへの直接 Write/Edit は PreToolUse hook が拒否するので、 worktree 側のtask-state.shを経由する(script 自身が tmp+不変条件検証+atomic rename+.bakを行う):
WT="../crowi-<id>"
bash "$WT/.claude/scripts/task-state.sh" queue set-current "<id>"
Step 5: 実装指示の投入(gw が開いた window へ send-keys)
window を特定:
pane_current_pathが worktree 配下にある window を探す (window 名ではなくパスで引く — 名前は表示上の都合でいつでも変わりうるが、 パスは worktree の同一性そのもの。integrate-worktree Step 6.5 も同じ引き方):tmux list-panes -a -F '#{window_id}|#{pane_current_path}' \ | awk -F'|' -v p="<worktree-abs-path>" '$2 ~ "^"p"(/|$)" {print $1}' | sort -u補助的に window 名(= branch から
/implを除いたもの)でも引けるが、 一致しないことを理由に中止しない。claude の pane を特定して起動完了を待つ: hook 構成によっては window に dev pane(右・
pnpm dev)が併設されるため、window 宛(= アクティブ pane 宛)の send-keys は使わない。tmux list-panes -t <window> -F '#{pane_id}|#{pane_current_command}'でpane_current_commandがバージョン形式(2.x.x)の pane が現れるまで 2 秒間隔で poll(上限 60 秒)し、その pane_id を投入先にする。まず実装モデルへ切り替える(hook の plain
claudeは既定モデル = 高価な session model で起動するため。実装は sonnet で十分な設計 — spec が implementation-ready であることは ready 判定済み):
tmux send-keys -t "<claude の pane_id>" "/model sonnet"
sleep 1
tmux send-keys -t "<claude の pane_id>" Enter
sleep 2
- agmsg の受信を自分宛だけに絞る。
watch.shは role 名を渡さないと そのプロジェクトに登録された全 (team, agent) ペアを購読するので、既定のままだと worktree セッションに manager⇄planner のやり取りまで流れ込む(実測。impl セッションが 自分宛でない版数の議論を読まされ、そのぶんのトークンと注意を払っていた)。role 名は spec id をそのまま使う(worktree 名・task id と同じ値にして、どのセッションの role かを一意にする)。actasは未登録なら join も行うので事前の join は要らない:
tmux send-keys -t "<claude の pane_id>" "/agmsg actas <id>"
sleep 1
tmux send-keys -t "<claude の pane_id>" Enter
sleep 3
投入した role は worktree セッション自身が終端で drop する
(/crowi-complete-feature か /crowi-handoff)。actas の排他ロックは
セッション ID に紐づくので、main 側から外して回ることはできない。
- 指示を投入(入力 → 1 秒待ち → Enter。slash メニューの誤発火を避けるため
平文で書き、行頭を
/にしない):
tmux send-keys -t "<claude の pane_id>" \
"crowi-kickoff からの指示です。/crowi-feature <id> を実行してください。COMMITTED まで完走したら /crowi-complete-feature、中断・引き継ぎ時は /crowi-handoff を実行。push は禁止(ユーザー指示待ち)。"
sleep 1
tmux send-keys -t "<claude の pane_id>" Enter
送信確認(必須): セッション初期化直後は Enter が入力欄に届かず、指示文が
未送信のまま入力欄に残ることがある(実例 2026-07-19: redis-docs-page が
1 時間 0 commit — capture-pane で入力欄に指示が残っているのを発見)。Enter の
2-3 秒後に tmux capture-pane -t <pane_id> -p | tail -8 で入力欄(❯ 行)が
空になっていることを確認し、指示文が残っていれば Enter を 1 回だけ再送
して再確認する。それでも残る場合は追いパンチせず「投入したが未送信の可能性」
として報告する(このリトライは初回指示の配送完了であり、鉄則の「指示は 1 通
のみ」の例外ではない — 新しい内容は送らない)。
- fallback: tmux 環境でない / window が見つからない / 60 秒待っても claude が 起動しない → 投入せず、手動手順を表示して終わる(中止ではない — worktree は 作成済みなので Step 6 の報告に含める):
cd <worktree-abs-path> && claude
→ 最初に: /agmsg actas <id> (受信を自分宛に絞る。省くと他 role 宛まで流れ込む)
→ 次に: /crowi-feature <id>
→ 完了時: /crowi-complete-feature / 中断時: /crowi-handoff
--no-launch 指定時は Step 5 全体を skip して手動手順の表示のみ。
Step 5.7: orchestrate watch を張る(main セッション側・未起動なら)
worktree 側の完了は /crowi-complete-feature が .feature-state/tasks/<id>.json に立てる
READY_TO_INTEGRATE signal で伝わる(shared store 経由 = agmsg 等のチーム基盤に依存しない
repo-native の契約)。ただし signal は push されないので、kickoff した main セッションは
orchestrate-watch.sh を Monitor で常駐させて検知する(TaskList に「orchestrate watch」が
既にあれば張り直さない。event 対応表は crowi-orchestrate の「運用モード: watch」節が正本):
Monitor({ command: 'bash .claude/scripts/orchestrate-watch.sh',
description: 'orchestrate watch (A/C/D/E lanes)', persistent: true })
signal を受けたら orchestrate A と同じ裏取り(clean / headSha 一致 / main clean)をして
/integrate-worktree <id> へ。agmsg の完了通知(handoff skill の任意送信)は
二次チャネル — 正はこの signal file。
Step 6: 報告
- worktree path / branch / window(投入済み or 手動手順)
- signal watcher を張った(or 既存)ことを 1 行
- 次に人間がやること(通常なし。spec が multi-phase で gated phase を含むならその旨)
- staleness warnings: Step 2 で保持した
WARN:がある spec id ごとに見出しを立て、配下に validator の rawWARN:行を原文のまま転記する(要約・書き換えしない)。WARN:が無かった spec には見出しを出さない。
staleness warnings:
## feature-<id>
WARN: referenced path changed but grounded symbol lines are identical: <path> (symbols: <...>)
鉄則
- push しない / spec を書き換えない / not ready を勝手に ready 扱いしない
- worktree は gw 経由のみ(
git worktree add/remove直呼び禁止) - 投入する指示は最初の 1 通のみ。以後 worktree セッションの作業に割り込まない (進捗の把握は orchestrate A/E と agmsg 側に任せる)
エッジケース
| ケース | 挙動 |
|---|---|
| spec が specs/ に無い | wiki /crowi/spec/<id> から pull を試みる(Step 1)。wiki にも無ければ中止 |
| legacy / incomplete spec | validator の欠落を列挙して中止。/crowi-design spec で v2 化するか、直接 /crowi-feature の planner fallback を明示的に使う |
引用 symbol の行が grounded_at 後に変わった(symbol line hard stale) |
ERROR: で中止(exit 1)。安価な planner で黙って再設計せず、spec を再 ground / review する |
| path-only 参照、または symbol 粒度の 5 条件を満たさない変化(mode 不一致・binary・dirty 等、file-level hard stale) | ERROR: で中止(exit 1)。symbol hard stale と同様に再 ground / review する |
| symbol 外の変更だけが起きた(共有ファイルへの無関係な追加など) | WARN: は出るが exit 0。着手は止めない。Step 6 の staleness warnings に転記する |
| gw start 失敗(同名 branch 残骸等) | gw のエラーを提示して中止。-f 系は使わずユーザーに委ねる |
| claude 起動待ち timeout | 手動手順を表示(worktree は残す) |
| send-keys 後に反応が無い | 追いパンチしない。報告に「投入したが未確認」と書き、ユーザーに window 確認を促す |
| spec が umbrella(他 spec をフェーズとして参照する形式) | kickoff の手順自体は変わらない(通常どおり /crowi-feature <id> を投入)。umbrella は spec_contract の値に関わらず crowi-feature SKILL 側で needsPlanner=true に倒される(v2 fast path はどの spec からも sub-spec phases を機械導出できないため)。feature-planner が phases: の sub-spec 群から実装順序 / extraGates / longLived 相当を task state へ seed する。 |
crowi-feature / complete-feature との関係
- worktree 名 = spec id にするのは、
/crowi-complete-featureの id 解決 (dir basename からcrowi-除去)と orchestrate A の worktree 特定 (worktree 名 = task id)を成立させるため。変えない。 - kickoff は planner を起動しない(それは worktree 側の
/crowi-featureが scope 判定して行う)。kickoff の責務は「入口を開けて最初の指示を渡す」まで。