active: true
derived: false
gherkin: |
  Feature: LocalStorage
    Scenario: Saving blobs to local disk atomically
      Given a LocalStorage instance configured with a base directory
      When binary data is saved
      Then it is written to a temporary file and atomically renamed to prevent partial writes
groups:
- STORE
header: |
  LocalStorage
level: 2.9
links:
- ARCH004: gR_7wx5EpD_LA3s4JJTJmD1-WOKDjlo563zos3pn-aE=
normative: true
ref: ''
reviewed: qkS6djfOTch4aBtnn67KfkOGczgqXJIXZ5dBabRata4=
text: |
  ## インターフェース

  ```python
  class LocalStorage(BlobStorageMaintenable):
      def __init__(self, base_dir: str | Path): ...
      def save(self, key: str, data: bytes) -> str: ...
      def load(self, location: str) -> bytes: ...
  ```

  ## 振る舞い

  ### アトミック書き込み (`save`)
  1. 指定ディレクトリ内に `tempfile.mkstemp` で一時ファイルを作成
  2. データを書き込み、`flush()` および `os.fsync()` でディスク到達を保証
  3. `os.replace` で最終的なパスへリネーム（アトミックな置換）

  ### パストラバーサル対策 (`load` / `delete`)
  - 解決後の絶対パスが `base_dir` の配下にあることを `is_relative_to` で厳密にチェックし、範囲外へのアクセスを遮断する。

  ## パラメータ詳細

  | パラメータ | 説明 | 制限 |
  |---|---|---|
  | `key` | 保存ファイル名のベース | `..` や `/` を含む場合は `ValidationError` |
  | `location` | `save` が返した識別子 | 相対パス形式を推奨 |

  ## エラーハンドリング

  - **ファイル不在**: `load` 時にファイルがない場合 `CacheCorruptedError` を送出
  - **権限エラー**: `PermissionError` 発生時は、GCでの回収に委ねるため警告ログのみ出力

  ## エッジケース

  - **ディスクフル**: 書き込み中に容量不足になった場合、一時ファイルを削除して `OSError` を送出
  - **Windowsパス**: パス区切り文字として `/` と `\` の両方を検証対象とする

  ## 設計判断

  大きなキャッシュデータ（モデルの重み等）をローカルファイルシステムに安全かつ効率的に保存・読み込みするための具体的なインターフェースとファイル管理手法を定義するため。
