# feat #910 Phase 2a — 編集の退避先（3 世代バックアップ）

Phase 2 の本題は「離れるときに自動保存」だが、**自動保存は退避先が無いと危ない**。先に
バックアップ機構だけを入れる。UI の変更はゼロ。

## なぜ先にこれか

- 自動保存は「ユーザーが明示的に押していない書き込み」なので、取り消す手段が要る
- #916 で入れた 409 の 2 択（読み直す / このまま上書き）は、**今はどちらも破棄を伴う**。
  捨てる側を先に退避できれば、どちらを選んでも失われなくなる

## 置き場所

`~/.mulmoterminal/backups/<hash16>/<epoch_ms>-<basename>`

- **プロジェクト外**。`README.md.bak` を隣に置くと `git status` に出て、エージェントの視界に
  入って掃除されるかコミットされる
- `hash16` は**絶対パスの sha256 先頭 16 桁**。ディレクトリ名にパスを使うと、区切り文字・
  大文字小文字・長さ制限で移植性が壊れる
- 同じディレクトリに `source.txt`（元の絶対パス）を 1 度だけ書く。ハッシュ名だけだと人間が
  中身を辿れない

## 3 世代

新しい順に 3 つ残して古いものから消す。取るのは 2 箇所:

| いつ | 何を退避するか |
|---|---|
| `GET .../text`（ファイルを開いた） | そのときのディスクの内容 |
| `PUT .../write`（上書きする直前） | **これから消えるディスクの内容** |

`/text` は再読込でも呼ばれるので、**直前の世代と内容が同じなら世代を回さない**。回すと、
同じ内容 3 つで本当に必要な履歴が押し出される。

クライアントのバッファ（サーバが知らない編集内容）を退避するために `PUT .../backup` を足す。
409 バナーの「読み直す」で自分の編集を捨てる前に、これで銀行に預けてから捨てられる。

## 失敗しても本来の操作を止めない

バックアップは best-effort。ディスクフル・権限・競合で失敗しても、**読み込みと書き込み自体は
成功させる**。バックアップが取れないことを理由にユーザーの保存を拒否するのは本末転倒。

## テスト

- 純粋部分: パスからのディレクトリ導出 / 3 世代を超えたときにどれを消すか
- ルート: 開くと 1 世代できる / 4 回開くと 3 つに保たれる / **同じ内容なら回らない** /
  書き込み前にディスクの内容が退避される / `PUT .../backup` が任意のテキストを預かる /
  **バックアップが失敗しても read/write は 200**

## やらないこと（Phase 2b）

- 自動保存（離れるときに保存）とセルごとの記憶。それが入って初めてズーム移動のたびの
  確認ダイアログが消える
- 409 の 2 択を「捨てる側を先に退避」に繋ぎ込むのも 2b（本 PR は預かる口を用意するだけ）
