Remove terminal resize reflow flag gates (#27794)

## Why

`terminal_resize_reflow` is now stable and should behave as always on.
Keeping the disabled runtime paths around made the feature look
configurable even though the rollout is complete, and old config could
still suggest there was a supported off mode.

## What Changed

- Marked `terminal_resize_reflow` as `Stage::Removed` while keeping it
default-enabled for compatibility.
- Ignored `[features].terminal_resize_reflow` config entries so stale
`false` settings no longer affect the effective feature set.
- Removed TUI branches that depended on the flag being disabled, so
draw, replay buffering, stream finalization, and resize scheduling all
assume resize reflow is active.
- Simplified resize smoke coverage to exercise the always-on behavior
only.

## Verification

- `just test -p codex-features`
- `just test -p codex-tui resize_reflow`
- `just test -p codex-tui initial_replay_buffer
thread_switch_replay_buffer`
This commit is contained in:
Eric Traut
2026-06-15 08:23:02 -07:00
committed by GitHub
Unverified
parent 1aa3e432cc
commit 8a6a039f26
11 changed files with 86 additions and 272 deletions
+5 -6
View File
@@ -99,7 +99,7 @@ pub enum Feature {
/// on either `unified_exec` or `shell_zsh_fork` because those features have
/// separate rollout and enterprise controls.
UnifiedExecZshFork,
/// Reflow transcript scrollback when the terminal is resized.
/// Removed compatibility flag. Transcript scrollback reflow on terminal resize is always on.
TerminalResizeReflow,
/// Add terminal-specific visualization guidance to TUI developer instructions.
TerminalVisualizationInstructions,
@@ -461,6 +461,9 @@ impl Features {
"skill_env_var_dependency_prompt" => {
continue;
}
"terminal_resize_reflow" => {
continue;
}
"use_legacy_landlock" => {
self.record_legacy_usage_force(
"features.use_legacy_landlock",
@@ -807,11 +810,7 @@ pub const FEATURES: &[FeatureSpec] = &[
FeatureSpec {
id: Feature::TerminalResizeReflow,
key: "terminal_resize_reflow",
stage: Stage::Experimental {
name: "Terminal resize reflow",
menu_description: "Rebuild Codex-owned transcript scrollback when the terminal width changes.",
announcement: "",
},
stage: Stage::Removed,
default_enabled: true,
},
FeatureSpec {
+23 -7
View File
@@ -32,8 +32,7 @@ fn default_enabled_features_are_stable() {
for spec in crate::FEATURES {
if spec.default_enabled {
assert!(
matches!(spec.stage, Stage::Stable | Stage::Removed)
|| spec.id == Feature::TerminalResizeReflow,
matches!(spec.stage, Stage::Stable | Stage::Removed),
"feature `{}` is enabled by default but is not stable/removed ({:?})",
spec.key,
spec.stage
@@ -151,18 +150,35 @@ fn request_permissions_tool_is_under_development() {
}
#[test]
fn terminal_resize_reflow_is_experimental_and_enabled_by_default() {
fn terminal_resize_reflow_is_removed_and_enabled_by_default() {
assert_eq!(
feature_for_key("terminal_resize_reflow"),
Some(Feature::TerminalResizeReflow)
);
assert!(matches!(
Feature::TerminalResizeReflow.stage(),
Stage::Experimental { .. }
));
assert_eq!(Feature::TerminalResizeReflow.stage(), Stage::Removed);
assert_eq!(Feature::TerminalResizeReflow.default_enabled(), true);
}
#[test]
fn from_sources_ignores_removed_terminal_resize_reflow_feature_key() {
let features_toml = FeaturesToml::from(BTreeMap::from([(
"terminal_resize_reflow".to_string(),
false,
)]));
let features = Features::from_sources(
FeatureConfigSource {
features: Some(&features_toml),
..Default::default()
},
FeatureConfigSource::default(),
FeatureOverrides::default(),
);
assert_eq!(features, Features::with_defaults());
assert_eq!(features.enabled(Feature::TerminalResizeReflow), true);
}
#[test]
fn tool_suggest_is_stable_and_enabled_by_default() {
assert_eq!(Feature::ToolSuggest.stage(), Stage::Stable);
+14 -42
View File
@@ -1248,16 +1248,8 @@ See the Codex keymap documentation for supported actions and examples."
app_server: &mut AppServerSession,
event: TuiEvent,
) -> Result<AppRunControl> {
let terminal_resize_reflow_enabled = self.terminal_resize_reflow_enabled();
if self.should_handle_draw_pre_render()
&& matches!(event, TuiEvent::Draw | TuiEvent::Resize)
{
if matches!(event, TuiEvent::Draw | TuiEvent::Resize) {
self.handle_draw_pre_render(tui)?;
} else if matches!(event, TuiEvent::Draw | TuiEvent::Resize) {
let size = tui.terminal.size()?;
if size != tui.terminal.last_known_screen_size {
self.refresh_status_line();
}
}
if self.overlay.is_some() {
@@ -1289,8 +1281,7 @@ See the Codex keymap documentation for supported actions and examples."
}
// Allow widgets to process any pending timers before rendering.
self.chat_widget.pre_draw_tick();
let rendered_area =
self.render_chat_widget_frame(tui, terminal_resize_reflow_enabled)?;
let rendered_area = self.render_chat_widget_frame(tui)?;
if self.chat_widget.ambient_pet_image_enabled() {
let terminal_size = tui.terminal.size()?;
let ambient_pet_area = Rect::new(
@@ -1329,43 +1320,24 @@ See the Codex keymap documentation for supported actions and examples."
pub(super) fn show_shutdown_feedback(&mut self, tui: &mut tui::Tui) -> Result<()> {
self.disable_ambient_pet_before_shutdown(tui)?;
self.chat_widget.show_shutdown_in_progress();
let terminal_resize_reflow_enabled = self.terminal_resize_reflow_enabled();
if self.should_handle_draw_pre_render() {
self.handle_draw_pre_render(tui)?;
}
self.handle_draw_pre_render(tui)?;
self.chat_widget.pre_draw_tick();
self.render_chat_widget_frame(tui, terminal_resize_reflow_enabled)?;
self.render_chat_widget_frame(tui)?;
Ok(())
}
fn render_chat_widget_frame(
&mut self,
tui: &mut tui::Tui,
terminal_resize_reflow_enabled: bool,
) -> Result<Rect> {
fn render_chat_widget_frame(&mut self, tui: &mut tui::Tui) -> Result<Rect> {
let desired_height = self.chat_widget.desired_height(tui.terminal.size()?.width);
let mut rendered_area = Rect::default();
if terminal_resize_reflow_enabled {
tui.draw_with_resize_reflow(desired_height, |frame| {
let area = frame.area();
rendered_area = area;
self.chat_widget.render(area, frame.buffer);
if let Some((x, y)) = self.chat_widget.cursor_pos(area) {
frame.set_cursor_style(self.chat_widget.cursor_style(area));
frame.set_cursor_position((x, y));
}
})?;
} else {
tui.draw(desired_height, |frame| {
let area = frame.area();
rendered_area = area;
self.chat_widget.render(area, frame.buffer);
if let Some((x, y)) = self.chat_widget.cursor_pos(area) {
frame.set_cursor_style(self.chat_widget.cursor_style(area));
frame.set_cursor_position((x, y));
}
})?;
}
tui.draw_with_resize_reflow(desired_height, |frame| {
let area = frame.area();
rendered_area = area;
self.chat_widget.render(area, frame.buffer);
if let Some((x, y)) = self.chat_widget.cursor_pos(area) {
frame.set_cursor_style(self.chat_widget.cursor_style(area));
frame.set_cursor_position((x, y));
}
})?;
Ok(rendered_area)
}
}
-8
View File
@@ -249,14 +249,6 @@ impl App {
self.insert_completed_token_activity_output_after_stream_shutdown(tui);
}
AppEvent::ConsolidateProposedPlan(source) => {
if !self.terminal_resize_reflow_enabled() {
if !self.transcript_reflow.history_cell_refresh_requested() {
self.transcript_reflow.clear();
}
self.chat_widget.note_stream_consolidation_completed();
self.insert_completed_token_activity_output_after_stream_shutdown(tui);
return Ok(AppRunControl::Continue);
}
let end = self.transcript_cells.len();
let start = trailing_run_start::<history_cell::ProposedPlanStreamCell>(
&self.transcript_cells,
+15 -82
View File
@@ -18,7 +18,6 @@ use std::collections::VecDeque;
use std::sync::Arc;
use std::time::Instant;
use codex_features::Feature;
use color_eyre::eyre::Result;
use ratatui::text::Line;
@@ -109,18 +108,14 @@ impl App {
}
}
pub(super) fn terminal_resize_reflow_enabled(&self) -> bool {
self.config.features.enabled(Feature::TerminalResizeReflow)
}
/// Start retaining initial resume replay rows before they are written to scrollback.
///
/// Resume replay can insert thousands of already-finalized history cells before the first draw.
/// When resize reflow is enabled, buffering here lets the same row cap used by resize rebuilds
/// apply to the startup write. Starting this buffer while an overlay owns rendering would split
/// transcript ownership, so overlay replay continues through the normal deferred-history path.
/// Buffering here lets the same row cap used by resize rebuilds apply to the startup write.
/// Starting this buffer while an overlay owns rendering would split transcript ownership, so
/// overlay replay continues through the normal deferred-history path.
pub(super) fn begin_initial_history_replay_buffer(&mut self) {
if self.terminal_resize_reflow_enabled() && self.overlay.is_none() {
if self.overlay.is_none() {
self.initial_history_replay_buffer = Some(Default::default());
}
}
@@ -131,10 +126,7 @@ impl App {
/// defer terminal writes until the replay is complete and reuse the resize-reflow tail renderer
/// so only the rows the terminal would retain are formatted and inserted.
pub(super) fn begin_thread_switch_history_replay_buffer(&mut self) {
if self.terminal_resize_reflow_enabled()
&& self.resize_reflow_max_rows().is_some()
&& self.overlay.is_none()
{
if self.resize_reflow_max_rows().is_some() && self.overlay.is_none() {
self.initial_history_replay_buffer = Some(InitialHistoryReplayBuffer {
retained_lines: VecDeque::new(),
render_from_transcript_tail: true,
@@ -233,7 +225,6 @@ impl App {
}
fn schedule_resize_reflow(&mut self, target_width: Option<u16>) -> bool {
debug_assert!(self.terminal_resize_reflow_enabled());
self.transcript_reflow.schedule_debounced(target_width)
}
@@ -263,19 +254,6 @@ impl App {
/// source-backed reflow so terminal scrollback reflects the finalized cell instead of the
/// transient stream rows.
pub(super) fn maybe_finish_stream_reflow(&mut self, tui: &mut tui::Tui) -> Result<()> {
if !self.terminal_resize_reflow_enabled() {
if self.transcript_reflow.take_stream_finish_reflow_needed() {
self.schedule_immediate_history_cell_refresh(tui);
self.maybe_run_resize_reflow(tui)?;
return Ok(());
}
if self.transcript_reflow.history_cell_refresh_requested() {
return Ok(());
}
self.transcript_reflow.clear();
return Ok(());
}
if self.transcript_reflow.take_stream_finish_reflow_needed() {
self.schedule_immediate_resize_reflow(tui);
self.maybe_run_resize_reflow(tui)?;
@@ -286,42 +264,16 @@ impl App {
}
fn schedule_immediate_resize_reflow(&mut self, tui: &mut tui::Tui) {
if !self.terminal_resize_reflow_enabled() {
self.transcript_reflow.clear();
return;
}
self.transcript_reflow.schedule_immediate();
tui.frame_requester().schedule_frame();
}
fn schedule_immediate_history_cell_refresh(&mut self, tui: &mut tui::Tui) {
self.transcript_reflow.schedule_history_cell_refresh();
tui.frame_requester().schedule_frame();
}
pub(crate) fn retry_pending_history_cell_refresh(&self, tui: &mut tui::Tui) {
if self.transcript_reflow.history_cell_refresh_requested() {
tui.frame_requester().schedule_frame();
}
}
pub(super) fn should_handle_draw_pre_render(&self) -> bool {
self.terminal_resize_reflow_enabled()
|| self.transcript_reflow.history_cell_refresh_requested()
}
/// Force stream-finalized output through the resize reflow path.
///
/// Proposed plan consolidation uses this stricter path because a completed plan is inserted or
/// replaced as one styled source-backed cell. If this reflow is skipped after a stream-time
/// resize, the visible scrollback can keep the pre-consolidation wrapping.
pub(super) fn finish_required_stream_reflow(&mut self, tui: &mut tui::Tui) -> Result<()> {
if !self.terminal_resize_reflow_enabled() {
if !self.transcript_reflow.history_cell_refresh_requested() {
self.transcript_reflow.clear();
}
return Ok(());
}
self.schedule_immediate_resize_reflow(tui);
self.maybe_run_resize_reflow(tui)?;
if !self.transcript_reflow.has_pending_reflow() {
@@ -350,37 +302,24 @@ impl App {
self.chat_widget.on_terminal_resize(size.width);
}
if should_rebuild_transcript {
if self.terminal_resize_reflow_enabled() {
if reflow_needed && self.should_mark_reflow_as_stream_time() {
self.transcript_reflow.mark_resize_requested_during_stream();
}
let target_width = reflow_needed.then_some(size.width);
if self.schedule_resize_reflow(target_width) {
frame_requester.schedule_frame();
} else {
frame_requester.schedule_frame_in(TRANSCRIPT_REFLOW_DEBOUNCE);
}
} else if !self.terminal_resize_reflow_enabled()
&& width.changed
&& !self.transcript_reflow.history_cell_refresh_requested()
{
self.transcript_reflow.clear();
if reflow_needed && self.should_mark_reflow_as_stream_time() {
self.transcript_reflow.mark_resize_requested_during_stream();
}
let target_width = reflow_needed.then_some(size.width);
if self.schedule_resize_reflow(target_width) {
frame_requester.schedule_frame();
} else {
frame_requester.schedule_frame_in(TRANSCRIPT_REFLOW_DEBOUNCE);
}
}
if size != last_known_screen_size {
self.refresh_status_line();
}
if self.terminal_resize_reflow_enabled() {
self.maybe_clear_resize_reflow_without_terminal();
}
self.maybe_clear_resize_reflow_without_terminal();
should_rebuild_transcript
}
fn maybe_clear_resize_reflow_without_terminal(&mut self) {
if !self.terminal_resize_reflow_enabled() {
self.transcript_reflow.clear();
return;
}
let Some(deadline) = self.transcript_reflow.pending_until() else {
return;
};
@@ -400,7 +339,7 @@ impl App {
tui.terminal.last_known_screen_size,
&tui.frame_requester(),
);
if should_rebuild_transcript && self.terminal_resize_reflow_enabled() {
if should_rebuild_transcript {
// Resize-sensitive history inserts queued before this frame may be wrapped for the old
// viewport or targeted at rows no longer visible. Drop them and let resize reflow
// rebuild from transcript cells.
@@ -417,12 +356,6 @@ impl App {
/// reuse terminal-wrapped output here would preserve exactly the stale wrapping this feature is
/// meant to remove.
pub(super) fn maybe_run_resize_reflow(&mut self, tui: &mut tui::Tui) -> Result<()> {
if !self.terminal_resize_reflow_enabled()
&& !self.transcript_reflow.history_cell_refresh_requested()
{
self.transcript_reflow.clear();
return Ok(());
}
let Some(deadline) = self.transcript_reflow.pending_until() else {
return Ok(());
};
-37
View File
@@ -4400,13 +4400,6 @@ fn test_thread_session(thread_id: ThreadId, cwd: PathBuf) -> ThreadSessionState
}
}
fn enable_terminal_resize_reflow(app: &mut App) {
app.config
.features
.set_enabled(Feature::TerminalResizeReflow, /*enabled*/ true)
.expect("feature should be configurable");
}
fn plain_line_cell(text: impl Into<String>) -> Arc<dyn HistoryCell> {
Arc::new(PlainHistoryCell::new(vec![Line::from(text.into())])) as Arc<dyn HistoryCell>
}
@@ -4516,7 +4509,6 @@ async fn uncapped_resize_reflow_renders_all_cells_under_row_limit() {
#[tokio::test]
async fn initial_replay_buffer_keeps_recent_rows_when_row_cap_present() {
let (mut app, _rx, _op_rx) = make_test_app_with_channels().await;
enable_terminal_resize_reflow(&mut app);
app.config.terminal_resize_reflow.max_rows = TerminalResizeReflowMaxRows::Limit(3);
app.begin_initial_history_replay_buffer();
@@ -4551,7 +4543,6 @@ async fn initial_replay_buffer_keeps_recent_rows_when_row_cap_present() {
#[tokio::test]
async fn thread_switch_replay_buffer_uses_transcript_tail_mode_when_row_cap_present() {
let (mut app, _rx, _op_rx) = make_test_app_with_channels().await;
enable_terminal_resize_reflow(&mut app);
app.config.terminal_resize_reflow.max_rows = TerminalResizeReflowMaxRows::Limit(3);
app.begin_thread_switch_history_replay_buffer();
@@ -4567,7 +4558,6 @@ async fn thread_switch_replay_buffer_uses_transcript_tail_mode_when_row_cap_pres
#[tokio::test]
async fn thread_switch_replay_buffer_is_disabled_without_row_cap() {
let (mut app, _rx, _op_rx) = make_test_app_with_channels().await;
enable_terminal_resize_reflow(&mut app);
app.config.terminal_resize_reflow.max_rows = TerminalResizeReflowMaxRows::Disabled;
app.begin_thread_switch_history_replay_buffer();
@@ -4578,7 +4568,6 @@ async fn thread_switch_replay_buffer_is_disabled_without_row_cap() {
#[tokio::test]
async fn height_shrink_schedules_resize_reflow() {
let (mut app, _rx, _op_rx) = make_test_app_with_channels().await;
enable_terminal_resize_reflow(&mut app);
let frame_requester = crate::tui::FrameRequester::test_dummy();
assert!(!app.handle_draw_size_change(
@@ -4595,32 +4584,6 @@ async fn height_shrink_schedules_resize_reflow() {
assert!(app.transcript_reflow.has_pending_reflow());
}
#[tokio::test]
async fn disabled_resize_reflow_preserves_pending_history_cell_refresh() {
let (mut app, _rx, _op_rx) = make_test_app_with_channels().await;
let frame_requester = crate::tui::FrameRequester::test_dummy();
app.config
.features
.set_enabled(Feature::TerminalResizeReflow, /*enabled*/ false)
.expect("feature should be configurable");
assert!(!app.should_handle_draw_pre_render());
app.transcript_reflow.schedule_history_cell_refresh();
assert!(app.should_handle_draw_pre_render());
assert!(!app.handle_draw_size_change(
ratatui::layout::Size::new(/*width*/ 118, /*height*/ 35),
ratatui::layout::Size::new(/*width*/ 118, /*height*/ 35),
&frame_requester,
));
assert!(app.handle_draw_size_change(
ratatui::layout::Size::new(/*width*/ 119, /*height*/ 35),
ratatui::layout::Size::new(/*width*/ 118, /*height*/ 35),
&frame_requester,
));
assert!(app.transcript_reflow.history_cell_refresh_requested());
}
fn test_turn(turn_id: &str, status: TurnStatus, items: Vec<ThreadItem>) -> Turn {
Turn {
id: turn_id.to_string(),
+2 -4
View File
@@ -1098,8 +1098,7 @@ impl App {
self.chat_widget
.set_initial_user_message_submit_suppressed(/*suppressed*/ true);
self.chat_widget.handle_thread_session(session);
let should_buffer_initial_replay =
self.terminal_resize_reflow_enabled() && !turns.is_empty();
let should_buffer_initial_replay = !turns.is_empty();
if should_buffer_initial_replay {
self.app_event_tx
.send(AppEvent::BeginInitialHistoryReplayBuffer);
@@ -1289,8 +1288,7 @@ impl App {
resume_restored_queue: bool,
) {
self.refresh_mcp_startup_expected_servers_from_config();
let should_buffer_replay = self.terminal_resize_reflow_enabled()
&& (!snapshot.turns.is_empty() || !snapshot.events.is_empty());
let should_buffer_replay = !snapshot.turns.is_empty() || !snapshot.events.is_empty();
if should_buffer_replay {
self.app_event_tx
.send(AppEvent::BeginThreadSwitchHistoryReplayBuffer);
+1 -1
View File
@@ -294,7 +294,7 @@ impl App {
}
self.overlay = None;
self.backtrack.overlay_preview_active = false;
self.retry_pending_history_cell_refresh(tui);
tui.frame_requester().schedule_frame();
if was_backtrack {
// Ensure backtrack state is fully reset when overlay closes (e.g. via 'q').
self.reset_backtrack_state();
+2 -3
View File
@@ -1652,9 +1652,8 @@ impl ChatWidget {
/// Update resize-sensitive chat widget state after the terminal width changes.
///
/// The app calls this even when terminal resize reflow is disabled so live stream wrapping
/// remains consistent with the current viewport. Finalized transcript rebuilding stays gated at
/// the app layer.
/// Live stream wrapping stays consistent with the current viewport while finalized transcript
/// rebuilding runs through app-level resize reflow.
pub(crate) fn on_terminal_resize(&mut self, width: u16) {
let had_rendered_width = self.last_rendered_width.get().is_some();
self.last_rendered_width.set(Some(width as usize));
+3 -26
View File
@@ -29,7 +29,6 @@ pub(crate) struct TranscriptReflowState {
last_reflow_width: Option<u16>,
pending_reflow_width: Option<u16>,
pending_until: Option<Instant>,
history_cell_refresh_requested: bool,
ran_during_stream: bool,
resize_requested_during_stream: bool,
}
@@ -37,9 +36,9 @@ pub(crate) struct TranscriptReflowState {
impl TranscriptReflowState {
/// Reset all width, pending deadline, and stream repair state.
///
/// Call this when resize reflow is disabled or when the app discards the transcript state that
/// pending reflow work would have rebuilt. Leaving stale deadlines behind would make a later
/// draw attempt to rebuild history from unrelated cells.
/// Call this when the app discards the transcript state that pending reflow work would have
/// rebuilt. Leaving stale deadlines behind would make a later draw attempt to rebuild history
/// from unrelated cells.
pub(crate) fn clear(&mut self) {
*self = Self::default();
}
@@ -94,12 +93,6 @@ impl TranscriptReflowState {
self.pending_until = Some(Instant::now());
}
/// Schedule an immediate rebuild because an existing history cell changed its rendered output.
pub(crate) fn schedule_history_cell_refresh(&mut self) {
self.history_cell_refresh_requested = true;
self.schedule_immediate();
}
#[cfg(test)]
pub(crate) fn set_due_for_test(&mut self) {
self.pending_until = Some(Instant::now() - Duration::from_millis(1));
@@ -117,14 +110,9 @@ impl TranscriptReflowState {
self.pending_until.is_some()
}
pub(crate) fn history_cell_refresh_requested(&self) -> bool {
self.history_cell_refresh_requested
}
pub(crate) fn clear_pending_reflow(&mut self) {
self.pending_until = None;
self.pending_reflow_width = None;
self.history_cell_refresh_requested = false;
}
/// Remember the terminal width that actually rebuilt transcript scrollback.
@@ -274,17 +262,6 @@ mod tests {
assert!(state.reflow_needed_for_width(/*width*/ 100));
}
#[test]
fn clear_pending_reflow_clears_history_cell_refresh_request() {
let mut state = TranscriptReflowState::default();
state.schedule_history_cell_refresh();
assert!(state.history_cell_refresh_requested());
state.clear_pending_reflow();
assert!(!state.history_cell_refresh_requested());
}
#[test]
fn mark_reflowed_width_reports_unchanged_width() {
let mut state = TranscriptReflowState::default();
+21 -56
View File
@@ -31,11 +31,7 @@ async fn tmux_split_preserves_fresh_session_composer_row_after_resize_reflow() -
let server = MockServer::start().await;
let _response_mock = responses::mount_sse_once(&server, resize_reflow_sse()).await;
let openai_base_url_config = format!("openai_base_url=\"{}/v1\"", server.uri());
write_config(
codex_home.path(),
&repo_root,
/*terminal_resize_reflow_enabled*/ true,
)?;
write_config(codex_home.path(), &repo_root)?;
write_auth(codex_home.path())?;
let session_name = format!("codex-resize-reflow-smoke-{}", std::process::id());
@@ -179,8 +175,7 @@ async fn tmux_repeated_resizes_do_not_push_composer_down() -> Result<()> {
return Ok(());
}
run_repeated_resize_smoke(/*terminal_resize_reflow_enabled*/ false).await?;
run_repeated_resize_smoke(/*terminal_resize_reflow_enabled*/ true).await?;
run_repeated_resize_smoke().await?;
Ok(())
}
@@ -203,11 +198,7 @@ async fn tmux_width_resize_restore_keeps_visible_content_anchored() -> Result<()
let server = MockServer::start().await;
let _response_mock = responses::mount_sse_once(&server, resize_reflow_sse()).await;
let openai_base_url_config = format!("openai_base_url=\"{}/v1\"", server.uri());
write_config(
codex_home.path(),
&repo_root,
/*terminal_resize_reflow_enabled*/ true,
)?;
write_config(codex_home.path(), &repo_root)?;
write_auth(codex_home.path())?;
let session_name = format!("codex-resize-width-{}", std::process::id());
@@ -320,26 +311,17 @@ async fn tmux_width_resize_restore_keeps_visible_content_anchored() -> Result<()
Ok(())
}
async fn run_repeated_resize_smoke(terminal_resize_reflow_enabled: bool) -> Result<()> {
async fn run_repeated_resize_smoke() -> Result<()> {
let repo_root = codex_utils_cargo_bin::repo_root()?;
let codex = codex_binary(&repo_root)?;
let codex_home = tempdir()?;
let server = MockServer::start().await;
let _response_mock = responses::mount_sse_once(&server, resize_reflow_sse()).await;
let openai_base_url_config = format!("openai_base_url=\"{}/v1\"", server.uri());
write_config(
codex_home.path(),
&repo_root,
terminal_resize_reflow_enabled,
)?;
write_config(codex_home.path(), &repo_root)?;
write_auth(codex_home.path())?;
let suffix = if terminal_resize_reflow_enabled {
"enabled"
} else {
"disabled"
};
let session_name = format!("codex-resize-repeat-{suffix}-{}", std::process::id());
let session_name = format!("codex-resize-repeat-{}", std::process::id());
let _session = TmuxSession {
name: session_name.clone(),
};
@@ -433,30 +415,20 @@ async fn run_repeated_resize_smoke(terminal_resize_reflow_enabled: bool) -> Resu
let restored_history_row =
first_row_containing(&restored_capture, "resize reflow sentinel")
.with_context(|| format!("history row after resize cycle {cycle}"))?;
if terminal_resize_reflow_enabled {
anyhow::ensure!(
restored_row == baseline_row,
"composer row drifted after resize cycle {cycle} with terminal_resize_reflow={terminal_resize_reflow_enabled}: \
baseline={baseline_row}, restored={restored_row}\n\
baseline:\n{baseline_capture}\n\
restored:\n{restored_capture}"
);
anyhow::ensure!(
restored_history_row == baseline_history_row,
"history row drifted after resize cycle {cycle} with terminal_resize_reflow={terminal_resize_reflow_enabled}: \
baseline={baseline_history_row}, restored={restored_history_row}\n\
baseline:\n{baseline_capture}\n\
restored:\n{restored_capture}"
);
} else {
anyhow::ensure!(
restored_row <= baseline_row + 1,
"composer row snapped downward after resize cycle {cycle} with terminal_resize_reflow={terminal_resize_reflow_enabled}: \
baseline={baseline_row}, restored={restored_row}\n\
baseline:\n{baseline_capture}\n\
restored:\n{restored_capture}"
);
}
anyhow::ensure!(
restored_row == baseline_row,
"composer row drifted after resize cycle {cycle}: baseline={baseline_row}, \
restored={restored_row}\n\
baseline:\n{baseline_capture}\n\
restored:\n{restored_capture}"
);
anyhow::ensure!(
restored_history_row == baseline_history_row,
"history row drifted after resize cycle {cycle}: baseline={baseline_history_row}, \
restored={restored_history_row}\n\
baseline:\n{baseline_capture}\n\
restored:\n{restored_capture}"
);
}
Ok(())
@@ -489,20 +461,13 @@ fn codex_binary(repo_root: &Path) -> Result<PathBuf> {
Ok(fallback)
}
fn write_config(
codex_home: &Path,
repo_root: &Path,
terminal_resize_reflow_enabled: bool,
) -> Result<()> {
fn write_config(codex_home: &Path, repo_root: &Path) -> Result<()> {
let repo_root_display = repo_root.display();
let config = format!(
r#"model = "gpt-5.4"
model_provider = "openai"
suppress_unstable_features_warning = true
[features]
terminal_resize_reflow = {terminal_resize_reflow_enabled}
[projects."{repo_root_display}"]
trust_level = "trusted"
"#