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

128 lines
4.1 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
**DOs**
- **Use one-liner mutex ops**: Acquire the lock, do the operation, and let the guard drop immediately.
```rust
running_requests_id_to_codex_uuid
.lock()
.await
.insert(request_id.clone(), session_id);
// ...
running_requests_id_to_codex_uuid
.lock()
.await
.remove(&request_id);
```
- **Handle error events decisively**: On `EventMsg::Error`, return a response, unregister, and break the loop.
```rust
match event {
EventMsg::Error(err) => {
outgoing
.send_response(request_id.clone(), serde_json::json!({ "error": err.message }))
.await;
running_requests_id_to_codex_uuid.lock().await.remove(&request_id);
break;
}
_ => { /* ... */ }
}
```
- **Make notifications truly async**: If the handler awaits, mark it `async` and await at the call site.
```rust
// lib.rs
match msg {
JSONRPCMessage::Notification(n) => processor.process_notification(n).await,
_ => { /* ... */ }
}
// message_processor.rs
pub(crate) async fn process_notification(&mut self, n: JSONRPCNotification) { /* ... */ }
```
- **Minimize lock scope and avoid nested locks**: Read what you need, drop the guard, then lock the next map.
```rust
let session_id = {
let g = self.running_requests_id_to_codex_uuid.lock().await;
match g.get(&request_id).copied() { Some(id) => id, None => return }
};
let codex = {
let g = self.session_map.lock().await;
match g.get(&session_id).cloned() { Some(c) => c, None => return }
};
```
- **Defer derived data until used**: Only compute `request_id_string` where you actually need it.
```rust
let maybe_id = self.running_requests_id_to_codex_uuid.lock().await.get(&request_id).copied();
if maybe_id.is_none() {
let request_id_string = match &request_id {
RequestId::String(s) => s.clone(),
RequestId::Integer(i) => i.to_string(),
};
tracing::warn!("Session not found for request_id: {request_id_string}");
return;
}
```
- **Write precise, portable tests**: Assert JSON-RPC errors explicitly and make blocking commands crossplatform.
```rust
// Match JSON-RPC error
match mcp_process.read_jsonrpc_message().await? {
JSONRPCMessage::Error(e) if e.id == RequestId::Integer(codex_request_id) => { /* ok */ }
other => anyhow::bail!("unexpected message: {other:?}"),
}
// Cross-platform blocking command
#[cfg(target_os = "windows")]
let shell_command = vec![
"powershell".to_string(), "-Command".to_string(), "Start-Sleep -Seconds 60".to_string()
];
#[cfg(not(target_os = "windows"))]
let shell_command = vec!["sleep".to_string(), "60".to_string()];
```
**DONTs**
- **Dont hold locks longer than necessary**: Avoid temporary guard variables and extra braces that extend lifetimes.
```rust
// ❌ Holds the lock longer than needed
let mut guard = running.lock().await;
guard.insert(request_id.clone(), session_id);
// guard lives until end of scope
```
- **Dont ignore cleanup on all exit paths**: Always unregister request IDs on success, error, and submit failures.
```rust
// ❌ Missing cleanup on error
if let Err(e) = codex.submit_with_id(submission).await {
tracing::error!("submit failed: {e}");
// running_requests_id_to_codex_uuid.remove(...) is missing
}
```
- **Dont treat MCP errors as “responses”**: Dont assert on `JSONRPCResponse` with null/embedded error; match `JSONRPCMessage::Error`.
```rust
// ❌ Fragile: assumes error tunneled as a response payload
let JSONRPCMessage::Response(r) = msg else { /* ... */ };
assert!(r.result.get("error").is_some());
```
- **Dont precompute derived strings you may not use**: Compute `request_id_string` only in the branches that need it.
```rust
// ❌ Work done upfront even if early-return
let request_id_string = match &request_id { /* ... */ };
// early return before using request_id_string
```
- **Dont blanket-allow dead code/imports**: Prefer targeted allowances or remove unused helpers.
```rust
// ❌ Blanket allow across a module
#![allow(dead_code, unused_imports)]
// ✅ Narrow allowance for a single helper used by some tests
#[allow(dead_code)]
async fn read_stream_until_error(/* ... */) { /* ... */ }
```