coding-tools 0.6.1

Declarative, agent-friendly CLI tools behind one 'ct' command: search, view, verifiable edits, and framed command tests.
Documentation
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
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
# ct rules by example — a worked rule for each invariant category

> A practical companion to `ct-rules --explain` and `docs/specs/rules.md`:
> one copy-pasteable rule for every category in the catalog (`rules.md` §6),
> plus the rule *shapes* — adapters, lanes, severity, defs — that cut across
> them. Each entry says what to type and **why it earns its place in the
> store**.

A rule is recorded with `ct rules --add ID --question … -- PROBE`. `--add`
gate-validates the probe and **runs it immediately**: it is recorded only if
it holds right now, so a green store is an established truth, not a wish. The
probe is an argv vector (never a shell); it scans for known violations and a
failing probe arrives carrying its own evidence. `ct check` re-verifies the
whole store forever after.

Every probe below uses only the suite's read-only tools or the compiled-in
bridge, so all of them pass the gate. Where the *native* observer for a
category is not built yet, the example uses the supported approximation today
and names the native form on the roadmap — the same honesty `rules.md` §6
keeps.

## How to read each entry

- The **probe** is everything after `--`.
- **Why it's valuable** is the one-line case for making it a *project* rule —
  a recorded invariant the whole team and every agent inherit.
- **Today** says whether the form is native, an approximation, or bridged, so
  you never adopt a probe that can't actually enforce.

---

## The catalog, by example

### 1. Layer ordering

Dependencies between named layers flow one way — the single most-cited
architecture rule.

```sh
# Workspace-member layering: the api crate must not reach the db crate.
ct rules --add api-not-on-db \
  --question "Does the api crate stay off the db crate?" \
  --why "the api layer reaches storage only through the service layer" \
  --tag layering \
  -- deps --forbid 'my-api=>my-db'
```

```sh
# The whole ordered stack in one rule: api may use service may use db, never up.
ct rules --add layer-order \
  --question "Do the crates respect the layer order?" \
  --why "api -> service -> db is one-way; an upward edge inverts the architecture" \
  --tag layering \
  -- deps --layers my-api,my-service,my-db --layers-closed
```

```sh
# Module-level layering inside one crate: nothing in domain may import infra.
ct rules --add domain-no-infra \
  --question "Is the domain layer free of infrastructure imports?" \
  --why "domain code must not depend on infrastructure" \
  --tag layering \
  -- mods --layers 'infra*,domain*'
```

**Why it's valuable:** one-way dependencies are what keep a codebase
changeable — a violation means a lower layer has reached up and pinned an
upper one in place. The native form reports the offending dependency path as
evidence.

**Today:** workspace-member layering is native — a single boundary via
`deps --forbid`, or the whole ordered stack via `deps --layers` (with
`--layers-closed` for exhaustiveness, naming every offending member);
module-level layering inside one crate is native too — `mods --layers` over
the heuristic `use`-edge graph (ct-outline honesty class).

### 2. Cycle freedom

No dependency cycles among crates or modules — "cycles are the death of
modularization."

```sh
# One probe sweeps the whole workspace-member graph for cycles.
ct rules --add no-crate-cycles \
  --question "Is the workspace free of crate dependency cycles?" \
  --why "a cycle defeats incremental builds and local reasoning" \
  --tag cycles \
  -- deps --acyclic --members
```

```sh
# Or pin one specific boundary by forbidding its back-edge.
ct rules --add no-db-to-api-cycle \
  --question "Is the api/db boundary acyclic?" \
  --why "a db -> api edge would close a dependency cycle" \
  --tag cycles \
  -- deps --forbid 'my-db=>my-api'
```

**Why it's valuable:** a cycle defeats incremental builds and local
reasoning — you can no longer understand or rebuild one side without the
other. Each violation carries a concrete cycle path as evidence.

**Today:** native — `deps --acyclic --members` sweeps the workspace-member
graph in one probe (`--members` is what keeps it actionable: crate cycles among
third-party deps are unfixable dev-dependency noise). Module-level cycles inside
a crate are native too — `mods --acyclic` over the heuristic `use`-edge graph.

### 3. Banned symbols & APIs

Never call or use X — Rust's popular form of clippy's `disallowed-methods` /
`disallowed-types`.

```sh
# No .unwrap() anywhere under src.
ct rules --add no-unwrap-in-src \
  --question "Is src free of .unwrap() calls?" \
  --why "unwrap() panics in production; surface the error instead" \
  --tag hygiene \
  -- ct-search --base src --ext rs \
       --grep '.unwrap(' --mode literal --expect none --detail
```

**Why it's valuable:** bans a footgun API across the whole project in one
line, and the failing probe lists every offending site so the fix path is
obvious.

**Today:** native textually (`ct-search --expect none`). For references text
can't see — aliased paths, re-exports — the bridge admits `rust-analyzer
search` (search-only SSR), at the cost of a cold workspace load the rule
accepts knowingly via `--timeout`.

### 4. Banned & allowed dependencies

The crate graph must not contain X (cargo-deny `bans`; the rust-lang tidy
allowlist).

```sh
# Native: no openssl in the shipped artifact (pure-Rust TLS only).
ct rules --add no-openssl \
  --question "Is the dependency tree free of openssl?" \
  --why "musl cross-builds require pure-Rust TLS" \
  --tag deps,portability \
  -- deps --deny openssl --edges normal
```

```sh
# Bridge alternative — the matcher adapter reads cargo tree's own words.
# cargo tree -i prints "did not match any packages" when the crate is absent.
ct rules --add no-openssl-bridge \
  --question "Is openssl absent from the tree?" \
  --expect-ok 'did not match any packages' \
  -- cargo tree -i openssl
```

**Why it's valuable:** keeps a crate with the wrong license, portability cost,
or security history out of the build for good; the native form reports the
shortest dependency path that proves the violation.

**Today:** native (`deps --deny`); equivalents are the bridged `cargo tree
-i` with an `--expect-ok` matcher, or `cargo deny check bans` deferring to
your `deny.toml`.

### 5. Sibling independence

Modules or features in a set must not depend on each other — plugins, bounded
contexts (import-linter's `independence`).

```sh
# Two plugin crates must each stay off the other (--forbid is repeatable).
ct rules --add plugins-independent \
  --question "Do the plugin crates stay independent of each other?" \
  --why "plugins must compose without a sibling dependency" \
  --tag boundaries \
  -- deps --forbid 'plugin-a=>plugin-b' --forbid 'plugin-b=>plugin-a'
```

**Why it's valuable:** independence is what makes a set of features swappable
and individually testable; one sibling edge quietly turns a plugin set into a
tangle.

**Today:** native per pair via repeated `--forbid`; for a larger set, fan
`deps --forbid` across the pairs with `ct-each`.

### 6. Role–name–location coherence

Things of a role are named and placed accordingly (`*Test` naming, entities
in the domain layer, binaries in `src/bin`).

```sh
# Types belong in modules — the crate root is wiring only.
ct rules --add no-types-in-crate-root \
  --question "Is the crate root free of type declarations?" \
  --why "lib.rs wires modules together; types live inside them" \
  --tag structure \
  -- ct-outline --base src/lib.rs --kind struct,enum --expect none
```

**Why it's valuable:** when role implies location, a reader (or an agent)
can navigate by convention instead of grep — and a misplaced type is caught
the day it lands, not at the next big cleanup.

**Today:** native — `ct-outline --kind`/`--match` composes with `ct-search
--name`; defs keep the role vocabulary in one place.

### 7. Boundary leak prevention

Internal types don't surface in the public API; visibility stays tight.

```sh
# The public surface must not re-export the internal module.
ct rules --add no-internal-reexport \
  --question "Does the public API avoid re-exporting internals?" \
  --why "internal types must not leak through the crate's public surface" \
  --tag boundaries \
  -- ct-search --base src/lib.rs \
       --grep 'pub use crate::internal' --mode literal --expect none --detail
```

**Why it's valuable:** a small, deliberate public surface is what lets you
refactor internals freely; every accidental `pub use` is a future breaking
change waiting to happen.

**Today:** a text approximation (the re-export scan). Semantic `pub(crate)`
visibility reporting is a planned `ct-outline` extension, after which this
becomes exact rather than heuristic.

### 8. Dependency hygiene: duplicates & versions

No duplicate crate versions; workspace-unified versions (cargo-deny
`multiple-versions`, `cargo tree -d`).

```sh
# Native: the version list is the evidence when an assertion fails.
ct rules --add no-duplicate-deps \
  --question "Is the tree free of duplicate crate versions?" \
  --why "duplicate versions bloat builds and split trait impls" \
  --tag deps \
  -- deps --duplicates
```

```sh
# Bridge alternative — the `empty` adapter: holds iff the probe prints nothing.
ct rules --add no-duplicate-deps-bridge \
  --question "Is the tree free of duplicate crate versions?" \
  --expect empty \
  -- cargo tree -d
```

**Why it's valuable:** an accidental second version of a crate silently bloats
the binary and fractures trait coherence (two `serde::Serialize`s that aren't
the same trait) — exactly the kind of drift no one notices until it bites.

**Today:** native (`deps --duplicates`) or bridged (`cargo tree -d` with
`--expect empty`).

### 9. Supply-chain policy

License allowlist, trusted sources, no known advisories — cargo-deny's whole
domain, the clearest "leverage, don't rebuild" case.

```sh
# Defer to the project's own deny.toml; the rule gives it a question and a why.
ct rules --add supply-chain-clean \
  --question "Does the dependency set pass our deny.toml policy?" \
  --why "license allowlist, trusted sources, and bans are release gates" \
  --tag supply-chain \
  -- cargo deny check
```

```sh
# Fresh advisories need the network — a reviewed, per-rule opt-in.
ct rules --add advisories-clean \
  --question "Is the tree free of known advisories?" \
  --why "block shipping against a freshly disclosed advisory" \
  --network --timeout 60 \
  --tag supply-chain \
  -- cargo deny check advisories
```

**Why it's valuable:** policy stays exactly where it belongs (`deny.toml`),
and the rule earns it a seat in the unified `ct check` report so a license or
advisory regression fails the same gate as everything else.

**Today:** native via the bridge; hermetic by default, with `--network` the
visible opt-in where freshness matters.

### 10. Presence contracts

Every X has its Y — every file a license header, every module a test, every
public item documented.

```sh
# Every Rust source must carry an SPDX header (per-file probe via the walker).
ct rules --add license-headers \
  --question "Does every Rust source carry an SPDX header?" \
  --why "every file must declare its license" \
  --tag hygiene \
  -- ct-each --base src --name '*.rs' -- \
       ct-search --base '{ITEM}' --grep 'SPDX-License-Identifier' --quiet
```

**Why it's valuable:** "every X has its Y" is the broadest invariant family of
all, and `ct-each`'s walker item source turns it into one rule — the
violation report names each file that is missing its Y, so it doubles as a
to-do list.

**Today:** native — the `ct-each` walker for per-file presence, `ct-outline
--kind` for per-symbol presence.

---

## Rule shapes & lifecycle

The ten above all vary one underlying form. These are the knobs that form
exposes — each is its own kind of rule.

### Three ways to read a probe (outcome adapters)

Suite observers speak the exit contract (`0` holds / `1` violated / `2`
broken), so they need no adapter — every `ct-search`/`deps`/`ct-outline`/
`ct-each` example above relies on it. Bridge tools don't, so a rule supplies
one at `--add`:

- **`--expect empty`** — holds iff the probe exits `0` printing nothing (the
  `cargo tree -d` shape in §8).
- **`--expect-ok PATTERN` / `--expect-err PATTERN`**`ct-test`-style
  matchers over the probe's output, `err` decisive over `ok` (the `cargo tree
  -i` shape in §4).

### Park an aspiration: `--pending`

Record the goal the day you decide it — before the code is clean.

```sh
ct rules --add no-unwrap-in-src --pending \
  --question "Is src free of .unwrap() calls?" \
  -- ct-search --base src --ext rs --grep '.unwrap(' --mode literal --expect none

ct rules --promote no-unwrap-in-src   # re-runs the probe; enforces once it holds
```

**Why it's valuable:** `ct check` reports a pending rule in its own lane
without failing the build, so an aspiration is visible and tracked instead of
living in someone's head; `--promote` flips it to enforced the moment it
actually holds.

### Soft enforcement: `--severity warn`

The probation ramp before a rule blocks merges.

```sh
ct rules --add no-todo-comments --severity warn \
  --question "Is src free of TODO comments?" \
  -- ct-search --base src --ext rs --grep TODO --expect none
```

**Why it's valuable:** a violated `warn` rule reports in the `WARN` lane but
never reddens the exit, so you can socialize a new rule — and let the existing
violations drain — before tightening it to `fail`.

### Shared vocabulary: defs

Name a set once; reference it everywhere as `{def:NAME}`.

```sh
ct rules --def 'banned-apis=[".unwrap(", ".expect("]'
ct rules --add no-panic-prone-calls \
  --question "Is src free of panic-prone calls?" \
  --tag hygiene \
  -- ct-each --items '{def:banned-apis}' --quiet -- \
       ct-search --base src --ext rs --grep '{ITEM}' --mode literal --expect none
```

**Why it's valuable:** the banned set lives in exactly one place — a list def
splices into `ct-each --items`, so adding a fourth banned call is a one-line
def edit, not a hunt through every rule that referenced the old three.

### Keep the request: `--prompt`

`--prompt TEXT` (or `--prompt file:PATH`) retains the verbatim human ask
behind a rule as provenance — months later it is the difference between
editing the rule confidently and guessing at intent. Verification never reads
it; `ct rules --flatten` strips every retained prompt in one pass once it has
served its purpose.

---

## Runners-up worth recording

Not top-ten by citation, but real project rules today:

- **Orphan / dead structure** — a core type no longer referenced anywhere
  signals an unfinished refactor: `ct-each --items '{def:core-types}' -- ct-search
  --base src --grep '{ITEM}' --quiet` (holds while every name is still used).
- **Test placement** — assert a convention with `ct-outline`/`ct-search`
  framing, e.g. a `tests` module present where one is expected.

Size and complexity thresholds (god modules, fan-out) are visible today with
`ct-tree --min-lines`/`--max-lines` as a *report*, but `ct-tree` carries no
verdict framing, so it is not yet a single-probe rule — treat it as a review
aid until a thresholded observer lands.

## See also

- `ct-rules --explain` — the canonical reference for the writing side
  (`docs/explain/ct-rules.md`).
- `ct-check --explain` — how the store is verified, and the five lanes.
- `docs/specs/rules.md` — the surface spec: store schema, the probe gate, the
  bridge, and the full §6 catalog these examples realize.