Files
codex/prs/bolinfest/study/PR-1594-study.md
2025-09-02 15:17:45 -07:00

6.3 KiB
Raw Blame History

DOs

  • Boldly handle both streaming and nonstreaming paths: print headers on first delta, flush each chunk, and still render the final nondelta message/reasoning if no deltas arrived.
match event {
    EventMsg::AgentMessageDelta(AgentMessageDeltaEvent { delta }) => {
        if !self.answer_started {
            ts_println!(self, "{}\n", "codex".style(self.italic).style(self.magenta));
            self.answer_started = true;
        }
        print!("{delta}");
        #[allow(clippy::expect_used)]
        std::io::stdout().flush().expect("could not flush stdout");
    }
    EventMsg::AgentMessage(AgentMessageEvent { message }) => {
        if !self.answer_started {
            ts_println!(self, "{}\n{}", "codex".style(self.italic).style(self.magenta), message);
        } else {
            println!();
            self.answer_started = false;
        }
    }
    _ => {}
}
  • Stream “thinking” only when enabled and use the correct label; mirror the answer flow for reasoning.
EventMsg::AgentReasoningDelta(AgentReasoningDeltaEvent { delta }) => {
    if self.show_agent_reasoning {
        if !self.reasoning_started {
            ts_println!(self, "{}\n", "thinking".style(self.italic).style(self.magenta));
            self.reasoning_started = true;
        }
        print!("{delta}");
        #[allow(clippy::expect_used)]
        std::io::stdout().flush().expect("could not flush stdout");
    }
}
EventMsg::AgentReasoning(AgentReasoningEvent { text }) => {
    if self.show_agent_reasoning {
        if !self.reasoning_started {
            ts_println!(self, "{}\n{}", "thinking".style(self.italic).style(self.magenta), text);
        } else {
            println!();
            self.reasoning_started = false;
        }
    }
}
  • Flush stdout on every streamed chunk to keep the CLI/TUI responsive.
print!("{delta}");
#[allow(clippy::expect_used)]
std::io::stdout().flush().expect("could not flush stdout");
  • Prefer if/else over early returns inside large match arms to keep control flow easy to reason about.
if !self.answer_started {
    ts_println!(self, "{}\n{}", "codex".style(self.italic).style(self.magenta), message);
} else {
    println!();
    self.answer_started = false;
}
  • Keep use statements at the top (or fully qualify) instead of sprinkling imports inside match arms.
// Top of file:
use std::io::Write;

// or fully qualify where used:
std::io::stdout().flush().expect("could not flush stdout");
  • Inline variables directly in formatting macros.
println!("tokens used: {total_tokens}");
ts_println!(self, "command: {cmd}");
  • Implement streaming in the TUI with buffers and “replace last” semantics.
// Agent message streaming
EventMsg::AgentMessageDelta(AgentMessageDeltaEvent { delta }) => {
    if self.answer_buffer.is_empty() {
        self.conversation_history.add_agent_message(&self.config, "".to_string());
    }
    self.answer_buffer.push_str(&delta);
    self.conversation_history.replace_prev_agent_message(&self.config, self.answer_buffer.clone());
    self.request_redraw();
}
EventMsg::AgentMessage(AgentMessageEvent { message }) => {
    if self.answer_buffer.is_empty() {
        self.conversation_history.add_agent_message(&self.config, message);
    } else {
        self.conversation_history.replace_prev_agent_message(&self.config, message);
        self.answer_buffer.clear();
    }
    self.request_redraw();
}
  • When replacing the last rendered cell, recompute height and set 0 if width is unknown.
let width = self.cached_width.get();
let height = if width > 0 { entry.cell.height(width) } else { 0 };
entry.line_count.set(height);
  • Place throttling/debounce at the top-level TUI event loop (not inside widgets), and never throttle user-typed input redraws.
// Event loop sketch
match app_event {
    AppEvent::Key(_) => {
        needs_immediate_redraw = true;
        draw_now();
    }
    AppEvent::Redraw => {
        if needs_immediate_redraw || last_draw.elapsed() >= Duration::from_millis(16) {
            draw_now();
            needs_immediate_redraw = false;
        } else {
            schedule_coalesced_redraw();
        }
    }
    _ => {}
}
  • Keep code/comments concise and accurate; remove redundant doc comments on obvious fields like answer_started and reasoning_started.

  • Finish streamed answers cleanly: print a newline and reset flags to prepare for the next message.

println!();
self.answer_started = false;
  • Maintain small style fixes that improve clarity (e.g., ASCII apostrophes in user-facing text, no stray trailing commas in macro calls).
ts_println!(self, "{}\n", "codex".style(self.italic).style(self.magenta));

DONTs

  • Dont rely solely on deltas; nonstreaming responses must still render final messages/reasoning.
// Missing final AgentMessage handler means non-streaming prints nothing — avoid this.
  • Dont use early return inside match arms when a simple if/else suffices.
// Avoid
if !self.answer_started {
    ts_println!(self, "{}\n{}", "codex".style(self.italic).style(self.magenta), message);
    return;
}
println!();
  • Dont use std::io::Write; inside a match arm or branch.
// Avoid
use std::io::Write; // nested inside logic
  • Dont throttle redraws in widgets like ChatWidget; doing so can drop visible updates and feel laggy.
// Avoid throttling here:
fn request_redraw(&mut self) {
    if Instant::now().duration_since(self.last_redraw_time) > Duration::from_millis(100) {
        self.app_event_tx.send(AppEvent::Redraw);
    }
}
  • Dont leave misleading or redundant comments (e.g., “else, we rerender one last time” when theres no exclusive else).
// Avoid comments that restate code or mislead the reader.
  • Dont use the wrong label for reasoning vs. answers (“thinking” vs. “codex”).
// Avoid
ts_println!(self, "{}\n", "codex".style(self.italic).style(self.magenta)); // for reasoning
  • Dont forget to guard reasoning output behind show_agent_reasoning.
// Avoid
print!("{delta}"); // without checking self.show_agent_reasoning
  • Dont ignore zero width when recomputing layout; not setting line_count can break scrolling and rendering.
// Avoid
entry.line_count.set(entry.cell.height(width)); // width may be 0