ターミナルを閉じても、claude --bgで投げたセッションは走り続けます。親のシェルが死んだのに、なぜ残るのか。Claude Code v2.1.246で、この常駐の裏側を状態ファイルまで降りて観察しました。
ターミナルを閉じてもセッションが残る仕組み
supervisorプロセスがセッションをホストする
公式ドキュメントの “How background sessions are hosted” セクションによると、バックグラウンドセッションを動かしているのは起動元のターミナルではなく、ユーザーごとに1つ立つsupervisorという別プロセスです。最初のセッションを背景化するか、agent viewを開いた時点でClaude Codeが裏で起動します。シェルをexitしてもセッションはsupervisorの下にぶら下がったまま。だから消えません。
起動遅延への対策も “The supervisor process” セクションに書かれています。
The supervisor keeps one pre-warmed worker process ready so a dispatch from agent view or claude --bg starts without the delay of a cold launch.
次のディスパッチに備えてワーカーを1つだけ温めて待機させ、割り当てたら補充する。コールドスタートを避ける作りです。
claude daemon statusで実態を見る
仕組みの説明はここまでにして、手元のsupervisorを覗きます。
$ claude daemon status
pid: 1391 version: 2.1.246 uptime: 57983s origin: transient — started on-demand by `claude` (pid 1378) config: /Users/moha/.claude/daemon.json log: /Users/moha/.claude/daemon.log bg sessions: sock dir: /tmp/cc-daemon-501/1098680c control.sock: reachable bg workers: 2 running (control.sock), 2 in roster.json daemon.log: 148.2KB at /Users/moha/.claude/daemon.log holding this daemon open: 2 bg workers running (daemon waits for them to settle) `claude agents` (pid 1378)
読みどころはorigin行のtransient — started on-demand。launchdに登録するタイプの常駐サービスではなく、claudeコマンドが必要になった時だけオンデマンドで起動し、仕事がなくなればidle-exitします。手元のuptimeは57983秒、約16時間の稼働。末尾の「holding this daemon open」を見れば、いま誰がデーモンを掴んでいるかまで分かります。この時掴んでいたのは実行中のbgワーカー2つとclaude agentsの画面でした。
状態は~/.claude配下のファイルに残る
supervisorはセッションの状態をすべてディスクに書き出します。
~/.claude/daemon.log: supervisorのログ。手元では148.2KB~/.claude/daemon/roster.json: 実行中セッションの台帳~/.claude/jobs/<id>/state.json: セッション単位の状態
{
"proto": 1,
"supervisorPid": 1391,
"workers": {
"d29a41c6": {
"pid": 1411,
"sessionId": "d29a41c6-893f-4fa1-820a-729965712d80",
"rendezvousSock": "/tmp/cc-daemon-501/1098680c/rv/d29a41c6.sock",
"cliVersion": "2.1.246",
"cwd": "/Users/moha/workspace/app"
}
}
}
roster.jsonの中身です。自動アップデートやsupervisorの再起動をまたいでもセッション状態はディスクに残ると公式ドキュメントに明記があり、supervisorが不意に落ちた時はこの台帳から各ワーカーへ再接続します。ワーカーごとにcliVersionを持つため、アップデート直後のバージョン混在も台帳を見れば分かる仕組みです。
バックグラウンド実行の入口
claude –bgでシェルから投げる
$ claude --bg --name blog-observe --exec 'sleep 90'
backgrounded · 97b2f873 · blog-observe claude agents list sessions claude attach 97b2f873 open in this terminal claude logs 97b2f873 show recent output claude stop 97b2f873 stop this session
claude --bg "プロンプト"でモデルに投げるセッションを起動でき、--execに切り替えるとシェルコマンドだけの背景ジョブになります。起動直後にclaude側がattach / logs / stopのコマンド例をIDごと出力するので、控えを取る手間がない。--model opusや--permission-mode planもこの時に指定できます。
–exec以降は全部コマンド扱い
1回ハマりました。--execの後ろに別のフラグを置くと、そこまで含めてシェルコマンドとして渡ります。
# NG: --nameがsleepの引数になる
$ claude --bg --exec 'sleep 90' --name blog-observe
$ claude logs eff81c0c
sleep: invalid time interval: --name sleep: invalid time interval: blog-observe usage: sleep number[unit] [...]
ジョブはexit 1で失敗し、~/.claude/jobs/eff81c0c/state.jsonにはこう残っていました。
{
"state": "failed",
"detail": "exit 1 — Unit can be 's' (seconds, the default), ...",
"template": "exec",
"intent": "sleep 90 --name blog-observe",
"backend": "daemon"
}
intentフィールドを見ると、--name blog-observeまでコマンドに含まれていました。フラグは--execより前に置く。stderrの要約がdetailに入るため、失敗の一次調査はこのファイルだけで足りました。
実行中のセッションを/bgで背景に送る
対話中のセッションを背景へ送るのは/background(短縮形/bg)。公式の “What carries over when you background” に引き継ぎ条件がまとまっていて、実行中のツールは最大10秒だけ完了を待ち、未送信の入力が残っている場合はClaude Codeが背景化をキャンセルします。会話を手元に残したまま複製だけ背景で走らせるなら、/forkで会話を分岐するのが確実です。
claude agentsで全セッションを確認する
一覧には対話中のセッションも載る
検索サジェストに出る「claude code バックグラウンド 確認」の最短ルートがこのコマンドです。
$ claude agents --json
[
{
"id": "5a7700b2",
"cwd": "/Users/moha/workspace/app/.claude/worktrees/fix-e2e",
"kind": "background",
"status": "idle",
"state": "done"
},
{
"pid": 54292,
"cwd": "/Users/moha/workspace/ops",
"kind": "interactive",
"name": "wp-auto-poster-99"
}
]
意外だったのはkind: "interactive"の行。バックグラウンドだけでなく、いまターミナルで開いている対話セッションも同じ一覧に並びます。claude agentsで開くTUI(agent view)はこの情報を1画面に束ねたもので、--jsonならスクリプトから叩けるため、監視やstatuslineへの流用もできます。
行頭のアイコンが状態を表す
| 表示 | 状態 |
|---|---|
| ✽ (点滅) | 実行中。ツール実行か応答生成中 |
| ✻ (黄) | 入力待ち。質問・許可・回答を待っている |
| ∙ | アイドル。プロセス終了済み |
| ✢ | /loopセッションのイテレーション間スリープ |
| 緑 / 赤 / グレー | 完了 / 失敗 / 停止 |
放置すると進まないのは✻の入力待ちなので、一覧ではまず黄色を探します。✢は/loopで繰り返し実行しているセッションだけに付く専用マークです。
Spaceで覗いて、そのまま返信する
公式ドキュメントで “Peek and reply” と呼ばれている操作です。一覧でSpaceを押すとピークパネルが開き、アタッチせずに直近のやり取りを覗けます。数字キーで選択肢に答え、Tabで提案回答を採用、!を頭に付ければBashコマンドも送れる。腰を据えて見るならEnterでアタッチし、Ctrl+Zでデタッチして一覧に戻ります。
ファイル編集は最初の書き込みでworktreeへ隔離
先ほどの--json出力に、cwdが.claude/worktrees/fix-e2eの行がありました。バックグラウンドセッションは起動時点では元のディレクトリのまま動き、最初にファイルを書き込む直前にClaude Codeがセッションをgit worktreeへ移します。作られるのは起動の瞬間ではなく書き込みの瞬間。読むだけのセッションはworktreeを作りません。
並列で同じリポジトリを触っても作業ツリーが衝突しない代わりに、変更は元のブランチへ直接は乗りません。1本だけ背景に送る使い方なら、.claude/settings.jsonに"worktree": {"bgIsolation": "none"}を書いて隔離を切ると、マージの一手間が省けます。
停止と削除の挙動
放置セッションは約1時間で止まる
Once a session finishes and sits unattached for about an hour, the supervisor stops its process to free resources.
セッションが完了し、ユーザーがアタッチしないまま約1時間経つと、supervisorがプロセスを止めます。トランスクリプトと状態はディスクに残るため、止まった後もclaude --resumeで再開できます。止めたくないセッションはagent viewでCtrl+Tのピン止め。ホストのメモリが逼迫した時も、supervisorはピン止めなしのアイドルセッションから順に停止していきます。
どこから削除するかで結果が変わる
シェルからの停止・削除は素直でした。
$ claude stop 97b2f873
stopped 97b2f873
$ claude rm 97b2f873
removed 97b2f873
ただ、削除経路でworktreeの扱いが変わります。agent viewのCtrl+X2連打で削除すると、Claude Codeが作ったworktreeごと消えます。未コミットの変更も一緒です。一方claude rmは未コミット変更があるとworktreeを保持し、未pushのコミットがあると削除自体を拒否します。消す前にgit logで取りこぼしを確認する癖を付けた方が安全です。
バックグラウンドBash・/fork・Remote Controlとの違い
「裏で動かす」系の機能が増えたので、何を単位に動くかで整理します。
| 機能 | 単位 | どこで動くか |
|---|---|---|
バックグラウンドセッション(--bg / /bg) | セッションまるごと | supervisor配下の別プロセス |
| バックグラウンドBash(Ctrl+B) | シェルコマンド1つ | 現在のセッション内 |
/fork | 会話のコピー | 背景セッションとして複製 |
| Remote Control | 操作チャネル | 既存セッションにスマホから接続 |
| サブエージェント | セッション内の子タスク | 親セッションが管理 |
Remote Controlは実行場所を変えずに操作側を増やす機能で、詳細はRemote Controlの設定に書きました。バックグラウンドセッションも通常セッションと同じサブスクリプション枠を消費します。公式のLimitationsにあるとおり、10並列なら約10倍の速さで使用量が減ります。
まとめ
- バックグラウンドセッションはターミナルではなくsupervisorがホストする。シェルを閉じても残るのはこのため
- 確認は
claude daemon statusとclaude agents --json。台帳は~/.claude/daemon/roster.json、個別状態は~/.claude/jobs/<id>/state.json --exec以降は全部コマンド扱い。--nameなどのフラグは前に置く- 放置は約1時間で停止、削除はagent view経由だとworktreeごと消える
agent viewはresearch preview中の機能で、公式ドキュメントにも “Agent view has evolved quickly during research preview” とあり、バージョンで挙動が変わります。手元のv2.1.246と差分が出たら、公式のVersion historyの表で追えます。