The Rust standard defines Wavenumber's first Rust profiles for host-side applications and embedded firmware. It keeps the audit boundary static: dev-std validates Cargo, rustup, repository shape, metadata, and signoff wiring. Tree-sitter Rust validates owned-source structure while Cargo, rustc, rustfmt, Clippy, rustdoc, tests, and hardware signoff prove Rust semantics.
rust-appstd
library with application-owned signoff.
rust-firmwareno_std intent,
cross-compilation, target, linker, runner, panic, allocator, and hardware
signoff policy.
Returns the host-side Rust application and library profile.
Returns the embedded Rust firmware profile.
Renders the host-side Rust profile for human or machine use.
Renders the embedded Rust firmware profile for human or machine use.
Cargo.tomlCargo.lock in the audited root for applications, firmware, and workspacesrust-toolchain.toml, unless [rust.exceptions].ambient_toolchain documents an ambient stable policyclippy.toml with pinned structural-lint thresholdsrust-hygiene.toml with strict or ratcheted source-shape policysrc/ for Rust-only projects or a configured Rust source root such as src/rs/ or src/rust/tests/ and tests/rack.tomldev-std.toml or another supported dev-std config markerdocs/design/rust-standard.html, unless configured through [documentation.standard_docs]
Rust profile metadata lives under [rust]. The most common
polyglot setting is source_root:
[rust]
source_root = "src/rs"
[rust.hygiene]
config = "rust-hygiene.toml"
The configured path must be repository-relative, stay inside the project
root, and contain at least one owned .rs file. Generated,
vendored, binding, and target/ output paths are excluded from
owned-source discovery.
Documented exceptions live under [rust.exceptions]. The first
supported keys are ambient_toolchain for projects that follow
the ambient stable Rust toolchain and unsafe for reviewed
unsafe-code boundaries.
[rust.exceptions]
ambient_toolchain = "docs/design/rust-toolchain-exception.html"
unsafe = "docs/design/unsafe-boundary.html"
Greenfield rust-app and rust-firmware projects use
strict structural signoff. The canonical limits are seven parameters per
function or method, 100 physical lines per production function, 150 per
test function, 1000 per owned source file, cyclomatic complexity 10, and
control-flow nesting depth 4. These are Rust-specific review limits; they
are intentionally not copied from the larger C/C++ file and function
thresholds.
schema = 1
mode = "strict"
[limits]
max_parameters = 7
max_function_lines = 100
max_test_function_lines = 150
max_file_lines = 1000
max_cyclomatic_complexity = 10
max_nesting = 4
[paths]
source_roots = ["src", "tests"]
The dev-std scanner uses the maintained Tree-sitter Rust grammar rather than regular expressions. It recognizes free functions, methods, trait implementations, async functions, generics, attributes, nested modules, and macro token boundaries. Declarative or procedural macro expansion remains owned by rustc and Clippy because the unexpanded source tree does not contain generated function bodies. Nested functions receive their own identities and metrics; their branches and nesting do not inflate the containing function's measurements.
Source roots must cover the configured [rust].source_root and
root integration tests, and every Cargo workspace member source and test
directory. The canonical exclusions
cover generated bindings, generated/, vendor/,
third_party/, target/, and build output without
excluding adjacent package-owned Rust. Polyglot siblings are not scanned
because only .rs files below the declared Rust roots are owned.
The relaxed test-function line limit applies to functions in a
tests module, exact #[test] or namespaced
#[...::test] attributes, and exact #[cfg(test)]
test helpers. Conditions such as #[cfg(not(test))] and
#[cfg_attr(test, ...)] remain production functions and use the
100-line limit.
Clippy owns semantic lint interpretation; dev-std owns the reviewed policy
that pins thresholds and verifies failing signoff. Package lints, or root
[workspace.lints.clippy] inherited by every member, must deny
too_many_arguments, too_many_lines,
cognitive_complexity, and
allow_attributes_without_reason. The matching
clippy.toml values are:
too-many-arguments-threshold = 7
too-many-lines-threshold = 100
cognitive-complexity-threshold = 15
The Tree-sitter gate separately measures cyclomatic complexity and nesting, properties that are not delegated to an implicit Clippy default.
Existing debt uses mode = "ratchet" with a checked-in JSON
baseline. A baseline identifies path, qualified item, rule, and current
value. New violations and increases fail; resolved baseline entries remain
visible as warnings until pruned. Baselines do not weaken canonical limits.
A reviewed exception is narrower than a baseline entry and must declare a
stable id, exact path, qualified item, rule, accepted maximum value,
rationale, and concrete review trigger. An unmatched exception fails as
stale, and growth beyond the accepted maximum fails. Trait methods use the
containing trait in their qualified identity so equally named methods do
not share an exception or ratchet entry. Source-level
#[allow] attributes also need Clippy's reason syntax.
[[exceptions]]
id = "ffi-callback-v1"
path = "src/ffi/callback.rs"
item = "Callback::invoke"
rule = "max_parameters"
max_value = 8
reason = "The reviewed C ABI fixes the callback signature."
review_trigger = "Remove when ABI v2 is the minimum supported version."
Starter files live in docs/templates/rust/. Local and CI/Rack
signoff use the same failing command:
dev-std audit . --scope language. Report-only structural lanes,
missing configs, weaker thresholds, missing workspace inheritance, and
incomplete baselines or exceptions fail the audit.
Tree-sitter and its Rust grammar install as locked runtime dependencies of
wn-dev-std; consumers do not install a separate parser binary.
The scanner supports the Rust grammar shipped with that reviewed package
version and reports deterministic path, qualified item, rule, measured
value, and limit details through normal text or JSON audit output. The TOML
policy and JSON baseline contracts are published as
docs/contracts/rust_hygiene.schema.v0.json and
rust_hygiene_baseline.schema.v0.json.
Cargo.toml must define either [package] or
[workspace]. Package metadata must declare
edition and rust-version. Workspace member
packages may inherit those fields with edition.workspace = true
and rust-version.workspace = true only when
[workspace.package] defines the inherited values. Workspace
roots should centralize shared metadata with [workspace.package],
[workspace.dependencies], [workspace.lints], and
root profiles where appropriate.
Workspace member discovery follows [workspace].members and
honors bounded repository-relative [workspace].exclude entries.
Member globs are treated as package-directory globs; non-directory matches
such as notes files under crates/ are ignored.
Workspaces must declare resolver. Edition 2024 workspaces use
resolver = "3"; Edition 2021 workspaces may use
resolver = "2" or "3" when the project documents
the pairing.
rust-toolchain.toml must contain a [toolchain]
table with a stable channel, rustfmt, and clippy
components. Nightly, beta, or custom channels require a documented
exception and review trigger. Firmware profiles must include the configured
cross-compilation target unless the target is a checked-in custom target
JSON file.
Host Rust projects must set unsafe_code = "forbid" in
[lints.rust] or [workspace.lints.rust].
Workspace lints are active only for packages that opt in with
[lints] workspace = true; a member may also satisfy the policy
with a local [lints.rust] entry, but local member overrides
must still use the profile's allowed unsafe lint level.
Firmware may use "forbid" or "deny" by default,
or declare a documented [rust.exceptions].unsafe boundary for
hardware registers, generated bindings, FFI, or reviewed low-level code.
The audit validates the lint metadata and exception document; it does not
scan Rust source for unsafe blocks.
Rack or an equivalent project signoff surface must expose these host Rust lanes:
cargo fmt --all -- --checkcargo check --workspace --all-targets --lockedcargo clippy --workspace --all-targets --all-features --locked -- -D warningscargo test --workspace --lockedcargo test --doc --workspace --lockedRUSTDOCFLAGS="-D warnings" cargo doc --workspace --no-deps --lockeddev-std audit . --scope language for syntax-aware structural hygiene
Rustdoc warning failures are controlled with the
RUSTDOCFLAGS environment variable, not a Cargo flag. Cargo
commands should use --locked in CI and release-facing signoff
so dependency resolution remains deterministic. Audit checks command
markers per declared command entry; --locked on one command
does not satisfy another Cargo lane. The separate
cargo test --doc lane is required even when a plain
cargo test lane may also run doctests, because the standard
wants doctest coverage to remain visible and independently auditable when
projects later add target filters.
rust-firmware adds [rust.firmware] metadata:
[rust.firmware]
target = "thumbv7em-none-eabihf"
no_std_ref = "docs/design/rust-firmware-runtime.html"
panic_ref = "docs/design/rust-firmware-runtime.html"
allocator_ref = "docs/design/rust-firmware-runtime.html"
hardware_ref = "docs/setup.html"
memory_layout = "memory.x"
runner = "Embed.toml"
The audit treats no_std as metadata and documentation. It
does not inspect Rust source for #![no_std]. Firmware
projects must declare the target triple or custom target spec,
.cargo/config.toml, target/rustflags/runner policy, memory or
linker artifacts such as memory.x or a documented
link.x provider, panic strategy, allocator policy, hardware
setup, flashing/debugging workflow, and host-test split.
Embedded signoff should include:
cargo fmt --all -- --checkcargo check --target <target-triple-or-json> --lockedcargo clippy --target <target-triple-or-json> --locked -- -D warningscargo build --release --target <target-triple-or-json> --lockedcargo test --workspace --locked and cargo test --doc --workspace --locked for host-testable cratesRUSTDOCFLAGS="-D warnings" cargo doc --workspace --no-deps --lockedcargo embed, probe-rs run, cargo-embed, or a project wrapper
Firmware repositories that are pure no_std binaries must
still provide a host-buildable crate, feature split, or wrapper surface for
the host cargo test, doctest, and rustdoc lanes. Board-only
code remains covered by the target build, Clippy target lane, and hardware
runner.
Tokio is the recommended default for host-side async applications that need async I/O, networking, timers, scheduling, or service runtime facilities. Embassy is the recommended default for embedded async firmware when target and HAL support are acceptable. These are not hard dependencies in the first slice. Projects that stay synchronous, use a framework-owned runtime, use RTIC, or rely on a vendor or RTOS scheduler document the runtime choice in setup or architecture docs.
Contract-first repositories may keep shared schemas, fixtures, docs, and
test cases at the umbrella root while language implementations live under
src/<language>/. Rust model code may live under
src/rs/data_models/<domain>/ or
src/rust/data_models/<domain>/. Rust transforms may use
operation-first roots such as
src/rs/transforms/importers/<namespace>/<domain>/,
src/rs/transforms/exporters/<namespace>/<domain>/,
src/rs/transforms/projections/<source-domain>_to_<target-domain>/,
and src/rs/transforms/migrations/<domain>/.
Generated Rust bindings or schema copies must be isolated under generated roots and governed by artifact/vendor policy instead of counted as owned implementation source.