yoi/.yoi/tickets/00001KV04NJ8D/item.md

126 lines
9.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.

---
title: 'TUI rewind picker の Enter 後に live 表示が巻き戻らない問題を調査・修正する'
state: 'done'
created_at: '2026-06-13T09:23:07Z'
updated_at: '2026-06-13T11:24:32Z'
assignee: null
readiness: 'implementation_ready'
risk_flags: ['tui', 'pod-protocol', 'persistence', 'history-rewind']
queued_by: 'workspace-panel'
queued_at: '2026-06-13T10:53:20Z'
---
## Background
通常 TUI の manual rewind は、`Ctrl+R` で rewind targets 画面を開き、対象 user message を選択して `Enter` で Pod-authoritative に巻き戻し、選択 input を composer に復元する仕様である。
実運用中に、rewind targets 画面で `Enter` を押しても画面上は無反応に見え、巻き戻らないことがあると観測された。追加情報として、他のキーは効き、`Enter` を押した後に `Esc` で戻ると、押した回数分だけ `Rewound session:` が表示される。また、一度 `Ctrl+X` で TUI を落としてから Restore すると、巻き戻し後状態として効いている。Pod の状態は通常の停止状態のはずだった。
既存関連 Ticket:
- `00001KSKBPBX0` — Pod/TUI: 手動 rewind 導線closed
- `00001KSKBPGS8` — Pod: 任意ターンからの Fork複数ターン巻き戻しplanning、今回の不具合とは別件
## Observed behavior
- `Ctrl+R` で rewind targets 画面を開く。
- 上下移動など、他のキーは効く。
- 対象上で `Enter` を押しても画面上は無反応に見える。
- その後 `Esc` で rewind targets 画面を閉じると、押した `Enter` の回数分だけ `Rewound session:` が表示される。
- `Ctrl+X` で TUI を落としてから Restore すると、巻き戻し後の状態として効いている。
- Pod の状態は通常の停止状態、少なくとも Running 中ではないはずだった。
## Investigation notes
read-only 調査で以下を確認した。
- `Ctrl+R` / picker 中の `Enter``crates/tui/src/single_pod.rs` の key handling から `app.submit_rewind_picker()` に到達し、`Method::RewindTo` を返す。
- `submit_rewind_picker()``Method::RewindTo { target, expected_head_entries }` を返すだけで、picker を閉じず、applying/pending 状態も持たず、追加 `Enter` を抑止しない。
- Pod 側 `Method::RewindTo``crates/pod/src/controller.rs` で Idle 時に `apply_rewind()` され、成功すると `Event::RewindApplied``Event::Status { Idle }` を送る。
- `Rewound session:` という文字列は Pod 側ではなく `crates/tui/src/app.rs``Event::RewindApplied` handler でのみ生成される。したがって、`Esc` 後にこの表示が出る時点で、Pod 側 rewind は成功し、TUI も最終的には `Event::RewindApplied` を処理している。
- `Event::RewindApplied` handler は、`self.greeting.clone()` が `Some` の場合だけ `restore_snapshot(&entries, greeting)` する。`self.greeting == None` の場合、rewind 後 `entries` payload は transcript restore に使われないが、success alert は push される。
- `restore_snapshot()``self.blocks.clear()` する。複数回 `RewindApplied` を処理しても、restore が動いていれば古い `Rewound session:` alert は消えるはずである。`Enter` 回数分 alert が残る観測は、`restore_snapshot()` が呼ばれていない可能性、特に `self.greeting == None` の可能性と整合する。
現時点の有力仮説は、Pod 側 rewind は成功しているが、live TUI が `Event::RewindApplied``entries` を使って derived transcript view を再構築できておらず、reconnect/Restore 時の fresh `Event::Snapshot` で初めて表示が正しくなる、というもの。
別途、`Event::RewindApplied` の処理または描画反映が rewind picker 表示中に進まず、`Esc` などの terminal event 後の drain でまとめて処理されている可能性も調査する必要がある。
## Requirements
- `Ctrl+R` で開いた rewind targets view において、eligible な target 上で `Enter` を押した場合、Pod 側 rewind 成功後に live TUI の transcript/composer/view state が直ちに一貫した巻き戻し後状態へ更新される。
- Pod 側 rewind は成功しているのに live TUI が古い transcript 表示のまま残り、TUI restart/Restore で初めて正しい状態になる挙動を解消する。
- `Event::RewindApplied``entries` restore が `App::greeting` 欠落等で silently skip されないようにする。
- `RewindApplied` が picker 表示中に処理される場合でも、picker が適切に閉じるか、少なくともユーザーが次に行うべき操作が分かる状態に遷移する。
- apply が拒否または restore 不可能な場合は、無反応ではなく可視 diagnostic / notice を出す。
- `Enter` 連打により同じ target への `RewindTo` が複数積まれ、後から `Rewound session:` がまとめて出る挙動を防ぐ。
- 既存の manual rewind 仕様を維持する:
- picker 開始は Idle / Paused の既存仕様を尊重する。
- apply は Pod-authoritative に検証・適用する。
- Running 中は拒否する。
- picker 表示時から head が変わった場合は apply 時に再検証して拒否する。
- 成功時は composer が空なら選択 message を composer に復元する。
- 選択だけでは auto-run しない。
## Acceptance criteria
- 再現条件または失敗条件が実装報告に説明されている。
- `Ctrl+R` → target 選択 → `Enter` で Pod 側 rewind が成功した場合、TUI restart/Restore なしに live TUI の transcript が巻き戻し後状態へ更新される。
- `Event::RewindApplied` に含まれる `entries` が、`App::greeting` 欠落等の理由で silently ignored されない。
- `self.greeting == None` または同等の metadata 欠落が起き得る場合、その経路を修正するか、self-contained event / fresh snapshot request / explicit diagnostic など設計上妥当な挙動にする。
- rewind picker 表示中に `Enter` 成功 event が来た場合、`Esc` 後に初めて `Rewound session:` が出るのではなく、その場で view state / composer / status が一貫して更新される。
- `Enter` 連打が複数 rewind request や成功 notice の後出し表示を生まない。必要なら pending/applying 状態で追加 submit を抑止する。
- apply 不可または restore 不可の場合は、無反応ではなく actionbar / diagnostic / error event 等で理由が見える。
- 既存の Esc cancel、Running 中 rejection、stale-head rejection、composer restore の挙動を壊さない。
- 関連する TUI key handling / rewind view / Pod protocol path の focused test が追加または更新されている。
## Binding decisions / invariants
- TUI がローカルに履歴を削るのではなく、rewind 適用は引き続き Pod が authoritative に検証・適用する。
- Pod 側 rewind が成功したのに live TUI が stale view のまま残る UX は許容しない。
- `Event::RewindApplied` の restore failure を silently skip しない。
- この Ticket では fork / alternate history は実装しない。
- Tool side effect の undo は実装しない。
- rewind semantics は `00001KSKBPBX0` の既存仕様を前提にし、必要な場合のみ不具合修正として局所的に調整する。
## Implementation latitude
実装者は原因調査の結果に応じて、以下のいずれかまたは複数を修正してよい。
- `Event::RewindApplied` を self-contained にするため、必要な metadata例: greeting/status 等)を event に含める。
- `App::greeting``None` になり得る経路を修正する。
- `RewindApplied` restore 不可時に fresh snapshot を要求する、または明示的 diagnostic を出す。
- rewind picker に applying/pending state を追加し、submit 後の二重 `Enter` を抑止する。
- TUI event loop / socket delivery / wake-up に、picker 表示中の Pod event 処理遅延がある場合は修正する。
- focused tests を追加するために、既存 helper の分離や再利用を行う。
ただし、manual rewind の authority boundary と destructive rewind semantics は変更しない。
## Readiness
- readiness: implementation_ready
- risk_flags: [tui, pod-protocol, persistence, history-rewind]
## Escalation conditions
- `Event::RewindApplied` を self-contained にするために protocol schema / compatibility への明示判断が必要になった場合。
- `App::greeting` 欠落が broader snapshot / restore / connection lifecycle の設計問題だった場合。
- dedicated view 表示中の Pod events / notices の扱いが TUI 全体の UX/architecture 判断を必要とする場合。
- current active segment / compacted segment / stale head の扱いについて、既存 Ticket の仕様と矛盾する判断が必要になった場合。
- Pod-authoritative rewind ではなく TUI 側ローカル mutation に寄せる設計変更が必要に見える場合。
## Validation
- focused test: `Event::RewindApplied` により live TUI transcript が rewind 後 `entries` で reseed され、picker が閉じる。
- focused test: `App::greeting` 欠落または metadata 欠落時に silently skip せず、設計した failure/recovery path が動く。
- focused test: rewind picker submit 後の二重 `Enter` が複数 `Method::RewindTo` を生成しない、または idempotent/rejected として可視化される。
- focused test: success 後に composer が空なら selected input が復元され、非空なら既存 composer を上書きしない。
- 必要に応じて event loop / pod event wake-up の focused test を追加する。
- `cargo fmt --check`
- `cargo check -p protocol -p pod -p tui`
- 関連 focused tests
## Related work
- `00001KSKBPBX0` — Pod/TUI: 手動 rewind 導線
- `00001KSKBPGS8` — Pod: 任意ターンからの Fork複数ターン巻き戻し