/SKILL.md
Comprehensive Rust coding guidelines with 265 rules across 26 categories. Use when writing, reviewing, or refactoring Rust code. Covers ownership, error handling, async patterns, concurrency, unsafe code, API design, memory optimization, performance, numeric safety, conversions, serde, pattern matching, macros, closures, observability, testing, and common anti-patterns. Invoke with /rust-skills.
npx skillsauth add leonardomso/rust-skills rust-skillsInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Comprehensive guide for writing high-quality, idiomatic, and highly optimized Rust code. Contains 265 rules across 26 categories, prioritized by impact to guide LLMs in code generation and refactoring. Current for Rust 1.96 (2024 edition).
Reference these guidelines when:
unsafe code| Priority | Category | Impact | Prefix | Rules |
|----------|----------|--------|--------|-------|
| 1 | Ownership & Borrowing | CRITICAL | own- | 12 |
| 2 | Error Handling | CRITICAL | err- | 12 |
| 3 | Memory Optimization | CRITICAL | mem- | 17 |
| 4 | Unsafe Code | CRITICAL | unsafe- | 7 |
| 5 | API Design | HIGH | api- | 17 |
| 6 | Async/Await | HIGH | async- | 18 |
| 7 | Concurrency | HIGH | conc- | 4 |
| 8 | Compiler Optimization | HIGH | opt- | 12 |
| 9 | Numeric & Arithmetic Safety | HIGH | num- | 5 |
| 10 | Type Safety | MEDIUM | type- | 13 |
| 11 | Trait & Generics Design | MEDIUM | trait- | 6 |
| 12 | Conversions | MEDIUM | conv- | 3 |
| 13 | Const & Compile-Time | MEDIUM | const- | 4 |
| 14 | Serde | MEDIUM | serde- | 8 |
| 15 | Pattern Matching | MEDIUM | pat- | 5 |
| 16 | Macros | MEDIUM | macro- | 8 |
| 17 | Closures | MEDIUM | closure- | 5 |
| 18 | Collections | MEDIUM | coll- | 4 |
| 19 | Naming Conventions | MEDIUM | name- | 16 |
| 20 | Testing | MEDIUM | test- | 15 |
| 21 | Documentation | MEDIUM | doc- | 12 |
| 22 | Observability | MEDIUM | obs- | 7 |
| 23 | Performance Patterns | MEDIUM | perf- | 13 |
| 24 | Project Structure | LOW | proj- | 14 |
| 25 | Clippy & Linting | LOW | lint- | 13 |
| 26 | Anti-patterns | REFERENCE | anti- | 15 |
own-borrow-over-clone - Prefer &T borrowing over .clone()own-slice-over-vec - Accept &[T] not &Vec<T>, &str not &Stringown-cow-conditional - Use Cow<'a, T> for conditional ownershipown-arc-shared - Use Arc<T> for thread-safe shared ownershipown-rc-single-thread - Use Rc<T> for shared ownership in single-threaded contextsown-refcell-interior - Use RefCell<T> for interior mutability in single-threaded codeown-mutex-interior - Use Mutex<T> for interior mutability across threadsown-rwlock-readers - Use RwLock<T> when reads significantly outnumber writesown-copy-small - Implement Copy for small, simple typesown-clone-explicit - Use explicit Clone for types where copying has meaningful costown-move-large - Move large types instead of copying; use Box if moves are expensiveown-lifetime-elision - Rely on lifetime elision rules; add explicit lifetimes only when requirederr-thiserror-lib - Use thiserror for library error typeserr-anyhow-app - Use anyhow for application error handlingerr-result-over-panic - Return Result<T, E> instead of panicking for recoverable errorserr-context-chain - Add context with .context() or .with_context()err-no-unwrap-prod - Avoid unwrap() in production code; use ?, expect(), or handle errorserr-expect-bugs-only - Use expect() only for invariants that indicate bugs, not user errorserr-question-mark - Use ? operator for clean propagationerr-from-impl - Implement From<E> for error conversions to enable ? operatorerr-source-chain - Preserve error chains with #[source] or source() methoderr-lowercase-msg - Start error messages lowercase, no trailing punctuationerr-doc-errors - Document error conditions with # Errors section in doc commentserr-custom-type - Define custom error types for domain-specific failuresmem-with-capacity - Use with_capacity() when size is knownmem-smallvec - Use SmallVec for usually-small collectionsmem-arrayvec - Use ArrayVec<T, N> for fixed-capacity collections that never heap-allocatemem-box-large-variant - Box large enum variants to reduce overall enum sizemem-boxed-slice - Use Box<[T]> instead of Vec<T> for fixed-size heap datamem-thinvec - Use ThinVec<T> for nullable collections with minimal overheadmem-clone-from - Use clone_from() to reuse allocations when repeatedly cloningmem-reuse-collections - Clear and reuse collections instead of creating new ones in loopsmem-avoid-format - Avoid format!() when string literals workmem-write-over-format - Use write!() into existing buffers instead of format!() allocationsmem-arena-allocator - Use arena allocators for batch allocationsmem-zero-copy - Use zero-copy patterns with slices and Bytesmem-compact-string - Use compact string types for memory-constrained string storagemem-smaller-integers - Use appropriately-sized integers to reduce memory footprintmem-assert-type-size - Use static assertions to guard against accidental type size growthmem-take-replace - Use mem::take / mem::replace to move a value out of a &mut without cloningmem-drop-order - Know and control drop order: struct fields drop top-to-bottom, locals in reverseunsafe-safety-comment - Write a // SAFETY: comment above every unsafe block and a # Safety section in every unsafe fn.unsafe-minimize-scope - Keep unsafe blocks as small as possible — mark only the operation that requires unsafety, not the surrounding safe code.unsafe-miri-ci - Run cargo miri test in CI for every crate that contains unsafe code.unsafe-maybeuninit - Use MaybeUninit<T> for uninitialized memory; never use mem::uninitialized() or mem::zeroed() for types with validity invariants.unsafe-extern-block - In Rust 2024, wrap extern blocks in unsafe extern { } and annotate each item as safe or unsafe.unsafe-send-sync-manual - Document the invariants when manually implementing Send or Sync; prefer letting the compiler derive them automatically.unsafe-no-mangle-unsafe - In Rust 2024, write #[unsafe(no_mangle)], #[unsafe(export_name = "...")], and #[unsafe(link_section = "...")] — not the bare attribute forms.api-builder-pattern - Use Builder pattern for complex constructionapi-builder-must-use - Mark builder methods with #[must_use] to prevent silent dropsapi-newtype-safety - Use newtypes to prevent mixing semantically different valuesapi-typestate - Use typestate pattern to encode state machine invariants in the type systemapi-sealed-trait - Use sealed traits to prevent external implementations while allowing useapi-extension-trait - Use extension traits to add methods to external typesapi-parse-dont-validate - Parse into validated types at boundariesapi-impl-into - Accept impl Into<T> for flexible APIs, implement From<T> for conversionsapi-impl-asref - Use AsRef<T> when you only need to borrow the inner dataapi-must-use - Mark types and functions with #[must_use] when ignoring results is likely a bugapi-non-exhaustive - Use #[non_exhaustive] on public enums and structs for forward compatibilityapi-from-not-into - Implement From<T>, not Into<U> - From gives you Into for freeapi-default-impl - Implement Default for types with sensible default valuesapi-common-traits - Implement standard traits (Debug, Clone, PartialEq, etc.) for public typesapi-serde-optional - Make serde a feature flag, not a hard dependency for library cratesapi-impl-fromiterator - Implement FromIterator and Extend for collection types, and IntoIterator for all three reference formsapi-operator-overload - Overload operators only when the semantics are natural and unsurprisingasync-tokio-runtime - Configure Tokio runtime appropriately for your workloadasync-no-lock-await - Never hold Mutex/RwLock across .awaitasync-spawn-blocking - Use spawn_blocking for CPU-intensive workasync-tokio-fs - Use tokio::fs instead of std::fs in async codeasync-cancellation-token - Use CancellationToken for graceful shutdown and task cancellationasync-join-parallel - Use join! or try_join! for concurrent independent futuresasync-try-join - Use try_join! for concurrent fallible operations with early return on errorasync-select-racing - Use select! to race futures and handle the first to completeasync-bounded-channel - Use bounded channels to apply backpressure and prevent unbounded memory growthasync-mpsc-queue - Use mpsc channels for async message queues between tasksasync-broadcast-pubsub - Use broadcast channel for pub/sub where all subscribers receive all messagesasync-watch-latest - Use watch channel for sharing the latest value with multiple observersasync-oneshot-response - Use oneshot channel for request-response patternsasync-joinset-structured - Use JoinSet for managing dynamic collections of spawned tasksasync-clone-before-await - Clone Arc/Rc data before await points to avoid holding references across suspensionasync-fn-in-trait - Use native async fn in traits (stable 1.75) instead of the async_trait macroasync-async-fn-bounds - Use AsyncFn/AsyncFnMut/AsyncFnOnce bounds instead of F: Fn() -> Fut, Fut: Futureasync-cancel-safety - Ensure futures used in tokio::select! branches are cancellation-safeconc-rayon-par-iter - Use rayon's par_iter() for CPU-bound data parallelismconc-scoped-threads - Use std::thread::scope to borrow stack data across threadsconc-atomic-ordering - Use the weakest correct memory Ordering for every atomic operationconc-thread-local - Prefer thread_local! with Cell/RefCell over static mutopt-inline-small - Use #[inline] for small hot functionsopt-inline-always-rare - Use #[inline(always)] sparingly—only for critical hot paths proven by profilingopt-inline-never-cold - Use #[inline(never)] and #[cold] for error paths and rarely-executed codeopt-cold-unlikely - Mark unlikely code paths with #[cold] to help compiler optimizationopt-likely-hint - Use code structure to hint at likely branches; use intrinsics on nightlyopt-lto-release - Enable LTO in release buildsopt-codegen-units - Set codegen-units = 1 for maximum optimization in release buildsopt-pgo-profile - Use Profile-Guided Optimization (PGO) for maximum performanceopt-target-cpu - Use target-cpu=native for maximum performance on known deployment targetsopt-bounds-check - Use iterators and patterns that eliminate bounds checks in hot pathsopt-simd-portable - Use portable SIMD for vectorized operations across architecturesopt-cache-friendly - Organize data for cache-efficient access patternsnum-overflow-explicit - Handle integer overflow explicitly: checked_/saturating_/wrapping_/overflowing_num-cast-try-from - Avoid as for narrowing casts; use From for widening and TryFrom for narrowingnum-float-compare - Don't compare floats with ==; use a tolerance, and total_cmp for orderingnum-saturating-clamp - Bound values with clamp and saturating arithmeticnum-nonzero - Use NonZero* types to forbid zero and unlock the niche optimizationtype-newtype-ids - Wrap IDs in newtypes: UserId(u64)type-newtype-validated - Use newtypes to enforce validation at construction timetype-enum-states - Use enums for mutually exclusive statestype-option-nullable - Use Option<T> for values that might not existtype-result-fallible - Use Result<T, E> for operations that can failtype-phantom-marker - Use PhantomData to express type relationships without runtime costtype-never-diverge - Use ! (never type) for functions that never returntype-generic-bounds - Add trait bounds only where needed, prefer where clauses for readabilitytype-no-stringly - Avoid stringly-typed APIs; use enums, newtypes, or validated typestype-repr-transparent - Use #[repr(transparent)] for newtypes in FFI contextstype-deref-coercion - Implement Deref/DerefMut only for smart-pointer and transparent wrapper typestype-display-vs-debug - Use Display for user-facing output and Debug for diagnostics; never swap themtype-numeric-fmt - Implement LowerHex, UpperHex, Octal, and Binary for numeric newtypestrait-associated-type-vs-generic - Use an associated type when each impl has exactly one output type; use a generic parameter when a type can implement the trait for many input typestrait-blanket-impl - Use a blanket impl impl<T: Bound> Trait for T to give behaviour to every type that satisfies a boundtrait-coherence-newtype - Respect the orphan rule; wrap a foreign type in a newtype to implement a foreign trait on ittrait-default-methods - Define a trait in terms of a few required methods plus defaulted ones built on top of themtrait-dyn-vs-generic - Choose static dispatch (generics / impl Trait) vs dynamic dispatch (dyn Trait) deliberatelytrait-object-safety - Keep a trait dyn-compatible (object-safe) when you need dyn Traitconv-tryfrom-fallible - Implement TryFrom for fallible conversions instead of ad-hoc conversion functionsconv-fromstr-parsing - Implement FromStr to enable str::parse for string-to-type conversionsconv-asmut-mutable - Accept impl AsMut<T> for flexible mutable borrowed inputs instead of concrete mutable referencesconst-block - Use inline const { } blocks for compile-time evaluation and assertionsconst-fn - Make functions const fn when they can run at compile timeconst-generics - Parameterize over values with const generics <const N: usize>const-vs-static - Use const for an inlined value and static for a single addressed instanceserde-rename-all - Match the external naming convention with #[serde(rename_all = ...)]serde-default-compat - Use #[serde(default)] for optional and backward-compatible fieldsserde-skip-empty - Omit empty fields with skip_serializing_ifserde-flatten - Inline nested structs or capture extra keys with #[serde(flatten)]serde-enum-representation - Choose enum tagging deliberately: externally, internally, adjacently tagged, or untaggedserde-deny-unknown-fields - Reject unexpected keys with #[serde(deny_unknown_fields)]serde-custom-with - Customize a field's (de)serialization with with / serialize_with / deserialize_withserde-try-from-validate - Validate while deserializing with #[serde(try_from = "Raw")]pat-let-else - Use let ... else for early-return pattern extractionpat-matches-macro - Use matches!() for boolean pattern testspat-if-let-chains - Use if let chains to combine pattern bindings and conditionspat-exhaustive-enum - Match owned enums exhaustively; avoid catch-all _ that hides new variantspat-at-bindings - Use @ bindings to capture a value while matching it against a patternmacro-prefer-functions - Reach for a macro only when a function or generic cannot express itmacro-rules-hygiene - Rely on macro_rules! hygiene and use $crate for paths to your crate's itemsmacro-fragment-specifiers - Capture with precise fragment specifiers, not raw :tt, where you canmacro-export-crate-path - Export declarative macros with #[macro_export] and a clean import pathmacro-private-helpers - Hide macro-generated helper items behind a #[doc(hidden)] pub mod __privatemacro-proc-two-crate - Put procedural macros in a dedicated proc-macro = true crate and re-export from the facademacro-proc-syn-quote - Build procedural macros with syn, quote, and proc-macro2macro-proc-error-spans - Report proc-macro errors as spanned compile errors, never by panickingclosure-fn-trait-bounds - Require the least restrictive Fn trait a callback needs (FnOnce ⊇ FnMut ⊇ Fn)closure-impl-fn-return - Return closures as impl Fn/FnMut/FnOnce, not Box<dyn Fn>closure-move-capture - Use move for closures that outlive the current scope; clone before move to keep the originalclosure-static-vs-dyn - Accept impl Fn (generic) for hot callbacks; use &dyn Fn/Box<dyn Fn> to cut code size or to store themclosure-disjoint-capture - Capture only what you use; lean on edition-2021 disjoint closure capturescoll-binaryheap - Use BinaryHeap for a priority queue or repeated max-extractioncoll-map-choice - Pick the map by access pattern: HashMap (fast, unordered), BTreeMap (sorted / range queries), IndexMap (insertion order)coll-seq-choice - Default to Vec; use VecDeque for queue/deque behaviour; avoid LinkedListcoll-set-membership - Use HashSet/BTreeSet for membership tests and dedup, not linear Vec::containsname-types-camel - Use UpperCamelCase for types, traits, and enum namesname-variants-camel - Use UpperCamelCase for enum variantsname-funcs-snake - Use snake_case for functions, methods, variables, and modulesname-consts-screaming - Use SCREAMING_SNAKE_CASE for constants and staticsname-lifetime-short - Use short, conventional lifetime names: 'a, 'b, 'de, 'srcname-type-param-single - Use single uppercase letters for type parameters: T, E, K, Vname-as-free - as_ prefix: free reference conversionname-to-expensive - Use to_ prefix for expensive conversions that allocate or computename-into-ownership - Use into_ prefix for ownership-consuming conversionsname-no-get-prefix - Omit get_ prefix for simple gettersname-is-has-bool - Use is_, has_, can_, should_ prefixes for boolean-returning methodsname-iter-convention - Use iter/iter_mut/into_iter for iterator methodsname-iter-method - Name iterator methods iter(), iter_mut(), and into_iter() consistentlyname-iter-type-match - Name iterator types after their source methodname-acronym-word - Treat acronyms as words in identifiers: HttpServer, not HTTPServername-crate-no-rs - Don't suffix crate names with -rs or -rusttest-cfg-test-module - Put unit tests in #[cfg(test)] mod tests { } within each moduletest-use-super - Use use super::*; in test modules to access parent module itemstest-integration-dir - Put integration tests in the tests/ directorytest-descriptive-names - Use descriptive test names that explain what is being testedtest-arrange-act-assert - Structure tests with clear Arrange, Act, Assert sectionstest-proptest-properties - Use proptest for property-based testingtest-mockall-mocking - Use mockall for trait mockingtest-mock-traits - Use traits for dependencies to enable mocking in teststest-fixture-raii - Use RAII pattern (Drop trait) for automatic test cleanuptest-tokio-async - Use #[tokio::test] for async teststest-should-panic - Use #[should_panic] to test that code panics as expectedtest-criterion-bench - Use criterion for benchmarkingtest-doctest-examples - Keep documentation examples as executable docteststest-loom-concurrency - Use loom to exhaustively test lock-free and concurrent codetest-snapshot-testing - Use snapshot testing (insta) for complex or serialized outputdoc-all-public - Document all public items with /// doc commentsdoc-module-inner - Use //! for module-level documentationdoc-examples-section - Include # Examples with runnable codedoc-errors-section - Include # Errors section for fallible functionsdoc-panics-section - Include # Panics section for functions that can panicdoc-safety-section - Include # Safety section for unsafe functionsdoc-question-mark - Use ? in examples, not .unwrap()doc-hidden-setup - Use # prefix to hide example setup codedoc-intra-links - Use intra-doc links to reference types and itemsdoc-link-types - Use intra-doc links to connect related types and functionsdoc-cargo-metadata - Fill Cargo.toml metadata for published cratesdoc-crate-readme - Unify the README and crate root docs with #![doc = include_str!("../README.md")]obs-tracing-over-log - Use tracing for structured, span-aware diagnostics instead of println! or bare logobs-library-facade - Libraries emit through the tracing/log facade and never install a subscriberobs-structured-fields - Record structured key-value fields, not values interpolated into the message stringobs-instrument-spans - Use #[tracing::instrument] and spans to attach context to async tasks and requestsobs-levels-filter - Use log levels meaningfully and filter with EnvFilter / RUST_LOGobs-error-chain - Log errors with their full source chain, and log each error exactly onceobs-no-sensitive-data - Never log secrets or PII; redact or skip themperf-iter-over-index - Prefer iterators over manual indexingperf-iter-lazy - Keep iterators lazy, collect only when neededperf-collect-once - Don't collect intermediate iteratorsperf-entry-api - Use entry API for map insert-or-updateperf-drain-reuse - Use drain to reuse allocationsperf-extend-batch - Use extend for batch insertionsperf-chain-avoid - Avoid chain in hot loopsperf-collect-into - Use collect_into for reusing containersperf-black-box-bench - Use black_box in benchmarksperf-release-profile - Optimize release profile settingsperf-profile-first - Profile before optimizingperf-ahash - Use a faster hasher (ahash / FxHashMap) when DoS resistance is not neededperf-io-buffering - Wrap Read/Write in BufReader/BufWriter for many small operationsproj-lib-main-split - Keep main.rs minimal, logic in lib.rsproj-mod-by-feature - Organize modules by feature, not typeproj-flat-small - Keep small projects flatproj-mod-rs-dir - Use mod.rs for multi-file modulesproj-pub-crate-internal - Use pub(crate) for internal APIsproj-pub-super-parent - Use pub(super) for parent-only visibilityproj-pub-use-reexport - Use pub use for clean public APIproj-prelude-module - Create prelude module for common importsproj-bin-dir - Put multiple binaries in src/bin/proj-workspace-large - Use workspaces for large projectsproj-workspace-deps - Use workspace dependency inheritance for consistent versions across cratesproj-feature-additive - Design Cargo features to be strictly additiveproj-msrv-declare - Declare rust-version (MSRV) in Cargo.toml and test it in CIproj-build-rs-minimal - Keep build.rs minimal, deterministic, and idempotentlint-deny-correctness - #![deny(clippy::correctness)]lint-warn-suspicious - Enable clippy::suspicious for likely bugslint-warn-style - Enable clippy::style for idiomatic codelint-warn-complexity - Enable clippy::complexity for simpler codelint-warn-perf - Enable clippy::perf for performance improvementslint-pedantic-selective - Enable clippy::pedantic selectivelylint-missing-docs - Warn on missing documentation for public itemslint-unsafe-doc - Require documentation for unsafe blockslint-cargo-metadata - Enable clippy::cargo for published crateslint-rustfmt-check - Run cargo fmt --check in CIlint-workspace-lints - Configure lints at workspace level for consistent enforcementlint-cfg-check - Enable unexpected_cfgs and declare known cfgs to catch feature-gate typoslint-clippy-nursery-selected - Enable high-value clippy::nursery lints selectively, not the whole groupanti-unwrap-abuse - Don't use .unwrap() in production codeanti-expect-lazy - Don't use expect for recoverable errorsanti-clone-excessive - Don't clone when borrowing worksanti-lock-across-await - Don't hold locks across await pointsanti-string-for-str - Don't accept &String when &str worksanti-vec-for-slice - Don't accept &Vec<T> when &[T] worksanti-index-over-iter - Don't use indexing when iterators workanti-panic-expected - Don't panic on expected or recoverable errorsanti-empty-catch - Don't silently ignore errorsanti-over-abstraction - Don't over-abstract with excessive genericsanti-premature-optimize - Don't optimize before profilinganti-type-erasure - Don't use Box<dyn Trait> when impl Trait worksanti-format-hot-path - Don't use format! in hot pathsanti-collect-intermediate - Don't collect intermediate iteratorsanti-stringly-typed - Don't use strings where enums or newtypes would provide type safety[profile.release]
opt-level = 3
lto = "fat"
codegen-units = 1
panic = "abort"
strip = true
[profile.bench]
inherits = "release"
debug = true
strip = false
[profile.dev]
opt-level = 0
debug = true
[profile.dev.package."*"]
opt-level = 3 # Optimize dependencies in dev
This skill provides rule identifiers for quick reference. When generating or reviewing Rust code:
rules/ for detailed examples| Task | Primary Categories |
|------|-------------------|
| New function | own-, err-, name-, pat- |
| New struct/API | api-, type-, conv-, doc- |
| Async code | async-, own- |
| Concurrency / parallelism | conc-, async-, own- |
| Unsafe code | unsafe-, type-, test- |
| Error handling | err-, api-, pat- |
| Type conversions | conv-, api- |
| Serialization (serde) | serde-, type-, api- |
| Numeric / arithmetic | num-, type- |
| Macros / code generation | macro-, anti- |
| Closures / callbacks | closure-, type- |
| Logging / observability | obs-, err- |
| Memory optimization | mem-, own-, perf- |
| Performance tuning | opt-, mem-, perf- |
| Code review | anti-, lint- |
This skill is an independent synthesis of official Rust guidance, well-known books, and patterns from widely-used crates. It is not affiliated with or endorsed by the Rust project or any crate author; the text and code examples are original.
Official Rust documentation
Books & guides
Tooling
Real-world codebases studied for idioms
This project is MIT-licensed. Referenced upstream materials remain under their own licenses (the official Rust docs and API Guidelines are dual MIT / Apache-2.0).
development
Maintainer-only workflow for handling GitHub Secret Scanning alerts on OpenClaw. Use when Codex needs to triage, redact, clean up, and resolve secret leakage found in issue comments, issue bodies, PR comments, or other GitHub content.
development
Maintainer workflow for OpenClaw releases, prereleases, changelog release notes, and publish validation. Use when Codex needs to prepare or verify stable or beta release steps, align version naming, assemble release notes, check release auth requirements, or validate publish-time commands and artifacts.
development
Run, watch, debug, and extend OpenClaw QA testing with qa-lab and qa-channel. Use when Codex needs to execute the repo-backed QA suite, inspect live QA artifacts, debug failing scenarios, add new QA scenarios, or explain the OpenClaw QA workflow. Prefer the live OpenAI lane with regular openai/gpt-5.4 in fast mode; do not use gpt-5.4-pro or gpt-5.4-mini unless the user explicitly overrides that policy.
development
End-to-end Parallels smoke, upgrade, and rerun workflow for OpenClaw across macOS, Windows, and Linux guests. Use when Codex needs to run, rerun, debug, or interpret VM-based install, onboarding, gateway smoke tests, latest-release-to-main upgrade checks, fresh snapshot retests, or optional Discord roundtrip verification under Parallels.