1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
|
# 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:
- install the canonical Rust style doctrine link in target `AGENTS.md`
- 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
- move stale hardcoded Rust versions toward the latest system toolchain unless an explicit external MSRV constraint forbids it
- add or preserve project-local Cargo target-dir config under `/data/main/cargo-target/<project-slug>`
- 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`
- `.cargo/config.toml` and any runner-side `--target-dir` usage
- `AGENTS.md` and any nested agent instruction files
- `check.py`, `xtask`, shell wrappers, `justfile`, or other runners
- CI workflow files
- any existing auto-fix or canonicalization commands
- `clippy.toml`
- `rust-toolchain.toml`
- active `rustc --version` and `cargo --version`
- 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)
- [docs/rust-style-doctrine.md](/home/main/programming/projects/rust_starter/docs/rust-style-doctrine.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. Install style inheritance
Add or preserve a target `AGENTS.md` link to
[docs/rust-style-doctrine.md](/home/main/programming/projects/rust_starter/docs/rust-style-doctrine.md).
Do not overwrite local agent instructions; merge the doctrine link into them.
This is intentionally infectious. Future agents should read the Rust style
doctrine by entering the target repo, not only by invoking this bootstrap skill.
### 2. Use the latest system Rust toolchain
Inspect the active system `rustc --version` and `cargo --version`. Do not paste
a Rust version from this starter and do not preserve a stale numeric toolchain
pin unless the target repo has an explicit external MSRV or compatibility
constraint.
If the target records `rust-version`, derive it from the active system `rustc`
unless that explicit constraint says otherwise.
### 3. Install project-local Cargo target config
Add or merge a target repo `.cargo/config.toml`:
```toml
[build]
target-dir = "/data/main/cargo-target/<project-slug>"
```
Use the target repo directory name as the default slug; append a short stable
disambiguator if the basename would collide with another local repo. Preserve
unrelated Cargo config if the file already exists.
Do not mutate user-global Cargo config during retrofit. This is an incremental
repo-local opt-in: repos without the file continue to inherit the developer's
global default. Also do not encode target-dir through runner flags or CI. If a
script currently passes `--target-dir` only to keep artifacts off the source
disk, migrate that intent into `.cargo/config.toml` and remove the flag after
the config is present.
### 4. Install workspace lint tables
Move repo-wide policy into root `[workspace.lints.*]`.
Preserve the starter profile's wildcard/glob carve-outs unless the target repo
has a stronger explicit reason to ban them. Compact `*` imports are a house
style affordance for token economy, not a smell to suppress globally.
### 5. Make member crates inherit explicitly
Add `[lints] workspace = true` to every member crate.
### 6. 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.
That includes `-D warnings`: if warnings are meant to be fatal, encode that as
`warnings = "deny"` under `[workspace.lints.rust]` instead of leaving it as a command-line tail.
### 7. Tighten local suppression discipline
Prefer:
- `#[expect(..., reason = "...")]` for temporary or evidence-backed suppressions
- `#[allow(..., reason = "...")]` only for stable local policy
### 8. 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.
### 9. Install the source-file cap
Add `[workspace.metadata.rust-starter.source_files]` to the root manifest and set `max_lines` deliberately.
Default to `3000` 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.
### 10. 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.*]`
- target `AGENTS.md` links to [docs/rust-style-doctrine.md](/home/main/programming/projects/rust_starter/docs/rust-style-doctrine.md)
- target `.cargo/config.toml` routes Cargo output to `/data/main/cargo-target/<project-slug>` without changing user-global Cargo config
- every member crate inherits that policy explicitly
- scripts and CI no longer restate Clippy allowlists
- scripts, CI, and manifest-owned command vectors do not pass `--target-dir`
- the target uses the latest system Rust toolchain unless an explicit external MSRV or compatibility constraint says otherwise
- 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.
|