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

4.5 KiB
Raw Blame History

PR #1602 Review Takeaways

DOs

  • Investigate Before Parsing Changes: Reproduce the Windows quoting issue, document root cause, and add a regression test before modifying CLI -c key=value parsing.
// Build a TOMLsafe override; quotes + backslashes escaped.
let path = r"C:\logs\sessions\a.jsonl";
let arg = format!("-c experimental_resume={:?}", path);
// -> -c experimental_resume="C:\\logs\\sessions\\a.jsonl"

// Optional normalization approach (test both behaviors):
let arg_norm = format!(r#"-c experimental_resume="{}""#, path.replace('\\', "/"));
  • Decide Resume vs New Early (Keep IDs Immutable): Determine the session to use up front and pass it through; avoid mutating IDs later.
use uuid::Uuid;

async fn choose_session_id(resume_path: Option<&std::path::Path>) -> std::io::Result<Uuid> {
    if let Some(p) = resume_path {
        let (_rec, saved) = RolloutRecorder::resume(p).await?;
        Ok(saved.session_id)
    } else {
        Ok(Uuid::new_v4())
    }
}

// Later:
let session_id = choose_session_id(config.experimental_resume.as_deref()).await?;
  • Propagate I/O Errors From Background Writers: Have the writer return io::Result<()>, store the JoinHandle, and surface failures (e.g., in tests or shutdown).
async fn rollout_writer(
    mut file: tokio::fs::File,
    mut rx: tokio::sync::mpsc::Receiver<RolloutCmd>,
    meta: Option<SessionMeta>,
) -> std::io::Result<()> {
    if let Some(m) = meta {
        file.write_all(serde_json::to_string(&m)?.as_bytes()).await?;
        file.write_all(b"\n").await?;
    }
    while let Some(cmd) = rx.recv().await {
        // ... perform writes with `?`
    }
    file.flush().await?;
    Ok(())
}

// Keep the handle so callers can `await` or inspect errors.
let handle: tokio::task::JoinHandle<std::io::Result<()>> =
    tokio::spawn(rollout_writer(file, rx, Some(meta)));
  • Use Command::cargo_bin In Tests: Prefer compiled binary execution over cargo run for speed and determinism.
use assert_cmd::cargo::CommandCargoExt;
use std::process::Command;

let mut cmd = Command::cargo_bin("codex-cli").unwrap();
cmd.arg("exec")
   .arg("--skip-git-repo-check")
   .arg("-c")
   .arg(format!("experimental_resume={:?}", path));
cmd.assert().success();
  • Add Regression Tests For CLI Overrides: Cover quoting/escaping on Windows paths and fallback behavior so future changes dont regress.
#[test]
fn windows_path_is_preserved_in_override() {
    let win = r"C:\Users\me\sessions\run.jsonl";
    let arg = format!("-c experimental_resume={:?}", win);
    assert!(arg.contains(r#""C:\\Users\\me\\sessions\\run.jsonl""#));
}
  • Document New Config Clearly: If adding experimental_resume, specify expected format (absolute path, JSONL) and precedence.
/// Experimental: absolute path to a `.jsonl` rollout to resume.
/// Example: `-c experimental_resume="/home/user/.codex/sessions/2025/07/19/run.jsonl"`
pub experimental_resume: Option<std::path::PathBuf>,

DONTs

  • Dont Mutate session_id MidFlow: Avoid patterns that change identity after configuration.
// Bad: mutating the ID later in the loop.
let mut session_id = Uuid::new_v4();
// ...
if let Some(saved) = maybe_resume_id { session_id = saved; }

// Good: compute once, immutably (see DOs).
  • Dont Ignore I/O Errors: Avoid let _ = ... in the writer; it hides failures that make rollouts incomplete or corrupt.
// Bad:
let _ = file.write_all(json.as_bytes()).await;

// Good:
file.write_all(json.as_bytes()).await?;
  • Dont Land Parsing Changes Without Tests: Trimming quotes on parse failure can mask real bugs and create platformspecific surprises.
// Bad: silently “fixes” input and hides root cause.
let trimmed = value_str.trim().trim_matches(|c| c == '"' || c == '\'');
// Good: reproduce, understand, then fix with a tested approach (see DOs).
  • Dont Use cargo run In Integration Tests: Its slower, noisier, and more brittle than invoking the built binary.
// Bad:
let mut cmd = std::process::Command::new("cargo");
cmd.args(["run", "-p", "codex-cli", "--", "exec"]);

// Good: use Command::cargo_bin (see DOs).
  • Dont HandRoll Fragile -c Strings: Avoid manual quoting that breaks on Windows; use {:?} or normalize.
// Bad: breaks when `path` contains backslashes.
let arg = format!("-c experimental_resume=\"{path}\"");

// Good:
let arg = format!("-c experimental_resume={:?}", path);
// or normalize:
let arg = format!(r#"-c experimental_resume="{}""#, path.replace('\\', "/"));