feat(skills): add coding-dev.md and per-language rules under code-languages/
Generic coding rules and preferences distilled from existing agent rule files, project AGENTS.md/.rules, and saved memories: - strictness maximal; suppressions must be reported to the user - never filter build/lint/test output through grep/tail/head - reuse > fork > self-implement; generic code becomes its own package - 1000-line file / 100-line function limits, split by concern - regression test every fixed bug; never test against real user data - events over polling; scope discipline; honest reporting Per-language: rust, js-typescript, bash, python, go, csharp-dotnet, c-cpp. Each extends coding-dev.md rather than repeating it. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
1 parent
ba95f171b2
commit
57968904bf
9 files changed
+1499
-1
No files matched your search
@@ -0,0 +1,188 @@
|
||||
---
|
||||
name: rust
|
||||
description: >-
|
||||
Use this skill when writing, building, linting, testing, or releasing Rust code — including
|
||||
workspace lint configuration, clippy/fmt gates, error handling with Result/thiserror,
|
||||
unsafe policy, async patterns, and cargo workspace/submodule layout.
|
||||
---
|
||||
|
||||
# Rust — Rules & Preferences
|
||||
|
||||
Extends [`../coding-dev.md`](../coding-dev.md). Everything there applies; this adds Rust specifics.
|
||||
|
||||
---
|
||||
|
||||
## 1. The Gate
|
||||
|
||||
Nothing is "done" until all of these are clean:
|
||||
|
||||
```bash
|
||||
cargo fmt --all --check
|
||||
cargo clippy --workspace --all-targets --all-features
|
||||
cargo test --workspace
|
||||
cargo doc --workspace --no-deps
|
||||
cargo build --release
|
||||
```
|
||||
|
||||
> [!IMPORTANT]
|
||||
> **`--workspace` is not optional.** Without it only the root package is checked and every
|
||||
> member crate's warnings stay invisible. This has hidden real warnings for weeks at a time.
|
||||
> `--all-targets` additionally covers tests, benches, and examples — which are also code.
|
||||
|
||||
- `cargo fmt` before every commit. **No hand-formatting**, ever.
|
||||
- `cargo doc` matters: a broken intra-doc link is a reference to something that no longer
|
||||
exists, which is worth knowing about.
|
||||
- Run the project's build script (`./scripts/build.sh`) if it has one — it wires these
|
||||
together plus size checks and a smoke run.
|
||||
|
||||
---
|
||||
|
||||
## 2. Lints — Pedantic On, Configured In One Place
|
||||
|
||||
```toml
|
||||
# Cargo.toml (workspace root)
|
||||
[workspace.lints.clippy]
|
||||
pedantic = { level = "warn", priority = -1 }
|
||||
too_many_lines = "warn"
|
||||
unwrap_used = "warn"
|
||||
|
||||
[workspace.lints.rust]
|
||||
unsafe_code = "forbid"
|
||||
```
|
||||
|
||||
```toml
|
||||
# every member crate
|
||||
[lints]
|
||||
workspace = true
|
||||
```
|
||||
|
||||
> [!CAUTION]
|
||||
> **Lint config lives in `[workspace.lints]` only.** A package that repeats the configuration
|
||||
> carries a second copy that drifts — which is how a binary once kept reading its own stale
|
||||
> allows while the workspace's were being actively edited.
|
||||
|
||||
- **Never `#[allow]` on a whim.** If a lint is genuinely wrong for the codebase, allow it
|
||||
**workspace-wide** with a comment explaining why, so the decision is visible.
|
||||
- **Re-test every `#[allow]` periodically.** If removing one produces no warning, delete it.
|
||||
- `too_many_lines` is the mechanical enforcement of the 100-line function limit. A function
|
||||
that trips it wants extracting, not an allow.
|
||||
|
||||
---
|
||||
|
||||
## 3. Errors & Panics
|
||||
|
||||
- **`Result` and `Option` everywhere.** `thiserror` for library error enums;
|
||||
`anyhow` is acceptable at the application/binary boundary only.
|
||||
- **No `unwrap()` or `expect()` outside tests and `main`.** Anything that can be absent at
|
||||
runtime — a device, a file, a network peer, an env var — must be handled. Hardware and
|
||||
network resources appear and disappear while the program runs.
|
||||
- No `panic!` as flow control. A library never panics on input it was designed to receive.
|
||||
- Never `?`-swallow context — attach it (`.context(...)` / a typed variant) so the error says
|
||||
where it came from.
|
||||
|
||||
---
|
||||
|
||||
## 4. `unsafe`
|
||||
|
||||
**Forbidden by default**, enforced in `Cargo.toml` with `unsafe_code = "forbid"`.
|
||||
|
||||
If an FFI binding genuinely requires it, it goes behind a **safe wrapper in exactly one
|
||||
module**, with a `// SAFETY:` comment on every block stating the invariant being upheld.
|
||||
`unsafe` scattered through call sites is not acceptable.
|
||||
|
||||
---
|
||||
|
||||
## 5. Dependencies & Workspace Layout
|
||||
|
||||
Follow the global ordering (reuse → fork → write). In Rust specifically:
|
||||
|
||||
- Search crates.io and check maintenance status before adding. Prefer pure-Rust crates over
|
||||
ones requiring a C toolchain when the difference is not material — a C dependency turns a
|
||||
one-command build into a container requirement.
|
||||
- **Pin precisely when it matters.** If you need an API that only exists on a git revision,
|
||||
pin the revision and **write down why** — a future reader will otherwise "helpfully"
|
||||
bump it to the crates.io release that lacks the API.
|
||||
- **Reusable logic goes in its own crate**, published separately, with the application as a
|
||||
consumer. The workspace layout that follows from this:
|
||||
```
|
||||
crates/<name>-core/ # all reusable logic, no UI, no framework deps
|
||||
crates/<name>-proto/ # wire types & protocol parsing, dependency-light
|
||||
src/ # or crates/<name>-ui — presentation only
|
||||
```
|
||||
- **Submodule hygiene**: commit and push inside the submodule crate first, then commit the
|
||||
updated SHA pointer in the parent. Match each repo's own commit-trailer and identity
|
||||
conventions — a public library repo and a private app repo often differ deliberately.
|
||||
|
||||
---
|
||||
|
||||
## 6. Async
|
||||
|
||||
- Tokio runtime with `futures` streams; `async_trait` where trait methods must be async.
|
||||
- **Model new flows as streams or async methods**, matching what the codebase already does.
|
||||
- **Never block in async context.** Use `spawn_blocking` for genuinely blocking work.
|
||||
- Subscribe to event streams; never poll on a timer (see the global events-over-polling rule).
|
||||
- Don't short-circuit a hand-written `Stream` impl that manually pumps backends — it usually
|
||||
encodes ordering guarantees that are not obvious from the signature.
|
||||
|
||||
---
|
||||
|
||||
## 7. Feature Flags & `cfg`
|
||||
|
||||
- Feature flags declared in the root `Cargo.toml`.
|
||||
- **Gate at module level, not per function** — per-function `cfg` leaves a trail of empty
|
||||
stubs and makes the real platform differences hard to see.
|
||||
- Adding a platform backend: declare the feature, create the gated module, and **log which
|
||||
backend was selected at startup**. Silent backend selection is very hard to debug remotely.
|
||||
- Use `build.rs`-exported configs for conditions that aren't plain features.
|
||||
|
||||
---
|
||||
|
||||
## 8. Style Details
|
||||
|
||||
- `unsafe`-free, `unwrap`-free, and formatted — see above.
|
||||
- **No `pub` on anything that doesn't need to cross a module boundary.** Default to private
|
||||
and widen deliberately.
|
||||
- Max ~5 parameters; past that, pass a struct.
|
||||
- Prefer `f32` for UI/geometry when the framework's native unit is `f32` — mixing in `f64`
|
||||
produces a stream of casts and rounding inconsistencies.
|
||||
- Derive rather than hand-implement (`Debug`, `Clone`, `Default`, `PartialEq`) unless the
|
||||
hand implementation is doing something a derive cannot.
|
||||
- `#[non_exhaustive]` on public enums/structs that are likely to grow.
|
||||
|
||||
---
|
||||
|
||||
## 9. Testing
|
||||
|
||||
- Unit tests inline in `#[cfg(test)] mod tests`; integration tests in `tests/`.
|
||||
- `cargo test --workspace` — plus `--release` when timing or optimization matters to the
|
||||
behavior under test.
|
||||
- **Regression test every fixed bug.**
|
||||
- A dummy/mock backend that exercises the full pipeline without real hardware is worth
|
||||
building early — it is the only way most device code gets tested at all.
|
||||
- Never point test output at real hardware. Route test signal to a virtual/null endpoint and
|
||||
**verify the route resolved** — most tools silently fall back to the system default when
|
||||
the requested target does not exist, which sends your test straight at the user.
|
||||
|
||||
---
|
||||
|
||||
## 10. Building in Containers
|
||||
|
||||
When the crate needs system headers the host does not have (audio, graphics, webkit),
|
||||
build inside the project's designated container. Chain `cd` inside the same command string —
|
||||
entering a container resets the working directory:
|
||||
|
||||
```bash
|
||||
distrobox enter <container> -- bash -c "cd /path/to/project && cargo build --release"
|
||||
```
|
||||
|
||||
Prefer the project's `scripts/build.sh` over a hand-typed invocation; it also handles job
|
||||
throttling so a release build does not saturate the machine.
|
||||
|
||||
**Split so that as little as possible needs the container**: a `-core` crate with no GUI
|
||||
dependencies builds and tests directly on the host, which is seconds instead of minutes.
|
||||
|
||||
---
|
||||
|
||||
## 11. Related
|
||||
- [`../coding-dev.md`](../coding-dev.md) — the general rules this extends.
|
||||
- [`c-cpp.md`](./c-cpp.md) — when bindgen/FFI crosses into a C library.
|
||||
Reference in new issue
Block a user