Rust project standard
Skill VoldemortGin/AI-Coding-Skill-Bible/rust-project-standard
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.From its SKILL.md
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.
3 things to look at
- skips confirmationTells the agent to proceed without asking first, 1 time: "Apply it by default; don't wait to be asked.".
- 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.
- runs commandsInstructs the agent to run 5 commands, including `python scripts/scaffold.py <project_name> --target <dir> --domains ingestion retrieval generation agents` and 4 more.
SKILL.md
10.9 KB, ~2.5k tokens by cl100k_base, 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).
What ships with it: 36 files
41.3 KB alongside SKILL.md, 3 of them executable
assets/
- templates/app/Cargo.toml370 B
- templates/app/CLAUDE.md413 B
- templates/app/src/main.rs920 B
- templates/Cargo.toml1.1 KB
- templates/ci.shruns509 B
- templates/CLAUDE.md1.5 KB
- templates/clippy.toml86 B
- templates/configs/settings.toml184 B
- templates/crates/adapters/Cargo.toml391 B
- templates/crates/adapters/CLAUDE.md426 B
- templates/crates/adapters/src/lib.rs160 B
- templates/crates/adapters/src/mock.rs1.7 KB
- templates/crates/adapters/src/openai.rs937 B
- templates/crates/domain/Cargo.toml346 B
- templates/crates/domain/CLAUDE.md445 B
- templates/crates/domain/src/errors.rs511 B
- templates/crates/domain/src/lib.rs233 B
- templates/crates/domain/src/models.rs323 B
- templates/crates/domain/src/ports.rs1.0 KB
- templates/crates/kernel/Cargo.toml277 B
- templates/crates/kernel/CLAUDE.md469 B
- templates/crates/kernel/src/config.rs1.3 KB
- templates/crates/kernel/src/lib.rs135 B
- templates/crates/kernel/src/logging.rs822 B
- templates/crates/kernel/src/prompts/rag/answer.md153 B
- templates/crates/kernel/src/prompts.rs594 B
- templates/deny.toml896 B
- templates/docs/adr/0001-record-architecture-decisions.md446 B
- templates/.gitignore24 B
- templates/justfile213 B
- templates/README.md232 B
- templates/rustfmt.toml17 B
- templates/rust-toolchain.toml64 B
references/
- standard.md17.2 KB
scripts/
- check_conformance.pyruns3.5 KB
- scaffold.pyruns3.7 KB