diff options
| author | main <main@swarm.moe> | 2026-03-31 14:21:52 -0400 |
|---|---|---|
| committer | main <main@swarm.moe> | 2026-03-31 14:21:52 -0400 |
| commit | c927ac1c6e041f96326e4a32e76ca13da8f6f5be (patch) | |
| tree | 9e08adbfce36bfe2b306eb4e833807c2f799cbf3 /docs/bootstrap-retrofit.md | |
| download | rust_starter-c927ac1c6e041f96326e4a32e76ca13da8f6f5be.zip | |
Initial import
Diffstat (limited to 'docs/bootstrap-retrofit.md')
| -rw-r--r-- | docs/bootstrap-retrofit.md | 156 |
1 files changed, 156 insertions, 0 deletions
diff --git a/docs/bootstrap-retrofit.md b/docs/bootstrap-retrofit.md new file mode 100644 index 0000000..c07be56 --- /dev/null +++ b/docs/bootstrap-retrofit.md @@ -0,0 +1,156 @@ +# Retrofit + +Use this surface when the target repo already has Rust linting, but you want to tighten the ratchet without clobbering valid local structure. + +## Goal + +Migrate the repo toward the `rust-starter` posture while staying diff-aware: + +- preserve stricter existing rules +- preserve justified local carve-outs +- delete duplicated policy only after it has been re-homed into the manifest +- never paste the fresh template wholesale over a living repo +- add a hard source-file line cap without erasing stricter local limits or justified generated-code exemptions + +## First Pass: Inventory The Existing Surfaces + +Before editing, inspect all of the places the repo may already encode lint policy: + +- root and member `Cargo.toml` +- `check.py`, `xtask`, shell wrappers, `justfile`, or other runners +- CI workflow files +- any existing auto-fix or canonicalization commands +- `clippy.toml` +- `rust-toolchain.toml` +- editor settings +- existing oversized source files, checked-in generated code, and any current file-length checks + +You are trying to answer two questions: + +1. What policy already exists? +2. Where is that policy duplicated? + +## Comparison Baseline + +Use these as the baseline, not as paste targets: + +- [template/fresh](/home/main/programming/projects/rust_starter/template/fresh) +- [docs/rust-linting-proposal.md](/home/main/programming/projects/rust_starter/docs/rust-linting-proposal.md) + +The fresh template tells you the shape to converge toward. The proposal doc tells you why. + +## Rules Of Engagement + +### Preserve stricter local policy + +If the repo already does something stricter than the template, keep it unless there is a clear repo-specific reason to relax it. + +Examples: + +- stricter rustdoc policy +- additional Clippy bans +- a more demanding deep gate +- an intentionally tighter unsafe policy +- a lower source-file line cap + +### Preserve justified local exceptions + +If the repo has exceptions with clear local justification, do not erase them just because the template lacks them. + +Instead: + +- move them into root `Cargo.toml` if they are repo-wide +- keep them local if they are truly local +- add or preserve comments / `reason = "..."` +- use `workspace.metadata.rust-starter.source_files.exclude` for checked-in generated Rust or other deliberate file-cap carve-outs + +### Remove duplicate policy, not local intent + +When a repo encodes the same allowlist in both `Cargo.toml` and a runner script, the script copy should die. + +But only remove the script copy after the equivalent manifest-owned policy exists and the runner still executes the same effective gate. + +### Treat `clippy.toml` as configuration-only + +If the repo uses `clippy.toml` to hold the main allow/deny architecture, migrate that policy into `[workspace.lints.clippy]`. + +Keep `clippy.toml` only for structured knobs such as test allowances. + +## Ratchet Order + +Apply tightenings in this order: + +### 1. Pin or refresh the toolchain + +Add `rust-toolchain.toml` if missing. If present but floating, pin it. + +### 2. Install workspace lint tables + +Move repo-wide policy into root `[workspace.lints.*]`. + +### 3. Make member crates inherit explicitly + +Add `[lints] workspace = true` to every member crate. + +### 4. Re-home script flags into the manifest + +Take inline `cargo clippy -- -A/-D ...` tails from scripts and CI, and migrate them into the root manifest in grouped, commented form. + +### 5. Tighten local suppression discipline + +Prefer: + +- `#[expect(..., reason = "...")]` for temporary or evidence-backed suppressions +- `#[allow(..., reason = "...")]` only for stable local policy + +### 6. Simplify the runner + +Once the manifest is authoritative, collapse the runner to orchestration-only plus generic manifest-backed checks such as the source-file cap. + +If the repo already has an auto-fix pass, re-home it into root `workspace.metadata.rust-starter.canonicalize_commands` and make the default local `check` path invoke it before the verification gate instead of relying on engineers or agents to remember a separate pre-pass. + +### 7. Install the source-file cap + +Add `[workspace.metadata.rust-starter.source_files]` to the root manifest and set `max_lines` deliberately. + +Default to `2500` if the repo has no existing stance. If the repo already enforces a stricter cap, keep the stricter value. If the repo has checked-in generated Rust that would make the rule meaningless, exclude those paths explicitly instead of disabling the whole mechanism. + +### 8. Add deep-gate posture if the repo is ready + +Add `cargo hack`, docs, and dependency-hygiene checks only when the repo can support them without turning the whole effort into churn theater. + +## Special Cases + +### Existing `expect_used = "allow"` + +Do not keep this globally just because the repo had it historically. Check whether the real need is test-only ergonomics. If so: + +- set `expect_used = "deny"` in the root manifest +- move the relaxation into `clippy.toml` via `allow-expect-in-tests = true` + +### Existing repo-specific carve-outs + +If the repo carries exceptions for domain-heavy code such as geometry, parsing, or numerics, expect some of them to stay. The goal is not zero exceptions. The goal is explicit, centralized, justified exceptions. + +### Checked-in giant files + +If the repo already contains Rust files over the default cap, do not blindly raise the limit to fit them. Decide whether each file should be split, exempted, or accepted behind a tighter, evidence-backed local exception pattern. + +### Existing CI + +Do not rewrite CI into a second policy source. Make it call the canonical commands or the thin runner. + +If the repo wants CI to detect canonicalization drift rather than rewriting files in place, keep a non-mutating verification entrypoint such as `check.py verify` and let local `check` remain the mutating convenience path. + +## Acceptance Checklist + +- repo-wide policy lives in root `[workspace.lints.*]` +- every member crate inherits that policy explicitly +- scripts and CI no longer restate Clippy allowlists +- manifest-owned canonicalization exists where the repo wants auto-fixes, instead of living in shell aliases or tribal memory +- valid repo-specific exceptions remain intact and justified +- stricter pre-existing rules remain stricter +- the root manifest carries an intentional source-file cap and any justified exclusions +- fast gate commands still match the repo’s effective behavior + +Use [docs/rust-linting-proposal.md](/home/main/programming/projects/rust_starter/docs/rust-linting-proposal.md) when you need to justify a ratchet choice, not as a blind replacement spec. |