Skip to content

Rustfmt, Clippy, Docs, CI, and Supply Chain

A green test on one developer laptop is evidence about one environment. A release gate must state and reproduce:

  • source revision;
  • compiler/tool versions and target;
  • resolved dependency graph and features;
  • build/test commands;
  • database/schema/model/harness versions;
  • produced artifact identity;
  • policy results and exceptions.

The companion now pins Rust 1.96.0 plus Clippy/rustfmt, commits both application and fuzz lockfiles, centralizes dependency declarations, runs one checked-in CI script locally and in GitHub Actions, and enforces advisory, license, duplicate, wildcard, and source policy with cargo-deny 0.19.4.

  • pin the compiler, formatter, linter, and dependency-policy tools;
  • understand Cargo.toml ranges versus Cargo.lock resolution;
  • make rustfmt, Clippy, tests, examples, fuzz compilation, and rustdoc one reproducible gate;
  • define lint policy without enabling arbitrary restriction lints;
  • use documentation and doctests as part of the public API contract;
  • centralize workspace dependencies and inspect enabled features;
  • audit known advisories, licenses, duplicates, and package sources;
  • review build scripts, proc macros, native/prebuilt code, and CI actions as executable inputs;
  • harden CI permissions, credentials, caches, untrusted pull requests, and artifacts;
  • produce an artifact manifest/SBOM/provenance and operate an update/exception lifecycle.

Depends on workspace architecture and the preceding testing chapter. It precedes benchmarking, observability, and deployment because those results are meaningless when the build identity drifts.

reviewed source revision
+ pinned toolchain/tool versions
+ locked dependency graph and features
+ controlled CI environment/permissions
+ deterministic gates
─────────────────────────────────────────
= evidence for one candidate artifact

This is not mathematical reproducible-build proof. Timestamps, native toolchains, operating-system packages, network-fetched model assets, and non-hermetic build scripts can still differ. Record those inputs and reduce them deliberately.

The workspace root contains:

[toolchain]
channel = "1.96.0"
profile = "minimal"
components = ["clippy", "rustfmt"]

rust-toolchain.toml makes Rustup-backed environments select the same compiler and components. Every package also declares:

[workspace.package]
edition = "2024"
rust-version = "1.96"

These fields have different jobs:

  • toolchain pin: exact compiler used for repository gates;
  • edition: language semantics/migration boundary;
  • rust-version: minimum promised compiler for package consumers.

If the project claims a lower MSRV than its pinned development toolchain, CI must actually compile the supported feature sets with that MSRV. This companion currently intentionally makes them the same.

Upgrade the pin through a reviewed pull request. The diff includes compiler/Clippy/rustfmt behavior, full gates, benchmarks, platform checks, and release notes—not a silent floating stable.

rustfmt.toml records:

style_edition = "2024"
max_width = 100
use_small_heuristics = "Default"

Run:

Terminal window
cargo fmt --all -- --check

CI checks; it does not rewrite the branch. Pinning the style edition avoids editor/direct-rustfmt defaults disagreeing with Cargo. Keep configuration small: unusual formatting rules increase contributor/editor friction.

Generated Rust should either be formatted deterministically or excluded with an explicit generated boundary. Do not hand-edit generated output.

The workspace declares a narrow baseline:

[workspace.lints.rust]
unsafe_code = "forbid"
[workspace.lints.clippy]
dbg_macro = "deny"
todo = "deny"
unimplemented = "deny"

Every package opts into workspace lints. CI runs:

Terminal window
cargo clippy --locked --workspace --all-targets --all-features -- -D warnings

The flags matter:

  • --workspace: every member;
  • --all-targets: libraries, binaries, tests, examples, benches;
  • --all-features: the combined feature surface;
  • -D warnings: official gate rejects warnings;
  • --locked: linter cannot silently resolve a different graph.

Do not enable all clippy::restriction lints. A lint becomes policy only when the team understands the invariant/trade-off. A local #[allow] includes the exact lint and reason near the code; broad crate-level suppression requires stronger justification.

--all-features can create combinations never shipped, and it cannot prove each individual feature combination. Important mutually exclusive/default-minimal/platform feature sets need matrix jobs or cargo-hack.

Run:

Terminal window
cargo doc --locked --workspace --no-deps
cargo test --locked --workspace --all-features

Rustdoc checks public names/links and generates the navigable API. Documentation tests compile and, unless marked no_run, execute Rust code blocks. Use:

  • ordinary code block: compiles and runs;
  • no_run: compile network/process example without executing;
  • compile_fail: prove an invalid use is rejected;
  • text: pseudocode that is not Rust;
  • ignore: rarely, with a concrete reason.

Document:

  • invariants and ownership;
  • errors and cancellation;
  • security/panic behavior;
  • examples;
  • feature/platform constraints;
  • resource limits;
  • unsafe safety contract.

“Self-explanatory code” does not tell consumers what must remain true across releases.

scripts/ci.sh resolves its own workspace path and runs:

cargo metadata --locked
→ cargo fmt --check
→ cargo clippy --locked --workspace --all-targets --all-features -D warnings
→ cargo test --locked --workspace --all-features
→ cargo test --locked --workspace --examples
→ cargo doc --locked --workspace --no-deps
→ cargo check --locked --manifest-path fuzz/Cargo.toml

Use:

Terminal window
cd rust/rust-ai-engineering
sh scripts/ci.sh

The script is deliberately a thin, readable command list. CI orchestration calls it rather than reimplementing slightly different gates in YAML. Platform/service matrix jobs may add work around it.

set -eu stops on a failed command or unset variable. It is not a substitute for correct shell quoting; the script quotes the derived workspace path.

Cargo.toml states compatible ranges. Cargo.lock records one resolved graph and registry checksums. For an application/CLI/service, commit the lockfile and use --locked in CI/release.

  • --locked: fail if resolution would change/missing lockfile;
  • --offline: prohibit network and use locally available index/packages;
  • --frozen: both locked and offline.

--locked does not prove all build inputs are locally available or malicious-free. --offline does not verify a fresh machine can fetch the graph. CI commonly fetches locked dependencies once, then runs locked gates.

The independent fuzz workspace has its own lockfile because it is intentionally outside the normal workspace/toolchain lifecycle.

Centralize workspace dependencies and features

Section titled “Centralize workspace dependencies and features”

Direct shared dependencies live in [workspace.dependencies]; members use { workspace = true }. Internal path dependencies also carry version = "0.1.0" so their publish contract is explicit.

Inspect the actual graph:

Terminal window
cargo tree --workspace --locked
cargo tree --workspace --duplicates --locked
cargo tree --workspace -e features -i sqlx --locked

Feature questions:

  • who enabled this feature?
  • does it add network/TLS/native/build-script code?
  • is it used in production or only tests?
  • can default features be disabled?
  • do target-specific dependencies need CI?

Duplicate versions are not automatically vulnerabilities. They can increase compile time/binary size and duplicate security/behavior surfaces. The current graph warns on two transitive duplicate families (hashbrown, syn) introduced through independently evolving upstream trees. The policy keeps them visible without pretending the application can force incompatible upstream major versions into one.

A dependency request is an engineering decision

Section titled “A dependency request is an engineering decision”

DEPENDENCY_POLICY.md requires every direct dependency change to record:

  1. required capability and call sites;
  2. no-dependency/existing alternatives;
  3. source, maintainership, release history;
  4. license decision;
  5. enabled features;
  6. normal/build/dev/native/proc-macro and transitive impact;
  7. unsafe/untrusted-input/build-script exposure;
  8. supported targets/MSRV;
  9. update and rollback/removal plan;
  10. tests/evals detecting behavior change.

Review the lockfile diff, upstream diff, owners/permissions, features, crates included, and cargo tree -i. Popularity is not a trust proof.

Domain crates remain provider-free. Provider SDKs/native inference/media parsers sit behind narrow capability adapters so replacement and containment remain practical.

deny.toml configures:

  • RustSec advisories/yanked status;
  • explicit SPDX license allowlist;
  • wildcard requirements denied, local path workspace use allowed;
  • duplicate versions warned;
  • workspace dependencies centralized;
  • unknown registries and Git sources denied.

Run the pinned tool:

Terminal window
cargo install --locked cargo-deny --version 0.19.4
cargo deny check

The first policy run found that repeated internal path dependencies were not centralized and looked like wildcard requirements. The repair moved them into [workspace.dependencies], added internal versions, and changed member manifests to workspace = true. That is the gate producing a concrete architecture improvement.

An advisory scan compares the resolved lockfile with a current database. It knows only reported issues and affected version metadata. It does not prove:

  • the vulnerable function is reachable or unreachable;
  • an unreported backdoor/bug is absent;
  • build scripts are benign;
  • registry account/release process is trustworthy;
  • native libraries/container packages/model files are safe.

Do not ignore an advisory silently. A time-bounded exception records ID, reachability analysis, mitigation, owner, expiry, upgrade/removal issue, and release risk acceptance.

The allowlist expresses project policy, not legal advice. Cargo metadata/license-file detection can be incomplete or wrong. Review source distributions, native/linking implications, notices, model licenses, media/data rights, and distribution method with qualified guidance.

Narrow per-crate exception beats globally allowing a license only one dependency needs.

The companion accepts crates.io and denies unknown registries/Git dependencies. If a Git dependency is temporarily necessary, use an immutable revision, review source, record provenance, and plan an exit. A branch/tag can move.

Procedural macros and build scripts execute during compilation. Native libraries/prebuilt binaries can bypass assumptions of a pure Rust graph. Review:

  • build.rs;
  • proc-macro crates and transitive graph;
  • filesystem/environment/network access;
  • compiler/linker invocations;
  • downloaded/generated artifacts;
  • bundled/system native library choice;
  • unsafe/FFI surface and target matrix.

A lockfile checksum detects registry archive substitution, not malicious code intentionally published under the locked version.

The Rust AI job:

  • sets workflow contents: read;
  • checks out a full immutable action SHA;
  • disables persisted checkout credentials;
  • activates the checked-in toolchain;
  • fetches with --locked;
  • runs the checked-in CI script;
  • installs exact locked cargo-deny 0.19.4;
  • applies the dependency policy;
  • has a 20-minute timeout.

Treat pull-request code as attacker-controlled. It can change build scripts/tests/CI files and read whatever the job exposes.

Rules:

  • no production secrets in untrusted pull-request jobs;
  • avoid privileged pull_request_target checkout of contributor code;
  • minimum GITHUB_TOKEN permissions;
  • pin third-party actions to full commit SHA and review updates;
  • separate test from publish/deploy/sign jobs;
  • publish only protected reviewed commits/artifacts;
  • use short-lived scoped OIDC credentials where supported;
  • isolate/rate-limit self-hosted runners;
  • do not restore executable caches across untrusted trust boundaries without policy;
  • sanitize logs/artifacts; never upload prompts/media/secrets by default.

The wider monorepo still contains version-tag action references in older jobs; the new Rust job demonstrates the stricter pattern, and full repository hardening should migrate each action through the same reviewed process.

Caches improve speed; they are not evidence. Key them by operating system, target, compiler, lockfile, and relevant features. A cache hit must not skip the gate.

Release artifacts record:

  • Git revision/dirty status;
  • Rust/Cargo versions and target;
  • lockfile digest;
  • enabled features/profile;
  • schema/harness/model asset identities;
  • CI workflow/run identity;
  • binary/container digest;
  • SBOM/dependency inventory;
  • test/eval/policy/benchmark report identities;
  • signatures/attestations where threat model requires.

Build releases once and promote the same digest. Rebuilding independently for production creates a different candidate unless reproducibility is proven.

Tools such as cargo-auditable can embed dependency information in binaries; SPDX/CycloneDX/SLSA tooling can record inventories/provenance. Choose formats your incident and deployment tooling can query, then verify them.

Dependency/toolchain updates should be routine, isolated, and reversible:

  1. generate a narrow update pull request;
  2. inspect manifest/lock/source/feature/license/advisory changes;
  3. run deterministic gates and relevant fuzz/bench/evals;
  4. canary if runtime/model/provider behavior can change;
  5. retain rollback artifact;
  6. record release identity.

Run advisory checks on pull requests and a schedule because the code can remain unchanged while the known-risk database changes. Route failures to an owner with response deadlines.

Exceptions are data with owner, reason, scope, expiry, and cleanup condition. A forever comment is not risk management.

Failure investigation: locked CI changes or fails unexpectedly

Section titled “Failure investigation: locked CI changes or fails unexpectedly”
  1. confirm source revision and whether lockfiles changed;
  2. record Rust/Cargo/rustfmt/Clippy/platform versions;
  3. inspect target/features/config/environment;
  4. compare registry/source and native/system packages;
  5. disable cache without changing the commit;
  6. inspect build scripts/codegen/time/network inputs;
  7. make the missing input explicit and add a regression/gate.

Do not delete the lockfile and call a newly resolved graph the same candidate.

Failure investigation: dependency policy reports a new advisory

Section titled “Failure investigation: dependency policy reports a new advisory”
  1. preserve advisory DB revision, lockfile, dependency path, and released artifacts;
  2. identify affected versions/functions/configurations;
  3. determine shipped reachability without treating “not used” as proof;
  4. contain exposure/disable feature/route if needed;
  5. upgrade, patch, replace, or remove;
  6. rerun software/security/model gates;
  7. inventory and roll out fixed artifacts;
  8. document any narrow time-bounded exception.

Failure investigation: CI secret appears in logs

Section titled “Failure investigation: CI secret appears in logs”
  1. stop jobs and revoke/rotate the credential immediately;
  2. contain/delete artifacts/log exposure under platform capabilities;
  3. inspect all uses and downstream access;
  4. determine whether untrusted code could read it;
  5. replace long-lived secret with scoped short-lived identity;
  6. add redaction/negative tests and permissions review;
  7. preserve incident evidence safely.

Receives fixes quickly; lint/format/build can change without source review. Use an automated update PR rather than invisible drift.

Reproducible review point; requires deliberate security/bug-fix cadence.

Reduces graph variants but can block legitimate incompatible transitive ecosystems. Warn, explain, and deny selected high-risk duplicates where total denial is impractical.

Supports controlled/offline builds and source inventory. Increases repository/update/review work and still requires upstream/security tracking.

Convenient checksum-locked distribution. Trusts registry, publisher, upstream, and build contents.

Security:

  • lock and audit every shipped/fuzz/tool workspace;
  • protect CI credentials and publish separation;
  • review build-time/native/proc-macro code;
  • scan known advisories continuously;
  • record licenses/sources/features;
  • sign/attest/promote immutable artifacts where required;
  • include model/media/runtime assets in provenance, not only Rust crates.

Performance:

  • central features prevent accidental dependency bloat;
  • duplicate graphs increase compile/binary size;
  • caches reduce latency but never replace gates;
  • split fast deterministic gates from scheduled fuzz/platform/latest-dependency work;
  • use cargo tree and later binary/allocation profiling before removing dependencies by intuition;
  • avoid reinstalling tools in production pipelines by using verified pinned runner images/artifacts.
  1. Add a Linux/macOS/Windows matrix and document which crates are intentionally platform-limited.
  2. Add MSRV verification distinct from the pinned development compiler.
  3. Inspect why both syn versions are present and write an upstream-compatible removal criterion.
  4. Add a narrow dependency license exception with owner/expiry, then make an unused exception fail.
  5. Produce an SPDX or CycloneDX SBOM and bind its digest to a Forge release report.
  6. Threat-model a malicious pull request changing build.rs, tests, cache keys, and workflow files.
  7. Pin every remaining monorepo action to reviewed full SHAs.

Use matrix target/OS jobs only for supported targets and cross-test native/FFI behavior on real runners where necessary. Test MSRV with the declared minimum and appropriate feature sets. cargo tree -i syn@<version> identifies upstream paths; removal waits for compatible upstream releases. Exceptions include exact crate/advisory/license, reason, owner, link, and expiry. SBOM/provenance include lockfile/toolchain/features/native/model assets and artifact digest. Untrusted PR jobs get no secrets/write permissions; publish consumes a protected tested artifact. Action updates verify the new commit/tag relationship and review release/source diff.

  • How do toolchain pin, edition, and rust-version differ?
  • Why does --locked matter for an application CI gate?
  • What does --all-features fail to prove?
  • Why should CI call a local script?
  • What does an advisory scan not prove?
  • Why are build scripts/proc macros privileged?
  • When is a duplicate crate version acceptable?
  • Why pin an action by full SHA?
  • Why should test and publish jobs use different authority?
  • What identities belong in an AI release artifact?
  • Exact Rust toolchain/components and rustfmt style are committed.
  • All workspace crates inherit lint/dependency policy.
  • CI and local execution call the same checked-in gate.
  • Formatting, all-target Clippy, tests, examples, rustdoc, and fuzz build pass locked.
  • Application/fuzz lockfiles are committed.
  • Dependency features and duplicates are inspected.
  • Advisories, licenses, wildcards, and sources are checked.
  • Dependency/advisory/license exceptions have owner/reason/expiry.
  • Build scripts, proc macros, native/prebuilt inputs are reviewed.
  • CI has least permissions, no untrusted secrets, immutable action pins, and timeout.
  • Release provenance binds source/toolchain/dependencies/features/assets/tests to artifact digest.
  • I ran sh scripts/ci.sh and cargo deny check.