マニフェスト改修完了
This commit is contained in:
parent
4ec8f63482
commit
911d3b8d6c
1
TODO.md
1
TODO.md
|
|
@ -3,7 +3,6 @@
|
|||
- [ ] Bash ツール (Permission 層と統合) → [tickets/bash-tool.md](tickets/bash-tool.md)
|
||||
- [ ] Compact の改善(要約品質 + 挙動詳細) → [tickets/compact-improvements.md](tickets/compact-improvements.md)
|
||||
- [ ] Protocol の設計 → [tickets/protocol-design.md](tickets/protocol-design.md)
|
||||
- [ ] Manifest のパス解決: cwd ベース + manifest ファイル相対 → [tickets/manifest-path-resolution.md](tickets/manifest-path-resolution.md)
|
||||
- [ ] パーミッション: パターンベースのツール実行制御 → [tickets/permission-extension-point.md](tickets/permission-extension-point.md)
|
||||
- [ ] Pod オーケストレーション
|
||||
- [ ] Pod の上流イベント報告 (子 → 親) → [tickets/pod-upstream-events.md](tickets/pod-upstream-events.md)
|
||||
|
|
|
|||
|
|
@ -1,86 +0,0 @@
|
|||
# Manifest のパス解決: cwd ベース + manifest ファイル相対
|
||||
|
||||
レビュー中: [manifest-path-resolution.review.md](manifest-path-resolution.review.md)
|
||||
|
||||
## 背景
|
||||
|
||||
現状 manifest 内のパス(`pod.pwd` / `provider.api_key_file` / `scope.allow.target` / `scope.deny.target` / `compaction.provider.api_key_file`)は全て絶対必須で、相対パスは `ResolveError::RelativePath` で弾かれる。
|
||||
|
||||
これは 4 層(builtin / user / project / overlay)のカスケードで「相対の基準点が層ごとに違う」曖昧さを避けるための制約だったが、次の点で歪みを生んでいる:
|
||||
|
||||
- `pod.pwd` フィールドが Unix 慣習(cwd はプロセス状態、config には書かない)と乖離
|
||||
- project manifest で `scope.allow = [{ target = "<repo>" }]` のような絶対パスを強いられ、プロジェクトをどこに置いても動くはずの設定が壊れる
|
||||
- user manifest で `api_key_file = "~/.config/insomnia/keys/anthropic"` が書けない(`~` 展開もしていない)
|
||||
|
||||
cargo / pyproject / npm などに倣い「相対パスは manifest ファイルの位置基準」に切り替える。合わせて `pod.pwd` を廃止し、プロセスの cwd を使う。
|
||||
|
||||
## 新しい解決規則
|
||||
|
||||
- `pod.pwd` フィールドは削除。Pod の作業ディレクトリ = プロセスの cwd
|
||||
- 相対パスの基準は層ごとに決める
|
||||
- user manifest (`~/.config/insomnia/manifest.toml`) の相対 = そのファイルの親ディレクトリ
|
||||
- project manifest (`<project>/.insomnia/manifest.toml`) の相対 = **プロジェクトルート**(`.insomnia/` の親)。`target = "."` がワークスペース全体を指すように
|
||||
- overlay(インライン TOML、ファイル位置なし)の相対パスは **プロセスの cwd** 基準
|
||||
- builtin 層には manifest を埋め込んでいないので対象外
|
||||
|
||||
## 解決のタイミング
|
||||
|
||||
各層を**マージする前に絶対化**する。層をまたいだ相対パス合成は行わない。
|
||||
|
||||
```
|
||||
user.toml (partial) → resolve_paths(base=user_dir) → absolute partial
|
||||
│
|
||||
project.toml (partial) → resolve_paths(base=prj_dir) ─┤
|
||||
│── merge → PodManifestConfig
|
||||
overlay (partial) → resolve_paths(base=cwd) ──────────┤
|
||||
│
|
||||
builtin defaults → ────────────────────────────────── ┘
|
||||
```
|
||||
|
||||
`TryFrom<PodManifestConfig>` の時点では全パスが絶対になっているので、`ensure_absolute` は不変条件のチェックとしてのみ残す。
|
||||
|
||||
## 影響範囲
|
||||
|
||||
### `crates/manifest`
|
||||
|
||||
- `PodManifestConfig` から `pod.pwd` を削除
|
||||
- 各 partial config を「ベースパス付き」で解決するヘルパーを追加(関数シグネチャ案: `fn resolve_partial(cfg: PodManifestConfigPartial, base: &Path) -> PodManifestConfigPartial`)
|
||||
- 対象フィールド: `provider.api_key_file` / `scope.allow.target` / `scope.deny.target` / `compaction.provider.api_key_file`
|
||||
|
||||
### `crates/pod/src/factory.rs`
|
||||
|
||||
- `with_user_manifest` / `with_project_manifest_from` は渡された manifest ファイルの親ディレクトリを base として保存、解決時に使う
|
||||
- `with_overlay_toml` / `with_overlay_config` はプロセス cwd を base として使う
|
||||
- マージ順は現状のまま(overlay が最優先)
|
||||
|
||||
### `crates/pod/src/pod.rs` 他
|
||||
|
||||
- `pod.pwd` を参照している箇所を `std::env::current_dir()` に置き換え
|
||||
- `Pod::from_manifest` / `from_manifest_spawned` のシグネチャから pwd 関連を削除
|
||||
|
||||
### `crates/pod/src/spawn_pod.rs`
|
||||
|
||||
- overlay TOML 構築から `pod.pwd` を消す
|
||||
- 子プロセス起動時に `Command::current_dir(spawner_pwd)` で cwd を明示
|
||||
(現状の「spawner の pwd を子に引き継ぐ」挙動を維持するため)
|
||||
- 将来、LLM が子の cwd を明示的に指定したくなったら `SpawnPod` の入力に `cwd` を足す(本チケット範囲外)
|
||||
|
||||
### `crates/pod/src/main.rs`
|
||||
|
||||
- `--pwd` フラグは削除(cwd が代替)
|
||||
- 起動スクリプトや TUI 側で `cd` してから `pod` を起動する運用に変更
|
||||
|
||||
## 完了条件
|
||||
|
||||
- `pod.pwd` フィールドの削除
|
||||
- 各層のパスが manifest ファイル基準(overlay は cwd 基準)で解決される
|
||||
- マージは絶対化後の値で行う
|
||||
- 既存テストが通る / 相対パスを使った manifest で起動可能
|
||||
- `api_key_file = "keys/anthropic"` が user manifest 内で動作
|
||||
- project manifest で `scope.allow = [{ target = "." }]` が動作
|
||||
|
||||
## 範囲外
|
||||
|
||||
- `~` 展開(`dirs::home_dir()` ベースの展開は別途。まずは `./keys/anthropic` のような単純相対のみ)
|
||||
- `SpawnPod` の入力に子 cwd の指定を追加(cwd 明示は別チケット)
|
||||
- `pod` CLI の `--pwd` 廃止後の移行期間対応(一発破壊的変更で行く)
|
||||
|
|
@ -1,64 +0,0 @@
|
|||
# Review: manifest-path-resolution
|
||||
|
||||
実装コミット `aed46e6`(マニフェスト解決の相対パス化)に対するレビュー。`cargo build` clean、`cargo test --workspace` 全 pass。
|
||||
|
||||
## 総評
|
||||
|
||||
チケット要件(`pod.pwd` 削除、相対パス = manifest ファイル基準、overlay は cwd 基準、マージ前絶対化)を忠実に実装。テストもカスケード各層・不変条件違反・両 manifest レイヤの相対解決まで網羅。ビルド・テスト通過。
|
||||
|
||||
指摘 1 の UX 判断だけ合意したら完了可。
|
||||
|
||||
## 完了条件の対応
|
||||
|
||||
| 要件 | 状態 | 確認箇所 |
|
||||
|---|---|---|
|
||||
| `pod.pwd` 削除 | ✅ | `PodMeta` と `PodMetaConfig` から削除 |
|
||||
| Pod pwd = cwd | ✅ | `pod.rs:current_pwd()` = `current_dir().canonicalize()` |
|
||||
| 相対 = manifest ファイル基準 | ✅ | `PodManifestConfig::resolve_paths(base)` + `factory::manifest_base()` |
|
||||
| overlay は cwd 基準 | ✅ | `factory::resolve_and_merge_overlay` で `current_dir()` を base |
|
||||
| マージ前に絶対化 | ✅ | `factory::resolve()` で各層 `.resolve_paths(&base)` → merge |
|
||||
| `ensure_absolute` は不変条件チェックのみ | ✅ | `TryFrom` に残存、cascade 層で通れば通るだけ |
|
||||
| `--pwd` 廃止 | ✅ | CLI から削除 |
|
||||
| `api_key_file = "keys/anthropic"` 動作 | ✅ | `resolve_paths_joins_relative_api_key_file` テスト |
|
||||
| `scope.allow target = "."` 動作 | ✅ | `project_manifest_relative_paths_resolve_against_insomnia_dir` テスト |
|
||||
|
||||
## 指摘と判断
|
||||
|
||||
### 1. project manifest の base が `.insomnia/` であることの UX(要判断)
|
||||
|
||||
**状況**: 実装は `<project>/.insomnia/manifest.toml` に `target = "."` と書いた場合、**`<project>/.insomnia/`** を指す。チケットの「manifest ファイルのあるディレクトリ基準」を忠実に実装した結果。
|
||||
|
||||
```rust
|
||||
// factory.rs:538 テスト
|
||||
// project manifest 内 target = "." が .insomnia/ ディレクトリに解決される
|
||||
assert_eq!(manifest.scope.allow[0].target, insomnia_dir);
|
||||
```
|
||||
|
||||
**懸念**: ユーザが「project manifest で `target = "."`」と書いたら自然には「プロジェクトルート」を意図する。`.insomnia/` 下を scope の対象にしたい運用は稀。
|
||||
|
||||
**選択肢**:
|
||||
- **(a)** このまま。規則の一貫性(全層 "manifest と同じ dir")を優先し、ドキュメントで「`target = ".."` でプロジェクトルート」と案内
|
||||
- **(b)** project manifest のみ base を親 `<project>/` にする。user manifest と挙動が揃わなくなる例外ルール
|
||||
- **(c)** `.` 表記を使わず「絶対パス or `..` 表記」を案内のみ
|
||||
|
||||
**判断**: **(a) を推奨**。user manifest (`$XDG_CONFIG_HOME/insomnia/`) で `keys/anthropic` が `$XDG_CONFIG_HOME/insomnia/keys/anthropic` を指すのと同じ規則で、シンプル。ただし project で違和感が強いので README / docs で**典型例のテンプレ**を提示する必要あり(例: `target = ".."` で project root)。
|
||||
|
||||
この判断を合意できれば完了条件を満たす。(b) を選ぶなら `factory.rs:manifest_base` から派生した project 専用の親ディレクトリ計算に差し替える小改修が必要(1 関数程度)。
|
||||
|
||||
### 2. `pod.pwd` を書いた古い manifest の警告が埋もれる
|
||||
|
||||
**状況**: `from_toml_accepts_unknown_field` テストで確認されている通り、`pod.pwd = "/obsolete"` は `serde_ignored` の warn でスキップされる。`tracing_subscriber` が WARN 有効になっていないと出ない。
|
||||
|
||||
**判断**: 範囲外だが、移行ユーザが「設定したのに効かない」と混乱するリスクあり。README / CHANGELOG にマイ採用しないション注記を入れたい(本チケットに含めるか、別 issue を切るかは任意)。本チケットの完了条件には影響しない。
|
||||
|
||||
### 3. 軽微
|
||||
|
||||
- `PodManifestConfig::resolve_paths` の `debug_assert!(base.is_absolute())` は release で落ちないが、現状の呼び出し側(`manifest_base` / `current_dir`)が絶対を保証するので許容
|
||||
- `spawn_pod.rs` で overlay TOML から `pod.pwd` を消し、`Command::current_dir(spawner_pwd)` で子に cwd を伝える構造へ正しく移行
|
||||
- `Scope::from_config` の signature 変更(`base` 引数削除)が全呼出箇所に反映されている
|
||||
- 不変条件違反(`ResolveError::RelativePath` / `ScopeError::RelativeTarget`)が両層でチェックされていて、万一 cascade 解決漏れがあっても catch される二重防衛になっている
|
||||
|
||||
## 完了に向けた作業
|
||||
|
||||
- 指摘 1 について (a) で合意 → このままマージ
|
||||
- ドキュメントでの「`target = ".."` で project root」ガイドは別タスク化可(本チケット範囲外扱い)
|
||||
|
|
@ -22,7 +22,7 @@ spawned Pod(子)のライフサイクルに親 Pod が反応する仕組み
|
|||
```rust
|
||||
pub enum Method {
|
||||
Run { input: String },
|
||||
Notify { message: String }, // 既存: 人間・tool → LLM 文脈、副作用なし
|
||||
Notify { message: String }, // 人間・tool → LLM 文脈、副作用なし(本チケットで source を削除)
|
||||
PodEvent(PodEvent), // 新: 子 → 親、typed なライフサイクル報告
|
||||
Resume,
|
||||
Cancel,
|
||||
|
|
@ -34,7 +34,7 @@ pub enum PodEvent {
|
|||
/// 子が1ターン終えて IDLE になった
|
||||
TurnEnded { pod_name: String },
|
||||
|
||||
/// 子でエラーが発生した(ターンは続行されるとは限らない)
|
||||
/// 子で Worker 実行エラーが発生した
|
||||
Errored { pod_name: String, message: String },
|
||||
|
||||
/// 子が停止した
|
||||
|
|
@ -45,12 +45,24 @@ pub enum PodEvent {
|
|||
parent_pod: String, // 又貸し元(= 子自身)
|
||||
sub_pod: String, // 孫 Pod の名前
|
||||
sub_socket: PathBuf, // 孫 Pod の socket path
|
||||
scope: Vec<ScopeRule>, // 又貸しされた scope
|
||||
scope: Vec<ScopeRule>, // 又貸しされた scope(`protocol` crate に移動)
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
`Method::Notify` は触らない。`message: String` のまま。
|
||||
### `Method::Notify` の `source` 削除
|
||||
|
||||
現状 `Method::Notify { source: String, message: String }` の `source` フィールドを削除して `Method::Notify { message: String }` にする。`PodEvent` が typed な子 → 親報告を担うようになったことで、`Notify` は本来の「人間・tool が LLM 文脈に自由テキストを注入する」役割に純化する(発信者の識別は不要になる)。
|
||||
|
||||
影響範囲:
|
||||
- `protocol::Method::Notify` の定義変更、serde round-trip テストの更新
|
||||
- Controller main loop の `Method::Notify { source, message } => pod.push_notification(source, message)` を `pod.push_notification(message)` 形へ変更
|
||||
- `Pod::push_notification(source, message)` のシグネチャから `source` を落とす(呼び出し元を追って整理)
|
||||
- 既存テスト(`controller_test.rs` の Notify ケース、`pod_comm_tools_test.rs` など)の入力を新シグネチャに揃える
|
||||
|
||||
### `ScopeRule` / `Permission` の移動
|
||||
|
||||
`ScopeRule` と `Permission` は wire 型として `PodEvent::ScopeSubDelegated` で protocol を経由する必要があるため、現状の `manifest` crate から `protocol` crate へ移動する。`manifest` は `protocol::ScopeRule` を re-export するか、単に `protocol::ScopeRule` を直接参照する形に切り替える。移動により `protocol` → `manifest` の逆依存が発生しないようにする。
|
||||
|
||||
### 子(発信側)
|
||||
|
||||
|
|
@ -123,17 +135,14 @@ variant 別の (1) の中身:
|
|||
- 親が再起動した場合や送信漏れた場合は、親の `ListPods` ツール(既存)による health check + `scope_lock::reclaim_stale` の stale 回収で不整合を解消する
|
||||
- これは「コールバックは最適化、ポーリングが真のフォールバック」という方針の継続
|
||||
|
||||
## 設計で決めること
|
||||
|
||||
- **送信の接続タイムアウト**: `SpawnPod` / pod-comm-tools と揃える(5 秒想定)
|
||||
|
||||
### 決定事項
|
||||
## 決定事項
|
||||
|
||||
- **順序保証は求めず、ハンドラを冪等・遅延到着に強くする**: fire-and-forget の unix socket 接続は順序を保証しない。`TurnEnded` 直後に `ShutDown` が届いても、逆順で到着しても親側で成立するように `apply_event_side_effects` を設計する。具体的には:
|
||||
- `ShutDown` 受信時、すでに registry から削除済みでもエラーにしない(`release_pod` の `UnknownPod` を swallow する既存挙動を踏襲)
|
||||
- `TurnEnded` が `ShutDown` より後に届いても、該当 Pod が既に registry にいなければ render だけして終える(LLM 向け通知は出る、system 処理は no-op)
|
||||
- `ScopeSubDelegated` で孫が既に registry にいたら上書きせず no-op(`DuplicatePodName` を swallow)
|
||||
- **`ScopeSubDelegated` の親連鎖は直接の親のみ + 再発射**: 上記「又貸しの親連鎖」セクション参照。曾孫以上は再帰的に再発射で root まで届く
|
||||
- **送信の接続タイムアウト**: `SpawnPod` / pod-comm-tools と揃えて 5 秒固定
|
||||
|
||||
## 完了条件
|
||||
|
||||
|
|
@ -151,4 +160,3 @@ variant 別の (1) の中身:
|
|||
- リモート親への送信(SSH 越し)。ローカル Unix socket のみ
|
||||
- 配信保証(at-least-once / exactly-once)
|
||||
- 親再起動時の「見逃したイベント」の再送。ポーリングで補う前提
|
||||
- `Method::Notify` の `source` フィールド削除(別チケット)
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user