yoi/tickets/tui-pod-shutdown.review.md

100 lines
5.9 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# レビュー: TUI: Pod を明示的に終了させる操作
対象差分: `crates/protocol/src/lib.rs`, `crates/pod/src/{controller,lib,main}.rs`, `crates/pod/tests/controller_test.rs`, `crates/pod/examples/pod_protocol.rs`, `crates/tui/src/{app,main}.rs`staged、未コミット
## 要件達成状況
| 要件 | 状態 |
|---|---|
| TUI 内のキーバインドで Pod の終了を開始できる | ✅ `Ctrl-D``Method::Shutdown` を送信 |
| 実行中のターンがあれば確認を挟む | ✅ `handle_shutdown`: running 中は「Press Ctrl-D again」警告を表示、3秒以内の再押下で確定 |
| 実行中のターンがなければ即座に終了 | ✅ `!app.running` なら即 `Method::Shutdown` |
| 既存のキャンセル機構で中断する | ✅ `run_with_cancel_support` 内で `Method::Shutdown` 受信時に `cancel_tx.try_send(())` + `shutdown_requested = true` |
| キャンセル完了後 session-store にフラッシュ | ✅ `run_with_cancel_support``(status, shutdown)` を返した後、controller loop 内で `write_status` / `write_history` が走ってから `break` |
| shutdown 完了後 TUI が終了する | ✅ `Event::Shutdown``app.quit = true`、main loop が break |
| Pod プロセス自体も正常終了する | ✅ controller が `shutdown_tx.send(())``main.rs``shutdown_rx` が発火 → `drop(handle)``ExitCode::SUCCESS` |
| shutdown 中の進行状況が画面で分かる | 🟡 「Press Ctrl-D again to cancel and shut down.」メッセージは出るが、shutdown 後の「shutting down...」表示は無い(`Event::Shutdown` 受信時に `app.quit = true` だけで即終了)|
## アーキテクチャ
### Protocol 拡張
- `Method::Shutdown``Event::Shutdown` が protocol に追加。最小限の拡張で意味が明確
- `Method::Shutdown` は Run/Resume/Cancel と同格の method バリアント。socket 層での特殊扱い不要
### Controller 内の shutdown フロー
```
Idle 状態で Shutdown 受信:
→ Event::Shutdown を broadcast → controller loop break → shutdown_tx.send(())
Running 状態で Shutdown 受信 (run_with_cancel_support 内):
→ cancel_tx.try_send(()) で実行中ターンをキャンセル
→ shutdown_requested = true をフラグ
→ pod_future が Cancelled/Error で完了
→ (PodStatus::Idle, shutdown=true) を返す
→ controller loop が compaction → write_status → write_history の通常後処理
→ shutdown フラグを見て Event::Shutdown → break → shutdown_tx.send(())
```
**重要**: shutdown が来ても**通常の後処理compaction, persistを必ず経由**してから抜ける。session の整合性が壊れない。この設計は正しい。
### main.rs の二択 select
```rust
tokio::select! {
_ = tokio::signal::ctrl_c() => { ... }
_ = shutdown_rx => { ... }
}
```
Ctrl-Csignalと client 要求の Shutdown を同格に扱う。どちらが先に来ても `drop(handle)` → 正常終了。
### TUI の確認 UI
`shutdown_confirm: Option<Instant>` フィールドで「最後に Ctrl-D を押した時刻」を保持。3秒以内の再押下で確定。Timeout 後は再度リセット。シンプルで十分。
## 指摘事項
### 1. 🟡 shutdown 中の進行表示が無い
チケット要件:
> shutdown 中は進行状況が画面上で分かる「shutting down...」等の表示)。
実装:
- `Event::Shutdown` を受けた瞬間に `app.quit = true` → main loop が即 break → terminal restore
- ユーザーが「shutting down」を読む暇がない
実際には shutdown はほぼ瞬時(キャンセル完了 + persist + Event::Shutdown が数十 msなので視認できるタイミングがそもそも無い。要件の「shutting down...」は「長い shutdown を想定した表示」だが、実装上 shutdown は速いので**実用上問題にならない**。
**判断**: 実害なし。将来 shutdown が重くなった場合(大量ターンの persist 等)に表示を足すのは容易(`Event::Shutdown` 受信時に output_queue に push してから quit を遅延させればよい)。**不問**。
### 2. 🟢 `shutdown_confirm` が `Instant` ベース
wall clock ではなく `std::time::Instant` を使っている。NTP ジャンプに影響されない。正しい選択。
### 3. 🟢 Resume パスでも shutdown 対応
`Method::Resume` のハンドラも `run_with_cancel_support` を通っており、Resume 中に `Shutdown` が来ても同じフローで処理される。漏れなし。
### 4. 🟢 examples / tests の追随
- `pod_protocol.rs`: `PodController::spawn` の戻り値を `(handle, _shutdown_rx)` に分解
- `controller_test.rs`: 同上
- 既存テストが壊れていない
### 5. 🟢 `Ctrl-D` のキーバインド選択
`Ctrl-D` は shell の EOF 慣例に合っており、「この Pod との対話を終える」という意味が自然。`q` や `:quit` は通常入力との衝突リスクがあるので `Ctrl-D` は妥当。
## テスト
- `controller_test.rs`: 戻り値の型変更に追随。shutdown 固有のテストShutdown method を送って controller が break するか、persist 後に shutdown_rx が発火するか等)は**未追加**
- `handle_shutdown` のユニットテストrunning 時の confirm フロー、timeout 後のリセット等)は**未追加**
テストが薄い点はあるが、shutdown フローは controller loop 内の分岐であり、integration test で網羅すると MockClient の応答タイミング制御が必要になって重くなる。最小実装としては許容。
## 結論
**受け入れ可**。要件はほぼ達成、アーキテクチャは「通常の後処理を必ず経由してから終了」という最重要の不変条件を守っている。指摘1shutdown 中の進行表示)は実害なしで不問。テストの薄さは将来必要になったら足す程度。