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:
BluscreamandClaude Opus 5 committed 2026-09-18 12:33:44 +02:00
1 parent ba95f171b2
commit 57968904bf
9 files changed
+1499 -1

No files matched your search

+188
View File
@@ -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.