ADR-0004: 作業枠の成果物を「消えない場所」へ昇格する¶
文脈¶
試作が消えた。 private な Web 実装(コミット 4 本・約 1,500 行)が 5 日後に無くなった。 ローカル git で管理していたので安全だと判断していたが、置き場所が悪かった(#63)。
「tmp」が 2 つあり、寿命が違う¶
| パス | 寿命 | |
|---|---|---|
| ccs の作業枠 | ~/.cc-scratch/<workspaceId> |
ホーム配下。OS は消さない |
| ハーネスの scratchpad | /private/tmp/claude-…/…/scratchpad/ |
/tmp 配下。macOS が数日で消す |
Claude Code のシステムプロンプトは一時ファイルを後者へ置くよう指示する。ccs new --tmp の
セッションは cwd が前者になるが、「一時的な作業」と言われたエージェントは後者を選ぶ。
作業枠は消えないのに、作業枠に立てたセッションの成果物が消える。 ここが穴。
欲しい 3 段階¶
| レベル | 置き場所 | 保証 |
|---|---|---|
| tmp | ~/.cc-scratch/<workspaceId> |
リセットで消えてよい |
| local | ~/ghq/local/<owner>/<name> |
ディレクトリは消さない。remote は無い |
| remote | ~/ghq/github.com/<owner>/<name> |
GitHub にも残る |
実測(2026-08-31)¶
この 2 つが設計を決めた。
1. claude --resume <uuid> は cwd を跨げる¶
作業枠で 1 往復した会話を畳み、別のディレクトリ(~/ghq/local/ken-ty/…)へ中身を移して
claude --resume <uuid> を打ったところ、会話がそのまま復元された。セッション名も残る。
→ 昇格しても同じ会話を続けられる。 セッションの張り替えは「立て直す」だけでよく、
restore_launch と同じ手が使える。
2. 会話ログは古い場所に残る¶
同じ実測で、レジストリの cwd は新しい場所になったが、会話ログは元の
~/.claude/projects/<エンコードした古い cwd>/ に残ったままだった。
→ ccs restore の索引は古い枠を指し続ける。 昇格したセッションが「空になった(あるいは
消えた)枠に戻す候補」として並ぶ。ここを手当てしないと昇格は完成しない(→ 未決 1)。
決定¶
1. 根治を先に置く。昇格は事後の救済である¶
穴の本体は「エージェントが /tmp を選ぶこと」であって、成果物が枠に残らないこと。
昇格コマンドは既に散らばったものを拾う道具で、次に同じことが起きるのは止められない。
作業枠のセッションに「成果物は cwd に置く」と伝える。 hub が CLAUDE.md を置いている
ので同じ手が使える、と当初は考えた。
これは昇格より小さく、先に効く。 別項目として先に片付ける。
見直し(2026-08-31、P0 の実装時):
CLAUDE.mdは置かない。--append-system-promptで渡す。hub との類推が成り立たなかった。 hub の作業ディレクトリは自動承認の条件から外れて いる(
path_is_hub_homeは「中身が空である必要は無い」と明記)が、作業枠は「印以外に 何も無い」が自動承認の条件そのもの(決定 4、I2c)。枠にCLAUDE.mdを置くと:
- 自動承認が止まる(I2c の条件 3 を破る)
ccs gcが掃除できなくなる(I3a の「印だけ」に落ちず、「印と中身」=報告だけになる)逃げ道として「
ccsが置いたCLAUDE.mdは数えない」も考えたが、利用者がそこに書き足した ときに見分けが付かない ── 中身を見ないと判断できず、見たとしても「消してよい」と決める 根拠にはならない。外れたら消す側に倒れるので採らない。
claude --append-system-prompt <text>ならファイルを置かずに伝わるので、どの不変条件も 壊さない。文面はCCS_SCRATCH_NOTEで変えられ、空にすれば渡さない。
2. 移す先は ~/ghq/local/<owner>/<name>¶
- 既にその形で使われている(実測:
~/ghq/local/ken-ty/goita-proto、remote 無しの git リポジトリ) ghq listはlocal/配下を深さを問わず拾う(#63 の実測、ghq 1.7.1)ghq createは宛先にgithub.com/を前置するので使えない。代わりはmkdir -p && git initの 2 行しかなく、ghq 本体に手を入れる価値は無い
3. ccs は移送先を git リポジトリにする¶
~/ghq/local/… に置くだけでは ghq list に出ない(ghq は .git を見る)。「local に昇格した」が
意味を持つのは git リポジトリになったときだけなので、git init はここに含める。
新規ディレクトリに対する git init は破壊的ではないので、ccs の後片付けの線
(git/rmdir が安全と認めたものだけ触る)を越えない。
枠が既に git リポジトリなら .git ごと移す。 履歴を捨てない。
4. 昇格後、枠は空にして返す。symlink は残さない¶
- 空になれば
ccs gcの対象に戻る(I3a の「印だけ」=消す) - symlink は残さない ── 枠は捨てる前提の場所なので、移送先が消えたときに 壊れたリンクだけが残る。「枠を見れば分かる」は印が担う役割で、symlink の仕事ではない
5. セッションは立て直す¶
tmux のペインの cwd は後から変えられない。実測 1 のとおり --resume は cwd を跨げるので、
ccs adopt と同じ形(元を閉じる → 新しい cwd で --resume)で足りる。
新しい場所は ghq 配下なので ensure_trust が自動承認する(design.md §4.4)。実測でも
ccs を通さずに立てたときだけ信頼確認が出た。
6. remote 段階(gh repo create)は ccs がやらない¶
外部への送信は人の承認が要る(AGENTS.md)。ccs promote remote は打つコマンドを出すだけに
する ── ccs gc が消す候補を予告するのと同じ形。
ccs の 4 責務に「外部サービスにリポジトリを作る」は無い。ここを持つと 5 つ目になる。
未決(人の判断が要る)¶
1. 昇格した会話を ccs restore がどう扱うか¶
実測 2 のとおり、会話ログは古い枠の索引に残る。昇格して枠を空にしたあと、
ccs restore はその会話を「枠に戻す候補」として並べてしまう。
| 案 | 中身 | 気になる点 |
|---|---|---|
| A | 昇格時に枠の印へ promotedTo を書き、restore がそれを見て skip |
枠を gc が消すと印も消えるので、そのあとは効かない |
| B | restore_reject_reason の cwd 突き合わせに任せる |
会話ログの cwd が更新されるかが未検証。先に実測が要る |
| C | 昇格時に会話ログのディレクトリごと新しい索引名へ移す | Claude の内部の置き場所を ccs が書き換えることになる。いままで避けてきた |
B を先に実測すべきだと考えている。もし会話ログの cwd が新しい場所に更新されるなら、 既存の検査だけで弾けて、新しい仕組みが要らない。
2. ~/ghq/local/ が別用途で使われている環境との同居¶
素材置き場(git 管理外)として使われていることがありうる(#63)。ccs が
local/<owner>/<name> を掘るとき、既存の中身とどう同居させるかは決めていない。
影響¶
docs/design.md§4.4(作業枠)に「昇格」を足すROADMAPに、根治(決定 1)を昇格より前の項目として立てるccs promoteは P1b で実装する。未決 1 が決まってから