メインコンテンツへ

日々の開発ループ

contributor の inner loop は /wt + dev-lifecycle skill です。docker compose ではありません。 docker-compose.selfhost.yml は顧客向けセルフホストデプロイ用のバンドルで、日々の開発では登場しません(リポジトリ取得と環境構築 参照)。backend/frontend はホスト側で直接プロセスとして動きます。

/wt — worktree オーケストレーター

複数の変更を並行で進めるとき、1 つの checkout を使い回すとブランチの切り替えのたびに DB・ポート・node_modules が衝突します。/wt はこれを解決する仕組みで、変更ごとに独立した git worktree(.claude/worktrees/<name>)を作り、DB・ポート・MinIO の key-prefix をすべて自動で分離します。

コマンド

コマンドやること
/wt start <name>worktree を作成(.claude/worktrees/<name>、ブランチ feat/<name>)。frontend の node_modules を main からシンボリックリンク。スロット (1-9) を割り当て、DB (astarmanagement_dev_wt<N>) を作成し、Caddy proxy に反映。既定では backend/frontend を起動しません(provision のみ)
/wt start <name> --with-server上記に加えて backend/frontend も起動(start + serve を一度に)
/wt serve <name>start 済みの worktree に対して backend/frontend を起動。すでに動いていれば何もしない(冪等)
/wt sync <name>その worktree 自身の backend に対して openapi:all を実行し、frontend の型 (api.d.ts / zod-client.ts) を再生成
/wt verify <name>変更ファイルと必要な検証レイヤー (L1〜L4) を表示
/wt ship <name>検証 → main が clean なことを確認 → --no-ff で merge → worktree を片付け
/wt cleanup <name>サービス停止・DB 削除・スロット解放・git worktree 削除・ブランチ削除
/wt status稼働中の worktree 一覧

start が provision-only(起動しない) なのが重要な設計判断です。worktree を作るだけならメモリを消費しないので、並行で何個でも作って構いません。実際にサーバーが必要になったときだけ serve します。

スロットによるポート・DB 分離

各 worktree にはスロット番号(0-9、main は常に 0)が割り当てられ、全サービスのポートと DB 名がそこから決定論的に導出されます。

サービスslot 0 (main)slot N
Backend80808080 + N
Frontend30003000 + N
Storybook60066006 + N
DBastarmanagement_devastarmanagement_dev_wt<N>
MinIO key prefixtenant/localtenant/local-wt<N>

PostgreSQL・MinIO のコンテナ自体は全 worktree で共有(astarmanagement-postgres-dev など)。DB 名とポート番号だけで分離しているので、コンテナを worktree ごとに何個も立てる必要がありません。

dev-lifecycle skill — プロセスの起動・停止

/wt serve の内部、あるいは main checkout(slot 0)で直接作業するときは、dev-lifecycle skill (lifecycle.sh) が backend(Spring Boot)・frontend(Nuxt dev server)・Storybook の起動・停止・再起動・状態確認を担当します。

.claude/skills/dev-lifecycle/lifecycle.sh status
.claude/skills/dev-lifecycle/lifecycle.sh start backend
.claude/skills/dev-lifecycle/lifecycle.sh restart backend --profile dev
.claude/skills/dev-lifecycle/lifecycle.sh stop all

lsof + PID ファイルで既存プロセスを検出し、グレースフルシャットダウン(SIGTERM → 5 秒後 SIGKILL)とヘルスチェック(/actuator/health など)でポーリングします。

./gradlew bootRunbun run dev を直接実行することは settings.json の deny 設定で禁止されています。dev-lifecycle skill 経由でのみ起動できます。理由は、直接起動だと孤立プロセス(PID ファイルなしで動き続けるプロセス)が発生しやすく、複数セッションが並行するとポート競合や「誰が何を動かしているか分からない」状態になりやすいためです。

実装はコードを書く前に worktree に入る

コード変更は必ず worktree の中で行います。main の checkout を直接編集することはありません(hook で強制されています)。設計・スペック作成・レビューは main(read-only)で行い、/wt start で worktree ができたところから実装に入ります。

Astar Works の開発では、実装そのものは専用の実装エージェント(Sonnet ベースの worker)に委譲するのが規約です。メインの調査・設計セッションは実装コードを直接書かず、確定した設計とファイル一覧を worker に渡し、実装後に diff レベルでレビュー・検証してから /wt ship します。人間のエンジニアがこのリポジトリで作業する場合も、この「worktree を作ってから着手する」「変更後は検証してから ship する」というループ自体は同じです。

まとめ — 1 周の流れ

  1. /wt start <name> — worktree を作る(provision のみ)
  2. /wt serve <name> — backend/frontend を起動
  3. 変更を加える(worktree の中で)
  4. API を変更したら /wt sync <name> で frontend の型を再生成
  5. 検証の 4 層 に沿って検証
  6. /wt ship <name> — 検証 → merge → 片付け

次は 検証の 4 層 で、上記 5 の中身を詳しく見ます。