diff --git a/.agents/skills/ub-rust/SKILL.md b/.agents/skills/ub-rust/SKILL.md new file mode 100644 index 0000000..7f96fd0 --- /dev/null +++ b/.agents/skills/ub-rust/SKILL.md @@ -0,0 +1,381 @@ +--- +name: ub-rust +description: Use this skill for Rust code, crates, tests, and tooling. Apply it when the task involves Rust files, Cargo.toml or workspaces, ownership or lifetimes, traits and generics, error handling, async code, clippy or rustfmt, unsafe review, or Rust application and repository logic. +--- + +# UB Rust + +## Overview + +Use this skill to enforce latest stable Rust modern defaults: edition-aware +implementation, ownership-first API design, structured error handling, +lint-policy discipline, and repository-truth validation. + +Generate modern patterns for new code and refactor legacy patterns +incrementally when touching existing code. + +Write against the crate's declared edition and minimum supported Rust version, +but structure code so it can move cleanly toward newer stable releases instead +of accumulating compatibility layers. + +## When Not To Use + +- Do not use this skill for workflow intake, sprint planning, or resumable + initiative orchestration; defer that to `ub-workflow`. +- Do not use this skill as the primary surface when the main problem is owned + by a non-Rust workflow, documentation, or framework skill. +- Do not use this skill as a generic documentation-normalization layer when + the main task is Markdown structure or documentation quality rather than + Rust implementation. + +## Bundled Assets + +This skill ships a reusable Cargo lint-table starter under `assets/`. + +It is merge material for `Cargo.toml`, not a standalone config file: the merge +target always already exists, so adoption stays a deliberate, reviewed edit +instead of a scripted mutation. See +`references/cargo-lints-resolution.md` for when and how to apply it. + +## Load References On Demand + +- Read `../ub-authoring/references/authoring-conventions.md` when adjusting + shared routing guidance, output structure, or cross-skill authoring + conventions. +- Load and apply `references/rust-standards.md` for canonical Rust rules and + toolchain policy. +- Load and apply `references/repository-rust-workflows.md` for resolving the + target repository's actual Rust validation and build-tooling baseline. +- Load `references/cargo-lints-resolution.md` when lint config discovery, + `[lints]` table adoption, or the bundled starter matters. + +## Skill Coordination + +- Co-load this skill with `ub-quality` for cross-language structure, + readability, and decision-analysis baselines. +- Defer planning artifacts and resumable delivery to `ub-workflow` when the + Rust change itself needs them. + +## Forward-Compatibility Contract + +- Implement for the crate's actual declared edition, minimum supported Rust + version, and dependency floor. +- Bias design toward the next stable upgrade path rather than preserving + older-edition behavior by default. +- Do not emit syntax or standard library APIs that the crate's declared floor + cannot compile. +- Add feature-gated fallbacks or compatibility shims only when an explicit + support contract or staged migration plan requires them. + +## Core Workflow + +1. Detect runtime and tooling truth from project files (`Cargo.toml`, + `Cargo.lock`, `rust-toolchain.toml`, `.cargo/config.toml`, CI, existing + lint configs). +2. Confirm the edition and minimum-supported-Rust-version contract before + proposing implementation. +3. Compare official guidance, repo truth, and observed code reality for + non-trivial or version-sensitive recommendations. +4. Surface `OFFICIAL_CONFLICT` when authoritative sources, repo truth, or + live code reality materially disagree on a non-trivial recommendation. +5. Surface `UNVERIFIED` when a non-trivial claim could not be confirmed in + official sources after targeted research. +6. Propose at least two viable implementation paths for non-trivial Rust + work, with concise pros/cons. +7. Choose the simplest path that satisfies correctness, maintainability, and + project constraints. +8. Write or adjust tests first for behavior changes. +9. Implement minimal changes to pass tests, then refactor while preserving + behavior and boundaries. +10. Run repository-configured validation after modifying Rust code, starting + with the smallest relevant scope. +11. Report outcomes, bounded exceptions, any conflict or uncertainty + disclosures, and missing tooling gaps that materially reduce confidence. +12. If a repository wants this lint baseline but has no explicit lint policy + yet, walk the user through adopting the bundled lint-table starter as a + reviewed `Cargo.toml` edit and explain the required repo-local + adaptations. + +## Version & Research Policy + +- Target the latest stable release of Rust for guidance, but implement + against the crate's actual declared edition and version contract. +- Detect the project's actual Rust floor from `Cargo.toml` (`edition`, + `rust-version`), `rust-toolchain.toml`, lockfiles, and CI configuration. +- Use web search to verify current best practices, API availability, and + migration guidance against official Rust documentation and release notes. +- Treat repo truth as the gold implementation standard when deciding what can + actually ship without breaking the current project. +- Treat official Rust docs as the preferred guidance baseline for + forward-looking design and migration-ready patterns. +- If official guidance and repo truth diverge materially on a non-trivial + recommendation, surface `OFFICIAL_CONFLICT`, implement the repo-safe path, + and explain the forward migration path. +- If a non-trivial claim cannot be confirmed in official sources after + targeted research, mark it `UNVERIFIED` or avoid presenting it as settled + guidance. +- Keep conflict and uncertainty disclosure scoped to non-trivial, + version-sensitive, or contested guidance rather than trivial edits. +- Do not generate syntax or standard library behavior only available on + nightly or in unreleased Rust versions unless the user explicitly requests + it. +- When the crate's declared floor is behind latest stable, note the version + gap, recommend an upgrade path, and prefer patterns that will survive that + upgrade cleanly. +- Inspect the host repository's `AGENTS.md` or equivalent instructions when + present for project-specific version policy and tooling; do not assume it + contains this catalog's defaults. +- Do not hardcode version numbers in generated guidance; keep recommendations + evergreen. + +## Freshness Review + +- Volatility: high +- Review recommendation: review on touch and during periodic maintenance, + targeting a quarterly rhythm when practical. +- Trigger signals: Rust release and edition changes, standard library + additions that replace common third-party crates, clippy lint set changes, + or repository validation changes that alter the real enforced baseline. +- Enforcement: advisory only; freshness should highlight review targets + without pretending missing lint or test wiring is automatically a blocker + in every adopting repository. +- Stable core: ownership-first APIs, structured errors, explicit lint policy, + bounded unsafe, and repository-truth validation remain the durable baseline + even when tooling preferences evolve. + +## Implementation Rules + +### Editions, Toolchain, and Workspace + +- Treat `Cargo.toml` as the source of truth for edition, minimum supported + Rust version, features, and dependency floor. +- Respect `rust-toolchain.toml` pinning when present; do not assume a newer + toolchain than the repo declares. +- In workspaces, prefer workspace-inherited dependencies and lint tables over + per-crate duplication. +- Use the official edition migration tooling for edition moves instead of + hand-editing syntax across the tree. + +### Ownership and API Design + +- Prefer borrowing over cloning; treat `.clone()` calls added purely to + satisfy the borrow checker as design signals to revisit. +- Accept borrowed views (`&str`, `&[T]`, `impl AsRef<...>`) at API + boundaries when practical; return owned types when the caller needs + ownership. +- Use newtypes for domain identifiers and units instead of bare primitives. +- Use builder or typed-constructor patterns for complex construction; make + invalid states unrepresentable with enums and typestates where it pays off. +- Mark value-returning functions and important results `#[must_use]` when + ignoring them is a likely bug. +- Use `#[non_exhaustive]` on public enums and structs that will grow. +- Implement `From`/`TryFrom` for conversions instead of ad hoc methods. + +### Error Handling + +- Return `Result` and propagate with `?`; keep the happy path readable. +- Libraries: define structured error types (enum-per-module or dedicated + error crates) that implement `std::error::Error` with proper `source` + chains. +- Applications: a dynamic top-level error type is acceptable at the outermost + layers; keep typed errors where callers branch on failure kinds. +- Do not `unwrap()`/`expect()` on production paths; when an invariant truly + cannot fail, use `expect("why this cannot fail")` with the reason, and + allow relaxed policy inside tests. +- Never panic across an FFI boundary. + +### Enum Modeling and Exhaustiveness + +- Model state with enums and match exhaustively; avoid catch-all `_` arms + that would silently absorb future variants in domain logic. +- Use `let`-`else` for early returns on refutable patterns. + +### Concurrency and Async + +- Pick the async runtime from repo truth; do not introduce a second runtime + into a project that already has one. +- Keep async functions runtime-agnostic where practical; isolate + runtime-specific spawning at the edges. +- Never block inside async contexts; move blocking work to the runtime's + blocking facility or a dedicated thread. +- Make `Send`/`Sync` expectations explicit at public async boundaries and + document cancellation behavior for long-running tasks. +- Prefer scoped threads and message passing when they are simpler than shared + mutable state; keep lock scopes minimal. + +### Unsafe Policy + +- Keep `unsafe` minimal, encapsulated behind safe APIs, and justified with a + `// SAFETY:` comment stating the upheld invariants. +- Do not add new `unsafe` for performance without measurement. +- Recommend Miri or equivalent checking when touching nontrivial unsafe + code. + +### Lint and Format Policy + +- `cargo fmt` with the repo's rustfmt configuration is non-negotiable. +- Treat the repository's lint policy (`[lints]`/`[workspace.lints]` tables, + `clippy.toml`, CI flags) as the enforcement source of truth. +- Prefer lint-expectation attributes over blanket allows so suppressions + stay justified and self-cleaning where the toolchain supports them; always + scope suppressions narrowly with a reason. + +### Testing + +- Unit tests live in-module under `#[cfg(test)]`; integration tests live in + `tests/`; public API examples become doc tests. +- Test behavior, not implementation details; use property-based testing for + parser-like and invariant-heavy code when the repo embraces it. +- Keep tests hermetic; put external systems behind traits and use small + hand-written fakes before reaching for mock frameworks. + +### Post-Edit Validation Contract + +- After generating or modifying Rust code, run the repository-configured + checks that are relevant to the touched scope. +- Minimum expectation: `cargo fmt --check` (or the repo formatter command), + `cargo clippy` when clippy is part of the repo baseline, and targeted + `cargo test` coverage when tests exist or behavior changed. +- Start narrow (`cargo check -p `, targeted tests) and widen only when + failures indicate broader impact. +- Fix newly introduced lint/build/test failures before finalizing when they + are within the task scope. +- If a configured check cannot be run, state exactly why it was skipped or + blocked. + +Repository truth note: + +- Detect the host repository's actual format, lint, and test baseline before + presenting a command or gate as established truth. +- clippy, cargo-deny, and cargo-audit are often strong recommendations for + Rust-heavy repos, but present them as active gates only when the host + repository has actually wired them. +- If lint or test expectations are weak or absent, report that gap explicitly + instead of pretending those checks are already enforced. + +### Tooling Gap Warning Policy + +- If repository Rust tooling for linting, supply-chain auditing, or testing + is missing, weakly configured, or clearly not wired into normal workflows, + warn the user explicitly. +- Recommend concrete additions when relevant: clippy in CI, an explicit + `[lints]` policy, cargo-deny or cargo-audit for supply-chain checks, and + doc tests for public APIs. +- Treat missing quality gates as a reported risk, not as silent permission to + skip validation. + +## Toolchain Strategy (Primary + Fallback) + +- Treat repository-configured tooling as the source of truth. If clippy, + rustfmt, or CI checks are configured, generate code that satisfies their + active rules instead of relying on generic defaults. +- Primary stack: `cargo` (`check`, `build`, `test`, `doc`), `rustfmt`, + `clippy`. +- Recommended additions when relevant: cargo-deny or cargo-audit for + supply-chain checks, Miri for unsafe-heavy crates, a faster test runner + only when the repo has already adopted one. +- Keep one-command check workflows discoverable in project docs/automation + when possible. + +## Config Resolution And Scaffolding + +Treat real tool config as the source of truth: + +1. inspect `[lints]`/`[workspace.lints]` in `Cargo.toml`, `clippy.toml`, + `rustfmt.toml`, and CI lint flags first when they exist +2. inspect task-runner, CI, and `.cargo/config.toml` entrypoints before + assuming a command shape +3. offer the bundled lint-table starter only when no explicit lint policy + exists and the adopting repo wants this house style +4. apply the starter as a reviewed `Cargo.toml` edit, adapted to workspace + layout and the crate's supported toolchain floor +5. do not silently install tools, mutate CI, or rewrite `Cargo.toml` as part + of adoption + +## Tradeoff Handling + +- Use the shared `ub-quality` decision-analysis baseline for major design or + tooling choices. +- When Rust-specific tradeoffs matter, call out compile-time cost, API + stability, unsafe surface, and modernization cost explicitly in the option + pros/cons. +- Default to the safest modern option unless compatibility, policy, or + delivery constraints justify an alternative. +- Document any intentionally deferred cleanup or technical debt retained for + compatibility. + +## Legacy-Avoidance Guardrails + +- Do not generate pre-2018-edition idioms (`extern crate`, path-based module + declarations) in new code. +- Do not use `#[macro_use]` where explicit `use` imports work. +- Do not use deprecated uninitialized-memory APIs; use `MaybeUninit`. +- Do not add lazy-static-style dependencies when the standard library's + once-initialization types cover the need at the crate's floor. +- Do not use deprecated error-handling crates or the legacy `try!` macro. +- Do not add blanket `#[allow(...)]` at crate or module level to silence + lints without a documented reason. +- Do not introduce new nightly-only features into stable-channel crates. + +Migration-aware exception policy: + +- Allow temporary legacy retention only for explicit compatibility + constraints in existing code. +- Document exactly what is retained, why it is retained, and the next + modernization step. + +## Output Requirements + +Treat this section as the stable output expectation for non-trivial Rust work +in this catalog. + +When generating or reviewing Rust code, include: + +1. Environment note: detected edition, toolchain floor, workspace layout, and + relevant tooling context. +2. Source truth note: repo version/tooling reality and any material gap + versus latest stable guidance. +3. Decision note: chosen approach and at least one alternative. +4. Tradeoff note: concise pros/cons for chosen and rejected paths. +5. Legacy note: removed legacy patterns or bounded exceptions with rationale. +6. Validation note: checks run (format/clippy/test/doc) and outcomes. +7. Tooling gap note: missing or weak lint/audit/test enforcement that the + user should consider adding or wiring in. +8. Conflict note when relevant: `OFFICIAL_CONFLICT` or `UNVERIFIED` with a + concise explanation and the implementation consequence. + +When this skill is used to adopt the lint-table starter in another +repository, also include: + +1. which `Cargo.toml` sections were proposed or edited and why +2. which repo-local lint levels still need adaptation +3. the exact lint command the target repo should run next + +## References + +- Core rules and source-backed policy: `references/rust-standards.md` +- Repository-specific workflow and validation baseline: + `references/repository-rust-workflows.md` +- Lint policy discovery and starter adoption: + `references/cargo-lints-resolution.md` + +## Completion Checklist + +- Latest stable Rust baseline is enforced intentionally against the crate's + declared edition and version contract. +- APIs prefer borrowing, typed domain values, and unrepresentable invalid + states where practical. +- Errors are structured, source-chained, and free of production-path + `unwrap()`. +- Async and concurrency choices match the repo's runtime truth with explicit + boundaries. +- Unsafe code is minimal, encapsulated, and justified with safety comments. +- Repository-configured format/clippy/test checks were run after edits, or an + explicit reason was reported. +- No new legacy output is introduced. +- Missing lint/audit/test enforcement was reported when relevant. +- Any retained legacy behavior is documented with a migration path. +- Any material official-source conflict or unverified non-trivial guidance is + disclosed explicitly when relevant. +- Any adopted lint-table starter was reported as a starter profile rather + than silent repo policy. diff --git a/.agents/skills/ub-rust/assets/lints-template/Cargo-lints.toml b/.agents/skills/ub-rust/assets/lints-template/Cargo-lints.toml new file mode 100644 index 0000000..13816ca --- /dev/null +++ b/.agents/skills/ub-rust/assets/lints-template/Cargo-lints.toml @@ -0,0 +1,28 @@ +# UB Rust starter lint profile. +# Merge material for Cargo.toml; not a standalone config file. +# +# Workspaces: put these tables under [workspace.lints.rust] and +# [workspace.lints.clippy] in the root manifest, then add +# `[lints] workspace = true` to each member crate. +# Single crates: use them as [lints.rust] and [lints.clippy] directly. +# +# Starter only: adapt levels to repo truth before enforcing in CI. + +[lints.rust] +# Keep idiom migration lints visible; priority -1 lets specific +# lints below override members of this group. +rust-2018-idioms = { level = "warn", priority = -1 } +unreachable_pub = "warn" +unused_must_use = "warn" + +[lints.clippy] +# Baseline correctness/style/complexity/perf groups at warn; specific +# lints below override members of the groups. +all = { level = "warn", priority = -1 } +# High-signal additions beyond the default groups. +dbg_macro = "warn" +todo = "warn" +unimplemented = "warn" +# Production-path discipline; relax in tests with scoped, justified +# expect/allow attributes instead of removing the lint. +unwrap_used = "warn" diff --git a/.agents/skills/ub-rust/references/cargo-lints-resolution.md b/.agents/skills/ub-rust/references/cargo-lints-resolution.md new file mode 100644 index 0000000..cf6cc6b --- /dev/null +++ b/.agents/skills/ub-rust/references/cargo-lints-resolution.md @@ -0,0 +1,70 @@ +# Cargo Lint Policy Resolution + +Use this reference when deciding what lint policy actually applies in a target +repository and whether the bundled lint-table starter should be adopted. + +## Resolution Order + +1. `[workspace.lints]` in the workspace root `Cargo.toml`, inherited by + member crates that declare `[lints] workspace = true`, is the broadest + policy surface. +2. A per-crate `[lints]` table overrides nothing silently: a crate either + inherits the workspace table or defines its own. +3. `clippy.toml` configures individual clippy lint behavior (thresholds, + allowed names) but does not set lint levels. +4. `rustfmt.toml` (or `.rustfmt.toml`) owns formatting; it is separate from + lint policy. +5. CI flags (for example `cargo clippy -- -D warnings`) and environment + `RUSTFLAGS` can tighten enforcement beyond the manifest tables; check CI + before declaring the real gate. + +Treat the combination of manifest tables plus CI flags as the repository's +real lint policy. Do not present the bundled starter as active policy when +any explicit policy already exists. + +## Toolchain Floor Awareness + +- The manifest `[lints]` table requires a toolchain generation that supports + it; verify the repo's minimum supported Rust version before recommending + it. +- For repos below that floor, keep lint levels in CI flags and recommend the + manifest table as part of the upgrade path. + +## What Counts As Real Policy + +1. the discovered `[lints]`/`[workspace.lints]` tables and their levels +2. `clippy.toml` settings +3. CI lint invocations and deny levels +4. inline lint-expectation or narrowly scoped allow attributes with reasons, + as bounded local exceptions + +## Starter Adoption (Reviewed Edit, Not A Script) + +The starter lives at `assets/lints-template/Cargo-lints.toml`. Its merge +target (`Cargo.toml`) always already exists, so this skill intentionally +ships no scaffolding script: adoption must stay a deliberate, reviewed edit. + +Adopt it only when: + +1. no explicit lint policy exists in manifests, `clippy.toml`, or CI, and +2. the adopting repository wants this catalog's starter baseline + +Adoption steps: + +1. for workspaces, place the table contents under `[workspace.lints]` in the + root manifest and add `[lints] workspace = true` to member crates +2. for single crates, place the contents under `[lints]` in `Cargo.toml` +3. show the resulting diff to the user before finalizing +4. run the repo's clippy command and review new findings together instead of + silencing them wholesale + +## Adaptation Points After Adoption + +1. tune lint levels to repo noise tolerance; promote to `deny` only where the + team agrees +2. relax test-only ergonomics (for example unwrap-style lints) with scoped, + justified attributes rather than global allows +3. wire the clippy command into the repo's task runner or CI so the policy is + actually enforced +4. document the lint policy decision where the repo keeps its engineering + conventions diff --git a/.agents/skills/ub-rust/references/repository-rust-workflows.md b/.agents/skills/ub-rust/references/repository-rust-workflows.md new file mode 100644 index 0000000..5cc1bf0 --- /dev/null +++ b/.agents/skills/ub-rust/references/repository-rust-workflows.md @@ -0,0 +1,102 @@ +# Repository Rust Workflows + +Use this reference to resolve a repository's actual Rust toolchain, workspace, +and validation truth before recommending commands or tooling. + +It should help the agent inspect local reality, not hardcode one repository's +workflow into a distributable skill. + +## Toolchain and Workspace Baseline + +Inspect the target repository for: + +1. `edition` and `rust-version` in `Cargo.toml`, plus `rust-toolchain.toml` + channel pinning +2. workspace layout: single crate, virtual workspace, member globs, and + workspace-inherited dependencies or lints +3. whether the repo is an application, a library, a CLI tool, an FFI crate, + or a mixed workspace +4. the active lint policy: `[lints]`/`[workspace.lints]` tables, + `clippy.toml`, `rustfmt.toml`, and CI lint flags +5. feature flags and cfg matrices that change what compiles +6. `build.rs` scripts, code generation, and vendored or patched dependencies +7. `.cargo/config.toml` for aliases, target settings, and registry + configuration + +## Enforced Validation Baseline + +Treat the repository's wired checks as the real baseline. +Inspect task runners, Makefiles, CI, and pre-commit hooks for the actual +Rust-oriented gates. + +Common examples: + +1. `cargo fmt --check` +2. `cargo clippy` with explicit deny/warn levels +3. targeted or full `cargo test` commands, including doc tests +4. `cargo deny check` or `cargo audit` supply-chain runs +5. build matrix checks across targets, features, or toolchain versions +6. MSRV verification jobs + +## Not Yet Repository-Wired + +Recommend extra tools only as explicit gaps when they are not already wired +into the repository. + +Typical examples: + +1. clippy in CI +2. an explicit `[lints]` policy +3. cargo-deny or cargo-audit +4. Miri for unsafe-heavy crates + +When advising users inside any repository: + +1. treat the detected edition and `rust-version` as the shipping constraint + even when official docs describe newer features +2. recommend missing tools as improvements, not as if they were already part + of the enforced baseline +3. keep guidance consistent with the repo's actual workload instead of + assuming a service-backend shape +4. do not recommend syntax or standard library APIs above the crate's floor + as if they were currently shippable + +## Practical Command Patterns + +Use the command shape that matches the target repository's tooling. + +Recommended resolution order: + +1. a repo wrapper such as `task`, `make`, `just`, or a documented cargo alias +2. direct cargo commands scoped to touched crates first + (`cargo check -p `, `cargo test -p `) +3. lint and audit commands (`cargo clippy`, `cargo deny check`) only after + confirming they are installed or wired locally + +Scoping guidance: + +1. prefer `cargo check` for fast iteration before full builds +2. run doc tests when public API docs changed +3. run the full workspace only when failures indicate cross-crate impact +4. respect feature matrices: test with the feature set CI actually uses + +## Starter Lint Policy + +This skill ships a reusable lint-table starter: + +1. `assets/lints-template/Cargo-lints.toml` + +It is merge material for `Cargo.toml`, applied as a reviewed edit. Use it only +when a target repository wants this lint baseline and has no explicit lint +policy yet. See `references/cargo-lints-resolution.md` for adoption steps. + +## Packaging and Release Guidance + +Because adopting repositories range from libraries to services to CLI tools: + +1. keep binary crates thin; put logic in library crates so it stays testable +2. use workspace dependency inheritance to keep versions consistent +3. for published crates, keep `rust-version` accurate and document the MSRV + policy +4. align generated Rust with the active lint policy, including clippy levels + and rustfmt configuration diff --git a/.agents/skills/ub-rust/references/rust-standards.md b/.agents/skills/ub-rust/references/rust-standards.md new file mode 100644 index 0000000..848c64b --- /dev/null +++ b/.agents/skills/ub-rust/references/rust-standards.md @@ -0,0 +1,278 @@ +# Rust Standards (Portable, Non-Formatting, Latest Stable) + +> Verify these standards against the latest official Rust documentation and +> release notes via web search. + +This reference codifies Rust standards for the target repository or project +after local toolchain and workspace truth has been inspected. +It is aligned to official Rust and tooling guidance without assuming one +specific host repository. + +## 1. Platform and Environment + +- Runtime baseline: latest stable Rust (detect from `Cargo.toml`, + `rust-toolchain.toml`, and CI, and verify via web search). +- Prefer the newest stable edition for new crates; migrate existing crates + with the official edition tooling (`cargo fix --edition`) rather than + hand-editing syntax. +- Declare `rust-version` (MSRV) in `Cargo.toml` and keep it accurate; modern + cargo resolvers are MSRV-aware and use it to pick compatible dependency + versions. +- Respect `rust-toolchain.toml` pinning; do not assume a newer toolchain than + the repo declares. + +## 2. Implementation Strategy + +- Let the type system carry invariants: prefer designs where invalid states + do not compile over designs that validate at runtime. +- Prefer clear, boring Rust over clever type-level abstractions; readability + is a hard constraint. +- Implement against the crate's declared edition and MSRV, but bias design + toward the next stable upgrade path instead of preserving older-edition + behavior by default. +- Use iterators and combinators where they read clearly; drop to explicit + loops when the combinator chain stops being obvious. +- For non-trivial work, explicitly compare at least two approaches with + pros/cons. + +## 2A. Source Truth And Disclosure + +- Treat target-project truth as the shipping constraint: detect the active + edition, MSRV, features, and tooling before recommending implementation + details. +- Treat official Rust docs, release notes, and the Cargo book as the + preferred guidance baseline for forward-looking design and migration-ready + patterns. +- If official docs, target-project truth, or live code reality materially + disagree on a non-trivial recommendation, surface `OFFICIAL_CONFLICT`, + implement the project-safe path, and explain the forward migration path. +- If a non-trivial claim cannot be confirmed in official sources after + targeted research, mark it `UNVERIFIED` or avoid presenting it as settled + guidance. +- Keep these disclosures scoped to non-trivial, version-sensitive, or + contested guidance rather than every trivial edit. +- Do not teach nightly-only features (for example type-alias-impl-trait or + unstable cargo capabilities) as current practice; name the stable + alternative instead. + +## 3. Ownership and API Design + +- Prefer borrowing over cloning; treat `.clone()` added purely to satisfy the + borrow checker as a design signal to revisit. +- Accept borrowed views (`&str`, `&[T]`, `impl AsRef<...>`) at API + boundaries when practical; return owned types when the caller needs + ownership. +- Use newtypes for domain identifiers and units instead of bare primitives. +- Use builders or typed constructors for complex construction; use typestate + patterns where the extra machinery genuinely prevents misuse. +- Mark value-returning functions and important results `#[must_use]` when + ignoring them is a likely bug. +- Use `#[non_exhaustive]` on public enums and structs that will grow. +- Implement `From`/`TryFrom` for conversions instead of ad hoc methods. +- Follow the official API guidelines for naming, trait implementations + (`Debug`, `Clone`, `Default`, `PartialEq` where sensible), and + documentation of public items. + +## 4. Error Handling + +Choose the error model explicitly by layer: + +- Libraries: structured error types implementing `std::error::Error` with + proper `source` chains; derive-based error crates in the thiserror style + are the community default. +- Applications: a dynamic top-level error type in the anyhow style is + acceptable at the outermost layers; keep typed errors where callers branch + on failure kinds. + +Rules: + +- Return `Result` and propagate with `?`; keep the happy path readable. +- No `unwrap()`/`expect()` on production paths; when an invariant truly + cannot fail, use `expect("why this cannot fail")` with the reason, and + allow relaxed policy inside tests. +- Never panic across an FFI boundary. +- Use `let`-`else` for early returns on refutable patterns, and let chains + (including if-let match guards on floors that support them) instead of + nested conditionals. + +## 5. Modern Language and Standard Library Preferences + +- `std::sync::OnceLock` and `LazyLock` for lazy statics instead of + lazy_static or once_cell dependencies when the crate floor provides them. +- Native `async fn` in traits where object safety allows; know the + `Send`-bound caveats for generic executors, and use async closures on + floors that support them. +- `cfg_select!` for compile-time configuration selection instead of the + cfg-if crate on floors that provide it. +- `assert_matches!`/`debug_assert_matches!` for pattern assertions in tests + on floors that provide them. +- `matches!`, exhaustive `match`, and enum-driven state instead of boolean + flag clusters. +- Const generics and const fn where they move checks to compile time without + obscuring the API. +- Standard atomic and cell update helpers over hand-rolled read-modify-write + loops where the floor provides them. + +Notes: + +- Check the crate's MSRV before using newer standard library APIs; recommend + the upgrade rather than silently emitting incompatible code. +- Edition changes alter defaults (resolver behavior, lifetime capture, + unsafe requirements on extern blocks); read the edition guide when moving + editions instead of assuming old defaults. + +## 6. Concurrency and Async + +- Pick the async runtime from repo truth; tokio is the common default, but + never introduce a second runtime into a project that already has one. +- Keep async functions runtime-agnostic where practical; isolate + runtime-specific spawning at the edges. +- Never block inside async contexts; move blocking work to the runtime's + blocking facility or a dedicated thread. +- Make `Send`/`Sync` expectations explicit at public async boundaries and + document cancellation behavior for long-running tasks. +- Prefer scoped threads (`std::thread::scope`) and message passing when they + are simpler than shared mutable state; keep lock scopes minimal and never + hold a lock across an `.await`. + +## 7. Unsafe Policy + +- Keep `unsafe` minimal and encapsulated behind safe APIs. +- Every `unsafe` block carries a `// SAFETY:` comment stating the invariants + it relies on and why they hold. +- Use `MaybeUninit` for uninitialized memory; deprecated uninitialized-memory + APIs are forbidden. +- Do not add new `unsafe` for performance without measurement. +- Recommend Miri runs for crates with nontrivial unsafe code. +- Remember modern editions require `unsafe` on extern blocks; treat FFI + declarations as reviewed, unsafe surface. + +## 8. Lint, Format, and Toolchain Policy + +### Baseline + +- `cargo fmt` with the repo's rustfmt configuration is non-negotiable. +- clippy is the standard lint layer; express repo policy in + `[lints]`/`[workspace.lints]` manifest tables when the toolchain floor + supports them, with `clippy.toml` for lint-specific settings. +- Prefer `#[expect(lint, reason = "...")]` over `#[allow]` where the floor + supports it, so suppressions fail when they stop being needed; always + scope suppressions narrowly. +- Restriction lints such as `clippy::unwrap_used` enforce production-path + discipline; relax them in tests with scoped, justified attributes. + +### Tooling Stance (Primary + Fallback) + +Primary stack: + +- Build/test: `cargo check`, `cargo build`, `cargo test` (including doc + tests) +- Format: `cargo fmt` +- Lint: `cargo clippy` + +Recommended additions when relevant: + +- cargo-deny or cargo-audit for supply-chain and advisory checks +- Miri for unsafe-heavy crates +- a faster test runner (for example cargo-nextest) only when the repo has + already adopted one + +Operational guidance: + +- Treat actual manifest lint tables, `clippy.toml`, `rustfmt.toml`, and CI + flags as the policy source of truth when they exist; use the bundled + starter only when a repo wants this house style and has no explicit + policy. +- After generating or modifying Rust code, run the configured checks that + apply to the touched scope instead of assuming code generation is + sufficient. +- Start with touched crates (`cargo check -p`, targeted tests), then widen + only when failures indicate broader impact. +- If configured tooling cannot be run, report the exact blocker. +- If linting, auditing, or testing is missing or not wired into routine + workflows, warn the user and recommend concrete additions rather than + silently accepting the gap. + +## 9. Dependencies and Workspace Hygiene + +- Prefer the standard library first; add dependencies only when they provide + clear correctness, maintainability, or DX value. +- Use `cargo add` and workspace dependency inheritance + (`[workspace.dependencies]`) to keep versions consistent across member + crates. +- Keep feature flags additive and documented; test the feature combinations + CI actually builds. +- Review transitive dependency growth; supply-chain checks (cargo-deny, + cargo-audit) are strong recommendations for released crates and services. +- Keep `Cargo.lock` committed for applications; follow current official + guidance and repo policy for libraries. + +## 10. Architecture and Boundaries + +- Preserve layered flow: transport -> service -> repository -> data. +- Keep domain language consistent between implementation and tests. +- Avoid leaking storage or transport details into domain logic. +- Keep binary crates thin; put logic in library crates so it stays testable. +- Use visibility deliberately: `pub(crate)` and module boundaries are API + design tools, not afterthoughts. + +## 11. Testing Practice + +- Unit tests live in-module under `#[cfg(test)]`; integration tests live in + `tests/`; public API examples become doc tests. +- Test behavior, not implementation details; use pattern assertions where + they read better than equality on whole structs. +- Use property-based testing for parser-like and invariant-heavy code when + the repo embraces it. +- Keep tests hermetic; put external systems behind traits and use small + hand-written fakes before reaching for mock frameworks. +- Treat missing tests, missing doc tests on public APIs, or missing clippy + wiring as explicit quality risks worth reporting. + +## 12. Legacy Patterns to Avoid (Migration-Only Exceptions) + +- Pre-2018-edition idioms in new code: `extern crate`, path-based module + declarations, `#[macro_use]` where explicit `use` imports work. +- Deprecated uninitialized-memory APIs; use `MaybeUninit`. +- lazy_static or once_cell dependencies where the standard library's + once-initialization types cover the need at the crate floor. +- Deprecated error-handling crates (failure, error-chain) and the legacy + `try!` macro; use `?` with thiserror/anyhow-style errors. +- The cfg-if crate on floors where `cfg_select!` is available. +- Blanket `#[allow(...)]` at crate or module level without a documented + reason; prefer lint expectations. +- Nightly-only features in stable-channel crates. + +Exception policy: + +- Temporary retention is allowed only for explicit compatibility constraints. +- Document what is retained, why it remains, and the follow-up modernization + step. + +## 13. Source Map (Official References) + +- Rust release blog: +- Edition guide: +- The Rust Book: +- Rust API Guidelines: +- Standard library docs: +- Cargo book, manifest and `rust-version`: + +- Cargo `[lints]` reference: + +- Cargo workspaces and dependency inheritance: + +- Clippy lint index: +- rustfmt: +- Lint levels and `#[expect]`: + +- Rustonomicon (unsafe): +- Miri: +- Async book: +- async fn in traits announcement: + +- Tokio docs: +- thiserror: +- anyhow: +- cargo-deny: +- cargo-audit: diff --git a/.github/plugin/marketplace.json b/.github/plugin/marketplace.json index addf814..bc2cb52 100644 --- a/.github/plugin/marketplace.json +++ b/.github/plugin/marketplace.json @@ -12,7 +12,7 @@ "plugins": [ { "name": "uncle-bob", - "description": "11 domain skills (workflow, quality, authoring, governance, customizations, CSS, Tailwind, TypeScript, Python, Vue, Nuxt) and 1 custom agent (teacher).", + "description": "12 domain skills (workflow, quality, authoring, governance, customizations, CSS, Tailwind, TypeScript, Python, Vue, Nuxt, Rust) and 1 custom agent (teacher).", "version": "1.0.0", "author": { "name": "robert-hoffmann", diff --git a/AGENTS.md b/AGENTS.md index b39e181..a1a1c08 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -166,6 +166,7 @@ trigger rules inside the skill before producing work that depends on them. | ub-css | Plain CSS & Vue/Nuxt style blocks: design tokens, cascade layers, native nesting, container queries, progressive enhancement. | `.agents/skills/ub-css/SKILL.md` | | ub-nuxt | Nuxt (latest stable): typed composables, SSR/SSG/hybrid rendering, runtime config, Nitro/server routes, app-directory semantics. | `.agents/skills/ub-nuxt/SKILL.md` | | ub-python | Python (latest stable): typed patterns, boundary validation, structured error handling, pytest/ruff/mypy workflows. | `.agents/skills/ub-python/SKILL.md` | +| ub-rust | Rust (latest stable): ownership-first APIs, structured errors, async and unsafe discipline, clippy and cargo lint-table workflows. | `.agents/skills/ub-rust/SKILL.md` | | ub-tailwind | Tailwind CSS (latest stable): setup, migration, debugging across standalone HTML, Vue + Vite, and Nuxt projects. | `.agents/skills/ub-tailwind/SKILL.md` | | ub-ts | TypeScript (latest stable): typing, module/moduleResolution, compiler flags, tsconfig architecture. | `.agents/skills/ub-ts/SKILL.md` | | ub-vuejs | Vue (latest stable): SFCs, composables, reactivity, watchers, SSR/hydration, component contracts with strict TypeScript. | `.agents/skills/ub-vuejs/SKILL.md` | diff --git a/README.md b/README.md index 055a0ac..961b09f 100644 --- a/README.md +++ b/README.md @@ -281,6 +281,7 @@ core stack has already shaped the work. | ub-vuejs | Vue workflow for modern SFC authoring, strict TypeScript, current reactivity patterns, and hydration-aware component design. | | ub-nuxt | Nuxt workflow for app-structure detection, runtime/rendering choices, modern framework-native patterns, and migration away from legacy layouts. | | ub-tailwind | Tailwind workflow for environment detection, CSS-first setup, modern directives, upgrade-safe migration, and framework-specific integration. | +| ub-rust | Rust workflow for edition and MSRV detection, ownership-first APIs, structured errors, bounded unsafe, and lint-policy validation. | | Agent | Description | | ----------------- | ------------------------------------------------------------------------------------ | diff --git a/docs/.vitepress/config.ts b/docs/.vitepress/config.ts index ba26aff..b2025fa 100644 --- a/docs/.vitepress/config.ts +++ b/docs/.vitepress/config.ts @@ -138,6 +138,10 @@ export default withMermaid({ text : "UB Tailwind", link : "/skills/ub-tailwind", }, + { + text : "UB Rust", + link : "/skills/ub-rust", + }, ], }, { diff --git a/docs/skills/index.md b/docs/skills/index.md index ceaf30d..ea087cb 100644 --- a/docs/skills/index.md +++ b/docs/skills/index.md @@ -41,6 +41,8 @@ on. container queries, accessibility, and progressive enhancement. - [UB Tailwind](./ub-tailwind): Tailwind setup, CSS-first configuration, framework recipes, tokens, and migration guardrails. +- [UB Rust](./ub-rust): modern Rust, edition and MSRV truth, ownership-first + APIs, structured errors, bounded unsafe, and lint policy. ## References And Progressive Disclosure diff --git a/docs/skills/ub-rust.md b/docs/skills/ub-rust.md new file mode 100644 index 0000000..8eb2c76 --- /dev/null +++ b/docs/skills/ub-rust.md @@ -0,0 +1,104 @@ +# UB Rust + +Source: `.agents/skills/ub-rust/SKILL.md` + +`ub-rust` guides Rust implementation, tests, workspace hygiene, lint policy, +and repository logic. It favors modern Rust patterns while still implementing +against the target crate's real edition, toolchain floor, and tooling. It +treats repository truth as the shipping boundary for every recommendation. + +## Core Principles + +- Detect repository Rust truth first: edition, minimum supported Rust + version, workspace layout, and wired validation gates before choosing + commands or syntax. +- Write modern Rust against the crate floor: current stable patterns where + allowed and no nightly-only features presented as shippable. +- Design ownership-first APIs: borrow over clone, typed domain values over + bare primitives, and invalid states made unrepresentable where it pays + off. +- Keep errors structured and source-chained; production paths never rely on + `unwrap()`, and justified `expect` messages document real invariants. +- Match the repo's async runtime truth; keep blocking work out of async + contexts and make `Send`/`Sync` expectations explicit at boundaries. +- Keep `unsafe` minimal, encapsulated, and justified with safety comments. +- Treat lint policy as configuration, not prose: manifest lint tables, + clippy settings, and CI flags are the enforcement source of truth. +- Modernize incrementally instead of adding compatibility layers or + retaining legacy Rust patterns by habit. + +## Behavior In Practice + +- Starts by reading `Cargo.toml`, `rust-toolchain.toml`, workspace members, + `.cargo/config.toml`, CI, and lint configs before saying which cargo + command or syntax is safe. +- Compares repo truth with official Rust documentation and release notes. If + they disagree materially, it implements the repo-safe path and reports the + migration path instead of pretending there is no conflict. +- Designs APIs around ownership: borrowed views at boundaries, owned returns + where callers need them, newtypes for identifiers, builders for complex + construction, and conversion traits over ad hoc methods. +- Treats error flow as part of the API: structured error types with source + chains in libraries, dynamic top-level errors only at application edges, + and `?` propagation that keeps the happy path readable. +- Models state with exhaustive enums and avoids catch-all match arms that + would silently absorb future variants in domain logic. +- Respects the existing async runtime instead of introducing a second one, + and isolates runtime-specific spawning at the edges. +- Reviews `unsafe` blocks against documented invariants and recommends + deeper checking when nontrivial unsafe code is touched. +- Treats rustfmt, clippy, manifest lint tables, and supply-chain audit tools + as detected repo truth. Missing lint, audit, or test wiring is reported as + a tooling gap, not silently ignored. +- Avoids new legacy output: no pre-2018 idioms, no deprecated + uninitialized-memory APIs, no lazy-static-style dependencies where the + standard library covers the need, and no blanket lint allows without + documented reasons. + +## Reference Highlights + +- `.agents/skills/ub-rust/references/rust-standards.md`: encodes the modern + Rust baseline: edition-aware implementation, ownership-first API design, + structured errors, async and concurrency discipline, unsafe policy, + testing practice, and legacy-avoidance rules with an official source map. +- `.agents/skills/ub-rust/references/repository-rust-workflows.md`: how to + discover the target repository's actual Rust workflow, toolchain and + edition contract, feature matrices, active lint and test commands, and CI + truth before presenting any command as required. +- `.agents/skills/ub-rust/references/cargo-lints-resolution.md`: lint policy + discovery across manifest lint tables, clippy and rustfmt configs, and CI + flags, plus when and how the bundled lint-table starter is adopted as a + reviewed edit. +- `.agents/skills/ub-rust/assets/lints-template/Cargo-lints.toml`: starter + lint profile used only when a repository explicitly wants the house + baseline and has no explicit lint policy yet; always applied as a reviewed + `Cargo.toml` edit, never a scripted mutation. + +## Progressive Disclosure + +The main skill sets the Rust posture. Deeper references load when the task +needs specifics: Rust standards for implementation, workflow discovery for +repo validation, and lint-policy resolution plus the bundled starter only +when lint setup is actually in scope. + +## Common Invocation Examples + +- "Use `ub-rust` to add tests for this parser." +- "Review this crate for error handling and unnecessary clones." +- "Refactor this module without breaking the public API." +- "Help this workspace adopt an explicit lint policy." + +## Boundaries + +Do not use it as a general documentation or workflow-planning skill. Pair it +with `ub-workflow` when the Rust change itself needs planning artifacts. + +## Tradeoffs + +Strength: makes Rust changes safer by aligning modern language practice with +the repository's actual validation surface, ownership model, and unsafe +boundaries. + +Cost: strict ownership and error-contract work can reveal follow-up +modernization outside the initial task, especially in older or lint-free +projects.