コンテンツにスキップ

ADR-0001: 使い捨て作業枠は固定 8 枠をやめ、セッションごとに一意なディレクトリを作る

  • 状態: Accepted
  • 日付: 2026-08-20
  • 関連: docs/design.md §2.6 / §4.2 / §4.4 / §6、#6、bin/ccs の resolve_as_scratch / path_is_scratch_slot / ensure_trust / orphan_slots、 ADR-0002(同一性をどう表現するかを決めた続き)
  • 覆すもの: docs/design.md §6 決定表の「trust の扱い = §4.4 案 B + 限定的な A」、および §4.4 の案 B(固定枠を人が一度だけ信頼する)
  • 実装: 本 ADR では行わない。ドキュメントとコメントだけを直し、bin/ccs の変更は別 PR に分ける

文脈

ghq 配下に無い、プロジェクト化していない一時的な頼まれごとのために、ccs は使い捨ての作業 ディレクトリを配る。当初これを ~/.cc-scratch/{1..8} の固定 8 枠とした(§4.4 案 B)。 理由は 2 つあった。

  1. trust ダイアログの回避 — 新しいディレクトリで対話モードの claude を起動すると 信頼確認で止まる。枠を先に作って人が一度だけ信頼しておけば、以後は出ない
  2. 有限にする — 使い捨てセッションが無限に増えるのを防ぐ

運用してみて、3 つの問題が出た。

(a) 理由 1 は既に成立していない。 2026-08-18 の実機確認で「枠 1 本ごとに人の承認が要る」 ことが分かり、案 A(ccs が起動前に hasTrustDialogAccepted を書き込む)へ切り替えた。 ensure_trust() がそれで、cmd_new の中で tmux new-session より前に走る。つまり枠が 事前に作ってあることは、もう trust とは無関係になっていた。だが §4.4 本文と §6 決定表は 古いまま残り、bin/ccs が CCS_SCRATCH_SLOTS に添えたコメントも古い理由を語り続けていた。

# 使い捨て作業枠の本数。
#
# **枠を有限にするのが目的**(docs/design.md §4.4 案 B)。毎回新しい
# ディレクトリを作る方式だと、trust ダイアログが毎回出るうえ、使い捨て
# セッションが無限に増える。枠なら人が最初の 1 回だけ信頼すれば済む。
: "${CCS_SCRATCH_SLOTS:=8}"

「毎回出る」も「人が最初の 1 回だけ信頼すれば済む」も、2026-08-18 以降は成り立っていない。

(b) 枠の番号とセッションの名前がズレる。 立てたあと、Remote Control 上で分かりやすい 名前に変えて長時間使うことがある。ccs は枠の占有を「cc/tmp-N という名前の tmux があるか」 で推論しているため、cc/equipment(枠 4 で稼働中)のようなセッションは占有していないと 見なされる。実際 2026-08-19 に、稼働中の枠を空と誤認させないために、人がダミーファイルを 置いて非空にする回避を取った。ディレクトリの同一性を番号で表し、セッションの同一性を名前で 表しているのに、両者を結ぶものが命名規約しか無い。

(c) 汚れた枠が二度と配られない。 ccs kill は tmux を畳むだけでファイルを消さない。 ccs gc は中身の残った枠を報告するだけで消さない(そこにあるのは利用者のファイルだから、 という意図した設計)。消すのは空の枠だけで、それも rmdir を使う。一方 resolve_as_scratch は空の枠しか配らないので、人が手で片付けない限り、ゴミが残った枠はプールから永久に消える。 2026-08-20 時点で 8 枠中 2 枠しか空いていなかった。

さらに (c) は行儀の問題ではない。自動承認の条件は path_is_scratch_slot() が見るとおり 「~/.cc-scratch の直下」かつ「いま空」の両方であり、「枠が空である」ことは安全確認を 飛ばしてよいことの根拠そのものである。ゴミが残った時点で、その根拠が崩れている。

決定

  1. 使い捨て作業枠は、セッションごとに一意なディレクトリを作る。 固定 8 枠をやめる
  2. ディレクトリの同一性とセッションの名前を分離する。 名前は変えられる。ディレクトリは変わらない
  3. 枠の占有を命名規約から推論しない。 いま resolve_as_scratch は 「cc/tmp-N という名前の tmux があるか」で占有を当てているが、名前は変えられる。 占有はそのディレクトリを cwd にしているセッションがあるかで見る。 Claude 自身のレジストリが cwd を持っており(live_cwd_for が既に読んでいる)、 ccs が自前の状態ファイルを持つ必要は無い(design.md §2.1 の委譲方針を崩さない)
  4. 使い終わったディレクトリは削除する。 会話は claude --resume <uuid> で戻せるので、 ファイルの retention(アーカイブ・猶予期間)は設けない
  5. 有限性は枠数ではなく、確実な後片付けで担保する

根拠

trust は障壁ではない(実測、2026-08-20)

実験 1 — 準備なしで新規ディレクトリを起動する

一度も使ったことのないパスで claude を対話モードで起動した。

 Quick safety check: Is this a project you created or one you trust?
 ❯ 1. Yes, I trust this folder
   2. No, exit

止まっている間、~/.claude/sessions/ にセッションファイルは現れない。ccs から見ると 「立ったのか死んだのか分からない」状態になる(CCS_NEW_TIMEOUT=30 で待たされる正体)。

実験 2 — 起動前に trust を書き込んでから、新規ディレクトリを起動する

同じくまっさらなディレクトリを作り、起動前に ~/.claude.json の projects["<abs path>"].hasTrustDialogAccepted = true を書いてから起動した。

╭─── Claude Code v2.1.237 ───────────────────────────╮
│                  Welcome back Ken!                 │
│             /…/scratchpad/trust-probe2             │

ダイアログは出ず、そのままプロンプトに到達し、~/.claude/sessions/ にも登録された。

結論。 止まる条件は「ディレクトリが新しいこと」ではなく「起動時点で trust エントリが 無いこと」。ディレクトリをいつ作ったかは関係しない。

再現手順:

P=$(mktemp -d)
# 実験 1: そのまま起動 → ダイアログで止まる
tmux new-session -d -s probe1 -c "$P" 'claude -n probe1; exec $SHELL'
tmux capture-pane -p -t probe1

# 実験 2: 先に trust を書いてから起動 → 出ない
jq --arg p "$P" '.projects //= {} | .projects[$p] //= {}
                 | .projects[$p].hasTrustDialogAccepted = true' \
   ~/.claude.json > /tmp/cj && mv /tmp/cj ~/.claude.json
tmux new-session -d -s probe2 -c "$P" 'claude -n probe2; exec $SHELL'
tmux capture-pane -p -t probe2

一意なディレクトリは、今のコードのままでも自動承認を通る

path_is_scratch_slot() {
    _scratch_root=$(abs_dir "$CCS_SCRATCH_ROOT" 2>/dev/null) || return 1
    [ "$(dirname "$1")" = "$_scratch_root" ] || return 1
    dir_is_empty "$1"
}

枠番号を一切見ていない。 「~/.cc-scratch の直下」かつ「空」だけ。したがって ~/.cc-scratch/<uuid> を mkdir して渡せば、この判定を通り、ensure_trust が信頼を 書き込み、ダイアログは出ない。trust 側に必要な変更はゼロ。

安全性の理屈も崩れない。§4.4 の追記はこう述べている。

枠は ccs 自身が作ったディレクトリで、渡すのは空だと確かめたときだけ。つまり信頼確認が 守ろうとしている「知らないコード」がそこには存在せず、確認は情報を増やさない。

作りたてのディレクトリは空であることが保証されるので、この論拠はむしろ強くなる。

会話は既にソフト削除されている

transcript は ~/.claude/projects/<エンコードした cwd>/<uuid>.jsonl に残り続け、 claude --resume <uuid> で戻せる。実際 2026-08-19 に、畳んだセッション 3 本をこの手順で 復活させている。会話に retention があり、ファイルに無いという非対称があった。

ここで取れる道は 2 つある。ファイルにも猶予期間を与えて揃えるか、会話が戻せることを根拠に ファイルは即削除にするか。後者を採る。 一時作業のファイルは会話から作り直せることが 多く、猶予期間は「いつ消えるか」を人が覚えておく負債を生む。

捨てた案

案 捨てた理由
固定枠を維持し、掃除コマンドだけ足す (b) の名前ズレが残る。枠数の上限も残り、詰まったときに人が掃除する必要がある
削除したファイルをアーカイブへ退避し、N 日後に本削除 会話が resume で戻せる以上、二重の保険。retention の期限管理という新しい負債を作る
枠を汚したまま許容し、設計思想のほうを変える 「空であること」が trust 自動承認の根拠になっているので、思想の話ではなく安全条件の話。許容できない
send-keys で信頼ダイアログに答える(§4.4 案 C) 画面の見た目に依存して脆い。当初の判断を維持する

影響

コード

  • resolve_as_scratch() — 空き枠を探す処理が、一意なディレクトリを作る処理に変わる
  • path_is_scratch_slot() — 変更不要(枠番号を見ていないため)
  • dir_is_empty() — 占有の推論に使うのをやめる。レジストリが占有を持つ
  • cmd_kill — tmux を畳むプリミティブのまま。ファイルには触らない(責任の切り方として正しい)
  • 新規コマンド — 畳む + ディレクトリを削除する上位コマンド。日常はこちらを使う。 中身の削除は使い捨て作業ディレクトリにだけ適用し、ghq 配下では kill と同じ挙動にする
  • cmd_gc — セッションが生きていない使い捨てディレクトリを、中身ごと削除できるようにする
  • CCS_SCRATCH_SLOTS に添えたコメント — 古い理由(trust ダイアログの回避)を語っているので 書き換え、ADR-0001 を参照する。これだけは本 ADR と同じ PR で直す(残しておくと、 次に読む人が同じ前提から議論を始める)

ドキュメント

いま直すもの(古い理由が残っていると、次に読む人が同じ前提から議論を始めるため)。

  • docs/design.md §2.6 — 実測は残す。ただし「新しいディレクトリだと止まる」ではなく 「起動時点で trust エントリが無いと止まる」に正す
  • docs/design.md §4.4 — 案 B の記述と 2026-08-18 の追記を整理し、決定は ADR-0001 を参照する形にする
  • docs/design.md §6 決定表 — 「trust の扱い」の行を Superseded として ADR-0001 へ送る
  • docs/design.md §4.2 命名規約 — tmp-<枠番号> を選んだ根拠(枠が有限だから番号で足りる)が 崩れる。名前とディレクトリを分ける方針を書き、決定は ADR-0001 へ送る
  • docs/tutorial.md / docs/hands-on.md — 「枠は 8 本で固定」と tmp-N。実装が変わるまでは 現状の記述が正しいので、いま足すのは ADR-0001 への導線だけ

実装が変わってから直すもの。 先に書き換えると、動くものと文書が食い違う。

  • bin/ccs の usage() の CCS_SCRATCH_SLOTS の行と、その出力から起こした docs/img/help.svg
  • docs/tutorial.md / docs/hands-on.md の手順と期待される出力
  • docs/why.md の ccs new --tmp の実行例(tmp-8)— これは実測ログなので、撮り直しになる
  • docs/agent-tools.md の tmp-1 — SendMessage の打ち方の例
  • ROADMAP.md「確定している設計方針」の trust の行 — 固定枠を前提にしている

存在しないもの。 docs/configuration.md は無く、環境変数の一覧は ccs help(bin/ccs の usage())が持っている。README.md に tmp-N の例示は無い(--tmp の紹介だけ)。

運用

  • 稼働中のセッションを守るために置いたダミーファイル(~/.cc-scratch/4/SESSION.md、 ~/.cc-scratch/8/SESSION.md)は、占有がレジストリに移った時点で不要になる。削除する
  • 移行時、既存の ~/.cc-scratch/{1..8} に残っているファイルの扱いを決める必要がある (2026-08-20 時点で枠 5 に Zenn 記事の下書き、枠 6 にネット回線診断の成果物がある)

未決

以下は本 ADR では決めない。実装時に決めて、必要なら別 ADR にする。

  • 上位コマンドの名前(ccs close / ccs done / ccs finish)
  • 削除前の確認をどう出すか(非空なら列挙して y/N か、常に黙って消すか)
  • ディレクトリ名に何を使うか(session の uuid / 短いランダム / slug + 連番)
  • CCS_SCRATCH_SLOTS を廃止するか、同時に立てられる本数の上限として残すか