Rust project standard
Skill VoldemortGin/AI-Coding-Skill-Bible/rust-project-standard
Project-standard skills for AI coding agents — engineering charters (Rust / Swift / Python / TypeScript / Kotlin + polyglot core) that make agent output trustworthy by construction.
npx -y skills add VoldemortGin/AI-Coding-Skill-Bible --skill rust-project-standardAssembled from the repository path, not quoted from the project. Check it against their README if it does not work.
One thing to look at
- 1 stars1 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.
What its author says it does
Copied from the file, not written here
Enforce a strict, model-agnostic, AI-navigable Rust project standard: a Cargo workspace with crate-per-domain deep structure, #![forbid(unsafe_code)] + strict clippy workspace lints + a one-command zero-warning gate (fmt + clippy -D warnings + doc + test + cargo-deny), serde + newtypes at boundaries, trait-based provider seams with a zero-SDK domain crate and a default MockProvider, tracing, figment, include_str! + minijinja prompts, and a CLAUDE.md in every crate. Use whenever starting or scaffolding a Rust project, crate, or workspace; setting up Cargo.toml / clippy / rustfmt / CI; deciding crate or module structure; adding an LLM / embedding / vector-store dependency; wiring providers or adapters; setting up cargo-deny or supply-chain/license checks; or checking an existing Rust project conforms. Apply even when the user only says "start a Rust project", "structure this crate", "add an LLM", or "wire up CI" without naming the standard.
SKILL.md
10.9 KB, as published. Nobody here has run it
Rust Project Standard
This skill is the guiding standard for any Rust work. Apply it by default; don't wait to be asked.
Its spine: trust is placed not in the model, but in the machine-checkable code that constrains it. In Rust most of that scaffold is the compiler — types, ownership, exhaustiveness are enforced at compile time with no type erasure, so there's no runtime type-checker to bolt on (unlike the Python sibling's beartype). The job here is to (a) keep the few escape hatches shut, (b) push every external dependency behind a trait so the model is hot-swappable, (c) organize deep and name-navigable, and (d) mechanize the implicit knowledge a human would otherwise hold — via a zero-warning gate, per-crate contracts, and supply-chain checks. The agent's output ceiling equals the tightness of that loop.
Baseline: edition 2024, pinned toolchain (rust-toolchain.toml), #![forbid(unsafe_code)], strict clippy via workspace lints, serde, thiserror/anyhow, tracing, figment, minijinja, cargo-deny.
When starting a new project
python scripts/scaffold.py <project_name> --target <dir> --domains ingestion retrieval generation agents
This mirrors assets/templates/ into a Cargo workspace (root config + kernel/domain/adapters crates + app binary + a CLAUDE.md per crate + ci.sh + CI + ADR), substitutes __PROJECT__, and creates a crate skeleton per domain (members = ["crates/*", "app"] auto-includes them). When adapting an existing repo, copy from assets/templates/ by hand — only the workspace root, app/Cargo.toml, and CLAUDE.md headers carry the literal name; crate names (kernel/domain/adapters) are project-independent so use paths never depend on the project name.
Then: cargo check, and ./ci.sh (or just check) to verify. ./ci.sh needs cargo install cargo-deny once (CI uses the action).
When working on existing code
Apply the rules below, and verify structural invariants:
python scripts/check_conformance.py <project_root>
It checks the mechanically enforceable invariants (workspace layout, unsafe_code = "forbid", clippy lints configured, domain crate has zero vendor-SDK deps — parsed from its Cargo.toml, cleaner than grep, toolchain/deny/ci files present, a CLAUDE.md in every crate). Everything else is enforced by the gate and by applying the standard.
The non-negotiables
Full rationale in references/standard.md.
-
The compiler is the static guarantee; keep the escape hatches shut.
#![forbid(unsafe_code)]at the workspace level (a hard ban —#[allow]can't overrideforbid). Strict clippy via[workspace.lints]; the gate runsclippy -- -D warnings, so every warning is fatal. No bolted-on runtime type check — Rust doesn't need one. A crate that genuinely needsunsafe(FFI, SIMD) takes the controlled exit inreferences/standard.md§2 — isolate it in a dedicated crate that opts out of the workspace lint and drops todeny+ per-site// SAFETY:, rather than loosening the workspace. -
No silent failures:
Result<T, E>+thiserror, notunwrap/expectsprinkled around.unwrap_used/expect_used/panicare clippy-warn (→ error at the gate); escaping needs#[allow(...)]with a reason (the Rust analog of a justified# type: ignore).unwrap/expectare allowed in tests (clippy.toml). Vendor errors normalize at the adapter boundary intodomain::ProviderError; program bugs propagate — never swallow. -
Boundaries: parse, don't validate —
serde+ newtypes. Everything crossing a boundary (config, LLM output, tool results, files) deserializes into a strongly-typed struct viaserde; encode constraints in newtypes so invalid states are unrepresentable rather than runtime-checked. -
Model-agnostic: every external AI dependency behind a trait in
domain; SDKs only inadapters. Thedomaincrate has zero vendor-SDK dependencies (the checker parses its Cargo.toml to enforce this); traits stay object-safe (Box<dyn _>). SDKs areoptional+ feature-gated inadaptersand normalized toProviderError. The composition root (app) is the one place that selects an impl by config and injects it. A deterministic MockProvider is the default (not a test stub) so the binary, tests, and CI run offline with no SDK or key. -
Completion = one zero-warning gate, the agent's only correctness judge.
./ci.shrunscargo fmt --check→clippy -D warnings→cargo doc(withRUSTDOCFLAGS=-D warnings) →cargo test→cargo deny check, underset -euo pipefail. Run it after every change; fix until green; never "looks fine, commit". Pin it with a pre-push hook; CI mirrors it. -
cargo-denyis part of the gate. License allow-list (rejects AGPL/GPL/SSPL leaking into the tree), RUSTSEC advisories, and dependency hygiene are checked on every run — this doubles as supply-chain/SCA compliance.
Structure: deep Cargo workspace, crate-per-domain
The depth comes from the workspace, not from one crate with deep modules. "Fix the reranker" should resolve to crates/retrieval/src/reranking/ with no search.
- Workspace with a crate per bounded context (
crates/<domain>/), plus the fixed infra crates and a thin binary:kernel— cross-cutting infra:config(figment, envAPP_*+__nesting),logging(tracing; init once in the binary; library code only emits events;log_provenance+ payload-free traces),prompts(include_str!compile-time embed +minijinjastrict undefined). Namedkernel, notcore(which collides withstd::core).domain— ports (traits) + models + boundary errors; zero SDK deps.adapters— trait impls; the only crate that may depend on vendor SDKs (feature-gated);MockXdefaults.- domain crates (
ingestion/retrieval/…) depend ondomain+kernel, never onadaptersor SDKs. app— the binary and composition root; wires concrete adapters by config.
- A
CLAUDE.mdin every crate (the layered-context rule applied per directory): the root one is a routing table (hard constraints + where to look); each crate's states its responsibility, dependencies, and local contract. The checker requires one per crate. - Go deep: split crates into submodules by sub-capability; names map to paths.
Navigability
Naming-as-path is to navigation what types are to interface contracts — and Cargo workspaces make it crate-level. Group by capability, not by types//utils/. Nest until leaf modules have a single clear responsibility. See references/standard.md.
Principles for AI-touching code (advisory)
Beyond the type system — for any code where a model produces output. Upper-level discipline; not all mechanically checkable.
- Constrain, don't ask. Push non-negotiable properties (no fabrication, must-cite, no privilege escalation) into deterministic control flow so the model physically cannot violate them — don't rely on the prompt. Synthesize answers from typed values in code; discard model prose on the critical path.
- Narrow the emission surface. Make the model pick from a typed enum of options or call tools returning a tri-state result; take final values from tool results, not free-form model text. A Rust
enumis the natural controlled surface. - Guardrails are deterministic, independent, never pluggable. Intent parsing can be swapped; safety decisions are deterministic code re-evaluated from raw input.
Driving AI on big work (advisory)
Treat AI as supervisable labor, not unsupervised autopilot.
- Decision-first: a numbered, immutable ADR (
docs/adr/) before coding — context + chosen option + rejected alternatives and why. Rejected-reasons stop it re-walking excluded paths. - TDD red-light first; tests are the immutable spec. Write the failing
#[test](or doc test), then implement to green; never weaken a test to pass. - Numbered steps, each independently green; one commit per step through the full gate. No giant diffs.
- Adversarial independent review. After writing, run a separate, hostile, multi-perspective review prioritizing what tests can't cover (diagrams, docs, tradeoffs).
Scale to project size
The workspace split, per-crate CLAUDE.md, ADRs, and cargo-deny are real overhead — overkill for a 200-line CLI. Present them as triggered, scalable patterns ("the moment you call an LLM/embedding/vector store, put it behind a trait in its own crate"; "split a crate out the moment it takes a second responsibility"), not blanket mandates. A single-crate project with modules + clippy + the gate is a perfectly good small-scale instantiation.
Scale along two axes, not one — size and domain. The standard has a universal spine that holds for any Rust project whether or not it touches AI: #![forbid(unsafe_code)], the zero-warning gate, Result/thiserror (no silent failure), serde + newtypes at boundaries, workspace + deep naming-as-path, cargo-deny, tracing, figment. The rest is an AI-triggered layer that only switches on once the project actually calls an LLM / embedding / vector store: the domain trait seam, MockProvider-as-default, minijinja / include_str! prompts, log_provenance, and the constrain-don't-ask / narrow-the-emission-surface discipline. A pure systems / library / CLI crate that never calls a model should take the spine in full and skip the AI layer outright — bolting MockProvider or prompt-embedding onto such a project is cargo-culting, not conformance.
Resources
references/standard.md— the full standard with rationale and complete code for every crate.assets/templates/— exact workspace boilerplate;__PROJECT__is the only placeholder. Includeskernel/domain/adapters/app, workspace lints,clippy.toml,deny.toml,ci.sh, GH Actions, a per-crate CLAUDE.md, anddocs/adr/.scripts/scaffold.py— generate a conforming workspace.scripts/check_conformance.py— verify structural invariants (incl. domain-zero-SDK and per-crate CLAUDE.md).