OpenAI Codex Rust CLI Agent Best Practices
This skill teaches you to write Rust code in the style of the OpenAI Codex codebase - a production CLI/agent system with 50 crates and 787 Rust files.
Key Characteristics
-
Edition 2024 with strict Clippy configuration
-
Zero unwrap/expect in non-test code (enforced at workspace level)
-
Tokio async runtime with proper Send + Sync bounds
-
thiserror for library errors, anyhow for application code
-
Flat workspace structure with centralized dependencies
When to Apply
Apply this skill when:
-
Building CLI tools or agent systems in Rust
-
Writing async Rust with Tokio
-
Designing Rust workspace organization
-
Implementing error handling patterns
-
Working on production Rust codebases
Quick Reference
Critical Rules (Must Follow)
Rule Description
err-no-unwrap Never use unwrap() in non-test code
err-no-expect Avoid expect() in library code
err-thiserror-domain Use thiserror for domain errors
err-context-chain Add context to errors with .context()
Error Handling
Rule Description
err-anyhow-application Use anyhow::Result for entry points
err-from-derive Use #[from] for error conversion
err-transparent Use #[error(transparent)] for wrapped errors
err-structured-variants Include relevant data in error variants
err-io-result Use std::io::Result for I/O functions
err-map-err-conversion Use map_err for error conversion
err-doc-errors Document error conditions
Organization
Rule Description
org-workspace-flat Flat workspace with utils subdirectory
org-crate-naming kebab-case directories, project prefix
org-module-visibility Use pub(crate) for internal APIs
org-test-common-crate Shared test utilities crate
org-integration-tests-suite Tests in suite directory
org-feature-modules Feature-based module organization
org-handlers-subdir Handlers in dedicated subdirectory
org-errors-file Errors in dedicated file
Component Patterns
Rule Description
mod-derive-order Consistent derive macro ordering
mod-async-trait-macro Use #[async_trait] for async traits
mod-trait-bounds Send + Sync + 'static for concurrent traits
mod-extension-trait-suffix Ext suffix for extension traits
mod-builder-pattern Builder pattern for complex config
mod-type-alias-complex Type aliases for complex generics
mod-impl-block-order Consistent impl block ordering
mod-generic-constraints Where clauses for complex bounds
mod-newtype-pattern Newtypes for type safety
mod-struct-visibility Private fields with public constructor
mod-serde-rename Serde rename for wire format
mod-jsonschema-derive JsonSchema for API types
Naming Conventions
Rule Description
name-async-no-suffix No _async suffix for async functions
name-try-prefix-fallible try_ prefix for fallible constructors
name-with-prefix-builder with_ prefix for builder methods
name-handler-suffix Handler suffix for handlers
name-error-suffix Error suffix for error types
name-result-type-alias Crate-specific Result alias
name-const-env-var _ENV_VAR suffix for env constants
name-request-response Request/Response type pairing
name-options-suffix Options suffix for config bundles
name-info-suffix Info suffix for read-only data
name-provider-suffix Provider suffix for services
name-client-suffix Client suffix for API clients
name-manager-suffix Manager suffix for lifecycle mgmt
name-bool-is-prefix is_/has_/should_ for booleans
name-plural-collections Plural names for collections
Style
Rule Description
style-import-granularity One item per use statement
style-deny-stdout Deny stdout/stderr in libraries
style-inline-format-args Inline format arguments
style-module-docs Module-level documentation
style-expect-reason #[expect] with reason for lints
style-cfg-test-module Unit tests in mod tests
Cross-Crate
Rule Description
cross-workspace-lints Workspace-level lint config
cross-workspace-deps Centralized dependency versions
Example: Proper Error Handling
use thiserror::Error; use anyhow::Context;
// Domain error with thiserror #[derive(Debug, Error)] pub enum ConfigError { #[error("failed to read config file: {path}")] ReadFailed { path: PathBuf, #[source] source: std::io::Error, },
#[error(transparent)]
Parse(#[from] toml::de::Error),
}
// Library function returns domain error pub fn load_config(path: &Path) -> Result<Config, ConfigError> { let content = fs::read_to_string(path) .map_err(|source| ConfigError::ReadFailed { path: path.to_owned(), source, })?; toml::from_str(&content).map_err(Into::into) }
// Application code uses anyhow with context fn main() -> anyhow::Result<()> { let config = load_config(Path::new("config.toml")) .context("failed to load configuration")?; run(config).await }
Source
Patterns extracted from OpenAI Codex (codex-rs/ subdirectory) - a production Rust codebase with 50 crates and 787 Rust files.