inspect: auto-exit the TUI when the run finishes (no Enter to start the judge) · Entire
inspect: auto-exit the TUI when the run finishes (no Enter to start the judge)
1ecb920→main·
The TUI dashboard blocked on an explicit dismissal key (q/Esc/Enter) after inspectors finished, and only then did the judge (SynthesisSink) run — so the user had to press Enter just to start the final consolidation. Make the model exit on runFinishedMsg instead of waiting: the judge and the narrative dump (which write to stdout after the alt-screen tears down) now run automatically. Ctrl+C during the run is unchanged. Adds a test asserting runFinishedMsg returns tea.Quit.
Sessions
7c0a8804b13eView transcript
Changes
3
cmd/entire/cli/review
Mtui_model.go+6/-1
Mtui_model_test.go+17
Mtui_sink.go+8/-9
176 unmodified lines
177
178
179
180
180
181
182
183
184
185
186
187
188
176 unmodified lines
m.rows[i].runEnd = now
}
return m, nil
// Exit as soon as the run completes — do NOT wait for a keypress. The
// judge (SynthesisSink) and the narrative dump run after the TUI tears
// down (they write to stdout, which can't happen over the alt-screen), so
// blocking here would force the user to press Enter just to start the
// final consolidation. The dump renders the same per-agent outcome below.
return m, tea.Quit
case tickMsg:
if m.finished {
Mcmd/entire/cli/review/tui_model.go+6/-1
1203 unmodified lines
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1203 unmodified lines
}
}
}
func TestTUIModel_AutoExitsOnRunFinished(t *testing.T) {
t.Parallel()
m := newTestModel([]string{"agent-a"}, func() {})
updated, cmd := m.Update(runFinishedMsg{summary: reviewtypes.RunSummary{}})
if !mustModel(t, updated).finished {
t.Fatal("model should be finished after runFinishedMsg")
}
// The TUI must exit on its own when the run finishes (no keypress) so the
// judge (SynthesisSink) and the narrative dump run automatically.
if cmd == nil {
t.Fatal("runFinishedMsg should return a quit command (auto-exit), got nil")
}
if _, ok := cmd().(tea.QuitMsg); !ok {
t.Fatalf("runFinishedMsg command = %T, want tea.QuitMsg", cmd())
}
}
Mcmd/entire/cli/review/tui_model_test.go+17
145 unmodified lines
146
147
148
149
150
151
152
153
154
149
150
151
152
153
156
157
158
154
155
156
157
158
159
160
145 unmodified lines
}
// RunFinished (Sink interface): mark the run complete and send the final
// summary message. The TUI shows the dashboard one more frame with the
// terminal statuses, then waits for the user to dismiss it. After a run
// completes, dismissal requires an explicit exit key (q/Esc/Enter/Ctrl+C);
// Ctrl+O still drills into agent buffers for post-mortem inspection. Other
// keys are no-ops so users can navigate the completed run without
// accidentally dismissing.
// summary message. On completion the TUI exits on its own — it does NOT wait
// for a keypress — so the judge (SynthesisSink) and the narrative dump run
// automatically instead of forcing the user to press Enter to start the final
// consolidation.
//
// IMPORTANT: RunFinished blocks until the user dismisses so that post-run
// sinks (e.g. DumpSink) render their narrative AFTER the TUI has exited
// and the terminal is back in normal mode.
// RunFinished still blocks until the Bubble Tea program has exited so that
// post-run sinks (DumpSink, SynthesisSink) render AFTER the TUI has torn down
// the alt-screen and the terminal is back in normal mode; that block now ends
// as soon as the run finishes rather than on an explicit dismissal key.
func (s *TUISink) RunFinished(summary reviewtypes.RunSummary) {
s.mu.Lock()
if s.finished {