3.7 KiB
3.7 KiB
Investigation summary
Read-only investigation found that the most likely cause is live-event ordering between Event::UserMessage and the first Event::SegmentRotated in a fresh session.
Observed flow:
- TUI submit path calls
App::submit_input()/method_for_run()and sendsMethod::Run { input }. crates/pod/src/controller.rshandlesMethod::Runand currently broadcastsEvent::UserMessage { segments }before callingpod.run(input).- TUI receives
Event::UserMessageand appends aTurnHeader/UserMessageblock. Pod::run()callsprepare_for_run()/ensure_segment_head().- In a fresh session,
entries_written == 0, soLogEntry::SegmentStartis committed only at this point. crates/pod/src/segment_log_sink.rsconvertsSegmentStartintoEvent::SegmentRotated.- TUI handles
SegmentRotatedby clearing blocks throughreset_for_rotation()and replayingSegmentStart.history. - The current
UserInputhas not been committed yet, soSegmentStart.historydoes not contain the first user message. - Later
LogEntry::UserInputis committed, but the live path does not emitEvent::UserMessagefrom that commit, so the cleared user block is not restored in the live TUI view.
Likely affected files / functions:
crates/tui/src/app.rsApp::submit_input()method_for_run()App::handle_pod_event(Event::UserMessage)App::handle_pod_event(Event::SegmentRotated)reset_for_rotation()
crates/pod/src/controller.rsMethod::Runbranch
crates/pod/src/pod.rsPod::run()prepare_for_run()ensure_segment_head()
crates/pod/src/segment_log_sink.rs- session-log-derived live event conversion
Implementation intent
Move the live Event::UserMessage authority away from the controller's pre-pod.run() optimistic broadcast and toward the persisted LogEntry::UserInput commit path.
Preferred shape:
- Remove the controller-side
Event::UserMessagebroadcast that happens beforepod.run(input). - Emit
Event::UserMessagewhenLogEntry::UserInputis committed. - Prefer doing this in the session-log-derived event lane, such as
SegmentLogSink, soSegmentStart,UserInput, and other replayable entries share a single ordering source.
This should make a fresh session produce SegmentRotated first and then UserMessage for the committed input, preserving the first user message in the TUI after rotation.
Requirements / invariants for implementation
- Do not fix this by adding a TUI-only fake/pending user block after rotation.
- Do not reintroduce local optimistic user blocks in
App::method_for_run(). - The displayed message should correspond to
LogEntry::UserInput/ persisted history. - Existing session attach/restore must continue to replay
UserInputfromSnapshot.entries. - 2nd and later sends must continue to display normally.
- Running-state queued input should display when the queued run is actually accepted/committed, not merely when typed.
- Composer input history should remain TUI-local and unaffected.
Suggested validation
- Add or update a Pod/session-log event ordering test for a fresh session's first
Method::Run, asserting the live event stream makes the committed user message visible after the initial segment rotation. - Add or update TUI app/view-model tests if available:
- Current problematic ordering
UserMessagethenSegmentRotatedclears the block. - Corrected ordering
SegmentRotatedthenUserMessageleaves the block visible.
- Current problematic ordering
- Confirm snapshot restore still creates user blocks from
LogEntry::UserInput. - Run focused tests for
pod/tuicrates as appropriate, plus formatting/checks that are reasonably scoped to the touched code.