chore: record current yoi fixes

This commit is contained in:
2026-06-13 19:51:15 +09:00
parent ad4d0866ae
commit 8e9855cf56
17 changed files with 620 additions and 11 deletions
+123
View File
@@ -0,0 +1,123 @@
---
title: 'TUI rewind picker の Enter 後に live 表示が巻き戻らない問題を調査・修正する'
state: 'ready'
created_at: '2026-06-13T09:23:07Z'
updated_at: '2026-06-13T10:02:13Z'
assignee: null
readiness: 'implementation_ready'
risk_flags: ['tui', 'pod-protocol', 'persistence', 'history-rewind']
---
## 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(複数ターン巻き戻し)
+102
View File
@@ -0,0 +1,102 @@
<!-- event: create author: LocalTicketBackend at: 2026-06-13T09:23:07Z -->
## 作成
LocalTicketBackend によって作成されました。
---
<!-- event: plan author: hare at: 2026-06-13T10:00:19Z -->
## Plan
## 調査・修正計画
ユーザー合意により、実装時はまず一時 diagnostic を入れて live 挙動を確認し、原因を確定してから本修正する方針とする。問題が解消したら一時ログは外して完了する。
### Phase 1: 一時 diagnostic の追加
`Event::RewindApplied` 周辺と rewind picker submit 周辺に、秘密情報を含まない短い一時ログまたは TUI diagnostic を入れる。
確認する値:
- `Event::RewindApplied``Enter` 直後に処理されるか、`Esc` 後まで遅れるか。
- `Event::RewindApplied` 処理時の `App::greeting.is_some()`
- `restore_snapshot(&entries, greeting)` を呼べているか。
- `entries.len()`
- `rewind_picker.is_some()` / applying 状態。
- `input.is_empty()` と composer restore 分岐。
- `pod_status`
### Phase 2: live 再現確認
一時 diagnostic 入りの binary で、既知の手順を再現する。
1. TUI 起動。
2. `Ctrl+R` で rewind targets を開く。
3. target を選択して `Enter`
4. 画面が無反応なら少し待つ。
5. `Esc` で戻る。
6. diagnostic から、以下のどれに該当するか判断する。
判断観点:
- `RewindApplied``Enter` 直後に処理され、`greeting=false` なら、live TUI が rewind 後 `entries` を restore できず stale 表示になっている可能性が高い。
- `RewindApplied``Esc` 後まで処理されないなら、event loop / socket delivery / wake-up 側を主因として追う。
- `RewindApplied``Enter` 直後に処理され、`greeting=true` かつ restore 済みなら、draw / overlay / picker close / scroll state の問題を疑う。
### Phase 3: 本修正
原因に応じて修正する。
- `App::greeting` 欠落で restore が skip されている場合:
- `RewindApplied` restore failure を silent success にしない。
- `greeting` を失う経路を修正するか、`RewindApplied` を self-contained にする、または fresh snapshot request / explicit diagnostic の妥当な方針を実装する。
- picker 中に Pod event 処理が遅れる場合:
- single-pod TUI event loop / `PodClient` wake-up / drain ordering / connection gating を修正し、Pod event で即時 redraw されるようにする。
- restore は動いているが表示が stale の場合:
- `restore_snapshot()` 後の picker close、draw、scroll、overlay state を修正する。
### Phase 4: 二重 submit 防止
主因修正とは別に、rewind picker submit 後の `Enter` 連打を防ぐ。
- `RewindPickerState` に applying/pending 状態を持たせる、または同等の idempotency guard を追加する。
- submit 後は追加 `Enter` で複数 `Method::RewindTo` を生成しない。
- 必要なら `Applying rewind...` のような状態表示を出す。
- 成功 / failure / rejection で pending を解除または picker を閉じる。
### Phase 5: focused test と一時ログ削除
- 原因に対応する focused regression test を追加する。
- `Event::RewindApplied` で live TUI transcript が巻き戻し後 `entries` に reseed され、picker が閉じることを確認する。
- metadata 欠落時に silently skip しないことを確認する。
- pending 中の追加 `Enter` が複数 `Method::RewindTo` を生成しないことを確認する。
- composer restore 分岐を確認する。
- live 確認で問題が解消したら、一時 diagnostic / debug log を削除する。
### Validation
- focused tests
- `cargo fmt --check`
- `cargo check -p protocol -p pod -p tui`
この計画は、原因未確定のまま protocol/schema 変更へ飛ばず、まず live diagnostic で `greeting` 欠落・event 処理遅延・表示更新不整合のどれかを切り分けることを重視する。
---
<!-- event: intake_summary author: ticket-intake at: 2026-06-13T10:02:13Z -->
## Intake summary
ユーザー依頼を `00001KV04NJ8D` として具体化し、read-only 調査結果と追加観測を反映した。Pod 側 rewind は成功しているが live TUI が `Event::RewindApplied` の反映または snapshot restore を即時実行できていない可能性を主仮説として記録済み。合意済み計画として、一時 diagnostic を入れて live 再現で `RewindApplied` timing / `App::greeting` / `restore_snapshot()` / picker pending を切り分け、原因修正後に一時ログを外し、focused tests と `cargo fmt --check` / `cargo check -p protocol -p pod -p tui` で検証する。
---
<!-- event: state_changed author: ticket-intake at: 2026-06-13T10:02:13Z from: planning to: ready reason: planning_ready field: state -->
## State changed
ユーザーが Ticket の ready 化を明示したため、Orchestrator が routing できる状態にする。
---