番号が一つしかない場所には、二人は入れない

番号が一つしかない場所には、二人は入れない

一家に一台しか電話がなかった時代、長電話をしている人がいると、他の人は使えませんでした。番号が一つしかないので、順番を待つしかありません。急ぎの用があるときは受話器を置いてもらう交渉から始まります。

会社はこれを、代表番号の内側に内線番号を配ることで解きました。外から見える番号は一つでも、内側では机ごとに違う番号を持っている。同時に十本の通話が成立します。

番号が一つしかない場所には二人は入れない。だから番号を増やして配る。半世紀前に決着した話のはずでした。

ファイルは分かれているのに

AI に並列でコードを書かせるとき、作業フォルダを分けるところまでは素直に進みます。git には同じプロジェクトの二本目、三本目の作業フォルダを作る機能があって、それぞれが別の枝を開きます。ファイルは互いに影響しません。

そこまでやったあとで、両方で開発サーバーを立ち上げます。

Error: Port 3000 is already in use

ポートというのは localhost:3000 の 3000 の部分で、一つの番号は同時に一つのプログラムしか使えません。フォルダは分かれていても、ポートはパソコンに一組しかないので、二本目が入れません。

黒電話と同じ状態です。

データベースだともっと悪い

ポートは落ちるだけなので、まだ気づけます。

データベースの場合は落ちません。五本の作業フォルダが同じデータベースを見ていると、一本の作業がテーブルを作り替えた瞬間に、残りの四本がおかしくなります。しかもエラーは出ないので、なぜか動かないという形で現れます。

落ちるほうがまだ親切です。

自分専用に持つべきものを、宣言しておく

プロジェクトの設定に、自分専用に持つべきものを書いておけます。

{
  "worktreeEnv": {
    "PORT": { "kind": "port", "base": 3000 },
    "API_PORT": { "kind": "port", "base": 4000 },
    "DB_NAME": { "kind": "slug", "prefix": "myapp_" }
  }
}

これを書いておくと、作業フォルダを作るたびに、そのフォルダ専用の番号と名前が予約されて、そのフォルダのターミナルに渡ります。

フォルダ PORT DB_NAME
本体 3000 myapp_myapp
fix-login 3010 myapp_fix_login
add-search 3020 myapp_add_search

三本とも同時に立ちます。

~/src/myapp                          PORT=3000   DB=myapp_myapp
  └ yarn dev  → localhost:3000  ○ 立つ

~/.mulmoterminal/worktrees/…/fix-login    PORT=3010   DB=myapp_fix_login
  └ yarn dev  → localhost:3010  ○ 同時に立つ

~/.mulmoterminal/worktrees/…/add-search   PORT=3020   DB=myapp_add_search
  └ yarn dev  → localhost:3020  ○ 同時に立つ

この仕組みを持っているのが MulmoTerminal です。ターミナルで動く AI を何枚ものマスに並べて見張るための画面で、一マスが一体の AI にあたります。無料で、オープンソースとして公開されています。

刻みが1ではなく10である理由

ここがこの仕組みで一番よく考えられているところです。なぜ 3001、3002 ではなく 3010、3020 なのか。

多くの開発サーバーは、そのポートが埋まっていたら次のポートへ勝手にずれる作りになっています。3000 を渡されたフォルダで、何かの理由で 3000 が埋まっていると、開発サーバーは自分で 3001 にずれます。

刻みが1だと、その 3001 は隣のフォルダの枠です。割り当ては正しいのに、実際の動作で衝突します。

10刻みにしておけば、数回ずれても隣の枠には届きません。道具の親切な挙動を前提に、あらかじめ枠を空けてあるわけです。こういう理由は、書いてなければ誰も再発見できません。

二本目の複製にも効く

副産物として効いてくる場所があります。

同じプロジェクトを acme と acme2 の二つのフォルダに取ってきている人は、作業フォルダを使っていなくても同じ問題を持っています。どちらも同じ base を宣言するので、あとのほうは次の空きである 3010 になります。

二つの複製も、作業フォルダ同士と同じように 3000 を取り合っていました。同じ仕組みで解決します。

番号ではなく名前も配れる

ポートだけでなく、名前も配れます。作業の名前から作った文字列に、指定した前置きを付けたものです。小文字と数字と下線で、63文字で切ります。

データベースの名前、SQLite のファイル名、コンテナの名前にそのまま使えます。63文字で切るのはデータベースの名前の上限に合わせたもので、境界がはっきりしています。

ただし、データベースを作るところまではやりません。他とぶつからない名前を渡すところまでで、その名前をどう使うかは各プロジェクトのスクリプトの仕事です。名前を配ると、データベースを作るは別の仕事で、後者はプロジェクトのほうが知っています。

一度予約した番号は動かない

一度予約した値は固定です。マスを開き直しても、アプリを再起動しても、パソコンを再起動しても、同じフォルダには同じ番号が返ります。

これは親切心ではありません。もし起動のたびにポートを測り直すと、そのフォルダ自身の開発サーバーが掴んでいるせいで使用中と判定されて、番号がずれます。自分自身から逃げることになるわけです。一回目は 3010、二回目は 3020、三回目は 3030。動いているサーバーがあるほど逃げるという壊れた挙動になります。

もう一つ理由があって、既存の会話に戻ったマスは環境変数を読み直しません。動いた値は、実際に走っているプロセスと食い違ったままになります。

測るのは最初に配るときの一回だけです。そのときにパソコン上の他のものが掴んでいるポートは飛ばされます。

衝突は起きるが、残らない

MulmoTerminal を二つ動かしていると、予約を読んで書くまでの一瞬に、両方が同じ値を選ぶことはあり得ます。

対処が面白いところです。鍵をかけて防ぐのではなく、書いた直後に検出します。予約の記録は追記しかしないファイルなので、両方が同じように読んで、先に載っているほうが勝ちという判定が一致します。負けたほうが解放して次の値を取ります。

衝突は起きえますが、残りません。鍵を持たずに、判定基準を一致させることで解決しています。

もらった値が、マスのヘッダーに出る

そのフォルダが何をもらったかは、ヘッダーに :3010 のように出ます。ポートはクリックすると開発サーバーが新しいタブで開き、変数の名前はホバーで出ます。

そのフォルダの全部のマスに出ます。AI のマスも、素のターミナルも、コマンドを走らせたマスも。とくに効くのはコマンドを走らせたマスで、yarn dev を動かしているのはそこだからです。

宣言していないプロジェクトでは何も描きません。有効にする操作は不要で、使わない人には何も見えません。

使ってみる

MulmoTerminal は、次の一行で立ち上がります。ターミナル(Windows なら PowerShell、Mac ならターミナル)を開いて、そのまま貼り付けます。

npx mulmoterminal@latest

ブラウザが開いて http://localhost:34567 が表示されれば成功です。インストールという作業はなく、合わなければ閉じるだけで終わります。止めるときは、打ったターミナルで Ctrl + C を押します。

動かない場合、足りないものは次の二つのどちらかです。

確かめ方 無ければ
Node.js 22.12 以上 node -v nodejs.org/ja/download の LTS。入れたらターミナルを閉じて開き直す
Claude Code claude --version macOS は curl -fsSL https://claude.ai/install.sh | bash、Windows は irm https://claude.ai/install.ps1 | iex。そのあと claude でログイン

Claude Code は、AI をターミナルから使うためのコマンドです。使うには Claude の有料プラン(Pro / Max / Team / Enterprise)か API のアカウントが必要で、無料プランには含まれていません。MulmoTerminal 自体は無料で、AI の使用量はその契約から引かれます。

この記事の話に入るには、開発サーバーを持っている git のプロジェクトを一つ用意します。そのプロジェクトの .mulmoterminal.json に、次を書きます。

{
  "worktreeEnv": {
    "PORT": { "kind": "port", "base": 3000 }
  }
}

そのフォルダをマスで開いて、作業フォルダを二本切ります。起動フォームの下のほうに出る欄に名前を入れて、ボタンを押すだけです。

それぞれのマスのヘッダーに、:3000 と :3010 のように違う番号が出れば効いています。両方で yarn dev を走らせて、二つとも立てば確認は終わりです。

やめるときは、worktreeEnv の行を消します。予約の記録は残りますが、何も渡されなくなります。

まとめ

番号が一つしかない場所には二人は入れない、という問題は黒電話の時代からありました。会社はそれを、代表番号の内側に内線を配ることで解きました。

AI を並列で動かすと同じ問題が戻ってきます。ファイルは分けられてもポートは分かれないので、二本目の開発サーバーが落ちます。データベースの場合は落ちずに壊れます。

MulmoTerminal は、自分専用に持つべきものを宣言しておくと、作業フォルダごとに番号と名前を配ります。刻みが10なのは、開発サーバーが勝手にずれる先を隣の枠にしないためです。

この記事をシェア

関連記事

記事一覧に戻る