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
+293
@@ -0,0 +1,293 @@
|
||||
---
|
||||
name: coding-dev
|
||||
description: >-
|
||||
Use this skill whenever writing, reviewing, refactoring, building, testing, or releasing code
|
||||
in any language. Defines standing preferences on strictness, dependency choice, file and function
|
||||
size limits, test discipline, build output handling, error/warning policy, and commit hygiene.
|
||||
Links to per-language rules under code-languages/ for Rust, TypeScript/JavaScript, Bash, Python,
|
||||
Go, C#/.NET, and C/C++.
|
||||
---
|
||||
|
||||
# Coding & Development — Standing Rules and Preferences
|
||||
|
||||
These are the defaults for **all** code work, in any language. They are preferences with
|
||||
reasons attached, not style trivia — most exist because ignoring them caused a real problem.
|
||||
|
||||
> [!IMPORTANT]
|
||||
> **A project's own rules win.** If a repository has an `AGENTS.md`, `CLAUDE.md`, `AGENT.md`,
|
||||
> `.rules`, or `CONTRIBUTING.md`, read it first — it is more specific than this document and
|
||||
> overrides it on conflict. This file is the default for everything those files do not say.
|
||||
|
||||
Per-language specifics live in [`code-languages/`](./code-languages/):
|
||||
|
||||
| Language | Rules |
|
||||
| :--- | :--- |
|
||||
| Rust | [`code-languages/rust.md`](./code-languages/rust.md) |
|
||||
| TypeScript / JavaScript | [`code-languages/js-typescript.md`](./code-languages/js-typescript.md) |
|
||||
| Bash / Shell | [`code-languages/bash.md`](./code-languages/bash.md) |
|
||||
| Python | [`code-languages/python.md`](./code-languages/python.md) |
|
||||
| Go | [`code-languages/go.md`](./code-languages/go.md) |
|
||||
| C# / .NET | [`code-languages/csharp-dotnet.md`](./code-languages/csharp-dotnet.md) |
|
||||
| C / C++ | [`code-languages/c-cpp.md`](./code-languages/c-cpp.md) |
|
||||
|
||||
---
|
||||
|
||||
## 1. Strictness — Maximum, Always
|
||||
|
||||
**Configure every linter, compiler, and type checker as strictly as the toolchain allows.**
|
||||
Strict mode is the starting point, not an aspiration to migrate toward later.
|
||||
|
||||
- Warnings are errors in CI and in the build gate (`-warnaserror`, `-D warnings`, `--strict`).
|
||||
- Enable the pedantic/extra lint tiers, not just the defaults.
|
||||
- Type checking must pass with **zero** errors — `tsc --noEmit`, `mypy`, `cargo check` alike.
|
||||
- Never weaken a check to make a build pass. Fix the failing step. If a check is genuinely
|
||||
wrong for the codebase, disable it **project-wide in one place with a comment explaining
|
||||
why**, so the decision is visible and reviewable — never with a scattered inline suppression.
|
||||
|
||||
### Suppressions are a last resort, and they expire
|
||||
|
||||
- Try hard to actually fix an error or warning before suppressing it.
|
||||
- When you do suppress one, **say so explicitly in your report to the user** — name the
|
||||
warning, where it is, and why it could not be fixed. A silent suppression is the failure
|
||||
mode this rule exists to prevent.
|
||||
- **Re-test suppressions periodically.** Removing an `#[allow]` / `// eslint-disable` /
|
||||
`#pragma warning disable` that no longer suppresses anything should produce no warning —
|
||||
if it doesn't, delete it. Stale suppressions accumulate and hide real regressions.
|
||||
|
||||
---
|
||||
|
||||
## 2. Never Hide Build, Test, or Lint Output
|
||||
|
||||
**Do not pipe build, compile, lint, format, or test output through `grep`, `tail`, `head`,
|
||||
`sed`, or any other filter.** Run the command so its complete output is visible.
|
||||
|
||||
**Why:** filtering hides warnings, notes, and diagnostics that did not match the pattern.
|
||||
Grepping for `^warning|^error` drops multi-line notes, help text, deprecation notices, and
|
||||
anything a tool emits in an unexpected format.
|
||||
|
||||
```bash
|
||||
# WRONG — silently drops notes, helps, and deprecations
|
||||
cargo clippy 2>&1 | grep -E "^(warning|error)"
|
||||
|
||||
# RIGHT
|
||||
cargo clippy --workspace --all-targets --all-features
|
||||
```
|
||||
|
||||
If output is genuinely too large, **narrow the command** (one test, one package, one target)
|
||||
rather than filtering its output. The same applies to logging: do not swallow stderr, do not
|
||||
redirect diagnostics to `/dev/null`, and do not set a log level that hides warnings during a
|
||||
verification run.
|
||||
|
||||
---
|
||||
|
||||
## 3. Dependencies — Reuse Before You Build
|
||||
|
||||
A strict ordering for obtaining any non-trivial functionality:
|
||||
|
||||
1. **Use an existing library / crate / package / official SDK** that already does it.
|
||||
2. **If one is close but not exactly right, fork it** (as a git submodule or vendored fork)
|
||||
and modify it.
|
||||
3. **Write your own implementation only when nothing else fits** — and say why, explicitly.
|
||||
|
||||
**Why:** the ecosystem has already solved correctness, edge cases, and platform quirks that
|
||||
a fresh implementation will rediscover slowly and incompletely.
|
||||
|
||||
Before hand-rolling any subsystem — a protocol client, a parser, a date formatter, a
|
||||
debounce, a retry/backoff, a semver comparator — **search first and state the finding.**
|
||||
Prefer official/first-party SDKs over third-party wrappers.
|
||||
|
||||
### The corollary: new generic code becomes its own package
|
||||
|
||||
**When you implement something generic that no existing package provided — a parser or
|
||||
reader for a file format, a protocol implementation, a client for an undocumented API —
|
||||
it becomes its own package/crate/module in its own repository**, which the application then
|
||||
depends on like any other dependency.
|
||||
|
||||
**Why:** it keeps the app's tree focused on the app, it forces a clean public API boundary,
|
||||
and it gives back the library that was missing in the first place. Structure the split so
|
||||
the reusable core has no UI, no framework, and no app-specific types — only the app shell
|
||||
depends on those.
|
||||
|
||||
> [!TIP]
|
||||
> Design for this from the start: put **all reusable logic in a core library** and keep the
|
||||
> UI/CLI layer as presentation only. A core without GUI dependencies also builds and tests
|
||||
> far faster, often without the app's heavyweight toolchain.
|
||||
|
||||
---
|
||||
|
||||
## 4. Size Limits — Files and Functions
|
||||
|
||||
| Unit | Soft target | Split at | Hard limit |
|
||||
| :--- | :--- | :--- | :--- |
|
||||
| **Source file** | ~400 lines | 600 lines | **1000 lines** |
|
||||
| **Function** | under 50 lines | — | **100 lines** |
|
||||
| **Function parameters** | 3–4 | — | ~5, then pass a struct/object |
|
||||
| **Nesting depth** | 2 | — | 3, then extract |
|
||||
|
||||
- **Enforce the function limit with the linter, not by eye** (e.g. Clippy's `too_many_lines`).
|
||||
A function that trips it wants extracting, not a suppression.
|
||||
- **Enforce the file limit in the build script** — a check that fails over the hard limit and
|
||||
*names* anything past the soft split point, so the seam gets found while splitting is cheap.
|
||||
- **Split by concern, never by line count.** A large `strip.rs` becomes
|
||||
`strip/fader.rs`, `strip/knobs.rs`, `strip/routing.rs` — never `strip1.rs` / `strip2.rs`.
|
||||
- One coherent responsibility per module. A module that draws widgets should not also own
|
||||
application state.
|
||||
|
||||
---
|
||||
|
||||
## 5. Tests
|
||||
|
||||
- **Write tests for every feature where testing is possible.** State plainly when something
|
||||
is untestable (GUI, hardware, live third-party service) rather than quietly skipping it.
|
||||
- **Every bug that was fixed gets a regression test.** A bug that broke once will break again;
|
||||
the test is what makes the second time cheap. This is the highest-value test you can write.
|
||||
- **A skipped test fails the gate.** Silently skipped tests are worse than missing ones —
|
||||
they report green.
|
||||
- Unit tests for logic; integration tests for protocol/boundary behavior.
|
||||
- **Never test destructive operations against real data.** Use fixtures, temporary
|
||||
directories, injected backends, and disposable test accounts. The user's real config,
|
||||
data, devices, accounts, and files must never be used as scratch space.
|
||||
- **Prove the test exercises the path under test.** A tool that silently falls back to a
|
||||
default target when the requested one does not resolve will produce a passing measurement
|
||||
of the wrong thing. Verify the wiring before trusting the result.
|
||||
- **Test the value, not the presence of the key.** A check that counts entries which exist
|
||||
will pass against a cleared or null value — and reads exactly like a working fix.
|
||||
|
||||
---
|
||||
|
||||
## 6. Verification — Before Claiming Anything Works
|
||||
|
||||
Run the **whole** gate, not the convenient part of it. A typical full gate:
|
||||
|
||||
> format check → lint (strict/pedantic) → type check → build → tests (with race/sanitizer
|
||||
> where available) → docs build → size limits → smoke run
|
||||
|
||||
- **Prefer the project's build script** (`./scripts/build.sh`, `make check`, `./build.sh --lint`)
|
||||
over hand-typed commands. It encodes steps you will otherwise forget, and it stays correct
|
||||
when the toolchain moves.
|
||||
- **Read the numbers, not just the verdict.** A suppression count that has grown is worth
|
||||
noticing even when everything passes.
|
||||
- **Confirm the binary you are testing contains the change.** A file existing proves a file
|
||||
exists, not that it is current. Compare its mtime against the commit, or wait for the build
|
||||
to actually report success — testing a stale binary reads exactly like "the fix didn't work".
|
||||
- **A change to visual/UI code is not done until it has been looked at.** Build, run,
|
||||
screenshot, compare.
|
||||
- **State plainly what is stubbed.** A component that draws its frame but has no behavior is a
|
||||
scaffold, not a finished component — say so rather than implying completion.
|
||||
|
||||
---
|
||||
|
||||
## 7. Architecture Preferences
|
||||
|
||||
- **Events over polling — always.** When there is an event, callback, channel, or watch API to
|
||||
subscribe to, use it. Do **not** settle for a polling loop because a benchmark shows it
|
||||
currently costs nothing; a measurement of 0% CPU does not make polling the right design. It
|
||||
adds latency and can miss or coalesce events. If existing code polls where an event exists,
|
||||
treat that as a finding worth fixing. Only poll when no event source exists — and say so.
|
||||
- **Don't over-guard.** Check only conditions that can actually occur. A null check on a
|
||||
non-optional value, a `try/catch` around code that cannot throw, or an optional-chain on a
|
||||
type-guaranteed value is dead defensive code, not safety.
|
||||
- **No abstraction for one or two call sites.** A factory used once is just a function call.
|
||||
No class wrapping a single function, no state machine for a boolean, no
|
||||
`Manager`/`Handler`/`Strategy`/`Provider` suffix unless multiple implementations exist.
|
||||
- **Less code with the same behavior wins.** Between equivalent solutions, the shorter ships.
|
||||
Three similar lines beat a premature helper.
|
||||
- **Delete dead code, don't comment it out.** That is what version control is for.
|
||||
- **Don't cache what a store already owns.** Read from the source of truth on demand; cache
|
||||
only what has no source of truth.
|
||||
- **A search-and-replace that matches nothing is a silent failure.** Assert the target string
|
||||
exists before replacing, and verify the result afterward.
|
||||
|
||||
---
|
||||
|
||||
## 8. Scope Discipline
|
||||
|
||||
- **Solve the problem at hand.** No refactors unless asked. No "while I was in there" cleanups.
|
||||
- **Smallest diff that correctly solves it.**
|
||||
- **Don't refactor code you weren't asked to touch**, and don't mix unrelated concerns in one
|
||||
change — backend work and UI work land separately.
|
||||
- When you notice adjacent work worth doing, **describe it** rather than absorbing it.
|
||||
- **Check `git status --short` before editing** and preserve unrelated in-progress changes.
|
||||
- **Verify a TODO before acting on it.** Task documents are leads, not facts — grep for the
|
||||
symbol, read the function, check the history. Work has been redone from scratch because a
|
||||
stale TODO claimed something was missing that had shipped hours earlier.
|
||||
|
||||
---
|
||||
|
||||
## 9. Comments & Naming
|
||||
|
||||
- **Comment the *why*, never the *what*.** "A gap here, matching the reference layout" is
|
||||
useful; `// draw the button` is not.
|
||||
- **Don't narrate changes in comments** ("changed this to fix X") — that is git's job.
|
||||
- Keep existing comments; they carry context you do not have.
|
||||
- **Natural human text in user-facing strings** — errors, descriptions, toasts. Sentence case,
|
||||
ending in a period, no robotic phrasing.
|
||||
- **Never hardcode** what a constant, config file, or generated table already defines —
|
||||
endpoints, colors, class names, translated strings, magic identifiers.
|
||||
- Names checked mechanically stay correct; names checked by memory drift. If a project has a
|
||||
naming rule that matters (a one-letter difference between the app and its library, say),
|
||||
**put it in the build gate**.
|
||||
|
||||
---
|
||||
|
||||
## 10. Errors, Logging & Security
|
||||
|
||||
- Use the language's structured error type; do not stringify errors as the primary mechanism.
|
||||
- **Never swallow an error silently.** No empty catch blocks.
|
||||
- Use a real logger with levels — `debug` for traces, `info` for lifecycle, `warn` for
|
||||
recoverable, `error` for unrecoverable. Not bare `print`/`console.log`.
|
||||
- **Never log secrets** — tokens, auth headers, passwords, message content, personal data.
|
||||
- **Never commit secrets.** Reference an environment file or secret store; commit only an
|
||||
`.env.example` with empty values.
|
||||
- **Treat every input from outside the trust boundary as hostile** — validate type, length,
|
||||
and content. Normalize and bounds-check any path before touching the filesystem with it.
|
||||
Never pass an externally-derived string to a shell.
|
||||
- **Be extremely careful automating against real third-party services.** Use conservative
|
||||
rate limits, generous delays, retries with backoff, and abort fast when a response looks
|
||||
wrong. Assume you are one mistake away from a ban or a real-world side effect.
|
||||
|
||||
---
|
||||
|
||||
## 11. Git & Commits
|
||||
|
||||
- **Conventional Commits**: `feat(scope): …`, `fix(translator): …`, `test: …`, `docs: …`.
|
||||
- Short, imperative subject lines.
|
||||
- **Commit incrementally as work lands.** Never leave a large batch of uncommitted work at
|
||||
the end of a task.
|
||||
- **Submodules**: commit and push the submodule first, then the updated pointer in the parent.
|
||||
Never leave a submodule commit unpushed — the parent would reference a commit nobody can fetch.
|
||||
- Match the repository's existing trailer and attribution conventions; some repositories
|
||||
deliberately carry none.
|
||||
|
||||
---
|
||||
|
||||
## 12. Reporting Work Honestly
|
||||
|
||||
- If tests fail, **say so and show the output**. If a step was skipped, say that.
|
||||
- If a warning was suppressed rather than fixed, **name it**.
|
||||
- Distinguish verified from unverified: GUI behavior, packaging, hardware, and live
|
||||
third-party interactions are usually unverified unless you actually exercised them.
|
||||
- Do not claim a visual match without having compared the two images.
|
||||
- **Prefer the boring explanation.** A typo, a stale binary, or a self-matching pattern is
|
||||
far more likely than a toolchain or sandbox bug — and an environment explanation is more
|
||||
flattering and arrives faster, which is exactly why it is the one to distrust.
|
||||
|
||||
---
|
||||
|
||||
## 13. Environment & Tooling
|
||||
|
||||
- **Read `<tool> --help` before using a CLI.** Do not guess flags, and do not write an ad-hoc
|
||||
script when an existing tool already does the job.
|
||||
- **Build inside the project's designated environment** (container, venv, toolchain manager)
|
||||
rather than the host, when the project specifies one. Note that entering a container
|
||||
typically resets the working directory — chain `cd` inside the same command string.
|
||||
- **Don't saturate the machine.** A full release build that consumes every core makes the
|
||||
workstation unusable. When something latency-sensitive is running, halve the job count and
|
||||
`nice` the build.
|
||||
- **Prefer long-running work in the background** over blocking wait loops. Never write
|
||||
`until <check>; do sleep 2; done` — it hangs the terminal and has to be interrupted by hand.
|
||||
|
||||
---
|
||||
|
||||
## 14. Related Skills
|
||||
- [`code-languages/`](./code-languages/) — per-language rules that extend and specialize this document.
|
||||
Reference in new issue
Block a user