diff --git a/CLAUDE.md b/CLAUDE.md index 01cf6925..ec4881ff 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -202,6 +202,18 @@ See - After the memory win lands, spend dedicated rounds cutting its CPU cost; reaching the 1/3 ceiling is a limit, not a target. Put the ledger table in the PR and on the parent issue. +## Design principles (fleet directives, zackees/ci.yml#163) + +Agent and review guidance from zackees/ci.yml `docs/policy-rust.md` ("Design principles, narrowed"). Not enforced by ci-lint; no rule ID. + +1. **Search before you add.** Before adding a resolver, detector, predicate, guard or path helper, `rg` for an existing one. Reuse it, or say in the PR why it does not fit. +2. **Conflicting implementations get a research issue, not an inline winner.** If the same concept is implemented more than once with observably different behaviour and fixing it needs a design decision, file `research: N conflicting implementations of X -- which is canonical?` listing every copy (`file:line`) and what the difference does to real input; cross-reference it and carry on with your task. Copies that behave identically are a refactor, not an issue. +3. **Keep behaviour changes and refactors apart.** Separate commits, preferably separate PRs, never one mixed diff. This sits beside hard rule 2 (never mix C-core and `rust/` paths in one commit); both apply. +4. **Refactor first only when you would grow a giant function.** If a fix would add more than 50 lines to a function already over 200 lines, extract first in a separate behaviour-preserving change with characterization tests written first (RED -> GREEN). Otherwise fix it where it belongs; no unrequested restructures. Upstream mimalloc files are never restructured at all (hard rule 6): new logic goes in new files. +5. **Function and file size follow `RUST-018`'s ceilings.** The `rust/` workspace does not run the ratchet yet (no clippy `too_many_lines`/`cognitive_complexity` lint config), so nothing mechanical holds this line. New functions still stay under clippy's `too_many_lines` (100) and `cognitive_complexity` (25), and an existing function over them must not grow. Never split a file with `include!` fragments to dodge a length ceiling. Before splitting a hot file, check `git branch -a --contains` for in-flight branches -- a split is a rename and conflicts destructively. +6. **Fail fast, and delete before you abstract.** Reject invalid input at the boundary with a typed error; never fall back silently to a degraded path. Prefer deleting code to adding a layer. No new trait until there are two real implementations (a test double does not count). When reality diverges from a sub-issue, hard rule 7 still applies: escalate on the issue, do not improvise a fallback. +7. **Existing boundaries beat generic DRY.** The upstream overlay boundary (hard rules 2 and 6: fork logic in new files, guarded one-line hooks in upstream files), the Rust/C crate split in `rust/`, and documented deliberate copies win over removing duplication. Do not copy code across a crate boundary to get around it -- say the boundary hurts. An undocumented copy is a finding under 2; a documented one is not. + ## Repo facts - Branch layout: `main` is the **v3** line (crate 1.0.x, overlay pinned to `upstream/dev3`