# inspect: auto-exit the TUI when the run finishes (no Enter to start the judge)

`1ecb920`→[main](/content/gh/entireio/cli/commits/main/index.html)·

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 {
