rust-intel-cc 0.4.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md ADDED
@@ -0,0 +1,887 @@
1
+ # Changelog
2
+
3
+ Format — [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), versioning — [SemVer](https://semver.org/).
4
+
5
+ Major = breaking changes to BANNED/REQUIRED wording that tooling depends on.
6
+ Minor = new categories or substantive additions.
7
+ Patch = wording refinements, fixes, new sources.
8
+
9
+ ## [0.4.5] — 2026-06-17
10
+
11
+ **Distribution: Claude Code plugin marketplace + npm.** Two install paths that need no clone, alongside the existing shell installers.
12
+
13
+ ### Added (distribution)
14
+
15
+ - **`.claude-plugin/plugin.json` + `.claude-plugin/marketplace.json`** — the repo is now a Claude Code plugin *and* its own marketplace. Install: `/plugin marketplace add PHPCraftdream/rust-intel` → `/plugin install rust-intel@rust-intel`. The plugin loads the skill from `./skill/` (single-skill layout — `SKILL.md` frontmatter gains the `name: rust-intel` field so the invocation name survives cache-directory renames) and the commands from `./commands/rust-intel-cc/` under the plugin namespace (`/rust-intel:audit`, `/rust-intel:fix`, `/rust-intel:plan`). Updates flow through `/plugin` — no more uninstall/reinstall. **Release-checklist note:** `plugin.json`'s explicit `version` gates plugin updates — bump it in every release alongside README/CHANGELOG.
16
+ - **npm package `rust-intel-cc`** — `npx rust-intel-cc` installs project-locally (`./.claude/`), `--user` for global, `--uninstall` for the inverse; honors `CLAUDE_CONFIG_DIR`. Zero-dependency Node installer (`bin/install.js`) mirroring the shell installers' layout (flat `/rust-cc-*` command names). Package ships `skill/`, `commands/rust-intel-cc/`, both licenses, README, CHANGELOG.
17
+ - **`.github/workflows/npm-publish.yml`** — publishes to npm on every `v*` tag with `--provenance`; the package version is derived from the tag at publish time (`npm version "${GITHUB_REF_NAME#v}"`), so `package.json`'s version field can never drift from the release tag. Pipeline sanity-checks (`node --check` both JS files, file-presence asserts) and smoke-tests the installer in a sandbox before publishing. Requires the `NPM_TOKEN` repository secret.
18
+ - **`README.md`** — the Install section now leads with the plugin (recommended, auto-updates), then npx, then the shell installers; Layout tree gains `.claude-plugin/`, `bin/install.js`, `package.json`, and the workflow.
19
+
20
+ **Substitution catalog — a cheaper representation for the same job.** A Tier E appendix in `skill/data-and-types.md`: a write-time lookup table (pattern → cheaper alternative → gate) so the agent can *propose* representation swaps instead of only flagging hazards. No new categories (still **58**), no tier changes — the catalog rides §E2/§E3/§E4 and is bound by §E6's measure-first law.
21
+
22
+ ### Added
23
+
24
+ - **`skill/data-and-types.md` — Substitution catalog** (after §E3), four groups, ~22 rows: **ownership & allocation** (`Cow` instead of clone, `Arc<T>`/`Arc<str>` for shared immutables, `bytes::Bytes` for zero-copy pipelines, `compact_str`/`smol_str`, `SmallVec`/`arrayvec`/`tinyvec`, `write!`+`itoa`/`ryu` instead of hot `format!`); **lookup & complexity** (hoist to `HashSet`, `VecDeque`, sort-once/`BinaryHeap`, dense-key `Vec`/`slab`/`slotmap` instead of `HashMap<usize,_>`, bitsets, `match`/`phf` for fixed key sets, linear-scan `Vec<(K,V)>` for tiny dynamic maps, `BTreeMap` for range/order, `indexmap` for deterministic iteration — also the honest fix for iteration-order-dependent tests (§D1), `memchr`); **keys & hashing** (the hasher ladder — `FxHashMap` for integer keys, `foldhash` as the balanced string default, `ahash`; `fnv` marked obsolete, `gxhash`/`wyhash` niche behind §A1 + target-CPU; all behind §B16's trust boundary; `Borrow<str>` lookup instead of `get(&key.to_string())`, `entry()` instead of `contains_key`+`insert`); **concurrent maps by access shape** (the three architectural families: sharded locks — `dashmap`; per-bucket/lock-free reads — `scc`, `papaya`/`flurry` behind §A1; read-copy snapshots — `arc-swap`, `evmap`/left-right with the consistency-model gate; ordered concurrent — `crossbeam-skiplist`/`scc::TreeIndex`; plus the "global `Mutex<HashMap>` → first ask §A2/§E4's design question" row). Calibration built in: every row 🟡 under §E6, new crates via §A1, §B2's guard-across-`.await` rule restated for every guard-returning concurrent API, closing "when NOT to substitute" paragraph (cold path, unmeasured win, public-API type = semver event §C1, lost properties).
25
+ - **`skill/SKILL.md`** — one phrase-trigger row ("which container should I use", "avoid this clone", "too many allocations", "can this be cheaper", "which concurrent map / hasher" → §E2/§E3/§E4 + catalog).
26
+ - **`skill/concurrency-and-state.md` — §E4** gains a pointer to the catalog's concurrent-map selection table and hasher ladder.
27
+ - **`docs/sources.md`** — one documented-mechanism entry grounding the catalog's crates (bytes, smallvec, compact_str, slab/slotmap, fixedbitset, phf, memchr, itoa/ryu, indexmap, dashmap, scc, papaya/flurry, evmap/left-right, arc-swap, crossbeam-skiplist) and std's `Borrow<str>`/`entry()` contracts; no numeric claims, per the Tier E precedent.
28
+
29
+ ## [0.4.4] — 2026-06-15
30
+
31
+ **§D1a gains a fourth shape — the façade fitted to the test.** One bullet, no new categories (still **58**), no new tier. A defect class observed at scale in LLM-generated Rust: where the *circular oracle* (§D1a shape 1) writes the test from the code, the *façade fitted to the test* (new shape 4) writes the code from the test. Given the goal "make this test pass," an agent emits the declaration the test probes (a config key, a header, a status string) and nothing behind it. Unlike shapes 1–3, the test itself is valid — it merely under-specifies the feature, and the model optimizes into the gap (Goodhart on the suite).
32
+
33
+ ### Changed
34
+
35
+ - **`skill/testing.md` — §D1a** gains shape (4) in the trap intro, one BANNED bullet ("treating a declaration-only test as evidence a behavioral feature works" — with the Grit SHA-256 example), and one REQUIRED clause sharpening the audit-mode counterfactual against this shape: *would a stub that emits exactly the observables this suite checks — and nothing else — still pass?* If yes, the suite specifies a façade, not the feature; ship at least one end-to-end test that **uses** it. Calibration carried in the BANNED bullet: the declaration test itself is fine (§D1's "contract pins are not vacuous" carve-out); *relying on it as behavioral evidence* is the defect.
36
+ - **`skill/SKILL.md`** — one phrase-trigger row added ("make this test pass", "implement just enough to pass", "get the suite green", "satisfy these assertions" → §D1a façade).
37
+ - **`commands/rust-intel-cc/fix.md`** — one routing row added (a feature's tests are green but using it end-to-end fails or corrupts data; tests only check a config flag/header/status string → §D1a façade).
38
+ - **`docs/sources.md`** — new entry in the test-oracle-validity subsection: **Chacon 2026, "Grit"** — narrative field report of a ~360k-LOC LLM-generated Rust reimplementation of Git that reported 41,715 / 42,001 (99.3%) of Git's test suite green while its author publicly stated the build is "not *tested*" and "may even corrupt stuff," with the SHA-256 façade documented in his own words. Observed-pattern grounding (criterion c). Doubles as external corroboration of the spec's central premise that `cargo test` green ≠ correct.
39
+ - **`README.md`** — status block for v0.4.4.
40
+
41
+ ## [0.4.3] — 2026-06-15
42
+
43
+ **Siblings of safe-looking primitives** — three bullets, no new categories (still **58**). Each is a sibling-framed addition to an existing §: a safe-looking primitive has a look-alike that reintroduces the very hazard the primitive removes. The category count, tier list, module headers, `MODULES`/`AUDIT_UNITS`, and lettered-split parent list are all unchanged. Calibrated by an independent review pass: §B9 panic-propagation event corrected (`scope` *returns* via `resume_unwind`, not `Scope::drop`); §B16 REQUIRED gains the untrusted-*pattern* defense (`RegexBuilder::size_limit` + pattern-length cap — stacked counted repetition blows up program size on the linear engine too); `sources.md` no longer overstates `regex` as an "explicit linear-time guarantee" (the doc states `O(m·n)` worst case, with `O(m·n²)` for iterator methods).
44
+
45
+ ### Changed
46
+
47
+ - **`skill/security.md` — §C2 path-traversal recipe gains a TOCTOU caveat (CWE-367).** The existing recipe (reject `Component::Prefix`/`RootDir`/`ParentDir`; join; `canonicalize` + re-verify `starts_with(base)`) closes the *static* symlink case (a pre-existing link inside `base` pointing out) — but step (3) is not race-free against an attacker who can swap a component between the `canonicalize` and the subsequent `open` (a privileged process operating over an attacker-writable directory). The bullet now names the threat model: when the directory tree is not mutable by an untrusted party during the operation, the static check suffices; when it is, the check + `open` is a TOCTOU, and the race-free answer is component-wise `openat` + `O_NOFOLLOW` (or Linux `openat2(RESOLVE_BENEATH)`) — in Rust, the `cap-std` crate.
48
+ - **`skill/concurrency-and-state.md` — §B9 gains `std::thread::scope` (sync mirror of §B21).** Where `tokio::spawn` *detaches* work and loses it (§B21), `std::thread::scope` (stable 1.63) *force-joins* every child on the closing brace. Two silent consequences: (a) a child waiting on a resource the parent only releases *after* the scope deadlocks the closing brace — a non-lock deadlock invisible to §B9 lock-graph analysis; (b) a child panic re-panics the parent on scope drop (documented propagation), skipping any cleanup between the spawn site and the brace. REQUIRED: ensure children are reachable to completion before the brace (close channels / drop senders / signal cancellation *before* the scope ends); inspect each `ScopedJoinHandle::join()` when cleanup must run regardless. 🟡.
49
+ - **`skill/data-and-types.md` — §B16 gains ReDoS as the sibling of HashDoS (CWE-1333).** Same "untrusted input × non-linear primitive" shape, different primitive. The `regex` crate is linear by construction (RE2-style automaton; *why* it lacks lookaround/backreferences); LLMs reach for `fancy-regex`/`onig`/`pcre2` to recover those features and reintroduce backtracking — and with it catastrophic backtracking. BANNED: matching untrusted input with a backtracking-engine regex without an enforced input-size cap *and* a hard match timeout; an attacker-controlled *pattern* on any engine without those guards. REQUIRED: keep untrusted input on the `regex` crate; if a backtracking-only feature is genuinely needed, size-cap input and wrap the match in a hard timeout.
50
+
51
+ ### Infrastructure (consistency)
52
+
53
+ - **`skill/SKILL.md`** — five trigger rows added: two phrase ("open a path from untrusted input" → §C2 TOCTOU; "scoped threads / `std::thread::scope`" → §B9; "lookahead/lookbehind / fancy-regex / onig / pcre2" → §B16), three code-pattern (`canonicalize`+`open` under attacker-mutable dir → §C2; `std::thread::scope` child awaiting parent-controlled resource → §B9; `fancy_regex::Regex::new` / `onig::Regex::new` / `pcre2` on untrusted input → §B16).
54
+ - **`commands/rust-intel-cc/fix.md`** — three routing rows added (canonicalize+open TOCTOU → §C2; `thread::scope` hang / cleanup-skipped-by-panic → §B9; worker pinned at 100% CPU on regex match → §B16 ReDoS).
55
+ - **`docs/sources.md`** — new "Siblings of safe-looking primitives" subsection grounding §C2 TOCTOU (CWE-367 + `openat`/`openat2`/`cap-std`), §B9 `thread::scope` (std docs: auto-join + panic propagation), §B16 ReDoS (`regex` crate's untrusted-input section + CWE-1333). Documented-mechanism + observed-pattern; no numeric claims.
56
+ - **`docs/roadmap.md`** — new "Backlog — confirmed siblings, lower frequency" subsection holds the `tokio::sync::Semaphore` permit-lifecycle sibling (drafted, grounded, frequency too low to ship inline; promote on incident or reviewer flag).
57
+ - **`README.md`** — recall-honesty note after the "every category is backed by a source" paragraph: per-rule grounding ≠ measured coverage; an `examples/` regression corpus is the next infrastructure step (roadmap §4). Status block for v0.4.3.
58
+
59
+ ## [0.4.2] — 2026-06-14
60
+
61
+ **Concurrency / hang / flaky-test patterns** extracted and reviewed from real fixes: two new numbered categories plus three extensions. **56 → 58 categories** (§B3a is a lettered sub-section, counted under §B3; §D4/§D5 are new numbered categories). The two new categories and §B3a ship in the modules' `**The trap**`/`**BANNED**`/`**REQUIRED**` format and are grounded in `docs/sources.md` (the spec's "every category ships with a source" gate). Calibrated by an independent review pass: §B3a now distinguishes a bare `return` (a silent stall when the loop is the sole driver — escalate instead) from the livelock it fixes, and pairs the flag release with an `Acquire` on election (§B13); §B21 qualifies `Weak` (lifetime-coupled tasks) against `AbortHandle`/`CancellationToken` (explicit shutdown); §D1 no longer overstates `start_paused` (a lone interval auto-advances); §D5 drops an ungrounded lazy-handle aside; `fix.md` gains a §B21 "owner's `Drop` never runs" row.
62
+
63
+ ### Added
64
+
65
+ - **`skill/async.md` — §B3a Coordinator loops need a circuit-breaker on persistent error** (sub-section of §B3): a leader/drain/flush loop that retries a fallible op forever — completing waiters with `Err` but never *exiting* — livelocks on a persistent failure. REQUIRED: release leadership and `return` on a write/sync error so a fresh attempt re-enters from clean state; the leadership flag is released on every exit path, error included. 🟡.
66
+ - **`skill/testing.md` — §D4 Filtering test-runner output through `grep`/`head` hides hangs**: the filter silently drops `SLOW`/`TIMEOUT` lines (the only lines naming a hanging test) and, without `set -o pipefail`, masks the runner's non-zero exit so a deadlock reads as green. The `pipefail` foot-gun is generic; the `| grep`-on-every-test-run reflex is the agentic-Rust angle. BANNED: gating "passed" on a filtered live pipe without `pipefail`; raising a timeout to silence `SLOW`/`TIMEOUT`. REQUIRED: gate on the runner directly or tee-to-file-then-grep-the-file; root-cause every `SLOW`/`TIMEOUT` as a deadlock. 🟡.
67
+ - **`skill/testing.md` — §D5 Windows: a hung test process wedges the next link (LNK1104)**: a lingering zombie test process holds its own hashed `.exe`, failing the next link step (`LNK1104`) — one flake becomes a build cascade. Framed as CI-hygiene defense-in-depth: the root fix is the hang itself (§D4); REQUIRED on Windows CI is reaping stray `<crate>-<hex>.exe` at run start. 🟡.
68
+
69
+ ### Changed
70
+
71
+ - **`skill/async.md` — §B2** extended: a `dashmap::DashMap` / `scc::HashMap` `Ref`/`RefMut` held across `.await` is a synchronous per-shard `RwLock` invisible to `clippy::await_holding_lock`; holding it across an init `.await` deadlocks the shard. REQUIRED for async lazy-init in a concurrent map: store `Arc<OnceCell<T>>`, clone out of the map, drop the shard guard before awaiting the initializer. Cross-note added at §B13's `entry().or_insert_with()` endorsement ("synchronous only — see §B2").
72
+ - **`skill/async.md` — §B21** extended: a spawned periodic task (timer-driven flush/reaper/sweeper) must hold a `Weak<T>`, not a strong `Arc<T>`, to its owner and exit on `upgrade() == None` — a strong clone inverts RAII. Pair with a `..._exits_on_drop` test.
73
+ - **`skill/testing.md` — §D1** extended: for `interval`-driven background tasks under `start_paused`, `advance(period)` once per tick needed (the first tick fires immediately) and `yield_now().await` between advances so the spawned task runs between virtual-time steps.
74
+ - **`skill/SKILL.md`** — category count 56 → 58; §B3 added to the lettered-split parent list and §B3a to the sub-section enumeration; category→module map updated (§B3a → `async.md`; §D4/§D5 → `testing.md`); Tier D overview range §D1–§D3 → §D1–§D5; three self-monitoring trigger rows added (DashMap/concurrent-map lazy init + await → §B2; coordinator/leader/flush loop → §B3a; grep/filter test output → §D4).
75
+ - **`README.md`** — Tier D spec-architecture range §D1–§D3 → §D1–§D5 (threaded with the SKILL.md change; the recurring "range not propagated to README" gap, caught in review).
76
+ - **`docs/sources.md`** — new "Concurrency liveness & test-harness honesty" subsection grounding §B3a (circuit-breaker / bounded retry — Fowler, AWS Builders' Library), §D4 (`pipefail` Bash semantics + `cargo nextest` slow/timeout reporting), and §D5 (MSVC `LNK1104` + Windows image locking). Documented-mechanism + observed-pattern, no numeric claims — on the Tier E/F precedent.
77
+
78
+ ## [0.4.1] — 2026-06-10
79
+
80
+ **Tier F — Semantic conformance** (new tier, 4 categories) plus two testing additions: **51 → 56 categories**. Defects of *meaning* — code that compiles, passes its own tests and clippy, and implements the wrong thing. Unlike Tiers A–E these are not found by pattern-matching; the reviewer reads the *claim* (spec, README, function name) and checks the code against it counterfactually.
81
+
82
+ ### Added
83
+
84
+ - **`skill/semantics-and-conformance.md`** — new (10th) theme module holding Tier F:
85
+ - **§F1 Spec / reference conformance** — the code names an external source of truth (RFC, format, "port of X") and diverges from it; both halves of a round-trip can share the same misreading, so self-round-trip is not conformance. BANNED: implementing a named spec from memory; `_ =>` absorbing spec-mandated states; undocumented deviations. REQUIRED: cite the reference at the implementation site, test against external vectors/the reference implementation, enumerate spec states before checking. 🔴 when the divergence affects a wire format, security guarantee, or persisted data; 🟡 otherwise.
86
+ - **§F2 Documented guarantees** — the project's own README/SECURITY.md/design docs state guarantees in prose ("tokens are never logged", "this port is untrusted") and a locally-reasonable diff violates one. The doc, not the call graph, defines the trust boundary; a Tier-F review that cites no project doc has not performed §F2. Same 🔴/🟡 split as §F1.
87
+ - **§F3 Boundary / error-path resource lifecycle** — cleanup reachable from fewer paths than the acquisition (early-`?` leaks), no read deadline on an untrusted peer (connect-then-silence DoS), `Ok(0)`-EOF mishandling, proxy shutdown propagation, per-connection sibling tasks not aborted on every exit. 🟡; 🔴 when the leak is attacker-extendable.
88
+ - **§F4 Round-trip obligations for inverse pairs** — `encode`/`decode`, `parse`/`Display`, `encrypt`/`decrypt` written together but never proven inverse over the domain; one `proptest` round-trip property per pair, shipped in the same change; knows the difference between `decode(encode(x)) == x` and the canonical-form law. 🟡.
89
+ - **`skill/testing.md` — §D1a Oracle validity** (sub-section of §D1): the circular oracle (test written from the implementation; snapshot-blessing a new implementation), the world-erasing stub (in-memory transport hiding fragmentation/partial reads), and the missing negative control (a test that passes with the fix reverted is not evidence). The audit-mode counterfactual: *what mutation would this test catch?*
90
+ - **`skill/testing.md` — §D3 Test/prod divergence**: `cargo test` runs debug profile, toy sizes, single-task — production runs release, real scale, real concurrency. Release-profile CI run when arithmetic/`debug_assert!` is load-bearing; one boundary-scale test per documented limit; `loom`/stress for concurrency claims. (§B26 owns the debug-vs-release fixes; §D3 is its testing-side enforcement plus the scale and concurrency axes.)
91
+ - **`skill/SKILL.md`** — "Tier F — how to review for meaning" stance section (fetch the reference first; reason counterfactually; enumerate, don't sample; read the project's promises as a checklist); 6 phrase-trigger + 7 code-pattern rows for §F1–§F4/§D1a/§D3; conditional 🔴 entries for §F1/§F2 (wire format / security guarantee / persisted data) and §F3 (attacker-extendable); pre-flight grows 7 → 9 (reference claim check; inverse-pair round-trip obligation); category count and map updated (56; §D1 (a), §D3; §F1–§F4 → `semantics-and-conformance.md`).
92
+ - **`skill/audit-project.workflow.js`** — 11th audit agent (`semantics`); the scoper now inventories guarantee-bearing docs (`docsFiles`) and extracts a verbatim guarantee/spec-claim digest (`docsDigest`); only the semantics agent receives the digest — §F1/§F2 are unauditable from source alone.
93
+ - **`docs/sources.md`** — "Semantic conformance & test-oracle validity" section grounding the new categories on the Tier E precedent (normative/methodological, no numeric claims): RFC test-vector practice (RFC 8439 §2.8.2) for §F1; QuickCheck (ICFP 2000) + the proptest book round-trip for §F4; mutation testing / `cargo-mutants` for §D1a; CWE-404/772/400 + slowloris-class and the std/tokio `Ok(0)`-EOF contracts for §F3; Cargo Book profile defaults + `loom` for §D3; §F2 marked definitional. CRUST-Bench "Used in" extended with the external-oracle premise (§F1/§D1a).
94
+
95
+ ### Changed (conformance pass on the addition itself)
96
+
97
+ - **`commands/rust-intel-cc/audit.md` — Tier F was invisible to `/rust-cc-audit`** (recurrence of the v0.3.x "Tier D invisible" bug, caught in review this time): the category walk stopped at §E6 and grouping stopped at E. Now walks §A1 → §F4, groups A → … → E → F, carries the Tier F review stance (obtain reference + project docs first, enumerate, report unavailable references), and the Post-flight illustrative list gains the two conditional-🔴 Tier F lines.
98
+ - **`commands/rust-intel-cc/fix.md`** — routing table gains six symptom rows: green-on-pre-fix-code → §D1a; works-in-tests-breaks-in-prod → §D3; interop-fails-against-real-peer → §F1; behavior-contradicts-README → §F2; connection/FD leak on error paths / stalled-peer pin → §F3; round-trip corrupts on special characters → §F4.
99
+ - **`skill/SKILL.md`** — the fan-out section's theme enumeration now includes semantics/conformance (was missing the 10th theme).
100
+ - **`skill/semantics-and-conformance.md`** — header tier-line aligned to the other modules' template ("Derived from SKILL.md → Enforcement tiers (canonical)").
101
+ - **`README.md`** — six tiers + Tier F row in the spec-architecture table; Verify range §A1–§F4.
102
+
103
+ ## [0.4.0] — 2026-06-10
104
+
105
+ Fan-out audit workflow + module header enrichment for agent audit ergonomics. **No category changes** (still **51**).
106
+
107
+ ### Added
108
+
109
+ - **`skill/audit-project.workflow.js`** — shipped fan-out workflow for auditing a Rust project against the skill. One agent per module (async splits into two: await-discipline vs machinery/cost), with a Prepare phase that slices SKILL.md's trigger tables at runtime (zero knowledge duplication), structured FINDINGS_SCHEMA per agent, and a Synthesize phase that merges into the `/rust-cc-audit` report format with a Post-flight 🔴-summary. Closes G1 (trigger-table slicing), G3 (canonical finding schema), G5 (artifact-vs-process semantics), G6 (async split). Launched via `Workflow({scriptPath: '<skill-dir>/audit-project.workflow.js', args: {target, skillDir}})`.
110
+ - **Module headers enriched (all 9 theme modules).** Each module's blockquote header now carries a `Tiers in this module` line (derived from SKILL.md's Enforcement tiers — the canonical source) and an `Audit semantics` line (🔴/🟡/🟢 meaning + artifact-vs-process rule). Closes G2 (tier badges) and G4 (dangling operational references).
111
+
112
+ ### Changed
113
+
114
+ - **`skill/SKILL.md` — "Running a full pass" section** now points to the shipped `audit-project.workflow.js` instead of telling the reader to "write the equivalent fan-out". `dev/review-modules.workflow.js` (maintainer self-review) reference preserved.
115
+ - **`commands/rust-intel-cc/audit.md` — step 4** gains a "Fan-out preferred for broad scope" note directing to the workflow for whole-crate audits (serial walk remains the single-file fallback). Closes G7.
116
+ - **Installers** (`rust-cc-install.sh`, `rust-cc-install.ps1`) now copy `audit-project.workflow.js` alongside the `*.md` modules.
117
+
118
+ ## [0.3.3] — 2026-06-10
119
+
120
+ Accuracy pass — a completeness/correctness audit against the high-level Rust hazard map plus a version-date re-verification against primary sources. **No category changes** (still **51**); these are factual fixes and small clarifications to existing bodies.
121
+
122
+ ### Fixed (factual / dating)
123
+
124
+ - **`SKILL.md` version pins — `clippy::await_holding_lock` group history.** Previously claimed "warn-by-default (suspicious group) since clippy 1.45". Corrected: introduced in 1.45 (`correctness`, deny), downgraded to `pedantic` (allow) around 1.50, promoted to `suspicious` (warn-by-default) only in **Rust 1.61** — so on a 1.50–1.60 toolchain the bare `cargo clippy` does *not* emit it and the explicit `-W` is required. The present-day "bare clippy emits it" claim was already right; only the dating was wrong.
125
+ - **`SKILL.md` MSRV floor `1.84` → `1.85`.** Edition 2024 (which the spec targets) stabilized in **Rust 1.85**; a crate with `edition = "2024"` cannot build on 1.84, so the stated floor was internally contradictory. The strict-provenance API (stable 1.84) is subsumed by the 1.85 floor. The `Vec::into_raw_parts` pin's "MSRV floor is 1.84" line updated to match.
126
+ - **§B4a (`drop-and-raii.md`) — `never_type_fallback_flowing_into_unsafe` dating.** Previously "deny-by-default in edition 2024 (Rust 1.92)". Corrected: warn-by-default in all editions since **1.80**, deny-by-default in edition 2024, which stabilized in **Rust 1.85**. "1.92" was phantom precision.
127
+ - **§C7 (`deps-macros-ergonomics.md`) — target-specific feature unification.** The "`features = [...]` activates globally, not per-target (cargo#2524)" claim is **resolver v1** behavior. Under resolver v2 (default since edition 2021; the spec targets 2024) target-specific dependency features are not enabled for targets not being built. Now version-qualified and consistent with §C10's v1/v2 split.
128
+
129
+ ### Changed (clarity)
130
+
131
+ - **§B9 (`concurrency-and-state.md`)** — note that `parking_lot::deadlock::check_deadlock()` requires the `deadlock_detection` cargo feature (the module is absent without it).
132
+ - **§B12 (`security.md`)** — flagged that `rust-crypto` (in the API-hallucination example list) is itself unmaintained since 2016 (RUSTSEC-2022-0011) and must never be *proposed*; pointed to the maintained `RustCrypto` crates.
133
+ - **§B2 / §B15a (`async.md`)** — trimmed two internal duplications (the `await_holding_lock` blind-spot list; the RPITIT-vs-AFIT desugar explanation) to single statements with back-references.
134
+
135
+ ---
136
+
137
+ ## [0.3.2] — 2026-06-09
138
+
139
+ Content additions from a study of Microsoft's *Rust Patterns & Engineering How-Tos* training book, filtered hard through the spec's grounding bar (most of the book is either already covered, deliberately out-of-scope because the compiler catches it — e.g. `&` to a `repr(packed)` field is now the hard error `E0793` — or general style that does not clear the "systematic LLM mistake" test). Four additions survived; all are **within-category sub-sections / body expansions**, so the headline count stays **51** (sub-sections are counted under their parent, like §B1a/b, §B4a, §B15a–e).
140
+
141
+ ### Added
142
+
143
+ - **§C1a — missing `#[non_exhaustive]` on a published API's enums/structs** (`lifetimes-and-api.md`, 🟡). Author-side semver rule: a public enum/struct (especially an **error enum**) in a *published* crate without `#[non_exhaustive]` makes adding a variant/field a major break on consumer CI. Includes the struct-literal-construction cost (ship a constructor) and calibration (published-lib only; not for closed types; not retroactive). Complements the existing consumer-side §B6.
144
+ - **§B18a — variance & `PhantomData` soundness in raw-pointer wrappers** (`unsafe-and-ffi.md`, 🔴). Wrong/absent `PhantomData` on a hand-written `*const T`/`*mut T`/`NonNull<T>` type silently picks the wrong variance (covariance where invariance is needed → use-after-free with no `unsafe` at the call site), drop-check, or auto-traits. The hole is in the *type declaration*, not any `unsafe` block, so a per-block `// SAFETY:` (§B5) cannot catch it — hence its own 🔴 check, with `PhantomData`-selection guidance and a `compile_fail`-doctest recommendation.
145
+
146
+ ### Changed
147
+
148
+ - **§B4 expanded** (`drop-and-raii.md`) with three drop hazards: (1) the **memory-vs-resource fork at process exit** — memory-only `Drop` (a huge tree/map/arena) is wasted work at shutdown and may be skipped via `mem::forget`/`process::exit`, while resource-cleanup `Drop` must still run (resolves the apparent tension with the existing `process::exit`-skips-Drop ban); (2) **recursive auto-`Drop` overflows the stack** on deep self-owning structures (the §B7 DoS shape via the destructor) — write an iterative `Drop`; (3) the **drop-order shutdown deadlock** (a `JoinHandle` joined before its `Sender` closes → `join()` blocks forever) made concrete with the field/local drop-order rule and fix.
149
+ - **§B5 gained the unsafe→safe boundary principle** (`unsafe-and-ffi.md`): split invariants into **value-invariants** (runtime-checkable → a total fallible `&[u8] → Result<Typed, _>` constructor that runs *on the bytes before minting the type* and never panics on adversarial input) and **relational invariants** (lifetime/aliasing/provenance/`Send` — not runtime-checkable; the only defense is the upstream `// SAFETY:` proof + correct variance/markers). §B18a is the concrete relational-class special case.
150
+ - **`SKILL.md`** — trigger table and code-pattern table gained rows for all four additions; the 🔴 enforcement list gained §B18a; the category→module map now shows §C1 (a) and §B18 (a); the scope note now distinguishes safe-code variance (compile-caught, out of scope) from §B18a unsafe variance soundness (not caught).
151
+ - **`docs/sources.md`** — added normative references (the Rustonomicon variance/PhantomData/dropck chapters, the Cargo Book SemVer chapter, the Rust Reference destructors page, the `from_utf8`/`TryFromBytes`/`Pod` constructors) plus the Microsoft RustTraining book for provenance.
152
+
153
+ ---
154
+
155
+ ## [0.3.1] — 2026-05-31
156
+
157
+ Structural repackaging — **no rule changes, no category changes** (still **51**). The single-file spec was split into a **modular skill**: `SKILL.md` (core — scope, blocking protocol, operating mode, enforcement tiers, the trigger table, version pins, and a new **category→module map**) plus nine theme modules under `skill/` holding the category bodies. Motivation: a single ~50k-token file overloads a reviewing/auditing agent — it loses detail mid-document; per-module files let one agent go deep on one theme.
158
+
159
+ ### Changed
160
+
161
+ - **Split `rust-intel.md` → `skill/` modules** by theme: `async`, `unsafe-and-ffi`, `concurrency-and-state`, `data-and-types`, `security`, `drop-and-raii`, `deps-macros-ergonomics`, `lifetimes-and-api`, `testing`. Tier (A–E) is now a per-category **label**, not a file. Content is byte-complete — every original content line was verified present in the modules. The single-file `rust-intel.md` is **retired** (kept in git history under `v0.3.0`); cross-references between modules (e.g. `§B22 → §B4`) are navigational by design.
162
+ - **`SKILL.md` gained a `category→module map`** (which module holds each §) and a **"Running a full pass"** section: it instructs the agent, for a full audit/review, to **fan out one sub-agent per module** (via the Workflow tool) and synthesize — instead of grinding all ~51 categories in one context. A single trigger or one category match still applies inline.
163
+ - **Installers** now copy `skill/*.md` → `<target>/skills/rust-intel/` (the single-file reference is no longer installed); a prior monolithic `SKILL.md` is swept like any prior layout. `install.sh`/`install.ps1` updated and prove-tested; `uninstall.*` unchanged (already removes the whole directory).
164
+ - **Docs** — `README.md`, `commands/README.md`, `docs/roadmap.md`, `docs/sources.md` updated for the modular layout.
165
+
166
+ ### Added
167
+
168
+ - **`dev/review-modules.workflow.js`** — maintainer workflow: one agent per module (all lenses) → synthesis agent. The repeatable, one-command fan-out for reviewing the skill.
169
+
170
+ ---
171
+
172
+ ## [0.3.0] — 2026-05-29
173
+
174
+ Release 0.3.0 — the first tagged release since 0.2.2, collapsing all interim work (drafted under provisional 0.3.x / 0.4.0 labels and an `[Unreleased]` staging area, none of which were ever tagged on GitHub) into a single version. Net effect since 0.2.2: the taxonomy grew from **26 to 51 categories** across **five tiers (A–E)**. Work batches in this release: a fifth-pass accuracy/content batch, a sixth-pass **usability refactor**, a seventh-pass **final consistency/usability fix pass**, an eighth-pass **corrective pass** (external review — one verified bug, a 🔴-propagation gap, three undisclosed-precondition gaps, meta-layer recalibration), a **Tier E content batch** that opens a new top-level axis (systemic cost / performance) alongside the correctness tiers, and a **discipline-hardening batch** of within-category bullets (vacuous-test ban §D1, workspace version-unification + crate-boundary timing §C10, benchmark-as-regression-guard §E6). The first four batches left the category count unchanged at **44** (no categories added, cut, merged, or renumbered); the Tier E batch raises it to **50** (§E1–§E6) and the tier count from **four** to **five**; the discipline-hardening batch adds bullets only and keeps the count at **50**; the latest **review-driven pass** (external multi-agent review) adds one category (§B29) to bring it to **51**, alongside accuracy fixes, within-category completeness bullets, and anti-dogmatism calibration. Two subsequent verification passes — a second- and a third-review correction batch — fix residual defects, recalibrate a few rules, and add within-category security bullets (OS-command and SQL injection, allocation-size DoS, a Miri/FFI caveat, `cargo-vet`); both keep the count at **51**. Full per-iteration detail is preserved in the sub-sections below.
175
+
176
+ ---
177
+
178
+ ### Iteration: third-review corrections + security bullets
179
+
180
+ A six-lens internal agent review (correctness, internal consistency, signal-density, fix-precision, anti-zealotry, anti-stupidity) over the 0.3.0 content, cross-checked against three further external reviews (which contributed the SQL-injection, Miri/FFI, and `cargo-vet` items). Every edit is within-category — the count stays at **51**.
181
+
182
+ #### Fixed (factual)
183
+
184
+ - **§B17** — `Arc<RefCell<T>>` no longer described as "does not compile": it is `!Send`+`!Sync` (fine single-threaded), so it fails to compile only when *sent across threads* — now matching §A2's correct phrasing.
185
+ - **§B18** — `NonNull<T>` removed as a phantom `Send`/`Sync` remedy (it is `!Send`/`!Sync` like the raw pointer and closes no race); the real fix (`Arc<Mutex<RawHandle>>`, or a cited happens-before via `join`) is now the only one offered.
186
+ - **§B16** — inconsistent-comparator sort softened from "**panics**" to "**may** panic; in any case yields an unspecified order, never UB" (the std contract); `f64::to_bits` "conflates −0.0/+0.0" corrected to "splits them into two distinct keys" (the verb was backwards — the parenthetical was already right).
187
+ - **§B5** — `mem::uninitialized` deprecation date corrected October → November 2019.
188
+
189
+ #### Fixed (fix-precision)
190
+
191
+ - **§B4** — panic-in-`Drop` now names `std::thread::panicking()` as the primary guard (skip the fallible work while already unwinding); `catch_unwind` demoted to the secondary, non-panicking-path guard.
192
+ - **§C9** — the `spawn_blocking` span-restore fix now notes that `tokio::task_local!` values do **not** cross the `spawn_blocking` boundary, so request context (tenant/auth/request-id) must be captured explicitly before the call — otherwise the closure reads whatever last touched that pool thread.
193
+ - **§B13** — the "insert if absent" remedy split into a sync form (`entry` + `&mut`-borrow on `std::HashMap` — the false "bucket lock" claim removed — or a `DashMap` shard lock) and an async form (`OnceCell`-per-key), the latter now primary for the lazy-cache example the category opens with.
194
+ - **§B16** — the BANNED manual-`PartialEq`-without-`Hash` bullet scoped to types that actually implement/derive `Hash` or are used as keys (a never-hashed manual `PartialEq` is sound).
195
+ - **§B7** — `Box::<[u8]>::new_uninit_slice` flagged as UB on the partial-read pattern (a `read` returning `n < N` leaves an uninit tail that must never be sliced); `vec![0u8; N]` remains the safe form for partial-read buffers.
196
+ - **§C2** — the `Path::join` traversal guard rewritten as a sequence (reject a leading `Prefix`/`RootDir` **and** any `..`; join; then `canonicalize` + `starts_with(base)` only on the read path, to defeat symlink escape) rather than "reject `..` *or* canonicalize".
197
+
198
+ #### Changed (calibration)
199
+
200
+ - **§B2** — "note each `.lock().unwrap()` inline" relaxed to a codebase-level poison policy, flagged inline only where a cascade is a live concern (the idiom is too common to annotate per call — it was contradicting the inline-flag policy).
201
+ - **§B12** — the secret-field-name `Debug` trigger scoped by *role*: a lexer `Token`, a map/cache `key`, a deterministic-PRNG `seed` are not secrets — require a second crypto/auth signal before redacting (mirrors the §B24 `"HS256"` carve-out).
202
+ - **§B11** — the "CPU > ~100µs → `spawn_blocking`" line reframed as a judgment (a rough floor, not a trigger to offload every burst); short/frequent bursts → `consume_budget`/`yield_now`, data-parallel → `rayon`.
203
+ - **§B13 / 🔴 list** — the 🔴 entry rescoped to the `Relaxed`-publish data race only; the broader check-then-act/TOCTOU body is 🟡 (write-time), now stated in both the 🔴 list and the §B13 Detection note.
204
+ - **trigger table / version pins** — the "trait object" row now reads §B15a AFIT/RPITIT with the "not `dyn`-compatible without `async-trait`" risk; the "fifty-one" headline gained a note that §B1/§B4/§B15 split into individually-referenced lettered sub-sections; `Vec::into_raw_parts` (1.93) and match-guard let-chains (1.95) hedged "verify against your pinned toolchain".
205
+
206
+ #### Added (within-category security bullets — count unchanged)
207
+
208
+ - **§C2** — **OS command / argument injection**: `Command::new("sh").arg("-c").arg(format!(…))` (RCE) and leading-`-` argument injection; spawn the program directly, insert `--` before positional input, allowlist the executable. Plus phrase + code-pattern triggers.
209
+ - **§C2** — **SQL injection**: `sqlx::query(&format!(…))` / `diesel::sql_query(format!(…))`; bind parameters (`$1` + `.bind` / `sqlx::query!` / `QueryBuilder`), allowlist dynamic column/table identifiers. Plus phrase + code-pattern triggers.
210
+ - **§B7** — **attacker-controlled allocation size**: `Vec::with_capacity(attacker_n)` / `vec![_; n]` → OOM/abort DoS; clamp before allocating and prefer `Read::take(limit)`. Plus phrase + code-pattern triggers.
211
+ - **§B25** — Miri caveat: it interprets Rust, not native code, so a real `extern "C"` call aborts ("can't call foreign function"); point Miri at the Rust side and isolate the foreign call behind `#[cfg(not(miri))]`.
212
+ - **§A1** — `cargo-vet` (human-audit attestations) added to the supply-chain defenses alongside `cargo-deny`/`cargo-audit`.
213
+
214
+ _Shipped as part of release 0.3.0 (2026-05-29)._
215
+
216
+ ---
217
+
218
+ ### Iteration: second-review corrections
219
+
220
+ A second external multi-agent review (max-model, over the 0.3.0 content) verified the prior edits and surfaced a few defects to fix before tagging.
221
+
222
+ #### Fixed
223
+
224
+ - **§B26** — `dbg!` removed from the "compiled out in release" list: `debug_assert!`/`debug_assert_eq!` are stripped, but `dbg!` evaluates and prints in release too (a forgotten `dbg!` leaks to stderr) — now noted as the converse trap.
225
+ - **§B19** — `mem::replace` corrected: it leaves the *passed-in* value, not a `Default` (only `mem::take`/`Option::take` leave a `Default`); the data-loss hazard is unchanged.
226
+ - **README** — spec-architecture table Tier B range `§B1–§B28` → `§B1–§B29` (missed when §B29 landed).
227
+
228
+ #### Changed (calibration)
229
+
230
+ - **§A1 build-time** — the "audit any dependency that ships a `build.rs`/proc-macro" mandate narrowed to a *newly-added, non-well-known direct* dependency (the transitive graph is covered by `cargo-deny`/`cargo-audit`/committed `Cargo.lock`/`--locked`, not by hand); the "code you are about to run" aphorism dropped.
231
+ - **§B29** — the `Vec::dedup` BANNED bullet reframed from "on an unsorted collection" (hard to prove locally) to "no `sort`/grouping visibly preceding it"; the chunk-size trigger now notes literal sizes are fine.
232
+ - **§B16** — the float-sort bullet gained a provably-non-`NaN` exception (`NotNan`/`OrderedFloat`/`Duration`/validated).
233
+ - **§C9** — log-injection scoped to plain-text/terminal sinks (a structured/JSON sink is largely immune).
234
+
235
+ _Shipped as part of release 0.3.0 (2026-05-29)._
236
+
237
+ ---
238
+
239
+ ### Review-driven pass — accuracy fixes, completeness (§B29), calibration (external multi-agent review)
240
+
241
+ An external multi-agent review (technical accuracy, completeness/currency, anti-dogmatism, internal consistency, evidence-base fact-check) drove this batch. One new category (§B29) raises the count **50 → 51**; everything else is within-category bullets, accuracy fixes, and calibration. Shipped as part of release 0.3.0.
242
+
243
+ #### Added
244
+
245
+ - **§B29 — Iterator and slice adapter traps** (new Tier B category; count 50 → 51). `zip` truncates to the shorter side (silent data loss); `Vec::dedup` removes only *consecutive* duplicates (not a set); `chunks(0)`/`windows(0)`/`step_by(0)` panic; `collect` into a `HashMap`/`HashSet` silently overwrites duplicate keys (last wins). The highest-frequency LLM surface, previously under-covered.
246
+ - **§A1 — build-time supply-chain vector.** A dependency's `build.rs` and proc-macros execute arbitrary code at `cargo build` (before any runtime guard), reading credentials/keys/CI secrets; plus `-`↔`_` typosquats and dependency confusion. Defenses: pin + commit `Cargo.lock`, audit `build.rs`/proc-macro deps, `cargo-deny`/`cargo-audit`, `--locked`/vendored builds.
247
+ - **Within-category bullets:** §B4a (edition-2024 `impl Trait` lifetime capture / `+ use<>`), §B16 (`partial_cmp().unwrap()` NaN-panic in float sort → `total_cmp`; inconsistent comparator now panics), §B19 (`mem::take`/`replace`/`Option::take` leave a `Default` on early-return/`?`/panic), §B26 (`debug_assert!`/`dbg!` compiled out in release), §C4 (`BufWriter`/`BufReader` drop-flush discards `io::Result`), §C9 (log/ANSI/control-char injection from untrusted input), §B14 (long sync step inside `FuturesUnordered`/`buffer_unordered` buries siblings → spurious timeout / semaphore self-deadlock).
248
+
249
+ #### Changed (triggers, counts, calibration)
250
+
251
+ - Both trigger tables gain a §B29 row (phrase + code-pattern). Counts propagated: "fifty" → "fifty-one", "all 50" → "all 51", "twenty-eight" (Tier B) → "twenty-nine", `(§B1–§B28)` → `(§B1–§B29)`. README spec range `§A1–§D2` → `§A1–§E6` (Tier E was uncovered).
252
+ - **Calibration (anti-dogmatism):** §A2/§B2 `Rc`/`RefCell` "forbidden" narrowed (legitimate under `spawn_local`/`LocalSet` and in synchronous single-threaded code; cross-thread misuse is already a compiler error); §B24 trigger narrowed to *secret* operands (no longer flags `algo == "HS256"`) plus a scope note; Post-flight drops the noisy `clippy::expect_used` and annotates `unwrap_used` as hand-triaged; §B26 overflow-checks flagged as a binary-crate lever (libraries use per-site `checked_*`) with softened tone; §B5 strict-provenance scoped to genuine address↔integer round-trips (not a blanket cast replacement); §B3 title `THE BIG ONE` → `invisible in signatures`; §B10 OOM phrasing softened.
253
+
254
+ #### Fixed (accuracy / evidence base)
255
+
256
+ - **§C2** — corrected the UNC-path claim: `\\server\share` parses as `[Component::Prefix, Component::RootDir]` (so `has_root()` / `is_absolute()` are `true`, not `false`); the first-component `Prefix`/`RootDir` guard is unchanged and correct.
257
+ - **Evidence base (spec intro, §B12, §A1 + `docs/sources.md`):** removed the unverifiable "~57% / CodeQL / crypto-Rust" SafeGenBench figure (the benchmark is multi-language, Semgrep-class, with no Rust track); corrected the Faros "+242.7%" framing (org-level incidents-to-PR at low→high adoption, not AI-vs-human PRs); removed the fabricated Rust-specific scope from the Lightrun "very confident" figure (it concerns AI-generated code in general); `rust_decimal` downloads `~3.5M` → `~100M` (the 3.5M figure was the repo's GitHub star count); supply-chain "~130%" → directional "~70–75% ecosystem-wide, with no crates.io-specific figure published".
258
+
259
+ _Shipped as part of release 0.3.0 (2026-05-29)._
260
+
261
+ ---
262
+
263
+ ### Discipline hardening — vacuous-test ban, workspace boundaries, benchmark-as-guard (bullets within existing categories; count stays 50)
264
+
265
+ A small follow-on pass integrating three pieces of engineering wisdom *into existing categories* rather than as new ones — keeping the taxonomy at 50 and avoiding bloat. The set was deliberately filtered ("not everything, the important parts, wisely placed"): project-hygiene items such as "keep a changelog" were judged out of scope for a silent-failure spec, "create a `crates/` folder up front / extract everything" was reframed (premature extraction is itself a hazard), and "benchmark everything" was reframed to fit §E6's measure-first discipline rather than contradict it.
266
+
267
+ #### Added (bullets inside existing categories — count stays 50)
268
+
269
+ - **§D1 — vacuous tests / coverage theater.** A test asserting a value against its own definition (`assert_eq!(MAX_RETRIES, 3)`), re-checking what the compiler / `std` / a `#[derive]` already guarantees, or exercising a dependency's behavior — inflates coverage and confidence while being structurally unable to fail. The silent twin of the happy-path mock. **Exception:** contract pins (FFI layout/size §B25, wire-protocol constants, serialized golden snapshots §B20) are *not* vacuous — changing them is a breaking change worth catching.
270
+ - **§C10 — workspace version unification + crate-boundary timing.** Declare shared deps once in `[workspace.dependencies]` (drifting per-member versions link multiple incompatible copies into one binary); and extract a crate *late, not early* — a premature boundary freezes an unproven API (§C1) and forces the very feature/version coordination §C10 is about, while copy-paste drift across members signals extraction is overdue.
271
+ - **§E6 — benchmark as regression-guard.** Lock a measured win with a `criterion` CI benchmark that fails on regression; bench only the paths actually optimized (benching cold/trivial code is its own coverage theater, §D1) — consistent with §E6's measure-first discipline, not "benchmark everything".
272
+
273
+ #### Changed (triggers)
274
+
275
+ - Phrase table gains rows → §D1 ("add tests / increase coverage" → test behavior/contracts, not tautologies), §C10 ("extract a crate / new workspace member"), §E6 ("benchmark this / lock in the speedup"). Code-pattern table gains a §D1 row (`assert_eq!(CONST, <literal>)` / `assert!(true)` / setter-then-getter).
276
+
277
+ _Shipped as part of release 0.3.0 (2026-05-29)._
278
+
279
+ ---
280
+
281
+ ### Tier E — Systemic-cost performance block (new content; 44 → 50 categories; four → five tiers)
282
+
283
+ A new top-level tier — **TIER E — Systemic cost: correct in the small, wrong at scale** (§E1–§E6) — opens a *different axis* from the correctness tiers A–D. This is a reframing of the frontier of failure: as the safe, locally-correct code in a system accumulates, the system breaks **as a whole** — latency, allocation pressure, accidental complexity, lock contention — even though no single line is "wrong". The cost is paid only under load, *outlives* correctness (a passing test on a small input proves nothing about it), and is invisible to `rustc` / `clippy` / `cargo test` exactly the way the Tier B/C/D bugs are. Tier E therefore does not use the BANNED/REQUIRED grammar of the correctness tiers; each law is framed as **where the cost hides / the cheap move / when not to touch it**. Nothing in Tier E is **🔴** (it is entirely 🟡/🟢) — a systemic-cost finding is never a hard blocker — and §E6 (measure-first) is built in specifically to keep the tier from degenerating into over-flagging: only accidental-O(n²) (§E3) and `clippy::perf`-obvious wins are proactive; everything else is profile-gated.
284
+
285
+ #### Added
286
+
287
+ - **New tier — TIER E (Systemic cost), §E1–§E6.** Sixth conceptual axis after the meta-layer and A/B/C/D; 🟡/🟢 only; profile-disciplined via §E6.
288
+ - **§E1 — Serialism that need not exist.** Independent `.await`s run sequentially → `join!` / `try_join!` / `buffer_unordered` / `JoinSet`; CPU-bound work on the async runtime → `spawn_blocking` / `rayon`.
289
+ - **§E2 — Allocation that need not happen.** Reflexive `clone` / `collect` / `format!`; `Vec::with_capacity` for known sizes; `Cow` / `&str` over owned `String`; reuse buffers; `bytes` for shared/zero-copy slices.
290
+ - **§E3 — Complexity that compounds.** Accidental O(n²) (the one always-fix case); the wrong container (`HashSet` / `VecDeque` / `SmallVec` / `BTreeMap` / `phf` chosen by access pattern).
291
+ - **§E4 — Contention that serializes.** `Arc<Mutex<_>>` on a hot path → atomic / `ArcSwap` / sharding / channel-ownership; shrink the critical section; pick the hasher by trust boundary (fast `FxHashMap` / `foldhash` / `ahash` for trusted keys, DoS-resistant default for untrusted — see §B16); false sharing → `CachePadded`.
292
+ - **§E5 — Work already done.** `Regex::new` / parsing repeated per call → `LazyLock` / `OnceLock`; unbuffered I/O → `BufReader` / `BufWriter`; reuse scratch buffers; lazy `tracing` evaluation; `dyn` dispatch in a hot path → generics / enum.
293
+ - **§E6 — Measure before you spend.** Profile-first discipline: `cargo flamegraph` / `perf`, `dhat` / `heaptrack`, `tokio-console`, `criterion`. §E3 and the `clippy::perf`-obvious wins are proactive; the rest is profile-gated — do not optimize on a guess.
294
+
295
+ #### Changed (structural propagation)
296
+
297
+ - **Category count 44 → 50** and **"four tiers" → "five"** propagated across every place either number is stated: the spec opening ("forty-four categories" and "The categories split into four tiers…" + tier list gains a Tier E line), the Enforcement-tiers preamble ("all 44 categories"), and the README's spec-architecture table (new Tier E row, count to 50).
298
+ - **Front-matter `description`** extended so the skill matches performance/scale queries (systemic-cost / latency / allocation / contention), without disturbing the existing correctness-hazard list.
299
+ - **Both trigger tables** route performance symptoms to §E*: the phrase table (slow at scale, two sequential `.await`s, too many allocations, lock contention, "faster HashMap", recompiles `Regex`) and the code-pattern table map onto §E1–§E6, with the hasher row split by trust boundary (§E4 + §B16).
300
+ - **`commands/rust-intel-cc/audit.md`** — the category walk now iterates §A1 → … → **§E6** and groups findings A → B → C → D → **E**, with an explicit note that Tier E is a different axis (systemic cost, never 🔴) and so never enters the 🔴-only Post-flight summary.
301
+ - **`commands/rust-intel-cc/fix.md`** — routing table gains performance rows (slow/high-latency-at-scale, sequential `.await`s, allocation churn, quadratic-at-scale, lock contention, "faster HashMap", `Regex`-in-loop / unbuffered I/O) mapping the symptom shape onto the right §E law, all under §E6 (measure first).
302
+ - **`docs/roadmap.md`** and **`docs/sources.md`** — Tier E logged as shipped content; normative performance sources added under §E* (see the sources.md entry in this batch).
303
+
304
+ This is a **MINOR** change by SemVer (new categories) — shipped in 0.3.0.
305
+
306
+ _Shipped as part of release 0.3.0 (2026-05-29)._
307
+
308
+ ---
309
+
310
+ ### Corrective pass (eighth review pass — external review: one verified bug, a 🔴-propagation gap, three undisclosed-precondition gaps, meta-layer recalibration)
311
+
312
+ The eighth pass was opened in response to an external review of the frozen spec — so the seventh pass's **"frozen"** verdict is hereby **superseded** (the freeze held for content saturation, not for correctness or for gaps the review surfaced from outside the loop). It found and closed one verified factual error (§C2's path-traversal guard), one propagation gap in the 🔴 list (§B13 lived in the tiers but never reached the operating-mode / audit / fix surfaces that consume it), and three in-scope gaps that were **unstated preconditions of the spec's own remedies** (`catch_unwind` × `panic = "abort"`, `thread_local!` × `.await`, `block_in_place` on a current-thread runtime — each a case where following an existing recommendation silently does nothing or panics unless the precondition is known). It also recalibrated several meta-rituals so that strictness is proportional. No category was added, cut, or renumbered — the count stays **44**; the seventh-pass block below is left intact as a historical record.
313
+
314
+ #### Fixed (correctness / consistency)
315
+
316
+ - **§C2 — verified factual error.** The path-traversal guard for `Path::join` was `has_root()`, which lets a bare `\\server\share` through (on Windows that is a `Component::Prefix` with no `RootDir` → `has_root() == false`), even though `join` discards the base anyway. The primary guard is now: reject a leading `Component::Prefix`/`RootDir` component.
317
+ - **§B3 — `write_all_buf` reclassified** from "cancel-safe-with-caveat" to cancel-safe (on cancellation the buffer is partially advanced — resume from the remainder).
318
+ - **🔴 list de-duplicated and propagated.** §B13 (`Relaxed`-publish) had lived only in "Enforcement tiers": Operating mode step 7 now **references** the canonical list instead of re-listing it; `commands/rust-intel-cc/audit.md` (step 6 and the summary header) likewise reference the canon; the Post-flight summary template in `audit.md` gained a §B13 line; `commands/rust-intel-cc/fix.md` gained a routing row for the atomic-ordering (ARM) symptom → §B13.
319
+ - **§B13 trigger-gap closed** — new row in the code-pattern table for `Ordering::Relaxed`-publication.
320
+ - **Operating mode step 1 ↔ Blocking protocol** — contradiction over unknown versions resolved ("ask" → "proceed with stated assumptions, ask to confirm"), aligning step 1 with the sixth-pass Blocking protocol.
321
+ - **Version pins** — `extern "C-unwind"` marked stable 1.71 (distinct from 1.81 = abort-by-default for plain `extern "C"`); `clippy::await_holding_lock` marked warn-by-default (`suspicious` group, since 1.45) — a manual `-W` is redundant.
322
+ - **`README.md`** — "Three tiers plus a meta-layer" → "Four" (a relic from before Tier D). **`docs/roadmap.md`** — the §B15a–e split marked ✅ shipped (sixth pass), the remaining dedup/rebalance separated out. **`CHANGELOG.md`** — removed the duplicated sixth-pass header (this same entry).
323
+
324
+ #### Added (bullets inside existing categories — count stays 44)
325
+
326
+ - **§B4 + §B25** — `catch_unwind` is inert under `panic = "abort"` (it catches nothing, and the guard code behind it never runs) and requires `UnwindSafe`; this was an unstated precondition of the spec's own recommendations.
327
+ - **§C9** — a `thread_local!` read after `.await` reads another worker's value or the default on a multi-thread runtime (the task can migrate between threads) → use `tokio::task_local!`.
328
+ - **§B11** — `spawn_blocking` pool starvation (default 512).
329
+ - **§B15c** — `block_in_place` panics on a current-thread runtime.
330
+ - **§B16** — HashDoS: for keys from untrusted input, do **not** swap the default `RandomState` for a fixed-seed `FxHashMap`/`fnv`/`ahash`.
331
+ - **§B20** — `#[serde(flatten)]` silently disables `deny_unknown_fields` and breaks `u128` / non-string map keys.
332
+ - **§B21** — a panic in a detached task (dropped `JoinHandle`) is silently swallowed.
333
+ - **§B26** — `saturating_sub` on `usize` (lengths / cursors) masks a logic bug.
334
+ - **§B5** — strict-provenance list extended (`map_addr` / `dangling` / `without_provenance`, stable 1.84); `Vec::into_raw_parts` preferred on ≥ 1.93.
335
+ - **§B4a** — let-chains have spread to `if let` match-guards (stable 1.95); the one silent-runtime never-type-fallback case is deny-by-default (edition 2024 / 1.92) and out of focus.
336
+ - **Version pins** — recent tokio APIs: `biased` in `join!` / `try_join!` (1.46), `SetOnce` (1.47), the coop module (1.44).
337
+
338
+ #### Changed (calibration / usability — strictness made proportional, no rule removed)
339
+
340
+ - The "Principle" section was condensed (~13 → 4 lines).
341
+ - Operating mode step 3 (text-first for traits) scoped to the public API of a published library.
342
+ - Operating mode step 5 (`/// cancel-safe:`) narrowed to functions actually in a cancellation context.
343
+ - Enforcement tiers: narrowing `as` stays 🟢 but with a trust-boundary caveat; the canonical "inline-flag policy" is stated once, in 🟡.
344
+ - **§A2** — "`Box<T>` for a small `Sized` is almost always wrong" softened (recursion / `Pin` / `dyn` are exceptions).
345
+ - **§B7** — the 64 KiB stack threshold moved from BANNED to a guideline (escalate on recursion / deep chains / a reduced stack); "2 MiB on tokio tasks" → "worker thread".
346
+ - **§B12** — "mandatory human cryptographer review" reserved for custom / protocol-level crypto.
347
+ - **§B16** — the inline flag for a manual `PartialEq`/`Ord` narrowed to non-trivial contracts.
348
+ - **§C4** — algorithmic O(n²) (always fix) separated from micro-allocations (profile-gated).
349
+ - Trigger table: three duplicated phrase rows merged. Tier A intro: the residual compile-only list collapsed to a pointer.
350
+
351
+ #### Tooling/docs
352
+
353
+ - `commands/rust-intel-cc/audit.md`, `commands/rust-intel-cc/fix.md`, `README.md`, `docs/roadmap.md` — see above.
354
+
355
+ _Shipped as part of release 0.3.0 (2026-05-29)._
356
+
357
+ ---
358
+
359
+ ### Final fix pass (seventh review pass — resolve refactor seams, close one currency gap, then freeze content)
360
+
361
+ The seventh pass found the content saturated but the sixth-pass refactor had left a few seams (the rebuilt post-flight contradicted leftover "surface every X" tails in category bodies; the closing manifesto still said "every rule is a HARD constraint" against the new tiers; §B26 had been *over*-softened into under-flagging). It also surfaced one genuinely new coverage gap (edition-2024 drop-order changes) and verdict'd that the review loop has hit diminishing returns — this is the last content pass; further signal should come from *using* the spec, not another audit.
362
+
363
+ #### Fixed (contradictions and one regression)
364
+
365
+ - **§B26 under-flagging regression.** The refactor had made `overflow-checks = true` the primary defense and gated manual `checked_*` to (a) untrusted boundaries and (b) typed-error-on-wraparound — leaving an ordinary long-lived counter in a project that doesn't set the global flag (the default) and isn't from a trust boundary protected by *nothing* in release. Restored a third case: `checked_*` covers any monotonically accumulating value when `overflow-checks = true` is not guaranteed in the build profile. Routine bounded `i + 1` / `(lo + hi) / 2` remain explicitly out (no return to over-flagging).
366
+ - **Post-flight ↔ category bodies contradiction.** The refactor rebuilt the post-flight checklist as "surface ONLY the 🔴 tier", but nine non-🔴 category bodies (§A3, §B1b, §B2, §B8, §B9, §B15a, §B16, §B20, §C5) still ended with "Surface every X **in the post-flight summary**" — pointing into a list that now excludes them. Those nine now say "flag inline (at write time)"; the rule stays, the contradiction is gone. Operating mode step 7 likewise rewritten to "surface the 🔴-tier (canonical list in Enforcement tiers), note the rest inline." The five remaining "in the post-flight summary" mentions are all 🔴 categories, where the reference is correct.
367
+ - **Closing manifesto vs Enforcement tiers.** The final "When this command is loaded" section still said "Treat every rule above as a HARD constraint … surface violations as blocking" — a direct contradiction of the just-added tier model. Reworded to: 🔴 are hard constraints (surface always, block per the Blocking protocol); 🟡 applied while writing without per-occurrence reporting; 🟢 owned by clippy.
368
+ - **§C5 "surface every clone".** Narrowed to: a `.clone()` introduced *to silence a borrow error* gets an inline one-line justification; routine / `Arc::clone` / `Copy`-type clones are 🟢 (clippy) / 🟡 (write-time), not surfaced — consistent with the tiers.
369
+
370
+ #### Added (one currency gap; a subsection, not a new category — count stays 44)
371
+
372
+ - **§B4a — Edition-2024 temporary-scope drop-order changes** (subsection of §B4, like §B1a/§B1b). The spec targets edition 2024 but had not covered edition 2024's own *silent* behavior changes. Two are genuine "compiles, tests green, drop order silently shifted" hazards: the `if let … {} else {}` scrutinee temporary now drops before the `else` block (`if_let_rescope`; the canonical case is an `RwLock` deadlock that 2021 has and 2024 silently fixes — or code relying on the extended temporary lifetime that now drops early), and tail-expression temporaries now drop before the block's locals (`tail_expr_drop_order`, advisory lint with **no autofix**). Pairs with let-chains (1.88). Plus a phrase trigger, a code-pattern trigger, and a version-pins note. (RPIT `use<>` capture, `unsafe extern`, `gen` blocks, static-mut `&raw`, never-type fallback were all evaluated and correctly left out — each is compile-only or deny-by-default, not a silent post-compilation bug.)
373
+
374
+ #### Changed (usability / dedup)
375
+
376
+ - **🔴 list de-duplicated.** The ~11-item 🔴 set lived identically in both "Enforcement tiers" and the post-flight checklist. Enforcement tiers is now the single canonical list; the post-flight references it and keeps only the toolchain commands (clippy/miri/test) and optional tools. Removes the fifth duplication surface the refactor had inadvertently created.
377
+ - **§C1 blanket-impl 🔴 scoped to published libraries.** Marked 🔴 only for a *published* library's public API (a semver hazard); for bin/internal crates it is not a 🔴 concern.
378
+ - **§B13 atomic `Relaxed`-publish promoted to 🔴.** A `Relaxed` store/load used to publish data to another thread is a data race invisible to tooling and to tests on x86 (the dev machine's strong memory model hides it) that breaks on ARM — it fits the 🔴 criteria (invisible to tooling, not caught by tests, silent corruption) better than some items already there.
379
+
380
+ #### Minor
381
+
382
+ - **§A2/§B2** — note that a `LazyLock`/`OnceLock` init closure that panics poisons the cell (every later access panics, not just the first); don't panic in lazy init.
383
+ - **§B3** — the `/// cancel-safe:` annotation requirement aligned with the softened Operating mode step 5 ("every *non-trivial* async fn").
384
+ - **§B8** — `async ||` closures (stable 1.85) added to the list of future-producing forms that are inert until polled.
385
+ - **§C1** — fixed a dangling "§B5/T4" cross-reference to "§B5".
386
+ - **`commands/rust-intel-cc/audit.md`** — the report-format example aligned to the 🔴-only post-flight (it had still listed `unwrap`/`Arc<Mutex<_>>`/`.lock().unwrap()` counts as mandatory summary lines).
387
+ - **`docs/sources.md`** — added the USENIX Security 2025 package-hallucination study (19.7% non-existent packages, 58% repeatable across runs) as a quantified slopsquatting anchor, with an explicit PyPI/npm-not-crates.io caveat.
388
+
389
+ The trigger table's risk column was reviewed for consolidation but left intact — on inspection each entry carries disambiguation or a memorable code signature rather than a verbatim restatement, so collapsing it would cost navigation nuance for little gain.
390
+
391
+ With these seams closed, the post-compilation content taxonomy is treated as **frozen**; the next signal comes from running the spec on real code, not from further review passes.
392
+
393
+ ---
394
+
395
+ ### Usability refactor (sixth review pass — "make it easier to apply", not new content)
396
+
397
+ The sixth pass found the content saturated and accurate, but the document bloated and ritualistic: a 56-bullet post-flight that duplicated category bodies, "everything is a HARD constraint" with no triage, and over-flagging (every `as`, every `+`, every `clone`) that trains the reader to ignore the whole spec. This refactor reorganizes for **applicability** without removing a single rule.
398
+
399
+ #### Added
400
+
401
+ - **"Enforcement tiers" section** — the core change. Three tiers tell the reader *how strictly* to act on each category, orthogonally to the A/B/C/D *what-kind-of-bug* tiers: **🔴 surface-always / may block** (~11 high-blast-radius classes: unsafe, crypto, FFI, slopsquatting/new-dep, manual `Send`/`Sync`, async-`Drop`, secret-`==`, unbounded channel, blanket impl, `Pin::new_unchecked`, dropped `JoinHandle`), **🟢 delegate to clippy** (narrowing `as`, `clone_on_copy`/`redundant_clone`, `unexpected_cfgs` — don't hand-check what the linter catches), **🟡 apply while writing** (everything else — write it right, don't spam the summary). Goal: a summary a human reads in ten seconds where every line is worth acting on.
402
+
403
+ #### Changed
404
+
405
+ - **Post-flight checklist rebuilt: ~56 bullets → ~11.** Now a flat signature list of only the 🔴-tier occurrences, with the "why/how" left in the category bodies (where it already lived) instead of duplicated. The clippy command gains `-W clippy::arithmetic_side_effects` (see accuracy fix below). All the dropped 🟡-bullets (`as`, `+`/`sum`, `clone`/`to_string`, `sort_unstable`, `pub fn` lifetimes, `RefCell::borrow_mut`, `Path::join`, `read`/`write`, …) **remain as rules in their category bodies** — only the noisy re-surfacing mandate was removed.
406
+ - **§B26 over-flagging softened.** `overflow-checks = true` (release profile) is now the *primary* defense; manual `checked_*` is reserved for untrusted boundaries and typed-error-on-wraparound. The BANNED wording no longer reads as "every arithmetic" — it targets values from untrusted input, unbounded growth, or monotonic accumulation; routine `i + 1` / `(lo + hi) / 2` are explicitly out.
407
+ - **Operating mode mandates narrowed.** `/// cancel-safe:` annotation is required only for an `async fn` with more than one side-effecting `.await` or one documented to run under `select!`/`timeout` — not every async fn (the old mandate generated noise the spec itself calls ~50% unreliable). "Show the caller" is required only when the returned reference binds more than one input lifetime (the actual §B1a shape), not every `&T`.
408
+ - **Blocking protocol narrowed.** Refuse-to-generate is now limited to three cases where the cost of guessing is catastrophic or irreversible: crypto without a threat model (§B12), `unsafe` with caller invariants unstated (§B5), and adding an unnamed/unverified dependency (§A1). Everything else (unknown crate versions, missing trait defs, drop semantics) switches to "proceed with explicitly stated assumptions" — generate the code, flag the assumptions, ask to confirm — instead of blocking the user.
409
+ - **§B15 split into labeled subsections** §B15a (AFIT vs RPITIT) / §B15b (Pin, Waker) / §B15c (sync↔async bridging) / §B15d (`Stream` vs `Iterator`) / §B15e (tokio sync/timing primitives), as sub-headings under the unchanged `## §B15` — like the existing §B1a/§B1b. No renumber; every bullet preserved; trigger references point at the sub-anchors where natural.
410
+ - **Opening de-duplicated.** The scope thesis ("compiles + tests ≠ correct") and the compile-only-exclusions list were restated 3–5 times across front-matter, the opening, the tier intro, "Principle", and the Tier B intro; each is now stated once canonically. The giant sentence that re-listed all 44 categories in prose was trimmed to a scope line plus a pointer to `docs/sources.md` for the empirical figures.
411
+
412
+ #### Accuracy fixes folded into the same pass
413
+
414
+ - **§B26 — `clippy::arithmetic_side_effects` is in the `restriction` group, not `pedantic`.** The text claimed it was pedantic and a "same blind spot as the cast lint"; in fact `-W clippy::pedantic` (which the post-flight runs) catches the lossy-cast lint but **not** integer overflow — you must enable `arithmetic_side_effects` explicitly. Reworded, and the flag added to the post-flight clippy command.
415
+ - **§C2 — `Path::join` guard corrected for Windows.** `is_absolute()` is the wrong check: `join` discards the base on `has_root()`, and `/etc/passwd` or `\\server\\share` give `is_absolute() == false` while still dropping the base. Now recommends `has_root()` (or rejecting a leading `RootDir`/`Prefix` component).
416
+ - **§B26 — `overflow-checks = true` hot-path caveat.** Noted the global runtime cost (~5–15%, inhibits autovectorization); for numeric hot paths, prefer targeted `checked_*` at the few real overflow sites over the global flag.
417
+ - **§B12 — unsourced "~23%" figure removed** (it had no entry in `docs/sources.md`); the documented "~57% of crypto vulnerabilities missed by static analyzers" is kept.
418
+
419
+ #### Tooling/docs (this pass)
420
+
421
+ - **`README.md`** — one paragraph distinguishing the A/B/C/D category tiers (what kind of bug) from the 🔴/🟡/🟢 enforcement tiers (how strictly to act). No version/Status/count change.
422
+ - **`docs/roadmap.md`** — the rejected `§B18 #[no_std]` draft moved to an explicit "Rejected — out of scope by design" section; the "add ~5 more trigger patterns" item inverted to "consolidate, don't grow"; the per-tier-file split question closed ("one `SKILL.md`; consolidate internally instead"); infrastructure (`examples/` corpus, CI link-checker) promoted to highest-value-next.
423
+
424
+ ---
425
+
426
+ ### Fifth review pass (accuracy + content)
427
+
428
+ A fifth review pass (empirical, against rustc 1.93 / tokio 1.52.3) found three fresh inaccuracies in the v0.4.0 text, one content gap (integer overflow) that survived the saturation sweep, and a command-file bug that survived all five rounds. Integer overflow was folded into §B26 rather than made a new category.
429
+
430
+ ### Changed (accuracy fixes — regressions from v0.4.0)
431
+
432
+ - **§B15 — `watch::Receiver` `changed()` claim corrected.** v0.4.0 said `changed().await` "returns immediately the first time" on a fresh receiver. Verified false on tokio 1.52.3: the initial value is marked **seen** at receiver creation, so `changed().await` is *pending until the next `send`* — it does not fire for the initial value. `borrow()`-returns-initial is correct and kept; the loop example now uses `borrow_and_update()`.
433
+ - **§B28 — `ß` case-mapping example was backwards.** The length-changing example `ß → ss` was attributed to the wrong direction: `ß` is unchanged by `to_lowercase()` and becomes `SS` under `to_uppercase()`. Corrected to `ß → SS` under `to_uppercase`; the Turkish `İ → i̇` example correctly illustrates `to_lowercase`.
434
+ - **§B27 — `Instant::saturating_add` does not exist on stable.** The overflow bullet recommended it; `Instant` has `checked_add` and `saturating_duration_since` but no `saturating_add` (that is a `Duration` method). Reworded to split the two types.
435
+
436
+ ### Added (content — folded into existing categories, no new category, count stays 44)
437
+
438
+ - **§B26 (renamed "Lossy numeric conversions and integer overflow") — integer overflow + div/rem-by-zero + index OOB.** The headline addition: bare integer `+`/`-`/`*`/`pow`/`sum` on untrusted or accumulating values **panics in debug but silently wraps in release** (`overflow-checks = false` is the release default), so `cargo test` (debug) stays green while the shipped release binary wraps a counter/offset/size through zero — the most dangerous debug-vs-release divergence in the language, caught by no default lint. Plus `a / b` / `a % b` panic on a zero divisor (debug *and* release), and `slice[i]`/`split_at` panic on an untrusted out-of-bounds index. REQUIRED: `checked_*`/`saturating_*`/`wrapping_*`, `overflow-checks = true` for prod release builds, `slice.get(..)`.
439
+ - **§C4 — partial `Read`/`Write`.** A single `read`/`write` may transfer fewer bytes than requested even without EOF (sockets, pipes); use `read_exact`/`write_all`/`read_to_end` or loop.
440
+ - **§C2 — `Path::join` with an absolute segment.** `base.join(untrusted)` silently discards `base` if the segment is absolute — a path-traversal hazard; validate with `Path::is_absolute` / reject `..` / canonicalize-and-check.
441
+
442
+ ### Changed (self-monitoring + checklist)
443
+
444
+ - Trigger table extended (+4 phrase, +5 code-pattern) for integer overflow, div-by-zero, partial read/write, and `Path::join`. Post-flight checklist gains the matching surface-able items. Version-pins note added: integer-overflow behavior is not version-gated (`checked_*` etc. stable since 1.0).
445
+
446
+ ### Fixed (command files)
447
+
448
+ - **`commands/rust-intel-cc/audit.md` — Tier D was invisible to `/rust-cc-audit`.** The category-walk said "iterate from §A1 through the final **§C** category" and grouped findings "by tier (A → B → **C**)", silently skipping Tier D (§D1, §D2), which has existed since v0.3.0. This is the audit-command analog of the README "§A1–§C11" bug fixed in v0.3.2 — it survived all five review rounds. Now walks through §D2 and groups A → B → C → D.
449
+ - **`commands/rust-intel-cc/fix.md`** — routing table extended with rows for §B26 (overflow / lossy cast / div-by-zero), §B27 (duration looks wrong / `.elapsed().unwrap()` panic), §B28 (`byte index not a char boundary` panic / mid-character truncation).
450
+ - **`README.md`** — stale Layout comment for `roadmap.md` ("Planned commands and category expansions" → "Roadmap: open directions and structural notes").
451
+
452
+ _Shipped as part of release 0.3.0 (2026-05-29)._
453
+
454
+ The post-compilation taxonomy is now near-saturated under the spec's scope. See [`docs/roadmap.md`](docs/roadmap.md) for the remaining work, which is now mostly **infrastructure** rather than content: an `examples/` regression corpus (deliberately-broken Rust per category, run through `/rust-cc-audit`), CI markdown/link checking, and the still-open structural question of splitting the overloaded §B15.
455
+
456
+ ### Iteration: std-primitives coverage (drafted as 0.4.0)
457
+
458
+ Content release. Closes the last systematically-missed gap under the spec's scope — everyday **`std` primitives that compile, pass ASCII/small-number tests, and break in production** — with three new Tier B categories plus a batch of bullet-level additions to existing ones. A fourth review pass found v0.3.2 itself clean (zero regressions — the first patch in the project's history to introduce no new bugs), so this release is purely additive. **Category count 41 → 44.** No renumber of existing categories; slash commands and install/uninstall behavior unchanged. Re-run the installer.
459
+
460
+ ### Added (new Tier B categories)
461
+
462
+ - **§B26. Lossy numeric conversions.** `as`-casts silently truncate, wrap, or saturate with no panic and no default warning (`clippy::cast_possible_truncation` is pedantic / off-by-default). Covers narrowing/sign-changing integer casts (`u64 as u32`, `len() as u32`), the `usize`-is-32-bit-on-wasm32 trap, and float→int saturation (since Rust 1.45: `300.0_f32 as u8 == 255`, `NaN as i32 == 0`). REQUIRED: `try_from` for narrowing; explicit range checks before float→int. This is the backing rule for the long-orphaned `as`-cast line in the post-flight checklist.
463
+
464
+ - **§B27. Wall-clock vs monotonic time.** Measuring durations/timeouts with non-monotonic wall-clock time (`SystemTime::now()`, `Utc::now()`) breaks when the clock steps (NTP, DST, manual change); `.elapsed().unwrap()` / `.duration_since().unwrap()` panic in production on a backwards step because both return `Result` for exactly that reason. REQUIRED: `Instant::now()` for all durations/deadlines/benchmarks; `SystemTime` only for absolute timestamps; handle the `Err` or use `saturating_duration_since`.
465
+
466
+ - **§B28. UTF-8 and string-boundary hazards.** String ops that are correct on ASCII and panic or corrupt on non-ASCII: `&s[a..b]` with computed indices panics on a non-char-boundary (`&"café"[0..4]`), `s.len()` (bytes) conflated with character count, `to_lowercase`/`to_uppercase` (full Unicode, can change length) used for ASCII protocol comparisons. REQUIRED: `s.get(a..b)` / `char_indices` / `chars().take(n)`; `unicode-segmentation` for graphemes; `eq_ignore_ascii_case` for protocol strings.
467
+
468
+ ### Added (bullet-level, existing categories)
469
+
470
+ The eight items previously parked in the roadmap's v0.4.0 backlog, plus four medium-priority finds from the fourth review pass, shipped into existing categories:
471
+
472
+ - **§A2** — `Box::leak(Box::new(...))` for globals (leaks on every re-init path; use `OnceLock`/`LazyLock`, stable ≥ 1.80); `RefCell` where `Cell` suffices for `Copy`/replace-whole interiors (avoids the §B17 `BorrowMutError` panic surface).
473
+ - **§B4** — `mem::forget`/`ManuallyDrop` without a manual drop silently disables RAII (fd/connection/lock never released) — the §C5 reflexive-`.clone()` reflex applied to `Drop`.
474
+ - **§B7** — unbounded recursion **depth** over untrusted input (recursive-descent parser, tree/JSON walk) overflows the stack, which is `SIGSEGV`/abort — *not* a catchable panic, so a clean DoS vector. (Distinct from the existing frame-size trap.) REQUIRED: explicit depth limit or iterative rewrite.
475
+ - **§B14** — `FuturesUnordered`/`JoinSet` grown unbounded (same hazard as an unbounded channel), and an empty `FuturesUnordered` in a `select!` arm returns `Poll::Ready(None)` immediately → 100% CPU busy-loop.
476
+ - **§B15** — `watch::Receiver::borrow()` returns the **initial** value before any `send`, and the first `changed().await` returns immediately; use `borrow_and_update()` to avoid re-processing.
477
+ - **§B16** — `sort_unstable*` when the relative order of equal elements matters silently breaks a multi-key sort's secondary order; use stable `sort`/`sort_by_key` when the tie-break is load-bearing.
478
+ - **§B20** — deserializing a large integer (snowflake ID, ns timestamp, `u64` > 2^53) into an `f64` field or via `Value::as_f64()` silently loses precision (53-bit mantissa).
479
+ - **§C2** — `env::var("X").unwrap()` panics both on a missing var and on a non-UTF8 value (common on Windows); use `var_os` / handle `VarError::NotPresent`.
480
+ - **§C4** — `Vec::remove(0)`/`insert(0, _)`/`contains` in a loop is O(n²) (use `VecDeque`/`swap_remove`/`HashSet`); `{:?}` on `&[u8]`/`Vec<u8>` prints a decimal array, not hex (use `hex::encode` for non-secret bytes).
481
+ - **§C9** — logging PII (email, name, phone, address, government ID, card, IP) through `Debug`/`tracing` is a compliance leak (GDPR/PCI) distinct from §B12's crypto-secret coverage; classify and redact PII fields.
482
+
483
+ ### Changed (wording accuracy)
484
+
485
+ - **§B15 — `Notify` pattern wording corrected.** v0.3.2's comment said `.enable()` "registers the waker"; per tokio's docs `enable()` does not register the task `Waker` (that happens at poll/await) — it *arms the future for wakeups* by adding it to the notify list. Reworded to "arms the wakeup"; the code and its load-bearing-`.enable()`-before-the-check semantics are unchanged.
486
+
487
+ ### Changed (self-monitoring + checklist)
488
+
489
+ - **Trigger table** extended for every new rule: +8 phrase triggers (numeric cast, time measurement, substring/case, global/singleton, large JSON id, env var, sort-by, recursive parser) and +10 code-pattern triggers (`as`-narrowing, `SystemTime` duration, `&s[..]`/`len()`-as-chars, `Box::leak`, `mem::forget`, `FuturesUnordered`, `watch::channel`, `Vec` front-mutation, `{:?}`-on-bytes, `sort_unstable*`).
490
+ - **Post-flight checklist** gains surface-able items for the new categories and bullets (narrowing casts, `SystemTime`-for-duration, computed `&s[..]`, `Box::leak`, `mem::forget`, unbounded `FuturesUnordered`, `env::var().unwrap()`, `sort_unstable*`, `Vec` front-mutation, depth-unbounded recursion, PII-through-`Debug`).
491
+ - **Version pins** — float→int saturating cast pinned to Rust 1.45; `LazyLock` to 1.80 (alongside `OnceLock`).
492
+
493
+ ### Tooling and documentation
494
+
495
+ - **`README.md`** — Status block gains a v0.4.0 entry (v0.3.2 preserved; v0.3.0 condensed to a one-line scope-reframe reference). Spec-architecture table Tier B range `§B1–§B25` → `§B1–§B28`. Category count updated to 44.
496
+ - **`docs/roadmap.md`** — the "Deferred to v0.4.0" backlog is now "Shipped in v0.4.0" with each item mapped to its landing category; the §B15-split and section-rebalance notes remain open as structural work; a saturation note redirects future effort to infrastructure.
497
+ - **`docs/sources.md`** — normative-source entries added for the three new categories (Rust Reference on `as`-cast semantics, `std::time` on monotonic vs wall-clock, `str` UTF-8 docs).
498
+ - **`CHANGELOG.md`** — the v0.3.2 line-endings note was corrected (it claimed the working tree was renormalized to LF; in fact only the index is LF-canonical, the Windows working copy stays CRLF by design under `eol=lf`).
499
+
500
+ ### Migration
501
+
502
+ Re-run the installer. The skill grew by three categories and ~a dozen bullets; nothing was renumbered or removed, so any reference to §A1–§D2 or §B1–§B25 remains valid (§B26–§B28 are new). Slash commands and scripts are unchanged.
503
+
504
+ ### Iteration: accuracy patch (drafted as 0.3.2)
505
+
506
+ Same-day patch on top of v0.3.1. Fixes three bugs **introduced by v0.3.1 itself** (a third review pass caught them), corrects an internal category count, catches the trigger table up to the v0.3.1 rules, and adds four bullet-level pitfalls under the existing scope. **No new categories** — total stays at 41. **No renumber.** Re-run the installer; nothing else changes.
507
+
508
+ ### Changed (accuracy fixes — all regressions from v0.3.1)
509
+
510
+ - **§B15 — the `Notify` lost-wakeup pattern was missing its load-bearing `.enable()`.** v0.3.1 added a bullet whose example (`let permit = notify.notified(); pin!(permit); if !condition() { permit.await; }`) registered the waker only at `.await` — *after* the condition check — leaving the exact race the bullet claimed to close. Per tokio's docs, a `Notified` future does not receive wakeups until it is polled or explicitly armed. The corrected pattern arms the waker with `notified.as_mut().enable();` between `pin!` and the check, so a `notify_one()` landing between check and await is not lost. Variable renamed `permit` → `notified` (it is a `Notified` future, not a semaphore permit).
511
+
512
+ - **§B11 + Version pins — `tokio::task::coop::consume_budget` was pinned to the wrong version.** v0.3.1 claimed the `coop::` path was stable since tokio 1.39.1. In fact the *function* is stable since 1.39.1 at `tokio::task::consume_budget`; the `tokio::task::coop` module did not exist until **tokio 1.44.0**, which is also when the old path became `#[deprecated]`. Both §B11 and the Version-pins section now give the correct dual path keyed on MSRV (`tokio::task::consume_budget` below 1.44, `tokio::task::coop::consume_budget` on 1.44+).
513
+
514
+ - **§C2 — the `thiserror` `#[from]` bullet was both inaccurate and out-of-scope; reframed.** v0.3.1 claimed two interconvertible `#[from]` variants make `?` "silently prefer" one impl. That is wrong: two `#[from]` on the same source type is a hard `E0119` compile error, not a silent preference — and a compile error is out of scope for this spec by design. The bullet is reframed onto a genuinely in-scope hazard: **reflexive `#[from]` erases call-site context** — `#[from] io::Error` collapses every `?` on an I/O operation into one variant, so production logs say "I/O error" with no indication of *which* operation failed. Compiles, tests pass, diagnostics rot. Fix: reserve `#[from]` for source types that already uniquely identify the failure; otherwise carry context with `#[source]` + explicit `.map_err(...)` per call site.
515
+
516
+ ### Changed (minor wording)
517
+
518
+ - **§B8 — `tokio::sync::oneshot::Receiver` has no `.recv()` method.** The bullet's variable was named `recv`, falsely implying a `.recv()` call (which `mpsc::Receiver` has, but `oneshot::Receiver` does not — it *is* a `Future`, awaited directly). Renamed to `rx` and added a parenthetical noting the receiver is awaited directly.
519
+
520
+ - **§B15 — `block_in_place` was loosely called a "sync-to-async bridge".** It is the opposite: it lets an async task run *blocking* code on the current worker without starving siblings; you still cannot `.await` inside it without a `Handle`. Reworded to distinguish it from `spawn_blocking` and from a sync→async bridge.
521
+
522
+ - **§B-tier intro — "twenty-four categories" → "twenty-five".** §B1–§B25 is twenty-five categories; the prose count had not been updated when v0.3.0 added §B16–§B25.
523
+
524
+ ### Changed (trigger table caught up to v0.3.1)
525
+
526
+ v0.3.1 added rules but no triggers for them, so the self-monitoring layer never surfaced them proactively. Added:
527
+
528
+ - **Phrase triggers** (5): `interval`/periodic/timer → §B15; exit/bail-out → §B4; wait-for-signal/condition-variable → §B15 (`Notify`); log-this-struct/derive-Debug on secret-bearing types → §B12; compare-floats/approximately-equal → §D1.
529
+ - **Code-pattern triggers** (8): `std::process::exit` below a live guard → §B4; `Arc::strong_count`/`Rc::strong_count` in a conditional → §B13; `assert_eq!` with an `f32`/`f64` operand → §D1; `notify.notified()` → §B15; `#[derive(Debug)]` on a struct with a `password`/`secret`/`token`/`key`/`seed` field → §B12; `impl Drop` whose body can panic → §B4; `tokio::time::interval(...)` → §B15; `oneshot::channel()` with the result discarded/`.unwrap()`-ed → §B8.
530
+
531
+ ### Added (bullet-level, no new categories)
532
+
533
+ - **§B15 — `tokio::time::interval` first-tick semantics.** The first `.tick().await` returns immediately (at creation), not after one period; the default `MissedTickBehavior::Burst` fires missed ticks back-to-back to "catch up", producing a load spike. REQUIRED: discard the first tick or use `interval_at(Instant::now() + period, period)`, and set `MissedTickBehavior::Delay`/`Skip` explicitly.
534
+
535
+ - **§B13 — atomic memory ordering.** `Ordering::Relaxed` on an atomic used to *publish* data establishes no happens-before edge — the reader can observe the flag before the payload writes, a data race that x86's strong model hides in tests but that breaks on ARM/AArch64. Use `Release`/`Acquire` (or `AcqRel`/`SeqCst` for RMW) when the atomic gates other memory; `Relaxed` only for standalone counters; don't blanket-`SeqCst`; model-check with `loom`.
536
+
537
+ - **§B14 — `broadcast::RecvError::Lagged(n)` is data loss, not a transient error.** `Lagged(n)` means `n` messages are gone forever and the receiver has skipped to the oldest still-buffered one; a `match { Err(Lagged(_)) => continue }` loop recovers nothing and masks the loss. Log/metric the skipped count and decide explicitly whether dropping is acceptable.
538
+
539
+ - **§D1 — tests against fiction.** Three blind-test antipatterns: a mock/fake that only ever returns success (proves behavior against fiction, never against the dependency's real failure modes); `#[ignore]` left on "temporarily" (invisible to `cargo test`, rots silently while CI stays green); tests sharing mutable global state (static cell, fixed-name temp file, hard-coded port) that pass only by run order and flake under `cargo test`'s default parallelism.
540
+
541
+ ### Tooling and documentation
542
+
543
+ - **`README.md`** — Status block gains a v0.3.2 entry (the v0.3.0 entry is preserved below it for the scope-reframe context). The "Verify" section's category range corrected from `§A1–§C11` to `§A1–§D2` so Tier D is visible.
544
+ - **`docs/roadmap.md`** — new "Deferred to v0.4.0" subsection listing the bullet-level additions surfaced by the third review pass (`env::var`, `Box::leak`, `mem::forget`, `serde_json` fidelity, `watch::Receiver`, `FuturesUnordered`, `{:?}`-on-bytes, `Cell` vs `RefCell`) plus structural notes (possible §B15 split, section-length rebalancing).
545
+ - **`rust-intel.md`** — re-confirmed LF-canonical in the index (the committed blob is LF); the Windows working copy stays CRLF by design under `* text=auto eol=lf`, and git no longer warns because the canonical eol is explicit. (No content change — this corrects the wording of the original v0.3.2 note; nothing was actually re-converted.)
546
+
547
+ ### Migration
548
+
549
+ Re-run the installer. The skill content changed (three corrections, two wording fixes, a count fix, twelve new trigger rows, four new bullets); slash commands and install/uninstall behavior are unchanged.
550
+
551
+ If you copied the v0.3.1 §B15 `Notify` pattern into your code, re-copy it — the v0.3.1 version had a real lost-wakeup race (missing `.enable()`). If you pinned tokio between 1.39.1 and 1.43 and used the `tokio::task::coop::consume_budget` path the v0.3.1 text suggested, switch to `tokio::task::consume_budget` (the `coop` module only exists from 1.44).
552
+
553
+ ### Iteration: accuracy patch + category extensions (drafted as 0.3.1)
554
+
555
+ Same-day patch on top of v0.3.0. Five accuracy bugs in the v0.3.0 text fixed, seven existing categories extended with bullets covering pitfalls under the spec's stated scope (compiles + tests pass but still breaks). **No new categories** — total stays at 41. **No renumber.** Anyone running v0.3.0 re-runs the installer; nothing else changes.
556
+
557
+ ### Changed (accuracy fixes)
558
+
559
+ - **§B23 — `tokio::sync::mpsc::Sender::send` is NOT cancel-safe in `select!`.** v0.3.0 text claimed it was; per tokio's own documentation, when `send` is cancelled in a `select!` arm, the message is **dropped and lost**. The two-step `Sender::reserve().await` → `Permit::send(value)` is the canonical cancel-safe pattern (reserve acquires capacity asynchronously and is cancel-safe; the synchronous `Permit::send` cannot fail at that point). Section rewritten to remove the false claim and document the correct pattern.
560
+
561
+ - **§B25 — `cargo expand --type-sizes` does not exist.** v0.3.0 text recommended this fictional invocation for FFI layout verification. `cargo expand` is a third-party macro-expansion plugin with no such flag. Replaced with the real nightly tool `cargo +nightly rustc --lib -- -Zprint-type-sizes` plus a stable-toolchain fallback using `std::mem::size_of`, `std::mem::align_of`, and `std::mem::offset_of!` in a unit test asserted against expected C-side values.
562
+
563
+ - **§B11 + Version pins — `tokio::task::consume_budget` path is deprecated.** The canonical location is `tokio::task::coop::consume_budget`; the older `tokio::task::consume_budget` re-export is now `#[deprecated]`. Spec text and version pins updated. Stable since **tokio 1.39.1** (1.39.0 was yanked).
564
+
565
+ - **§B24 — `subtle::ConstantTimeEq::ct_eq` returns `Choice`, not `bool`.** v0.3.0 phrasing "`x.ct_eq(&y).into()` returns `bool`" was technically correct but invited readers to write `if x.ct_eq(&y) { ... }` (which does not compile). Reworded to be explicit: `ct_eq` returns `subtle::Choice` and must be converted via `bool::from(choice)` or `choice.into()`. Also flagged: never branch directly on `Choice` — the whole point is to keep the comparison branch-free until the explicit conversion.
566
+
567
+ - **§C11 — C-DEREF citation made verbatim.** v0.3.0 paraphrased the API Guideline; the rest of the spec uses literal quotes. Now uses the verbatim form: *"Only smart pointers implement `Deref` and `DerefMut` (C-DEREF). The traits should be used only for that purpose."*
568
+
569
+ ### Changed (category extensions, no new categories)
570
+
571
+ - **§B12 (Crypto) — Debug leakage, JWT `alg: none`, AEAD nonce width, key zeroization.** New BANNED bullets cover `#[derive(Debug)]` on structs with `password`/`secret`/`token`/`api_key`/`private_key`/`seed`/`mnemonic`/`cookie` fields (printed by `{:?}` in logs); JWT verification that accepts `alg: none` (always pin allowed algorithms explicitly); AEAD encryption with a nonce length other than the algorithm's specified width (96 bits / 12 bytes for AES-GCM and ChaCha20-Poly1305). New REQUIRED bullet covers `zeroize` discipline (`#[derive(Zeroize, ZeroizeOnDrop)]`) for key material.
572
+
573
+ - **§C2 (Error handling) — `Box<dyn Error>` in libraries, ambiguous `#[from]`.** New BANNED bullets cover `Result<T, Box<dyn Error>>` as the return type of any `pub fn` in a published library crate (callers can't match), and `thiserror::Error` enums with two or more `#[from]` variants over interconvertible source types (the `?` operator's resolution becomes ambiguous).
574
+
575
+ - **§D1 (Tests by luck) — floating-point exact equality.** New BANNED bullet: `assert_eq!` on computed `f32`/`f64` values flakes between debug/release, architectures, and compiler versions. Use `approx::assert_relative_eq!` / `assert_abs_diff_eq!` or manual epsilon comparison.
576
+
577
+ - **§B4 (Drop and RAII) — `process::exit` skips Drop, panic-in-Drop.** New BANNED bullets: `std::process::exit(...)` from code paths with stack-local guards (transactions, file handles, lock guards) — `process::exit` does not unwind; `Drop::drop` body that can itself panic during a panic unwind (double-panic aborts the process). Cross-link added pointing to §B22 for the async cleanup constraint.
578
+
579
+ - **§B8 (Silent task dropping) — `oneshot` channel drop cascades.** New BANNED bullets: `let _ = tx.send(value);` on a `tokio::sync::oneshot::Sender` (discarding the `Err(value)` when the receiver is gone makes the producer's work invisible), and `recv.await.unwrap()` on a `oneshot::Receiver` when the producer can fail or be dropped. Cross-link added pointing to §B21 for the work-runs-but-can't-be-observed case.
580
+
581
+ - **§B15 (Advanced async) — `Notify` lost-wakeup, half-consumed `Stream`, `select! biased`.** Three new BANNED bullets: `notify.notified().await` without first checking the represented condition (the canonical fix is the `notified() → pin! → check → await` four-step); dropping a half-consumed `Stream` without explicit acknowledgement that buffered items are lost; `tokio::select! { ... }` without `biased;` when arm priority matters (default per-poll pseudo-random can starve a low-priority arm). One REQUIRED bullet: use `biased;` for deterministic left-to-right arm priority.
582
+
583
+ - **§B13 (TOCTOU) — `Arc` count races, HashMap iter order.** New BANNED bullets: `if Arc::strong_count(&arc) == 1 { ... }` is a TOCTOU race — use `Arc::into_inner(arc)` (returns `Option<T>`) or `Arc::try_unwrap(arc)`. Restated that the same TOCTOU pattern via `HashMap::iter` + `HashMap::insert` is broken. New REQUIRED bullet: for ordered iteration, use `BTreeMap` or collect-then-sort — `HashMap::iter` order is randomized per-process and per-rehash, and tests that depend on it flake across machines.
584
+
585
+ ### Changed (cross-links between overlapping categories)
586
+
587
+ - **§B17 ↔ §A2** — opening of §B17 now explicitly states it covers the single-threaded reentrant-borrow hazard, while §A2 covers the thread-safety dimension. Same `Rc<RefCell<T>>` symptom, different failure modes.
588
+ - **§B21 ↔ §B8** — opening of §B21 now distinguishes "future never polled" (§B8) from "work ran but you can't cancel/observe" (§B21).
589
+ - **§B22 ↔ §B4** — opening of §B22 now points to §B4 for sync RAII contracts and frames §B22 as "what is **not** possible with Drop in async".
590
+ - **§B23 ↔ §B3** — opening of §B23 now states explicitly that it is the `select!`-specific application of §B3's general cancel-safety rule.
591
+
592
+ ### Changed (front-matter)
593
+
594
+ - **`description` extended with hazard-area triggers.** Added a closing sentence: "Covers async, unsafe, FFI, concurrency, crypto, supply-chain, and tests-that-pass-by-luck hazards." This improves Claude Code's skill matching on user queries that name the hazard area rather than the failure mode.
595
+
596
+ ### Tooling and documentation
597
+
598
+ - **`README.md` Layout** — `.gitattributes` and `.gitignore` now appear in the repository diagram with one-line descriptions. Both are functionally significant (line-ending discipline, project-local install target ignored) and were previously invisible from the docs.
599
+ - **`docs/roadmap.md`** — Tier D (§D1, §D2) is now flagged `✅ shipped in v0.3.0`. The category-expansions section previously listed only `§B16`/`§B17`/`§C8`/`§C9` shipments and silently omitted the new tier.
600
+ - **`commands/rust-intel-cc/fix.md`** — routing table extended with 15 new rows mapping symptoms for §B16–§B25, §C8–§C11, §D1, §D2. The table is still declared "non-exhaustive", but the most common symptoms now route correctly.
601
+ - **`.gitattributes`** — deduplication pass. Removed seven explicit `text eol=lf` rules for `*.md`, `*.rs`, `*.toml`, `*.lock`, `*.json`, `*.yml`, `*.yaml` since they are already covered by `* text=auto eol=lf`. Kept the necessary overrides: `*.sh`/`*.bash` → LF; `*.ps1`/`*.bat`/`*.cmd` → CRLF. Binary-section comment block tightened.
602
+ - **`rust-intel.md`** — working-tree line endings renormalized to LF (the v0.3.0 commit landed with `i/lf w/crlf`, which would have re-triggered the CRLF warning on the next edit). Now `i/lf w/crlf attr/text=auto eol=lf` — git no longer warns because the canonical eol is explicit.
603
+
604
+ ### Migration
605
+
606
+ Re-run the installer. The skill content changed (eight new BANNED bullets, several technical corrections, extended description); slash commands and install/uninstall behavior are unchanged.
607
+
608
+ If you have automation that hard-codes routing for `tokio::task::consume_budget`, `cargo expand --type-sizes`, or the v0.3.0 §B23 "send is cancel-safe" claim, update it: those are gone in v0.3.1.
609
+
610
+ ### Iteration: scope reframe + taxonomy expansion (drafted as 0.3.0)
611
+
612
+ First content release since v0.1.x. The skill itself (`rust-intel.md`) is **substantively rewritten**: scope is explicitly reframed, eight accuracy bugs from the v0.2.x text are fixed, and the category count grows from 26 to **41**. Slash commands, install/uninstall scripts, and the layout are unchanged. Anyone who already has v0.2.x installed re-runs the installer; no other migration needed.
613
+
614
+ ### Changed
615
+
616
+ - **Scope, stated up front.** The spec is now explicitly scoped to bugs in code that **already compiles and passes tests**. Compile-only failure modes (lifetime variance, trait bound mismatch, GAT lifetime bound errors, object-safety from generic methods, cyclic workspace deps, `?`-in-`main`, HRTB depth, recursive macro limits, `no_std` reflexive `std::*`, self-referential structs, `From`/`Into` cycles, MSRV mismatch) are *deliberately omitted* — the compiler is sufficient, the LLM cannot ship them. This spec covers what survives `rustc`, `clippy`, and `cargo test` and still breaks. The opening section, the front-matter `description`, and the README "What this is" / "Spec architecture" sections all reflect the new scope.
617
+
618
+ - **§B3 — `AsyncWriteExt::write_buf` cancel-safety corrected (technical error).** v0.2.x text listed `write_buf` as cancel-UNSAFE; per tokio's documented cancel-safety contract, `write_buf` is cancel-safe (single-shot). The actually-unsafe variant is `write_all_buf` (safe-with-caveat: the buffer may be partially advanced) and `write_all` (unsafe). Text now distinguishes all three.
619
+
620
+ - **§B8 — `tokio::spawn(async_fn())` "future-of-future" claim removed (technical error).** v0.2.x text asserted that `tokio::spawn(async_fn())` creates a future-of-future and spawns the outer wrapper, dropping the inner. That is wrong: an `async fn` returns `impl Future` directly, and `tokio::spawn` polls it. The bullet is gone; replaced with the actual forgotten-await failure modes (a future bound to a variable but never awaited; a future-returning call in a non-async function).
621
+
622
+ - **§B9 — `tokio::sync::Mutex` "detects deadlock under `tokio-console`" claim corrected (technical error).** `tokio-console` provides *visibility* (which task holds which lock, who is waiting), not detection. Deadlock detection is `parking_lot::deadlock::check_deadlock()` for sync sections or human review of documented lock-acquisition orders. Reworded accordingly.
623
+
624
+ - **§B5 — `#[repr(Rust)]` framing corrected (technical error).** v0.2.x described `#[repr(Rust)]` as "unstable". The attribute itself is stable (it is the default repr); what is unspecified is the *layout* the default implies. Reworded; expanded list of pinned reprs (`repr(C)`, `repr(transparent)`, `repr(uN)`).
625
+
626
+ - **§B5 — `slice::align_to` removed from "safe abstractions" list (technical error).** `<[T]>::align_to::<U>` is `unsafe fn`; v0.2.x had it in the safe-defaults list alongside `bytemuck::Pod` / `bytemuck::cast_slice`. Removed from the safe list and from the "use instead of raw pointer arithmetic" list; explicit note added that it requires the same `Pod`-style invariants as `transmute` and a `// SAFETY:` block.
627
+
628
+ - **§B7 — `Box::new_uninit_slice` nightly tag removed (stale).** Stabilized in Rust 1.82 (October 2024); spec already targets Rust 1.84+. The method is now listed as a stable alternative to `vec![0u8; N].into_boxed_slice()` for zero-init-wasted scenarios, with `assume_init` flagged as `unsafe`.
629
+
630
+ - **§B7 — stack-overflow threshold rationale clarified.** The v0.2.x `N * size_of::<T>() > 4096` line conflated page size with stack budget. Replaced with the real numbers — 8 MiB on Linux main thread, 2 MiB on `std::thread::spawn`, ~2 MiB on tokio tasks — and the ~64 KiB practical rule of thumb. The `Box::new([0u8; N])` placement trap (array built on stack *before* being moved to heap) is now called out explicitly.
631
+
632
+ - **§B5 — `Vec::into_raw_parts` pinned to Rust 1.93.** Verified via the stdlib docs: stable since 1.93.0. The spec's MSRV is 1.84, so the `ManuallyDrop<Vec<T>>` + manual `(ptr, len, cap)` decomposition (stable since 1.0) is the default; the `Vec::into_raw_parts` convenience is opt-in on a bumped MSRV. The version pins section reflects this.
633
+
634
+ - **§B5 — `mem::uninitialized` / `mem::zeroed` promoted to BANNED list.** Previously surfaced inside a REQUIRED bullet about `MaybeUninit` discipline; now each has its own BANNED line spelling out the UB conditions (`mem::uninitialized` deprecated since 1.39 and UB for any type with invariants; `mem::zeroed` UB for `bool`/`&T`/`Box<T>`/`NonZero*`/restricted-discriminant enums/`#[repr(transparent)]` wrappers over those). The compiler does not stop either call.
635
+
636
+ - **§A1 — repositioned as "stale APIs and slopsquatting" (scope reframe).** Pure `E0599` hallucinations no longer qualify (compiler catches them). The category now covers stale-but-still-valid APIs, `#[deprecated]`-not-removed APIs, wrong-version-of-crate semantics drift, and supply-chain slopsquatting — exactly the cases where the code compiles and runs but is wrong (or malicious).
637
+
638
+ - **§A3 — repositioned as "`pub` as a hammer for `E0603`" (scope reframe).** Now framed as "LLM reflexively makes things `pub` to silence E0603; code compiles and works; semver surface silently expanded" — a real silent residue, not generic visibility hygiene. (Section is at §A3 in the final v0.3.0 numbering; see "Removed" below for the gap-closing renumber.)
639
+
640
+ - **§A2, §B5, §B11, §B12, §B15, §C1 — depth expansions.** §A2 (Smart pointer misuse) gains `Cow`, `Arc::make_mut`, `Rc::get_mut`/`Arc::get_mut`, `ArcSwap`. §B5 gains `MaybeUninit` discipline, strict provenance API rules (Rust 2024+), `slice::from_raw_parts` invariant list. §B11 gains `tokio::task::consume_budget`. §B12 cross-links to the new §B24 for constant-time comparison. §B15 gains `Stream` vs `Iterator` failure modes. §C1 gains `#[repr(transparent)]` zero-cost newtype guidance.
641
+
642
+ - **Trigger table — extended and split.** Phrase-based triggers extended (singletons, retries, rate-limit, batching, secret comparison, JSON parsing, tracing instrumentation, graceful shutdown, workspace features, channels, shared mutable state, type wrappers, async cleanup). New code-pattern triggers section: `async fn` with `Mutex<...>`, `Rc<RefCell<>>`, `unsafe impl Send/Sync`, untracked `JoinHandle`, `impl Drop` with `.await`, `impl Deref` on non-pointer wrappers, `#[serde(untagged)]`, untagged TOCTOU patterns, raw-bytes comparisons in security contexts, `select!` with arm side effects, `tokio::spawn` under active spans, `mem::transmute`/`ptr::read`/`slice::from_raw_parts`, large stack arrays.
643
+
644
+ - **Post-flight checklist — extended.** New surface-able items: manual `Send`/`Sync` impl (§B18), `#[serde(untagged)]` enums and string-keyed JSON (§B20), untracked `JoinHandle`s (§B21), `impl Drop` with async-looking work (§B22), `==` on secrets (§B24), every `extern "C" fn` and `Box::into_raw`/`Box::from_raw`/`Vec::into_raw_parts`/`Vec::from_raw_parts` pair (§B25), unbounded channels by runtime (§C8), spawn without `.in_current_span()` under instrumented contexts (§C9), default features that pull heavy deps (§C10), `impl Deref` on non-pointer wrappers (§C11), `thread::sleep` in tests (§D1), `#[should_panic]` without `expected` (§D1).
645
+
646
+ - **Front-matter `description`.** Was: "Hard rules for writing Rust that LLMs systematically get wrong... Defends against the full known taxonomy of LLM failure modes in Rust as of 2026." Now: "Hard rules for writing Rust in code that already compiles and passes tests but is silently broken, slow, or semver-fragile. Load this BEFORE writing any Rust code. Targets bugs that survive rustc, clippy, and cargo test but fail in production or rot the codebase."
647
+
648
+ ### Added
649
+
650
+ **Tier B — Silent correctness bugs.** Ten new categories.
651
+
652
+ - **§B16. Equality and hashing contracts.** Manual `PartialEq` without matching `Hash`, manual `PartialOrd` without total-order `Ord`, `f64`/`f32` keys without `OrderedFloat`/`NotNan`. Failure mode: `HashMap` silently loses keys, `BTreeMap` behaves nondeterministically. Compiles, often passes thin tests, corrupts data at contention.
653
+ - **§B17. `RefCell` / `Mutex` runtime borrow panics.** `Rc<RefCell<T>>` in callback/traversal chains, reentrant `borrow_mut()`, undocumented borrow-disjointness invariants. Compiles, tests pass at low concurrency, production panics. REQUIRED: `try_borrow_mut()` with `BorrowMutError` handling for tree traversals.
654
+ - **§B18. Manual `unsafe impl Send` / `unsafe impl Sync`.** Reflexive `unsafe impl Send` to silence `tokio::spawn` bound errors. Now requires explicit `// SAFETY:` citing the synchronization invariant; impls without one are BANNED.
655
+ - **§B19. Iterator invalidation through indirection.** Borrow checker catches `Vec` invalidation at compile time; it does *not* catch invalidation through `RefCell<Vec<T>>`, `unsafe`, or `for i in 0..vec.len()` loops that mutate `vec.len()` mid-loop. Now covered.
656
+ - **§B20. `serde` field-presence vs null vs default.** `Option<T>` with `#[serde(default)]` conflates absent with null. `#[serde(untagged)]` enums silently match wrong variants on overlapping shapes. `#[serde(rename = "...")]` without round-trip test. Compiles, deserializes, drift downstream.
657
+ - **§B21. `JoinHandle` semantics: drop ≠ abort.** Dropping a `tokio::task::JoinHandle` *detaches* the task; it does not abort it. Spawning fire-and-forget without explicit `// fire-and-forget: detached by design` annotation is now BANNED. `JoinSet` recommended for joinable fan-in.
658
+ - **§B22. `async Drop` is not real.** `impl Drop` calling `tokio::spawn`-ing an async cleanup is fire-and-forget and may not run before runtime shutdown; `block_on` inside `Drop` re-enters the runtime and deadlocks. Resources requiring async cleanup must expose an explicit `async fn close(self)`.
659
+ - **§B23. `select!` arm side effects under cancellation.** Side effects (DB writes, channel sends, file flushes) inside a `tokio::select!` arm may not be observed if another branch wins. Each arm must be cancel-safe or guarded; side effects belong after the `select!` returns on the winning branch.
660
+ - **§B24. Timing attacks via `==` on secrets.** `if token == expected { ... }` for any secret comparison (API tokens, password-after-hash, MAC tags, OTP codes) leaks timing information. REQUIRED: `subtle::ConstantTimeEq` or `constant_time_eq` crate. Cross-linked from §B12.
661
+ - **§B25. Panic and ownership across `extern "C"` ABI.** Panics escaping `extern "C"` boundaries (UB pre-1.81, process abort since), `Box`/`Vec`/`String`/`Rc`/`Arc` passed directly through FFI (no stable ABI), allocator-mismatched `Box::from_raw`, `cap`-mismatched `Vec::from_raw_parts`, missing paired free functions, gratuitous `#[no_mangle]`. REQUIRED: `catch_unwind` wrapping, paired `extern "C" fn rust_drop_T(p: *mut T)`, `ManuallyDrop<Vec<T>>` or `Vec::into_raw_parts` (≥ 1.93) with the full tuple documented, layout verification against C headers, miri in CI for every FFI file. Absorbs the previously-roadmapped §B17 (FFI Drop).
662
+
663
+ **Tier C — Architecture and ergonomics.** Four new categories.
664
+
665
+ - **§C8. Channel-and-runtime mismatch.** `std::sync::mpsc` in async (blocks executor), `tokio::sync::mpsc` for MPMC (only first receiver gets messages), `crossbeam::channel` in async (await around recv blocks the worker). Now mapped explicitly.
666
+ - **§C9. `tracing` span leakage across `tokio::spawn`.** Spawning without `.in_current_span()` (requires `tracing::Instrument` in scope) loses span context. `spawn_blocking` requires explicit `span.enter()` inside the closure.
667
+ - **§C10. Workspace feature unification surprises.** Default features pulling heavy deps for all workspace members, dev-dependency features leaking into release builds via cargo's feature unification. `cargo hack --feature-powerset --no-dev-deps` in CI now recommended.
668
+ - **§C11. `Deref` polymorphism antipattern.** `impl Deref<Target = Inner> for Wrapper` for inheritance-style composition. Rust API Guidelines C-DEREF rule cited; explicit-accessor pattern (`fn user(&self) -> &User`) given as the right shape.
669
+
670
+ **Tier D — Testing and CI gaps.** New tier. Two categories.
671
+
672
+ - **§D1. Tests that pass by luck.** `thread::sleep` waiting for async work (flaky), `#[should_panic]` without `expected = "..."` (any panic passes including in test setup), tests asserting absence of panic instead of postconditions. REQUIRED: `tokio::time::pause`/`advance`, explicit `Notify`/`oneshot` synchronization, `expected` substring pinning.
673
+ - **§D2. Integration vs unit test placement drift.** `#[cfg(test)] mod tests` referencing private items that are later split into siblings; integration tests in `tests/` depending on `pub(crate)`. Recommendation: unit tests for private items live next to the impl; integration tests use the public API only or a `#[cfg(feature = "test-support")]` gate.
674
+
675
+ **Version pins section.** New section at the end of the spec listing the stability cutoffs assumed throughout: `Box::new_uninit_slice` (1.82), `Vec::into_raw_parts` (1.93 — `ManuallyDrop<Vec<T>>` is the MSRV-safe fallback), strict-provenance API (1.84), tokio cancel-safety contracts (1.x stable), `rand` 0.8 → 0.9 `thread_rng()` → `rng()` rename, Rust 1.80+ `unexpected_cfgs` auto-lint, AFIT (1.75), `consume_budget` (tokio 1.x), panic across `extern "C"` ABI (UB → process abort at Rust 1.81; `extern "C-unwind"` available).
676
+
677
+ ### Removed
678
+
679
+ - **An earlier draft's Tier A category for trait bounds and type mismatches (E0277 / E0308).** Compile-only failure mode; rustc catches every case and the LLM cannot ship a binary with it. Out of scope for v0.3.0. Tier A numbering was tightened by renumbering the surviving categories: the former §A3 (Smart pointer misuse) is now §A2, and the former §A4 (`pub` as a hammer for E0603) is now §A3. The Tier A intro carries a short note about the historical retirement so older references resolve to context.
680
+ - **Empty roadmap entries §B16 (serde), §B17 (FFI Drop), §C8 (workspace), §C9 (tracing).** All four graduated into the main spec (now §B20, §B25, §C10, §C9 respectively). §B18 (`no_std`) remains in roadmap as low-priority but is explicitly flagged as out-of-scope by the new framing.
681
+
682
+ ### Tooling and documentation
683
+
684
+ - **`README.md` Status block, "What this is", install description, layout comment, and Spec architecture table** synced to v0.3.0. Tier D added to architecture table. The "26 categories" claim is removed in favor of "the categories from the spec" (count lives in the spec, not the README).
685
+ - **`docs/roadmap.md`** fully refreshed: all `/rust-{audit,fix,plan}` references corrected to `/rust-cc-*`, broken relative paths to `commands/rust-{audit,fix,plan}.md` corrected to `commands/rust-intel-cc/{audit,fix,plan}.md`. Categories that shipped into the spec marked `✅ shipped in v0.3.0`. Out-of-scope note added.
686
+ - **`docs/sources.md`** — the single `/rust-fix` reference corrected to `/rust-cc-fix`; SafeTrans and Rust-SWE-Bench entries updated to reflect the retirement of the historical §A2 category (the empirical figures are preserved as Tier A intro motivation) and the renumbering that followed.
687
+ - **`commands/rust-intel-cc/{audit,fix,plan}.md`** — references to "`rust-intel.md`" reworded to "the `rust-intel` skill" (decouples command files from the on-disk filename, which is `SKILL.md` after install). `audit.md` example header updated from `rust-audit report` to `rust-cc-audit report`. `26 categories` references removed. `fix.md` routing table E0277/E0308 row updated to point at `out-of-scope (compile-only)` with a check for §A2/§A3/§C5 residue from the reflexive fix.
688
+ - **`commands/README.md`** — "26 categories" wording dropped.
689
+ - **Line endings.** `.bat` and `.ps1` files were stored in the working tree as LF despite `.gitattributes` declaring `eol=crlf`. Working tree now matches the attribute. (Index was correct; only the working copy needed renormalization.)
690
+ - **Windows symlink note** added to README: `--symlink` is bash-only; PowerShell and cmd.exe installers always copy.
691
+
692
+ ### Migration
693
+
694
+ Re-run the installer (`./rust-cc-install.sh`, `.\rust-cc-install.ps1`, or `rust-cc-install.bat` — add `--user` / `-User` if you previously installed user-global). The skill file is byte-different; the slash commands are not; no other migration is needed.
695
+
696
+ If you have automation that hard-codes the category count or references Tier A by old number, update it: the historical §A2 category is gone, the surviving categories were renumbered (§A3 → §A2, §A4 → §A3), the total is now 41, and references to compile-only failure modes should be rerouted (the routing table in `/rust-cc-fix` already does this — E0277/E0308 etc. → `out-of-scope (compile-only)` with a check for reflexive-fix residue against §A2/§A3/§C5).
697
+
698
+ ## [0.2.2] — 2026-05-18
699
+
700
+ Same-day script renaming. The skill itself (`rust-intel.md`) is byte-identical to v0.1.2, v0.2.0, and v0.2.1 — no rule changes, no new categories.
701
+
702
+ ### Changed
703
+
704
+ - **Install and uninstall scripts gained the `rust-cc-` prefix.** Generic names like `install.bat` / `install.sh` are a footgun: if the repo lives on the user's `PATH`, or if multiple tooling repos share a common convention, an unprefixed `install` shadows other things in the system. Renamed all six scripts to be project-specific:
705
+ - `install.sh` → `rust-cc-install.sh`
706
+ - `install.ps1` → `rust-cc-install.ps1`
707
+ - `install.bat` → `rust-cc-install.bat`
708
+ - `uninstall.sh` → `rust-cc-uninstall.sh`
709
+ - `uninstall.ps1` → `rust-cc-uninstall.ps1`
710
+ - `uninstall.bat` → `rust-cc-uninstall.bat`
711
+ Internal references (`.bat` → sibling `.ps1`, `--help` text) and external docs (README, `commands/README.md`) updated to match.
712
+
713
+ ### Migration
714
+
715
+ If you previously cloned the repo and ran `./install.sh` / `.\install.ps1` / `install.bat`, the next pull will rename them. Update any automation, aliases, or notes accordingly. The script behaviour is unchanged.
716
+
717
+ ## [0.2.1] — 2026-05-18
718
+
719
+ Same-day rectification of v0.2.0. The skill itself (`rust-intel.md`) is byte-identical to v0.1.2 and v0.2.0 — no rule changes, no new categories.
720
+
721
+ ### Changed
722
+
723
+ - **Slash commands flattened from `/rust-intel-cc:*` to `/rust-cc-*`.** v0.2.0 misread the original intent: the repo's nested `commands/rust-intel-cc/` directory was meant for *file organization only*, with the installer flattening to a simple-prefixed slash surface. v0.2.1 honors that split:
724
+ - **Repo source** (unchanged from v0.2.0): `commands/rust-intel-cc/{audit,fix,plan}.md`.
725
+ - **Installed target** (new): `<claude>/commands/rust-cc-{audit,fix,plan}.md` — flat, with a `rust-cc-` prefix, no subdirectory.
726
+ - **Slash commands** (new):
727
+ - `/rust-intel-cc:audit` → `/rust-cc-audit`
728
+ - `/rust-intel-cc:fix` → `/rust-cc-fix`
729
+ - `/rust-intel-cc:plan` → `/rust-cc-plan`
730
+ The installer does the rename during copy. Repo stays tidy (one umbrella directory for three related commands); slash surface stays short (no namespace prefix in the prompt).
731
+ - **Installers and uninstallers sweep every prior layout** before copying:
732
+ - v0.2.1+ flat-with-prefix (`rust-cc-{audit,fix,plan}.md`)
733
+ - v0.2.0 namespace dir (`rust-intel-cc/`)
734
+ - v0.1.x legacy flat-no-prefix (`{rust-audit,rust-fix,rust-plan,rust-intel}.md`)
735
+
736
+ ### Migration from v0.2.0
737
+
738
+ Re-run the installer (`./install.sh`, `.\install.ps1`, or `install.bat` — add `--user` / `-User` if you previously installed user-global). It will remove the v0.2.0 `commands/rust-intel-cc/` directory and install the v0.2.1 flat files. Update any references to the old `/rust-intel-cc:*` slash commands to the new `/rust-cc-*` form.
739
+
740
+ ### Migration from v0.1.x
741
+
742
+ Same as v0.2.0's migration — re-running the installer sweeps the old `/rust-audit`, `/rust-fix`, `/rust-plan` automatically.
743
+
744
+ ## [0.2.0] — 2026-05-18
745
+
746
+ Tooling restructure. The skill itself (`rust-intel.md`) is byte-identical to v0.1.2 — no rule changes, no new categories. What changed is how the slash commands are organised and how the installers behave by default.
747
+
748
+ ### Changed
749
+
750
+ - **Slash commands moved into the `rust-intel-cc` namespace.** The three top-level commands are gone; they now live under `commands/rust-intel-cc/` and are invoked with the colon-namespace syntax Claude Code uses for nested commands:
751
+ - `/rust-audit` → `/rust-intel-cc:audit`
752
+ - `/rust-fix` → `/rust-intel-cc:fix`
753
+ - `/rust-plan` → `/rust-intel-cc:plan`
754
+ Rationale: a single `rust-intel-cc` umbrella is easier to remember, easier to grep, and isolates the three sub-commands into one Claude Code namespace instead of three top-level slots.
755
+ - **Installers default to project-local `./.claude/`** instead of user-global `~/.claude/`. Pass `--user` (bash) or `-User` (PowerShell) to get the v0.1.x global-install behaviour. `CLAUDE_CONFIG_DIR` env var still overrides everything. Rationale: a Rust skill is most useful scoped to the project being worked on; the global install is the rarer case and is now an explicit opt-in.
756
+ - **Installers and uninstallers now sweep the legacy v0.1.x flat layout** (`commands/rust-audit.md`, `commands/rust-fix.md`, `commands/rust-plan.md`, plus the very early `commands/rust-intel.md`) and the entire `commands/rust-intel-cc/` directory before copying. Re-running the installer cleanly migrates from any previous version.
757
+
758
+ ### Added
759
+
760
+ - **`install.bat` / `uninstall.bat`** — thin wrappers around the corresponding `.ps1` scripts for users in `cmd.exe`. Pass-through arguments work as expected (`install.bat -User`, etc.).
761
+ - `.gitattributes` now pins `*.bat` to CRLF (cmd.exe will not parse LF-terminated batch files reliably).
762
+ - `/.claude/` added to `.gitignore` so running the installer from the repo root does not pollute the working tree.
763
+
764
+ ### Migration
765
+
766
+ For anyone upgrading from v0.1.x:
767
+
768
+ 1. Pull the new repo state.
769
+ 2. Re-run the installer (`./install.sh`, `.\install.ps1`, or `install.bat`). It will sweep the old flat layout — `/rust-audit`, `/rust-fix`, `/rust-plan` — from whatever target it was previously installed to, and put the new namespaced layout in its place.
770
+ 3. If you previously installed to `~/.claude/` (the v0.1.x default), pass `--user` / `-User` on the new install — otherwise the installer will treat your current directory as the install target.
771
+ 4. Update any tooling or notes that invoked the old slash commands to use the new namespaced names.
772
+
773
+ The skill itself activates the same way as before. Only the slash-command names changed.
774
+
775
+ ## [0.1.2] — 2026-05-17
776
+
777
+ Tooling-only patch. No changes to `rust-intel.md` (the skill itself); no new categories.
778
+
779
+ ### Added
780
+
781
+ - **`uninstall.sh` / `uninstall.ps1`.** Inverse of the installers — removes the rust-intel skill directory and the named command files (`rust-audit.md`, `rust-fix.md`, `rust-plan.md`, and the legacy `rust-intel.md`) from `$CLAUDE_CONFIG_DIR`. Idempotent (safe to run when nothing is installed). Narrow by design: only touches paths the installers create, so other skills and commands under `~/.claude/` are left alone.
782
+ - README "Uninstall" section documenting both scripts.
783
+
784
+ ### Changed
785
+
786
+ - **`install.sh` / `install.ps1` also remove the legacy `commands/rust-intel.md`** before installing. Earliest iterations of the project shipped rust-intel as a single command file rather than a skill; that layout is no longer used, but a stale `commands/rust-intel.md` left over from such an install would shadow the proper skill in Claude Code's listing (appearing as a duplicate "rust-intel" entry). Both installers and both uninstallers now sweep this path explicitly.
787
+
788
+ ## [0.1.1] — 2026-05-17
789
+
790
+ Third- and fourth-round reviews surfaced eleven issues worth a same-day patch. Two are technical errors carried over from 0.1.0 (§B15 AFIT/RPITIT conflation, §B11 `yield_now` mis-substitution) that would propagate into reader code. Three are scope or statistical overreaches (§C2 anyhow, §B5 N=40, §B14 magic numbers). One is a structural split (§B1 → §B1a + §B1b). The fourth-round review caught five further refinements introduced by the third-round patches themselves: an over-categorical RPITIT claim, a temporally-fragile `dyn`-compatibility statement, a `Vec::push` formulation that read as if `push` itself was the failure, a `rand` 0.8→0.9 API gap, and a reframing of the §B15 Pin paragraph away from a strawman toward the actual LLM failure modes (`Pin<&mut>` vs `Pin<Box>`, `Unpin` as auto-trait). Install scripts now clean-replace any prior version. No new categories; no breaking changes to BANNED/REQUIRED wording.
791
+
792
+ ### Changed
793
+
794
+ - **§B15 AFIT vs RPITIT — terminology rewrite (technical correction).** The previous text described `fn bar(&self) -> impl Future + Send` as "native AFIT with a Send bound via RPITIT". This conflates two distinct syntactic forms: AFIT is `async fn bar(&self) -> T`, RPITIT is `fn bar(&self) -> impl Future + Send`. Section now leads with the AFIT/RPITIT distinction and a 4-row decision table mapping use case → construct (plain AFIT / RPITIT / `trait-variant` / `async-trait`).
795
+ - **§B11 — `yield_now` no longer presented as alternative to `spawn_blocking` for CPU-bound work (technical correction).** `yield_now` only schedules other tasks already on the same worker thread; the worker itself remains occupied. `spawn_blocking` uses a *separate* blocking-task thread pool and is the only correct answer for CPU-bound work. Text now explicitly disallows the substitution and explains the executor-starvation mechanism.
796
+ - **§C2 — anyhow rule narrowed.** "Never `anyhow::Error` in `lib.rs` public APIs" was too broad — it banned a legitimate choice in internal/workspace libraries. The rule now applies specifically to **published library crates** (anything shipped to crates.io with a `pub` API other authors consume). Internal/workspace libraries may use `anyhow` as a deliberate trade-off.
797
+ - **§B5 — `~55% UB rate` headline now discloses sample size.** Heading changed to "high UB rate in small-N studies"; body labels the 22/40 figure as directional rather than definitive, while preserving the structural claim that LLM-generated `unsafe` is significantly more dangerous than LLM-generated safe code.
798
+ - **§B14 — folk numbers "typically 100–10000" replaced with a sizing formula.** Size `N` from expected producer burst over one consumer cycle, capped by memory budget per pending message. If the right `N` cannot be reasoned about, that is itself a signal to design the backpressure policy before writing the channel.
799
+ - **§B15 — RPITIT vs AFIT softened from "different" to "share a desugar lineage, materially different at the source-code level".** AFIT desugars into RPITIT internally, so calling them "different" is technically too strong even though the *written* syntactic forms have different bound-expressing capabilities. Reworded to make the distinction precise without overclaiming.
800
+ - **§B15 — decision table `dyn`-compatibility row hedged temporally.** `dyn`-compatible RPITIT stabilization is in flight; row now says "as of stable Rust through mid-2026, verify against your `rustc --version`" rather than asserting it as a fixed property.
801
+ - **§B14 `Vec::push` example clarified.** Previous wording read as if `Vec::push` itself was the failure. Reworded to "a `Vec` that is `push`-ed in a hot loop with no consumer or cap" — the failure is the missing drain or bound, not the call.
802
+ - **§B12 — rand 0.8 / 0.9 API gap noted.** `thread_rng()` was renamed to `rng()` in `rand` 0.9. The rule (OS-backed entropy for keys/nonces/salts) is unchanged; the BANNED entry now states this explicitly and asks the user to pin the `rand` version assumed.
803
+ - **§B15 Pin reorientation.** The "you cannot hold a reference through `.await` and expect Pin to fix it" bullet was a strawman — that confusion is rare in practice. Replaced with the actual LLM-typical confusions: mixing up `Pin<&mut T>` (borrowing, stack) with `Pin<Box<T>>` (owning, heap), and the fact that `Unpin` is an auto-trait so most uses of `Pin` are incidental and add no real constraint.
804
+ - **Principle section — self-referential meta-acknowledgment.** Closes the third-round structural concern that the document's own empirics (percentages, rates, sample sizes) were stated without inline source-anchors. The Principle section now ends with a paragraph stating that every empirical figure maps to a sourced entry in `docs/sources.md`, with a recommendation to load that file alongside the skill when statistical precision matters. This makes the "prove, don't guess" principle apply to the document itself, not only the Rust it asks the reader to write.
805
+
806
+ ### Added
807
+
808
+ - **§B1b — Lifetime leaking through public APIs promoted to peer subsection.** The "Related anti-pattern" tail at the end of §B1 was conceptually a separate failure mode (exposing `'a` in `pub fn` signatures is not the same as binding too many things to one `'a` *inside* a function). It now has its own BANNED/REQUIRED block, parallel in structure to §B1a (laundering). Section header renamed to "Lifetime laundering and lifetime leaking".
809
+ - **`install.sh` / `install.ps1` clean-replace step.** Both installers now remove the target skill directory contents and the three named command files before copying, so stale files from a previous version cannot linger.
810
+
811
+ ## [0.1.0] — 2026-05-17
812
+
813
+ Initial release. 26 categories plus a meta-layer.
814
+
815
+ ### Added
816
+
817
+ **Meta-layer:**
818
+ - "Prove, don't guess" principle.
819
+ - Blocking protocol — explicit refusal format when context is insufficient.
820
+ - Operating mode — 7 mandatory steps before generating any Rust.
821
+ - Self-monitoring — "user-phrase → activated category" trigger table.
822
+ - Pre-flight checklist (7 questions) and Post-flight checklist (what to surface in the summary).
823
+
824
+ **Tier A — Mass compilation failures:**
825
+ - §A1. API hallucinations and stale APIs (+ slopsquatting with documented attacks: CrateDepression 2022, `faster_log`/`async_println` 2025).
826
+ - §A2. Trait bounds and type mismatches (E0277 / E0308).
827
+ - §A3. Smart pointer misuse.
828
+ - §A4. Module visibility and pub leaks.
829
+
830
+ **Tier B — Silent correctness bugs:**
831
+ - §B1. Lifetime laundering (+ lifetime leaking through public APIs).
832
+ - §B2. `std::sync::Mutex` across `.await` (+ Mutex poisoning cascade, oversized critical section).
833
+ - §B3. Async cancellation.
834
+ - §B4. Drop order and RAII contracts.
835
+ - §B5. Unsafe that looks safe.
836
+ - §B6. Pattern matching exhaustiveness drift.
837
+ - §B7. Large stack allocations and arena pitfalls.
838
+ - §B8. Silent task dropping (forgotten `.await`).
839
+ - §B9. Lock ordering and ABBA deadlock.
840
+ - §B10. Reference cycles in `Rc`/`Arc` graphs.
841
+ - §B11. Blocking the async executor.
842
+ - §B12. Cryptographic code (silent insecurity).
843
+ - §B13. Check-then-act races in concurrent collections (TOCTOU).
844
+ - §B14. Unbounded channels and backpressure neglect.
845
+ - §B15. Advanced async pitfalls (AFIT, Pin, Waker, block_on).
846
+
847
+ **Tier C — Architecture and ergonomics:**
848
+ - §C1. Blanket impls in public APIs (semver hazard).
849
+ - §C2. Error handling discipline.
850
+ - §C3. Async runtime and ecosystem coherence.
851
+ - §C4. Iterator and allocation discipline.
852
+ - §C5. Reflexive `.clone()` as a borrow-checker silencer.
853
+ - §C6. Procedural macro hygiene.
854
+ - §C7. Cargo feature flag hygiene.
855
+
856
+ **Tooling:**
857
+ - `commands/rust-audit.md` — scan existing Rust against all 26 categories.
858
+ - `commands/rust-fix.md` — map an error symptom to a category and propose a root-cause fix.
859
+ - `commands/rust-plan.md` — pre-flight a new task through the trigger table and 7-question checklist.
860
+
861
+ **Repository scaffolding:**
862
+ - `LICENSE` (MIT) at repo root.
863
+ - `install.sh` / `install.ps1` for `~/.claude/` installation.
864
+ - `.gitattributes` pinning LF on `.sh`/`.md` and CRLF on `.ps1`.
865
+ - `docs/sources.md` with verified URLs for every empirical claim, including the published 2026-05-16 uproger.com field report.
866
+ - `docs/roadmap.md` listing planned categories and tooling.
867
+
868
+ ### Refinements during the pre-tag polish round
869
+
870
+ - "6-month production study" relabeled to "published field report" throughout `rust-intel.md` and `docs/sources.md` — the source is a public article, not unattributed internal observation.
871
+ - README's "compiler catches ~76%" claim corrected: the 76.3% figure is the share of *compilation failures* concentrated in two categories per Rust-SWE-Bench, not a share of "typical mistakes caught."
872
+ - AFIT recommendation in §B15 reordered to lead with native AFIT + RPITIT + `+ Send`; `trait-variant` second; `async-trait` only for `dyn Trait`. (Terminology further corrected in 0.1.1.)
873
+ - `cargo check --check-cfg` instruction replaced with the actual Rust 1.80+ behavior (automatic `unexpected_cfgs` lint from `Cargo.toml` declarations).
874
+ - `bytes::Bytes` removed from the §B5 "safe abstractions" list; replaced with `bytemuck::Pod` / `bytemuck::cast_slice`, with a note that `Bytes` is a buffer container, not a safe-transmute abstraction.
875
+ - `rand::random()` "not cryptographically secure" claim corrected: `ThreadRng` is a CSPRNG; the actual ban targets `SmallRng` / `StdRng` for security work and recommends `OsRng` as the default for keys and nonces.
876
+ - Slopsquatting "~45%" figure in §A1 hedged to match the verification status in `docs/sources.md`.
877
+ - Tier B intro rewritten to motivate via §B2 (46%→19%) and SafeGenBench (~57%) instead of the unverifiable RustPrint citation.
878
+ - "The eleven categories below" in Tier B intro → "fifteen" (matches §B1–§B15).
879
+ - README slash-command misnaming: skills aren't invoked with `/`; clarified that the skill activates automatically.
880
+ - `commands/rust-audit.md` and `commands/rust-plan.md`: removed inline duplications of category-level rules and the Pre-flight checklist questions. Both now reference the skill as the source of truth.
881
+ - `commands/rust-fix.md`: clarified the symptom→category table is a routing layer, not duplicated rule knowledge.
882
+ - `RustPrint` benchmark entry removed from `docs/sources.md` and its citation in `rust-intel.md` (no verifiable source under that name).
883
+ - `LICENSE-APACHE` dropped (dual-licensing not needed for a prose-first repo; MIT alone is sufficient).
884
+
885
+ ### Source basis
886
+
887
+ Built on: a published 6-month field report (~80k LOC, tokio + sqlx + unsafe), benchmarks RustEvo², SafeTrans, CRUST-Bench, SafeGenBench, Rust-SWE-Bench, AkiraRust, industry reports from Faros AI and Lightrun (2026), and documented crates.io supply-chain incidents. Full list — [`docs/sources.md`](docs/sources.md).