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

184 lines
6.4 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.
**PR #2496 Review Takeaways**
**DOs**
- **Model Notifications As Enums:** Define a typed `ServerNotification` (parallel to `ClientRequest`) and let serde drive the wire shape.
```rust
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, TS)]
#[serde(tag = "method", content = "params", rename_all = "camelCase")]
pub enum ServerNotification {
AuthStatusChange { params: AuthStatusChangeNotification },
LoginChatGptComplete { params: LoginChatGptCompleteNotification },
}
```
- **Send Typed Notifications:** Add a helper that accepts `ServerNotification` directly; avoid manual JSON plumbing.
```rust
impl OutgoingMessageSender {
pub async fn send_server_notification(&self, n: ServerNotification) {
let json = serde_json::to_value(&n).expect("serialize notification");
let method = json.get("method").and_then(|v| v.as_str()).unwrap().to_string();
let params = json.get("params").cloned();
let msg = OutgoingMessage::Notification(OutgoingNotification { method, params });
let _ = self.sender.send(msg).await;
}
}
```
- **Derive Config Early:** Parse CLI overrides once, produce a `Config`, and pass `Arc<Config>` down to processors.
```rust
use std::io::{Error, ErrorKind};
use std::sync::Arc;
pub async fn run_main(sandbox: Option<PathBuf>, cli: CliConfigOverrides) -> IoResult<()> {
let kv = cli.parse_overrides()
.map_err(|e| Error::new(ErrorKind::InvalidInput, format!("error parsing -c overrides: {e}")))?;
let config = Config::load_with_cli_overrides(kv, ConfigOverrides::default())
.map_err(|e| Error::new(ErrorKind::InvalidData, format!("error loading config: {e}")))?;
let outgoing = OutgoingMessageSender::new(outgoing_tx);
let mut processor = MessageProcessor::new(outgoing, sandbox, Arc::new(config));
// ...
Ok(())
}
```
- **Keep Dependency Direction Clean:** Put shared auth types in `codex-protocol`; have `login` depend on `protocol`, not vice versa.
```toml
# login/Cargo.toml
[dependencies]
codex-protocol = { path = "../protocol" }
# login/src/lib.rs
pub use codex_protocol::mcp_protocol::AuthMode;
```
- **Scope Locks To Drop Guards:** Prefer scoped blocks over explicit `drop(guard)`.
```rust
{
let mut guard = self.active_login.lock().await;
if let Some(active) = guard.take() {
active.drop();
}
}
```
- **Emit Auth Events On State Changes:** Notify on successful login and on logout.
```rust
// After successful login
let payload = AuthStatusChangeNotification { auth_method: Some(AuthMode::ChatGPT) };
outgoing.send_server_notification(ServerNotification::AuthStatusChange { params: payload }).await;
// After logout
let payload = AuthStatusChangeNotification { auth_method: None };
outgoing.send_server_notification(ServerNotification::AuthStatusChange { params: payload }).await;
```
- **Use `From`/`Into` When Conversions Are Needed:** If you still convert to `OutgoingNotification`, implement standard traits.
```rust
impl From<ServerNotification> for OutgoingNotification {
fn from(n: ServerNotification) -> Self {
let v = serde_json::to_value(n).unwrap();
let method = v["method"].as_str().unwrap().to_string();
let params = v.get("params").cloned();
OutgoingNotification { method, params }
}
}
// usage: sender.send_notification(ServerNotification::AuthStatusChange { params }.into()).await;
```
- **Prefer `map_err` + `?` For Errors:** Keep error paths concise and readable.
```rust
let kv = cli.parse_overrides()
.map_err(|e| Error::new(ErrorKind::InvalidInput, format!("error parsing -c overrides: {e}")))?;
```
- **Update TS Codegen When Adding Types:** Export new enums/structs in `protocol-ts`.
```rust
pub fn generate_ts(out_dir: &Path, prettier: Option<&Path>) -> Result<()> {
codex_protocol::mcp_protocol::ServerNotification::export_all_to(out_dir)?;
// ...
Ok(())
}
```
- **Align Client/Server Shapes:** Keep `ClientRequest` variants and handler signatures consistent (params presence, names, and serde tags).
```rust
// Protocol
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, TS)]
#[serde(tag = "method", rename_all = "camelCase")]
pub enum ClientRequest {
GetAuthStatus { #[serde(rename = "id")] request_id: RequestId },
}
// Server
match req {
ClientRequest::GetAuthStatus { request_id } => self.get_auth_status(request_id).await,
// ...
}
```
**DONTs**
- **Dont Depend `protocol` On `login`:** Avoid `codex-login` in `protocol/Cargo.toml`; move shared types into `protocol` instead.
```toml
# ❌ protocol/Cargo.toml (wrong)
[dependencies]
codex-login = { path = "../login" }
```
- **Dont Handcraft Method Strings Or Use Event Constants:** Replace stringly-typed `"codex/event/..."/LOGIN_*` patterns with typed enums.
```rust
// ❌ Avoid
outgoing.send_notification(OutgoingNotification {
method: "codex/event/login_chatgpt_complete".to_string(),
params: Some(serde_json::to_value(&payload).unwrap()),
}).await;
```
- **Dont Thread Raw CLI Override Types Downstream:** Keep `TomlValue`/override maps out of processors; pass `Arc<Config>` instead.
```rust
// ❌ Avoid
let mut processor = MessageProcessor::new(outgoing, sandbox, cli_kv_overrides);
```
- **Dont Duplicate Auth Enums Across Crates:** Use the single source of truth in `codex-protocol`.
```rust
// ❌ Avoid redefining
#[derive(Serialize, Deserialize)]
enum AuthMode { ApiKey, ChatGPT }
```
- **Dont Rely On Explicit `drop(guard)`:** End the scope to release locks predictably.
```rust
// ❌ Avoid
let mut guard = self.active_login.lock().await;
// ...
drop(guard);
```
- **Dont Invent Custom Conversion Traits:** Prefer `From`/`Into` over bespoke traits like `IntoOutgoingNotification`.
```rust
// ❌ Avoid
pub trait IntoOutgoingNotification { fn into_outgoing_notification(self) -> OutgoingNotification; }
```
- **Dont Bake In A `codex/event` Prefix:** Use serde-tagged enums (`method`/`params`) rather than string concatenation.
```rust
// ❌ Avoid
let method = format!("codex/event/{}", notification);
```
- **Dont Forget Feature-Gating TS Derives (When Needed):** If `TS` leaks into crates that shouldnt depend on `ts-rs`, gate it.
```toml
# protocol/Cargo.toml
[features]
ts = ["ts-rs"]
# protocol/src/...
#[cfg_attr(feature = "ts", derive(TS))]
```
- **Dont Let Client/Server Drift:** Avoid mismatched request params (e.g., protocol expects `params`, server ignores them).
```rust
// ❌ Avoid: protocol defines params but server handler takes none
GetAuthStatus { request_id, params: GetAuthStatusParams }
```