# 複数 Pod 間の Scope 排他制御 ## 背景 [scope-redesign.md](scope-redesign.md) で Scope は「allow / deny の領域リスト + permission レベル」に再設計された。 これにより複数 Pod が同じファイルツリーに対して異なる権限を宣言する状況が日常化する。 現状、複数 Pod が同じ pwd や重なる allow を持っても何のチェックも警告もない。 両者が同じファイルに `write` を持っていた場合、ツール経由で同時に書き換えて 内容を破壊しうる。Git のコミット粒度より細かい競合は履歴からも復元しづらい。 Pod を「並行作業の単位」として扱う以上、scope の重なりは設計時に検知・制御 できる必要がある。 ## 要件 - **R1: write の重複を検出する** 同じファイルが 2 つ以上の Pod から `write` 権限で見えている状態を作らせない。 片方の Pod が起動中なら、もう片方の起動を拒否する(または昇格を拒否する)。 - **R2: read は共有可** `read` のみの重なりは許す。複数の閲覧者は問題ない。 - **R3: deny は重なりに影響しない** `deny` で write が落とされている path は、その Pod にとって write を持たないと みなして他 Pod との重複判定を行う。 - **R4: 解放の確実性** Pod の異常終了で排他が永久に残らないこと。lock の所有者プロセスが死んだら 自動解放されるか、stale lock として検知できる。 - **R5: 観測可能性** ユーザーが「今どの Pod がどこを write 占有しているか」を見られる。 競合で起動が拒否されたとき、競合相手をエラーメッセージで示す。 ## 設計上の論点 ### 排他の粒度 選択肢: - **A. ファイル単位** — 厳密だが lock 数が膨大になる - **B. allow rule 単位** — 宣言された target ごとに lock。粒度が荒いが宣言と一致して直感的 - **C. パス prefix の最小被覆** — write 領域を最小の prefix に縮約して lock。中間 おすすめは **B**。Pod の宣言と lock の単位が一致する方が説明しやすく、 ユーザーが「この Pod は src を握っている」と理解しやすい。 ### lock の保管場所 選択肢: - **A. lock file** — `~/.insomnia/locks/.lock` 等。各 Pod プロセスが直接握る - **B. 中央レジストリプロセス** — 常駐デーモンが scope の貸し借りを管理 - **C. session-store の拡張** — 既存の永続化基盤に lock テーブルを足す おすすめは **A**。デーモンを増やしたくないし、Pod は短命〜中時間生存の想定なので ファイルロック (`flock(2)` / `fcntl`) で十分。stale lock 検知は PID 死活で行う。 ### 競合発生時の挙動 選択肢: - **A. 起動失敗 (fail-fast)** — エラーで起動拒否、ユーザーが手で解決 - **B. 待機 (block)** — 競合相手が解放するまで起動を待つ - **C. 自動降格** — 競合する write を read に降格して起動 おすすめは **A**。並行作業の単位として Pod を起動するなら、競合は意図しない 状況であることが多い。明示エラーで気づける方が安全。`--wait` フラグや `--read-only` フラグで B / C を選べるようにするのは将来拡張で十分。 ### 取得タイミング - Pod 起動時に scope 全体を一括取得し、終了時に解放 - 起動中に scope を変えることは現状想定しない(manifest 編集 → 再起動) ### resume との関係 `Pod::restore` でも同じ scope が要求される。前回終了時に解放されているはずなので 取得が成功するのが正常。失敗する場合は別 Pod が起動している = ユーザーが意図的に 2 つ動かそうとしている。fail-fast に乗せる。 ## 影響範囲(想定) 実装時に詰める。現時点での見立て: - 新規モジュール `crates/pod/src/scope_lock.rs`(または独立クレート `scope-lock`) - lock の取得 / 解放 - stale lock 検知(PID 確認) - 競合情報の収集(誰がどの allow を握っているか) - `Pod::new` / `Pod::restore` の前段で lock 取得 - `Pod` の Drop / 明示解放で lock 返却 - `Controller` 層でのエラー伝搬とユーザー向けメッセージ - CLI に「現在のロック一覧」を見るコマンド(観測性 R5) ## 非ゴール - **同一 Pod 内の並行性制御** — 1 Pod 内のツール並行実行は本 ticket では扱わない - **ネットワーク越しの排他** — ローカルファイルロックのみ。複数マシンで同じワーキングコピーを共有する想定はしない - **read-write lock の細かな格上げ/格下げ** — 取得時に確定、起動中の変更はしない ## 依存 - [scope-redesign.md](scope-redesign.md) — allow / deny / permission レベルの構造が前提