# 2 つ目のインスタンスが、動いている 1 つ目のセッション設定を消す

Issue: #1061

## 何が起きていたか

```ts
const surviving = tmuxAvailable() ? tmuxListSessionIds() : [];   // Windows: 常に []
const droppedSettings = pruneOrphanSettings(new Set(surviving)); // → 全部消す
```

prune のコメントはこう書いていた:

> a PTY without tmux died with the server that owned it

**正しい。ただし「前の生存期間を所有していたプロセス」に対してだけ。** 今まさに走っている 2 つ目の
インスタンスは自分の生きた PTY を持っていて、tmux が無ければ `surviving` は空なので、そのファイルが
全部「置き去り」に見える。現場では**生きている 8 セッション分**が消えた。

## 直し方: 「言えること」だけを言う

2 つ目のインスタンスが正直に主張できるのは **「動いているどのピアよりも前に書かれたファイルは、その
ピアのものではありえない」** ということだけ。だから prune に cutoff を渡す:

- ピアなし → cutoff は `null`。**従来どおり全部消す**（自分のクラッシュの後始末はこれが目的）
- ピアあり → 最も早く起動したピアの時刻。それより古いファイルだけ消す

`statSync` できないファイルは**判定しない**（読めないことは古いことの証拠ではなく、間違えると
生きたセッションの状態を消す）。

## そのために要る原始的な機能

「今どのサーバが生きているか」。これが無かったので:

- **prune** は「自分以外は誰もいない」と仮定するしかなかった
- **ランチャー**はポートが衝突したときしか気づけなかった。`--port <空きポート>` は**無言で** 2 つ目を
  起動していた（現場の事故はまさにこれ）

`bin/instances.js` に置く。各サーバが起動時に `~/.mulmoterminal/instances/<pid>.json` を書き、終了時に
消す。生存判定は `process.kill(pid, 0)`（クラッシュで残ったエントリは読んだ側が掃除する）。

**bin/ に置いたのは、ランチャーが TypeScript の存在しない素の node で動くから。** `bin/update-check.js`
が同じ理由で同じ形をしていて、`server/config/update-status.ts` がそれを import している前例がある。

## ランチャーの確認（依頼のもう半分）

ポートの話をする**前に**聞く。既存の `secondInstancePrompt` はポートが埋まっているときだけ出るので、
別ポートを明示した起動は素通りしていた。**ポートの衝突は症状で、共有される `~/.mulmoterminal` の方が
本体**であり、それはどのポートでも同じ。

非 TTY では聞かずに警告だけ出して続行する。答える人がいないプロンプトで起動を止めるのは、サーバを
求めたスクリプトに対する答えとして間違っている（既存の `portInUseAction` の判断と揃えている）。

断ったときは `exit(0)`。聞かれたことに答えただけで、失敗ではない。

## やらないこと

- **インスタンスごとの state ディレクトリ**（issue の案 2）。`~/.mulmoterminal` の共有は config や
  hidden-session 一覧では**意図した設計**で、#672 / #705 もその前提で直っている。settings だけの
  問題に、全体の構造を変える答えを当てない
- **owner pid をファイルに書く**（案 4）。settings ファイルは `claude --settings` にそのまま渡るので、
  こちらの都合のキーを混ぜられない。サイドカーにする手もあるが、cutoff で足りる
