From cc9fa2d6329b0d3c8a77fd8d3af22b23dd48d8bf Mon Sep 17 00:00:00 2001 From: Hare Date: Sun, 19 Apr 2026 06:41:20 +0900 Subject: [PATCH] =?UTF-8?q?Pod=E6=93=8D=E4=BD=9C=E3=83=84=E3=83=BC?= =?UTF-8?q?=E3=83=AB=E5=AE=9F=E8=A3=85=E5=AE=8C=E4=BA=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- TODO.md | 1 - tickets/pod-comm-tools.md | 99 -------------------------------- tickets/pod-comm-tools.review.md | 85 --------------------------- 3 files changed, 185 deletions(-) delete mode 100644 tickets/pod-comm-tools.md delete mode 100644 tickets/pod-comm-tools.review.md diff --git a/TODO.md b/TODO.md index 4de94b20..b1a949cc 100644 --- a/TODO.md +++ b/TODO.md @@ -5,7 +5,6 @@ - [ ] Protocol の設計 → [tickets/protocol-design.md](tickets/protocol-design.md) - [ ] パーミッション: パターンベースのツール実行制御 → [tickets/permission-extension-point.md](tickets/permission-extension-point.md) - [ ] Pod オーケストレーション - - [ ] Pod 間通信ツール: SendToPod / ReadPodOutput / StopPod / ListPods → [tickets/pod-comm-tools.md](tickets/pod-comm-tools.md) - [ ] Pod 間コールバック通知 → [tickets/pod-callback.md](tickets/pod-callback.md) - [ ] 動的 Scope 変更 → [tickets/dynamic-scope.md](tickets/dynamic-scope.md) - [ ] ネイティブ GUI クライアント MVP → [tickets/native-gui-mvp.md](tickets/native-gui-mvp.md) diff --git a/tickets/pod-comm-tools.md b/tickets/pod-comm-tools.md deleted file mode 100644 index e9d6860b..00000000 --- a/tickets/pod-comm-tools.md +++ /dev/null @@ -1,99 +0,0 @@ -# Pod 間通信ツール: SendToPod / ReadPodOutput / StopPod / ListPods - -レビュー中: [pod-comm-tools.review.md](pod-comm-tools.review.md) - -## 背景 - -`SpawnPod` で Pod を生成した後、spawner の LLM が spawned Pod に指示を送り、結果を読み、完了したら停止させる手段が必要。 - -## 依存 - -- `tickets/spawn-pod-tool.md`: SpawnPod による Pod 生成と spawn 記録 - -## ツール群 - -すべて**都度接続の request-response** で動作する。spawned Pod の socket に接続し、操作を行い、切断する。 - -### `SendToPod` - -既知の Pod にメッセージを送る。 - -入力: -- `name`: 対象の Pod -- `message`: 送信するテキスト - -出力: -- 送信確認 - -内部動作: -- spawn 記録から `name` の socket path を引く -- socket に接続 → `Method::Run { input: message }` を送信 → ack 受信 → 切断 - -### `ReadPodOutput` - -既知の Pod の出力を読む。前回読んだ位置を自動追跡(カーソルベース)。 - -入力: -- `name`: 対象の Pod - -出力: -- 前回読んだ位置以降の assistant テキスト出力 -- 現在の到達性(`alive` / `stopped`) - -内部動作: -- spawn 記録から socket path を引く -- socket に接続 → `Method::GetHistory` で履歴取得 → 前回カーソル以降の assistant text を抽出 → カーソル更新 → 切断 -- 接続できなければ `stopped` として返す - -### `StopPod` - -既知の Pod を終了させ、譲渡した scope を回収する。 - -入力: -- `name`: 対象の Pod - -出力: -- 終了要求を送った旨 -- 回収された scope の要約 - -内部動作: -- socket に接続 → `Method::Shutdown` 送信(応答は待たない)→ 切断 -- scope lock file を flock → 対象の allocation 削除 → spawner の deny を解除 → unlock -- spawn 記録から対象を削除 - -### `ListPods` - -自分が知っている Pod の一覧と状態を返す。 - -入力: なし - -出力: -- 各 Pod の `name`、`name`、`status`、譲渡中の scope 要約、最終応答の要約 - -内部動作: -- spawn 記録を元にリストを構築 -- 各 Pod に都度接続して health check(`Method::GetHistory` でステータス取得、接続できなければ `stopped`) -- stopped な Pod は scope lock file の stale 回収をトリガー - -## 設計で決めること - -- **ReadPodOutput のカーソル管理**: spawn 記録内に `last_read_hash` を持つか、別の場所で管理するか -- **StopPod と scope 回収の順序**: 先に Shutdown してから lock file を更新するか、lock file を先に更新するか(Shutdown が失敗した場合の整合性) -- **ListPods の health check コスト**: 大量の Pod がいるとき全部に接続するのは重い。キャッシュ戦略 - -## 完了条件 - -- `SendToPod` で spawned Pod にメッセージを送り、Pod がそれを処理する -- `ReadPodOutput` で spawned Pod の最新出力を読め、カーソルベースの差分取得が動作する -- `StopPod` で spawned Pod を graceful に停止でき、scope が spawner に返却される -- `ListPods` で既知の Pod の状態を一覧でき、health check が機能する -- 接続できない Pod は `stopped` として扱われ、scope の stale 回収がトリガーされる -- 単体テストで各ツールの正常系・異常系が検証される - -## 範囲外 - -- コールバック通知は `tickets/pod-callback.md` -- Pod ネットワークの GUI / TUI 可視化 -- spawner プロセス再起動後の `spawned_pods.json` からの復旧(現状は write-through のみ) -- `ReadPodOutput` カーソルの永続化(インメモリのみ、再起動で 0 に戻る) -- Pod の詳細ステータス(`running` / `idle`)を `Event::History` に含める拡張 diff --git a/tickets/pod-comm-tools.review.md b/tickets/pod-comm-tools.review.md deleted file mode 100644 index 9890c28c..00000000 --- a/tickets/pod-comm-tools.review.md +++ /dev/null @@ -1,85 +0,0 @@ -# Review: pod-comm-tools - -実装コミット前レビュー。`cargo build -p pod` clean、`cargo test -p pod --test pod_comm_tools_test` 8/8 pass。 - -## 総評 - -チケット要件の中核(4 ツールの wire 動作、`SpawnedPodRegistry` の共有、scope 回収、stale reclaim トリガー)は達成。テスト構成もモックソケットで実 `pod` バイナリ非依存で速い。ただし以下のうち修正必須項目を解消してから完了にしたい。 - -## 指摘と判断 - -### 修正必須 - -#### 1. `SendToPod` が RUNNING 状態を検知しない(設計合意違反) - -**状況**: `connect_and_send` は `Method::Run` を書いて即切断し、応答 event を読まない。Controller は RUNNING 中の `Run` に対し `Event::Error { code: AlreadyRunning }` を返すが、tool 側はそれを見ないので LLM に「送信成功」と返る。 - -**設計合意**: pod-comm-tools 議論の結論は「A. SendToPod は `Method::Run` を使い、Pod が IDLE でなければエラーを返す」。 - -**判断**: 修正必要。書き込み後に short timeout で 1 event 読み、`Error { AlreadyRunning }` を検知したら `ToolError::ExecutionFailed` に変換する。正常時は `TurnStart` など先頭 event を見て「受け付けられた」ことを確認して戻る。 - -### チケット文言と実装の整合 - -#### 2. Pod status (`running / idle / stopped`) の取得手段がない - -**状況**: チケットは `ReadPodOutput`/`ListPods` 出力に `running / idle / stopped` を含めることを求めているが、`Event::History` にはステータスが乗っておらず、実装は「接続可=alive / 接続不可=stopped」の 2 値のみ。 - -**判断**: 今回のスコープからは外す。`Greeting` または別 event に `PodStatus` を載せる改修は `Method::GetHistory` のレスポンス設計変更を伴うので、別チケット化する。本チケット文言を `alive / stopped` に揃えて実装と一致させる。 - -#### 3. `StopPod` は終了確認を待たない - -**状況**: チケット本文は「`Method::Shutdown` 送信 → 終了確認受信 → 切断」だが、実装は fire-and-forget。scope の明示 release と `scope_lock` 上の stale reclaim で整合性は担保されているので実害なし。 - -**判断**: チケット文言を「`Method::Shutdown` 送信(応答は待たない)」に修正。 - -### 範囲外として明文化 - -#### 4. `spawned_pods.json` からの再起動復旧は未実装 - -**状況**: `spawned_pod_registry.rs` のモジュールコメントに「today only write-through is implemented」と明記されている。spawner プロセス再起動後、`ListPods` は空リストを返す。 - -**判断**: 今回のスコープ外。チケットの範囲外に「spawner 再起動時の `spawned_pods.json` からの復旧」を明記し、必要になったタイミングで別チケットを切る。 - -#### 5. `ReadPodOutput` cursor の非永続性 - -**状況**: `SpawnedPodRegistry::cursors` は `HashMap` でインメモリのみ。spawner 再起動後、カーソルは 0 にリセットされ、既読だった assistant text が再送される。モジュールコメントに「cursors intentionally do not persist」と明記されている。 - -**判断**: 現状のままで OK。設計判断としてレビューで合意し、チケットの「設計で決めること」の回答として本 review に残す。チケット側には残さない(gitで追える)。 - -### 軽微 - -#### 6. `extract_assistant_text` のメッセージ境界 - -**状況**: 複数の assistant message を `\n` で連結するのみ。メッセージ境界が曖昧になる。 - -**判断**: `\n\n` に変更して境界を明確にする。修正コスト小。 - -#### 7. blocking `flock` を async から呼んでいる - -**状況**: `release_scope` / stale reclaim 経路で `LockFileGuard::open`(内部で `flock(2)`)を async fn から直接呼ぶ。通常は瞬時だが厳密には tokio runtime を一瞬止める。 - -**判断**: 既存コード全体が同じ慣習なので今回は踏襲。気になったら別タイミングで `spawn_blocking` 化を検討。本チケットでは対応しない。 - -#### 8. `SpawnedPodRegistry::new` が `Arc` を返す - -**状況**: コンストラクタが smart pointer を返す API は珍しい。 - -**判断**: consumer が全員 `Arc` で持つ設計なので許容。リファクタは不要。 - -## 完了条件との対応 - -| 完了条件 | 状態 | -|---|---| -| SendToPod で spawned Pod にメッセージ送信・Pod が処理 | ✅(指摘 #1 の修正後) | -| ReadPodOutput でカーソルベース差分取得 | ✅ | -| StopPod で graceful 停止 + scope 返却 | ✅ | -| ListPods で既知 Pod の状態一覧 + health check | ⚠️ `alive/stopped` のみ(指摘 #2) | -| 接続不可 Pod は `stopped` 扱い + stale 回収トリガー | ✅ | -| 各ツール正常系・異常系の単体テスト | ✅(8/8 pass) | - -## 完了に向けた作業 - -1. 指摘 #1: `SendToPod` に応答確認を実装 -2. 指摘 #2, #3: チケット本文の文言修正(`running/idle/stopped` → `alive/stopped`、Shutdown 応答待ちの記述削除) -3. 指摘 #4: チケットの「範囲外」に `spawned_pods.json` 復旧を追記 -4. 指摘 #6: `extract_assistant_text` の区切りを `\n\n` に