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

141 lines
4.6 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 strum Display with camelCase:** Derive `Display` for `EventMsg` and set `#[strum(serialize_all = "camelCase")]` so `event.msg.to_string()` yields MCP-friendly method names.
```rust
use serde::{Deserialize, Serialize};
use strum_macros::Display;
#[derive(Debug, Clone, Deserialize, Serialize, Display)]
#[serde(tag = "type", rename_all = "snake_case")] // old schema tag values
#[strum(serialize_all = "camelCase")] // new schema method names
pub enum EventMsg {
SessionConfigured,
ShutdownComplete,
// ...
}
```
- **Emit both schemas during transition:** Send the legacy `"codex/event"` and the new per-event method to stay backward compatible.
```rust
let params = Some(serde_json::to_value(event)?);
// Old schema
self.sender.send(OutgoingMessage::Notification(OutgoingNotification {
method: "codex/event".to_string(),
params: params.clone(),
})).await.ok();
// New schema (method from enum Display)
self.sender.send(OutgoingMessage::Notification(OutgoingNotification {
method: event.msg.to_string(), // e.g., "sessionConfigured"
params,
})).await.ok();
```
- **Parse both in tests and assert consistency:** Accept either method and ensure the same `session_id` is seen in both.
```rust
let mut sid_old: Option<String> = None;
let mut sid_new: Option<String> = None;
if let JSONRPCMessage::Notification(n) = message {
if let Some(p) = n.params {
if n.method == "codex/event" {
if p.get("msg")
.and_then(|m| m.get("type")).and_then(|t| t.as_str())
== Some("session_configured")
{
sid_old = p.get("msg")
.and_then(|m| m.get("session_id"))
.and_then(|v| v.as_str())
.map(|s| s.to_string());
}
}
if n.method == "sessionConfigured" {
sid_new = p.get("msg")
.and_then(|m| m.get("session_id"))
.and_then(|v| v.as_str())
.map(|s| s.to_string());
}
}
}
if sid_old.is_some() && sid_new.is_some() {
assert_eq!(sid_old.as_ref().unwrap(), sid_new.as_ref().unwrap());
}
```
- **Use a single source of truth for method names:** Always call `event.msg.to_string()` instead of repeating string literals.
```rust
let method = event.msg.to_string(); // good
// let method = "sessionConfigured".to_string(); // avoid
```
- **Override per-variant names when needed:** Use strum variant attributes to pin specific spellings.
```rust
use strum_macros::Display;
#[derive(Display)]
#[strum(serialize_all = "camelCase")]
enum EventMsg {
#[strum(serialize = "sessionReady")] // custom override
SessionConfigured,
ToolRegistered,
}
```
- **Make the new params shape explicit:** Prefer typed params for the new schema to make intent clear.
```rust
use serde::Serialize;
#[derive(Serialize)]
struct SessionConfiguredParams<'a> {
session_id: &'a str,
}
// Later
self.sender.send(OutgoingMessage::Notification(OutgoingNotification {
method: "sessionConfigured".into(),
params: Some(serde_json::to_value(SessionConfiguredParams { session_id })?),
})).await.ok();
```
**DONTs**
- **Dont use lowercase method names:** Avoid `"sessionconfigured"`. MCP favors camelCase like `"sessionConfigured"`.
```rust
// BAD
let method = "sessionconfigured";
// GOOD
let method = "sessionConfigured";
```
- **Dont hand-roll fmt::Display impls:** Use `#[derive(Display)]` from strum instead of custom `impl fmt::Display`.
```rust
// Avoid manual impl; derive instead
#[derive(Display)]
#[strum(serialize_all = "camelCase")]
enum EventMsg { /* ... */ }
```
- **Dont collapse schemas into one ambiguous path:** Keep legacy `"codex/event"` and new per-event methods distinct; dont replace one with the other prematurely.
```rust
// Avoid: only emitting "codex/event" or only the new method during transition
// Do: emit both until consumers migrate
```
- **Dont assume params stay the same:** The new schema may change `params`. Dont lock into `serde_json::Value` forever—introduce typed structs as they stabilize.
```rust
// Avoid long-term:
let params: Option<serde_json::Value> = Some(serde_json::to_value(event)?);
// Prefer:
#[derive(Serialize)] struct ShutdownCompleteParams;
```
- **Dont duplicate magic strings in code/tests:** Dont repeat method names in multiple places; derive from the enum to prevent drift.
```rust
assert_eq!(event.msg.to_string(), "sessionConfigured"); // one reference
```
- **Dont introduce incidental changes:** Avoid unrelated edits (formatting, stray removals). Keep diffs focused so reviewers can disambiguate intentional protocol changes.