Format — Keep a Changelog, versioning — SemVer.
Major = breaking changes to BANNED/REQUIRED wording that tooling depends on. Minor = a wholly new numbered category — or another change to the spec's shape (a category retired, split, or renumbered); it also covers a raised runtime/install floor or other compatibility-breaking tooling change that makes a previously working install path fail. Patch = wording refinements, fixes, new sources, and new bullets/gaps/enrichments added within existing categories — however many, and however substantive — as long as they refine or extend coverage without adding a new numbered category or changing the BANNED/REQUIRED shape tooling depends on.
MINOR compatibility change: npm installation and repository tooling now require Node.js 24.0.0 or newer, enforced at startup as well as declared in package metadata. The existing 59 numbered categories, installer entry points, and installed paths are unchanged. Upgrade Node.js before using the npm installers on a previously supported older runtime.
- Specification correctness and source alignment. Audited and repaired security/crypto, async cancellation and shutdown, unsafe/FFI, ownership, concurrency, Serde/API compatibility, Cargo features/macros, and test-oracle guidance. Eight additional consistency passes closed cross-module contradictions and made preconditions explicit; the source ledger, core skill, command adapters, and generated Codex mirror were aligned.
- Ownership and performance. Added conditional guidance for
mem::take/replace, buffer reuse, shared-slice retention, capacity growth, sparse-map scans, partial top-k selection, cached sort keys, and reusable records. Failure/restoration, ordering, retention, and input-budget requirements accompany the optimizations. - Install and release recovery. Node, Bash, and PowerShell installers use staged inventories and restartable transactions. Fixed rollback/recovery defects, including PowerShell uninstall deleting a pre-existing foreign skill directory. Version manifests stay synchronized, and publishing verifies the committed version instead of rewriting the tagged tree. Windows recovery is tested for process interruption, not sudden power loss.
- Validation and CI. Hardened the repository-specific Markdown/JavaScript scanners, immutable workflow metadata checks, memory/work bounds, execution registry, and anti-vacuity controls. CI covers the exact Node
24.0.0floor, Windows validation, Bash 3.2, both PowerShell runtimes, and installer recovery matrices. - Release rollover. Fixed the validator rejecting the documented empty
Unreleasedsection after a release cut. It now checks a populatedUnreleased, or the latest dated release matching the package version. Six new regression controls cover that selection and its failure paths; README and all three version manifests are updated for0.7.0. - npm Trusted Publishing. Replaced the publish token with GitHub Actions OIDC. Publication now starts when a stable GitHub Release is published, not on tag push alone. The workflow checks the exact release commit against successful CI, enforces stable tag/manifest agreement and npm's OIDC runtime floor, and retains provenance and integrity-aware retry protection. README documents the npm connection fields and release sequence; drafts and prereleases do not publish.
Release evidence. Round 54 closed the reviewed P0/P1 blockers; CI run 34324764342 passed all 10 jobs at 886f53a. That run predates the subsequent spec-consistency passes and release preparation: the exact release commit must pass the full validation/recovery workflow before tagging. Preparing this entry does not itself publish a tag or npm package.
The entries below preserve the development and review chronology. A round's counts, timings,
open findings, and pending-review statements describe its named revision, not the final release
state; later fixing dispositions and reports supersede them. The current count is the single
unqualified claim in Net tooling state below. Underlying reports live in docs/reviews/
in the repository, not in the npm tarball.
Spec consistency review, round 8. Corrected read-only sharing bounds, Arc/Weak lifecycle conditions, lock-call and single-thread async race guidance. Protected single-flight entries before initialization as well as while running, bounded pinned admission, and separated cumulative counters from live-work gauges. Clarified broadcast reliability, receiver-directed close/drain, and nested panic containment during Drop.
Spec consistency review, round 7. Corrected initialization and raw-access preconditions, distinguished unique-ownership Send/Sync proofs from synchronization, and made FFI thread affinity an independent requirement. Clarified Drop-based drop-check, C ABI buffer/string transport and role-specific borrowed/output/owned pointer contracts; valid controls were checked with Miri.
Spec consistency review, round 6. Distinguished path patches from git pinning, facade macro re-exports from generated-path resolution, and implicit/built-in cfgs from custom declarations. Corrected build-script environment tracking and scoped feature unification to the selected build graph, retaining isolated-consumer checks. A no-new-dependency constraint now preserves the requested behavior rather than implying permission for an unsupported case.
Spec consistency review, round 5. Scoped crypto-error opacity to authentication boundaries and removed the implication that prehashing alone hides secret length. Corrected Windows argument-encoding guarantees, allowed deliberate SameSite=None; Secure flows with CSRF controls, and distinguished recursive directory ensure from exclusive creation.
Spec consistency review, round 4. Corrected positional-field compatibility under #[non_exhaustive], private-test access and support-crate limits, and release-test panic-strategy coverage. Scoped Serde contract tests to supported directions and intentional untagged priority; replaced whole-input buffering and universal slowdown claims with pinned, path-dependent behavior.
Spec consistency review, round 3. Separated tracing argument skips from explicit fields and return/error events; corrected future construction, forwarding and select! ownership rules; made abort guidance handle completion races and actual yield boundaries; clarified cooperative scheduling limits.
Spec consistency review, round 2. Restricted the single-shot hasher exception to randomized foldhash, separated overflow checks from debug assertions, corrected the subtraction-guard advice, and distinguished fallible length validation from programmer assertions. Equality/hash guidance now follows the chosen equivalence relation, including bit-identity float keys, while retaining the project lint-policy requirement.
Spec consistency review. Corrected §B19's blanket preference for a growing iteration bound, retained §B7's resource budgets for iterative traversal, and clarified newtype runtime destruction and edition-2024 tail-result lifetimes. Aligned the final checklists and audit adapter with the scoped Blocking protocol: missing evidence is reported during inspection, while affected implementations still require their security prerequisites.
Field ownership transfer. Added mem::take, Option::take, and mem::replace to §E2/§C5 and the substitution catalog, with buffer-reuse and failure-contract conditions. Refined §B19 to distinguish intentional handoff from failed restoration and to state the limits of drop-guard recovery.
Performance guidance. Expanded §E2/§E3/§E5 and the substitution catalog with destination-buffer reuse, shared-slice retention, capacity-growth policy, sparse-map scans, partial top-k selection, cached sort keys, and reusable record buffers. Each recommendation states its ownership, failure, ordering or input-bound constraints and retains §E6's measurement discipline; primary API contracts are recorded in the source ledger.
Node.js runtime floor raised to 24. This is a support-policy choice targeting the current LTS line, not an API requirement: fs.cpSync has been available since Node 16.7.0 and explains the former floor. package.json now declares node >=24.0.0; both validators and both npm installers enforce that floor at process startup. The primary validation and publish jobs run on the current Node 24 line, while CI also keeps a distinct exact 24.0.0 floor job with validation and npm-installer smoke coverage. Earlier review entries that mention Node 16.7.0 remain historical records of the then-supported environment and are superseded by this entry.
This release is 0.7.0 (MINOR). Raising the runtime/install floor makes previously working installs on older Node lines fail, so it is classified by the compatibility rule above even though the numbered spec categories and BANNED/REQUIRED shapes are unchanged.
Semver classification against v0.6.0. The release boundary is the last released tag, v0.6.0 (d5b15ec), not an intermediate green CI commit. Against that baseline, the Node engine floor changes from >=16 to >=24.0.0 and is enforced at startup by the npm installers and validators. The two npm bin targets, installer CLI arguments, installed paths, and 59-category numbering remain unchanged. The runtime compatibility change determines the MINOR classification; earlier point-in-time diff statistics remain in the review reports.
Release tooling hardening. The manifest updater now records a recoverable transaction: POSIX file and parent-directory syncs, same-volume Windows rename discipline, a recovery-only entry point, and calibration of abrupt process exits at every journal/rename boundary plus failures after each replacement. POSIX parent-directory fsync is the durable rename barrier. On Windows, the calibration establishes process-interruption recovery using Node's synchronous filesystem calls; it does not claim recovery after sudden power loss because this implementation has no write-through directory-metadata primitive. The calibration verifies old-or-new manifest agreement, bytes, modes, and recursive temporary/backup/journal cleanup, including a negative nested-artifact case. The round-33 and round-34 fixing dispositions are recorded in the review ledger. The release workflow verifies that all three manifest versions match the tag before publication.
Installer and recovery net state. Node, Bash, and PowerShell installers stage complete
inventories, preserve byte-aware rollback safeguards, and recover interrupted install/uninstall
transactions. Boundary-matrix tests exercise same-operation and cross-operation recovery,
independent replacement inventories, recursive artifact cleanup, and foreign-directory
preservation. The last Windows preservation P1 was fixed by 09bb180 and accepted in round 54;
subsequent CI at 886f53a passed all 10 jobs. Finite child/job timeouts and the 45-minute publish
timeout bound the checks. Ambiguous recovery states are retained for manual recovery rather than
guessing which paths may be removed. The Windows contract remains process interruption, not
sudden-power-loss durability; historical reports retain the defects and partial fixes that led
to this state.
Every
docs/reviews/*.mdcitation below points to a file tracked in this repository (stable via a commit-pinned link, e.g.https://github.com/PHPCraftdream/rust-intel/tree/1591d39/docs/reviews) — not to a file included in the npm package:docs/reviews/is not inpackage.json'sfilesallowlist, so clone or browse the repository for the underlying reports. The package also explicitly lists the two license files, while npm includes package metadata, README, and applicable license files under its standard package rules.
Three independent audits of the shipped spec (completeness, correctness, currency), synthesized into one fix plan and landed. No new category, no BANNED/REQUIRED semantics changed beyond precision fixes — PATCH-shaped despite the size. Full evidence for every item lives in docs/reviews/: gap-audit-completeness-2026-08.md, correctness-audit-2026-08.md, currency-audit-2026-08.md, and the synthesized fix-plan-2026-08.md (70 entries, §1–§7); this entry summarizes by group rather than repeating each item's evidence.
This release also closes out two prior commits that shipped without a changelog entry: eca76bf (7 findings from an independent review of v0.6.0) and 3bce0e1 (6 second-pass findings from @oh's review of eca76bf — a factually-wrong dyn-compatibility claim among them, corrected and re-verified against rustc).
- Breaks-today (6 items). The spec's literal advice no longer compiles or protects nothing against the current major of a named crate:
rand0.10 renamedOsRng→SysRng(RUSTSEC-2026-0097 floor added);argon2/password-hash0.6 moved the salt generator offSaltString::generate(&mut OsRng);jsonwebtoken11 removedinsecure_disable_signature_validation;sqlx0.9 changed the SQL-injection BANNED shape toAssertSqlSafe(format!(...)); §C6'sderivativerecommendation was itself unmaintained (RUSTSEC-2024-0388) since before this spec existed;reqwest0.13 renamed the TLS-bypass ban targets. Every crate/version claim re-verified against crates.io/docs.rs/rustc at merge time, not transcribed from the audit. - Wrong inside a 🔴 category (3 items). §B12's
jsonwebtokenaud-validation direction was inverted (a token omittingaudis the dangerous accept, not one carrying a foreignaud— reproduced against jsonwebtoken 9.3.1 and 11.0.0); §A1 named the real cratetokio-utilsas a hallucination (verified: 516k downloads since 2023) while conflating the actual weaponized typosquat (rustdecimal); §B5 listedboolas invalid formem::zeroed(zero isfalse's valid bit pattern) and undersoldmem::uninitialized's UB scope. - New gaps (8 items), no new category.
Instant/SystemTime::now()panicking onwasm32-unknown-unknown(§B27/§C3);#[tracing::instrument]'s defaultDebug-capture of every argument as a secret-leak channel distinct from#[derive(Debug)](§C9, 🔴 for secret-material parameters — highest-priority item in this batch);spawn_blockingwork survivingabort()/timeout(§B11/§B21); database read-modify-write races and rawBEGIN/COMMITthrough aPool(§B13/§B4); SSRF via default redirect-following (§C2, resolves a→ §C2pointer §C12's URL row already had); positional wire-format evolution defeating the §F4 round-trip test (§F1/§F4/§B20); edition 2024'sunsafestd::env::set_var/remove_var(§B25a);safe fninsideunsafe externblocks, the import-direction twin of §B25's export bullet. - Correctness outside 🔴 (12 items). Verified factual corrections spanning
serde_json's depth-limit bypass scope,#[serde(flatten)]+deny_unknown_fieldsinteraction direction,OnceLockvsLazyLockpoisoning,Handle::block_onvsfutures::executor::block_onper-runtime-flavor behavior, a fabricated §A1 stale-API example, a clippy lint-group misattribution, a swapped RustSec ID pair,trait_variant::makesemantics,thread::scopepanic-payload forwarding,f64::total_cmp's NaN placement, a nonexistentpanic_in_dropmechanism, and an unsourced crate-rename claim (corrected to the real one:iai-callgrind→gungraun). - Staleness/advisory hygiene (14 items). New §A1 incident: the 2026-08-20 arrayref/append-only-vec/internment maintainer-compromise campaign (RUSTSEC-2026-0259–0266) and two rounds of Cargo advisories (2026-33055/-33056, 2026-5222/-5223); version floors added for
ammonia(three XSS bypasses in ten months),lru(two unsoundness advisories),quick-xml(two DoS advisories),tar/zip(path-traversal/chmod advisories),anyhow;serde_yaml's successor hedge now namesserde_yml's unsoundness (RUSTSEC-2025-0068) explicitly;dotenv/async-std/bincodeadded to the earlier-era bullet;backoffdropped as a retry hedge (its own advisory namesbackon);sleddropped from the §C12a durability row per its own README; a dead citation URL updated (with the now-unverifiable figures flagged, not silently kept). - Enrichments (21 of 22 items; one gated). Bullet-level additions across nearly every module:
CancellationTokenclone-vs-child_token,select!re-poll-after-completion,Condvarspurious wakeup, atomic inc/dec pairing, four §B28 UTF-8/Unicode traps, iterator-adapter order/tie-break traps, per-request client construction, non-virtual-workspace CI blind spots, template-extension-keyed autoescape, cookie attributes,static mutdata races, volatile/MMIO loads,rerun-if-changedscope,target-cpu=native,Path::exists()TOCTOU,copy_nonoverlappingoverlap, a version-scoped rustls 0.23 worked example, plus dated Rust 1.86–1.98 std/compiler additions and clippy 1.93–1.98 lint mappings (each verified againstRELEASES.md/clippy'sCHANGELOG.mddirectly). Item 6.19 (Cargoglobal-min-publish-age) is gated on Rust 1.100 shipping (~2026-11-12) and was deliberately not written. - Process.
dev/validate.mjs's category-map parity check (added for §C12/§C12a in the prior release) is unaffected by this batch — no category was added or renumbered. A cited-third-party-API ledger + docs.rs-liveness probe (fix-plan §7.1) was scoped but not built this pass — flagged as a follow-up, not silently dropped.
A fourth independent review (docs/reviews/latest-commits-review-round-4-2026-09-02-1355.md) found genuine UB, FFI, concurrency, data, and API defects the three passes above missed, plus corrections to two of round-3's own findings. Same shape: no new category, PATCH-sized despite the count; full evidence in the round-4 report, this entry summarizes by group.
- Unsafe/FFI (6 items).
Box::<[u8]>::new_uninit_slice+read_exactwas unsound (forming&mut [u8]over uninitialized bytes is itself UB, independent of how much of it gets written); raw-pointer null/alignment/length checks were presented as sufficient without requiring a documented# Safetycontract for the non-checkable relational properties (liveness/provenance/aliasing);without_provenancewas listed as interchangeable withwith_addr/map_addrfor a dereferenced tagged pointer (it deliberately carries no provenance — UB to deref);slice::from_raw_parts's invariant list was missing the single-allocation and no-overflow-on-end-pointer requirements; the FFI type rule ("repr(C) types only") both excluded valid non-repr(C) primitives/pointers and admitted an invalid repr(C) aggregate containing aString/DST field;catch_unwind'sErrpayload was mis-typed as*mut PanicInfo(it'sBox<dyn Any + Send>, and dropping it can itself panic). - Concurrency/state (10 items).
OnceCell::get_or_initwas described as attempt-level exactly-once (cancellation/panic lets another caller re-run the initializer);try_send-with-explicit-drop was offered as a drop-oldest backpressure policy (it can only drop the newest, never-queued message); a blanket "prefertokio::sync::Mutex" contradicted Tokio's own short-critical-section guidance; an unbounded queue was excused by "bounded producer rate" alone (any sustained rate gap still grows it without bound); channel sizing usedsize_of::<Message>()instead of worst-case retained bytes; the module credited with Mutex-poisoning coverage had none; aMutex<BinaryHeap>+Notifypriority queue had no capacity policy or multi-consumer lost-wakeup protocol; the TOCTOU ban wrongly covered a plain single-ownerHashMap; the channel-mismatch trap self-contradicted on whether bare tokio mpsc can fan out;Sender::capacity()was recommended as a metric for the unbounded case, where it doesn't exist. - Data/types (7 items).
foldhash's default-random mode was declared DoS-safe for untrusted keys outright (its own docs disclaim protection against an adaptive attacker);read_to_endwas offered as a general short-read fix even for unbounded/attacker-controlled streams; the null-vs-absentOption<T>collapse was scoped only to fields carrying#[serde(default)](it happens without the attribute too);zip_eq's panic-on-mismatch guarantee was stated without noting a short-circuiting consumer never reaches it; full Unicode case-folding was recommended for display text (it's a matching-only transform);HashSet::insert's duplicate-element semantics were conflated withHashMap's (first-wins, not last-wins); "surface every clone" contradicted the module's own 🟡 write-time-discipline tier. - Async (5 items). A cancel-safety worked example certified an unshown
read_messageimplementation as safe; "wrap the stream in aDropthat drains" contradicted the file's own async-Drop-is-not-real rule;notified().awaitwas banned unconditionally instead of scoped to the actual multi-waiter race; aCloseGuardwas told to panic in debug on an unclosed drop (a double-panic risk);Handle::block_onvsfutures::executor::block_onper-flavor behavior was corrected per the already-verifiedcorrectness-audit-2026-08.mdfinding. - Semantics/conformance (3 items). Per-read
timeout(dur, read_exact(..))was offered as an idle-deadline defense (slowloris-able, and cancellation-unsafe on retry); a worked example called a dropped, ownedTcpStreama "leak" (it's simply dropped — the real defect is unmatched connection-count bookkeeping);read() == Ok(0)wasn't qualified for the legitimate zero-length-buffer case. - Security (2 items). The JWT
none-algorithm warning cited jsonwebtoken behavior that predates the crate's current API (noNonevariant exists;Validation::default()pins a fixed algorithm list, not "whatever the token claims");tls_certs_only(Certificate::from_pem(...)?)doesn't type-check against the crate's actualIntoIterator<Item = Certificate>signature. - Drop/RAII (3 items). SQLx's rollback-on-drop was called "blocking" (it queues, per the crate's own source); "Rust drops in reverse declaration order" was stated as a blanket rule (only true for locals — struct/array fields drop forward, closure captures are unspecified); returning from
mainwas implied to drop everything (static-owned resources never get a destructor call on normal exit). - Testing (4 items).
serial_testwas cited as sufficient proof against edition-2024'sunsafeenvironment-mutation hazard (it only serializes same-binary#[test]functions, not arbitrary threads);pub(crate) mod test_supportwas claimed reachable from sibling workspace crates and integration tests (neither is true —pub(crate)is same-crate-only); exact float comparison was banned even for operations with a genuine exact contract; every SLOW/TIMEOUT/stalled signal was called a deadlock (it can be livelock, starvation, blocking I/O, or complexity). - Lifetimes/API + SKILL.md (3 items). Enum-level
#[non_exhaustive]was presented as covering field growth inside an existing struct-like variant (it doesn't — the variant itself needs the attribute); every syntacticpubwas called a semver commitment regardless of external reachability; a barecargo semver-checksrun was said to catch a missing#[non_exhaustive]at introduction (that's an allow-by-default additive-policy lint, not the default baseline-diff behavior). - Deps/macros + audit workflow (6 items).
cargo build --workspacewas conflated with enabling dev targets; aserde "1.0.200"vs"1"example illustrated a duplicate-version hazard using two ranges that actually overlap and unify (replaced with a genuinely incompatiblerand0.8/0.9 pair); a custom build-script cfg was shown without its requiredrustc-check-cfgpairing; a hard-coded::serdepath was called hygienic (it breaks under dependency renaming); the audit workflow's caller-free evidence exception was scoped too broadly across §C5–§C11; grep-sampled coverage could produce a false §B9-complete disposition. - Documentation/report integrity (3 items). The public README's banner still read v0.5.0/58-categories/Tier-C-through-§C11 against an actual v0.6.0/59-category/§C12 spec; round-3's own report criticized a "broad redirect exemption" with no supporting finding (withdrawn —
security.mdcarries no SSRF text for anything to exempt); round-3'snpm packintegrity digest was recorded without the Node/npm versions that produced it (a clean reproduction under Node 24.12.0/npm 11.13.0 yields a different digest — recorded as a reproducibility gap, not a tampering claim).
Three further independent reviews (round-5, round-6, round-7) found and closed additional defects, mostly precision fixes plus a handful of genuine P1/P1-conditional gaps in the round-3/4 corrective batch above. No new category — PATCH-shaped. Full evidence in docs/reviews/latest-commits-review-round-{5,6,7}-*.md.
- Unsafe/FFI. The required FFI destructor's caught panic payload could itself panic on drop and abort the process (fixed: downcast to
&str/Stringby reference, else forget it); the direct-Box<T>FFI exception didn't distinguish exports (sound) from imports (a foreign function can't produce a pointer satisfyingBox's allocator contract); the exported-entry-point enum ban had a residual "#[repr(int)]/primitive scalars" phrasing that could be misread as permitting a validated#[repr(int)]enum directly (no such thing exists — the boundary must take the underlying integer, never the enum); a nullable-destructor note conflated "null is a safe no-op" with "a double-call is safe" (it isn't — a stale non-null pointer is still UB the second time). - Concurrency/state. The per-key
OnceCellasync-dedup pattern'stokio::spawn+JoinHandle::awaitremedy was presented as closing the exactly-once gap; it only prevents a drop-mid-await duplication, notOnceCell's own cancel-and-retry letting two attempts run — true exactly-once now requires a provider-side idempotency key or an atomically-coupled effect+record, with spawn+await and locks reframed as coordination only. TheCondvarpredicate-loop rationale named spurious wakeup as the only reason for the loop;notify_allwaking multiple waiters that reacquire one at a time is a second, non-spurious reason. The mutex-wrapped-ReceiverBANNED bullet buried a "not always a defect" admission inside itself; restructured so the banned (broadcast/routed) and not-banned (competing-workers) cases are separately stated. - Async. The Mutex-poisoning
parking_lotexception was scoped to "no cross-field invariant", which wrongly implied a single-value invariant (aVec, an enum state machine, a balance) is safe under it — broadened to "no protected invariant or state transition".futures::executor::block_onwas said to have no nesting check at all; it has its own same-thread re-entrancy panic (enter().expect(...)) distinct from tokio's cross-runtime one..abort()'s "no chance for any cleanup to run" is narrowed to async cleanup — synchronousDropof task locals still runs. - Security.
SmallRngwas grouped withStdRngunder "the hazard is the seed, not the type" —SmallRngis non-cryptographic by algorithm regardless of seeding and stays banned outright; onlyStdRng(a real CSPRNG) is rescued by OS seeding. - SKILL.md. Two confirmed internal contradictions: the §B1a lifetime-laundering witness in Operating-mode item 6 didn't match its own Pre-flight item 6 or
lifetimes-and-api.md(fixed to the correct short-lived-source-then-reuse witness, and broadened to the actual laundering shape — one lifetime captured into a longer-lived container, not "more than one lifetime"); Operating-mode item 1 and the post-flight summary told readers to readCargo.tomlfor "exact" versions, contradicting the already-corrected §A1 rule that the resolved version lives inCargo.lock/cargo metadata. The JWT-bypass trigger named only the removedinsecure_disable_signature_validation, missing currentjsonwebtoken::dangerous::insecure_decode. The idle-deadline trigger andsemantics-and-conformance.md's §F3 rule accepted a resettable idle timer as a standalone slowloris defense; both now require an absolute stage deadline or a minimum-progress-rate check, with an idle timer as an additional layer at most. Post-flight commands were barecargo test/cargo clippy; now--workspace/--all-targets, plus a conditional--releasejob. - Lifetimes/API. A round-6 fix claiming tuple-like enum variants aren't protected by variant-level
#[non_exhaustive]the way struct-like ones are was itself wrong and is reverted — they are protected the same way; a named payload is an ergonomics choice, not a semver requirement. - Testing.
assert_matches!(stable Rust 1.96) was recommended with no MSRV gate against this spec's declared 1.85 floor;#[should_panic]guidance only forbade panic-capable setup before the SUT call, missing an equally-real panic-capable statement after it. - Tooling. The README.md category-count negative-control fixture mutated the real repository's
README.mdin place, relying on afinallyblock that cannot run across a process kill (reproduced: an interrupted run leftREADME.mddirty) — rewritten to run against a temporary copy of the repo, never the caller's working tree. The stale-count scanner's\d+\s+categoriespattern couldn't cross a Markdown**between the digit and the required whitespace, so**58** categoriescoexisting with a correct**59**banner passed silently — fixed by stripping**before the scan, with a coexistence fixture added. - ~15 further P2/P3 precision fixes across
concurrency-and-state.md(JoinSetuses.spawn()not.push();rustls::ClientConfigisn't a connection pool),data-and-types.md(SystemTime::checked_add/checked_sub, notchecked_add_duration;u32 as usizeneeds atarget_pointer_widthqualifier;foldhash's narrow non-interactive exception reconciled with the catalog's "trusted keys only"),deps-macros-ergonomics.md(a proc-macro crate cannot itself host a plain re-export;[workspace.dependencies]reduces drift but doesn't guarantee one linked copy; resolver v2/v3 unification scoping),drop-and-raii.md(Anyisn'tDebug/Display— downcast by reference instead; the awaited-rollback REQUIRED rule now carries the same conditional as its own trap paragraph), andsecurity.md(salt uniqueness vs. predictability, nonce-width scope to the specific RustCrypto alias).
Twelve further review rounds (rounds 8–19) closed defects found by independent reviews of the batch above; normative fix commits a0197be (rounds 8–9), f2b4897 (10), 31a3505 (11), 5417d3c (12), d10427d (13), adee2e9 (14), 1591d39 (the round-15 synthesis fix, docs/reviews/round-15-synthesis-2026-09-03-2146.md), 91077f4 (16), e76c372 (17), 7726ed6 (18), plus this round's own fixes (19). No new category, no BANNED/REQUIRED shape change — PATCH-shaped. Full evidence per round in docs/reviews/latest-commits-review-round-{8..19}-*.md; this entry summarizes by group.
- Semantic trigger activation (the largest group). Enumerated method/type lists replaced with semantic guards so new-API shapes still activate a category: §B2 map-guard-across-
.await("any live value that owns or contains a map guard", with scc 3.8.8/dashmap 6.2.1's full guard-type inventory), §B14 unbounded growth (semantic admission gate;JoinSetspawn/insert/Extend/FromIteratorpaths; strictlen() < Npre-insertion cap), unbounded-channel constructors,JoinHandle-returning spawns, ordinaryCargo.tomldependency-table additions, §C12's three XSS shapes, §B1b unnameable-signature triggers, and §B1a's out-parameter cache capture (fn remember<'a>(s: &'a str, cache: &mut Vec<&'a str>)). - §F1 persisted-format oracle rebuilt. The golden-bytes corpus was typed-value-level (same wire bytes under different Rust types — bincode 2
bool/u8, spec-verified) and its recursive coverage unbounded; restated as a decode-observable permutation oracle (schema-mutation negative control as the mechanical form), over the finite graph of distinct serialized type/variant definitions with exactly two representatives per recursive edge, layered with the version-byte/dispatch/policy control required only when more than one layout is deployed. Round 16 correction: the pairwise-reduction shortcut recorded alongside this oracle in round 15 (checking theC(n, 2)transpositions is equivalent to checking all permutations) is false under mixed field widths —struct Record { a: [u8; 2], b: u8, c: u8 }witha = [1, 2], b = 1, c = 2serializes as01 02 01 02, and the three-cycle to declaration orderb, c, adecodes identically while every pairwise transposition is detected — so this oracle is sound only as the exact-proposed-layout decode per actual change (or the full permutation property), never as a pairwise-only check. Seedocs/reviews/latest-commits-review-round-16-2026-09-03-2320.mdconditional-P1 finding 1. - Primary-source factual corrections. rand 0.9/0.10 seeding APIs; scc
get_*/begin_*return types; DashMapRefMutas anRwLockwrite guard; regexfind_iter/captures_itercomplexity O(m·n²); the Rust Reference's blanket-impl "fundamental type constructor" definition;JoinSet's real API (poll_join_next, notStreampolling); HashSet/HashMapcollectcoalescing carries no survivor-identity promise; sealed traits tripunnameable_types, notprivate_bounds(verified on rustc 1.97); public type aliases are a naming path (a semver commitment, not a leak site); Argon2/password-hashper-major salt recipes with both feature obligations; SmallRng trigger scope, slowloris defenses requiring an absolute deadline or a capped progress rate, char/bare-fn C-ABI mislisting, caught-panic disposal unified across FFI/drop boundaries, FFI callback allow-list gaps. - Exemption and scope precision. TOCTOU exclusive-guard exemption (shared
RwLockcounterexample; pure readers exempt unless the invariant forbids observing intermediate state),select!judged by Tokio's drop-and-recreate criterion rather than "atomic", Borsh explicit-discriminant exemption narrowed to variant-tag stability,matches!copy caveat, non_exhaustive/field-growth scoping, new-dependency review activation separated from the named finding. - Tooling. The current gate validates a documented repository subset through two anchored top-level trigger tables, a
projectFenceOpener-based fence mask, bounded code-span scanning, explicit unsupported-style diagnostics, code-state-onlyMODULESparsing, and unique category ownership/body checks. The round-15–19 implementation details — the stateful header/delimiter/body machine,blockStartRe, tab expansion, and related GFM block-start/blank-line emulation — are historical; they were later replaced by this anchored contract and its fence mask. The fixture suite remains a temp-copy allowlist with physical-path checks and optional.app.json/.mcp.jsoninputs; Node floor 24.0.0 is enforced by hard startup guards in both installers and both validator entry points; its support-policy rationale and the historicalfs.cpSynccontext are recorded in the runtime paragraph above.
Rounds 20–21's two validator-conformance P3s are closed in the net architecture. The anchored contract's shared projectFenceOpener feeds the fence mask, and invalid backtick-info lines are body-width failures rather than table-boundary false negatives; no standalone table-boundary detector remains. The NBSP arbitrary-table delimiter case is moot because non-anchored tables are outside the anchored contract, while surviving delimiter normalization still treats only the cmark-gfm ASCII whitespace class as table space. See docs/reviews/latest-commits-review-round-20-2026-09-04-1206.md findings 1–2 and docs/reviews/latest-commits-review-round-21-2026-09-04-1228.md carried P3 findings 1–2.
Focused-lexer anti-vacuity gate rebuilt as behavioral differentials; dev/validate-lexer-observations.mjs retired. Six consecutive independent review rounds (43–47; docs/reviews/latest-commits-review-round-{43,44,45,46,47}-*.md) each demonstrated a genuine, empirically working bypass of the gate meant to prove that the fixture's control-401 evidence — a 2,000,000-unit scan by dev/js-lexer.mjs — is produced by executing that scan rather than by the probe vehicle, and every one exploited the same structural weakness: a parallel self-reporting oracle (dev/validate-lexer-observations.mjs) whose result the parent checked via source-text heuristics on the probe vehicle — regex pins, occurrence counts, position pins — so arranging text around the heuristic (comments, dead branches, wrappers, aliasing) defeated the gate without executing anything more. Following a dedicated redesign consultation, the mechanism is replaced wholesale and the observation module is retired deliberately, not silently dropped: its required-file entry, its node --check reference, the RUST_INTEL_CONTROL401_MIN_PEAK_RSS_MB knob, the telemetry work floors, and the source-inventory pins are removed with it. Every anti-vacuity assertion is now a behavioral differential: the same probe vehicle — byte-identical in both trees, so its own source text can distinguish nothing — runs against (a) the real dev/js-lexer.mjs and (b) a temp-tree copy whose dev/js-lexer.mjs itself carries a run-time-chosen mutation, and the parent requires exactly the outcome only genuinely executing the mutated spot can produce. Control ids are reused, not renumbered: 401/402 (unmodified tree accepts the exactly-2,000,000-unit marker input at the marker's index / rejects one unit above), 458 (budget lowered by one ⇒ rejection), 459 (a throw injected at a run-time-random operation count K inside step() with the K-specific message and index — unreachable for a facade that never calls step(), including one that reads the constant and compares lengths), 491 (budget raised by one ⇒ the 402 input accepted), 492 (completion identifier renamed ⇒ no diagnostics), and new 493/494 (block-comment masking liveness: a decoy call inside a comment is suppressed in the real tree and detected when the scanner's comment branch is disabled). The suite moves to 494 controls — 419 child-process (390 validator-entrypoint plus 29 focused) plus 75 in-process — re-derived from the registry's own spawn tally. Two residual limits are accepted and stated, not closed: (1) a vehicle that greps the mutated lexer for the specific mutation signatures and simulates the expected effect would defeat behavioral mutation testing itself — it would be a second interpreter of the code under test and a deliberate, visible forgery in any review of the probe file, not a plausible refactor or accidental vacuity; (2) a deliberate forgery inside dev/js-lexer.mjs itself is outside the threat model by construction, not by choice: the mutation is applied to the file that then executes, so that file holds its own mutation as ordinary program state, and no differential can distinguish a lexer that executes its mutation from one that reads it — the definitional boundary of mutation testing. The lexer's integrity is a review obligation, not a gate claim. An accidental large-input shortcut in the lexer is inside what the differentials detect (measured against the round-50 mechanism: an early return above 1,000,000 units fails all eight differentials, and a scanner that stops charging inside identifier runs fails all eight). All six historically demonstrated facades are caught by the new gate. (Round-48 correction, inserted in place: what the re-verification ran against is not recorded; five of the six facades lived in dev/validate-lexer-observations.mjs, deleted by this same commit, so they are no longer reconstructible as written — read the sentence as an analytical argument from the differentials' mutually exclusive expectations, not as six documented fresh reconstructions. Round 48's review additionally demonstrated a seventh, ordinal-channel bypass of this gate — a vehicle answering from canned data without scanning — which the round-48 fixing disposition below closes by randomizing the differential execution order and noncing the error-valued expectations; the residual list in this paragraph was short by one.) (Round-49 correction, inserted in place: residual (1) itself was too generous — the bypass round 49 demonstrated against the round-48 disposition read no code text and simulated nothing: one fs.statSync on the temp-tree lexer plus a hardcoded baseline answered five of the eight differentials, control 401 included, and 39-/43-unit behavioral probes identified three more; the round-49 fixing disposition below closes both channels, and the residual becomes the per-run guessing probability stated there plus the unchanged second-interpreter forgery.) No broader closure of the bypass class than that is claimed, and no push, bump, tag, or publication is implied by this entry.
Net tooling state. The validator enforces exactly two anchored top-level trigger tables, a shared project-fence mask, a bounded code-span scanner, and explicit unsupported-style diagnostics; it structurally parses and deep-freezes MODULES/AUDIT_UNITS, including the pinned policy matrix and SHA-256-pinned coverage block. Its JavaScript mutation scanner charges every delimiter-stack step, rejects mismatched nesting, preserves private-name token roles, tracks class-body roles across brace-bearing extends expressions, and handles ordinary, static, private, computed, string, and numeric class-field names with a bounded lexical cache. Function/class-expression division, direct function-heritage ordering, class-field initializer roles, and the associated completion-loop/workflow-mutation boundaries are covered by controls 415–484; indirect provenance remains a runtime deep-freeze backstop. The Node.js 24 floor is guarded at startup, with CI definitions for current Node 24 and exact 24.0.0. The fixture suite has 500 controls, with 425 child-process controls (396 validator-entrypoint and 29 focused lexer/helper children) and 75 in-process controls, a split the fixture registry now machine-checks against the spawns it actually routes. Controls 495–500 cover changelog release rollover. The 494-control/419-child/75-in-process split is historical to the pre-release spec-review state; the 492-control/416-child/76-in-process split is historical to the round-46 fixing state, the 490-control/414-child/76-in-process split is historical to the round-45 fixing state, the 484-control/409-child/75-in-process split is historical to the round-42 fixing state, and the 460-control/397-child/63-in-process split is historical to the round-42 review base at 633a0da. Installer interruption/recovery cases live in the separate dev/test-installer-recovery.mjs matrix/helper and are not included in that numbered total. Focused lexer children emit a structured semantic observation that the parent judges against a behavioral differential — the same child run against a temp-tree copy of the repository whose dev/js-lexer.mjs carries a run-time-chosen mutation (or none) — the tested accidental shortcuts fail these expectations, while deliberate forgery by the vehicle or lexer remains outside the gate's integrity claim. Parser-only one-column, HTML-block, list-container, and TABLE_VISITED probes remain outside the anchored contract. The cycle history and round-23 disposition, including CI run 34015308368 (both jobs green at the reviewed 375-control head), are recorded in docs/reviews/README.md and the corresponding round-23 report; those counts and results are revision-qualified historical evidence, not current-head CI claims.
Round-38 fixing disposition. 2948c85 tracks class-body roles across brace-bearing extends
expressions and adds the corresponding causal controls; 5d9e8a8 implements the reported installer
matrix reachability, indexing, fixture, inventory, and sibling-cleanup corrections; and 7e3afb3
qualifies the release-facing Status, CHANGELOG, and ledger claims. Round 39's independent review
shows these fixes are partial at the reviewed head: the direct function-heritage lexer case,
cross-operation transaction recovery, failed-inventory propagation, independent replacement and
cleanup oracles, finite child/job timeouts, full Bash 3.2 coverage, and PowerShell runtime coverage
remain open. Full Node and current-Bash same-operation matrices pass locally, but they are not a
release gate for those missing dimensions. No current-head CI result, version bump, tag, push, or
publication is claimed.
Round-39 fixing disposition (implementation parent 7042ce8, documentation at 8b2d576; pending independent review). f5a655e adds ordered
same-depth class/function heritage tracking and controls 423–440, bringing the fixture suite to
440 controls (374 child-process and 66 in-process at that historical disposition). 1363cc8 adds checked POSIX inventory
materialization, cross-operation transaction recovery for Bash/PowerShell, operation-correct
cleanup, finite child/release-version subprocess timeouts, CI job timeouts/sharding, and generated
Bash 3.2/PowerShell matrix execution. 7042ce8 makes replacement-hook inventory checks
independent of the hook used to seed them. 419fe32 records the pre-fix round-39 release state.
These commits address the round-39 P2/P3 implementation findings, but round 40 identified the
remaining blank-inventory, keyword-field, full-validator, and oracle gaps noted above. The
implementation parent and its documentation disposition are distinct revisions; neither is a
current-head release claim. No closure, current-head CI, version bump, tag, push, or publication is
claimed until those gaps and the independent follow-up are complete.
Round-40 fixing disposition (implemented, pending final proof and HS follow-up). 6b435f7
closes blank-only POSIX inventory acceptance and replaces the self-replaying cross-operation and
single-prefix debris oracles with independent clean-operation and complete-prefix checks.
090844b distinguishes keyword-named class fields and bounds the lexical cache, adding controls
441–449. 81b5d60 adds the measured 45-minute timeout to .github/workflows/npm-publish.yml,
corrects round-39 provenance, and qualifies the release-facing summaries. The fixture header was
449 controls (391 child-process and 58 in-process) at this historical disposition; 440/374/66
remains historical to the round-40 review base at 8b2d576. 7de2c56 adds opt-in per-control progress/peak-memory
attribution and moves the resource-heavy lexer probes into focused short-lived children. The
scanner's lazy typed UTF-16 mask avoids an unnecessary multi-million-element object allocation on
code-only budget probes. The former cumulative-cache retention is fixed; clean ordinary validator
and exact-head CI evidence remain release gates. No current-head CI, version bump, tag, push, or
publication is claimed.
The round-40 mitigation state is intentionally qualified: the lazy code-only mask and focused lexer children bound cumulative retention and keep parser-heavy probes out of the long-lived fixture process, but the progress trace samples the parent and cannot prove the focused child's peak memory. A clean ordinary validator run and exact-head CI remain release gates.
Round-41 fixing disposition (implemented, pending clean proof and HS follow-up). 9a675f8
adds a bounded negative that would fail if the cross-operation oracle regressed to self-replay.
c5b959f tracks class-element positions so genuine function expressions in field initializers
remain visible, and adds controls 449–457 for ordinary/static fields, workflow mutations, and the
bounded-cache invariant. 322a034 makes focused helper outcomes structured and canonical, adding
controls 458–460 while preserving child-owned terminal heap/RSS samples and treating fatal or
killed children as having no terminal sample. At that historical round-41 fixing state, the
fixture header was 460 controls: 397 child-process controls (380 validator-entrypoint and 17 focused
lexer/helper children) plus 63 in-process controls; the 449-control/391-child/58-in-process split
is historical to b907ded, and the later 484-control state is recorded below. A clean ordinary npm run validate, the installer-boundaries CI
job's generated --list loops over every same/cross Node case (578/578 cases across
node-claude and node-codex), independent HS review, and exact-head CI remain required; focused-child
validator memory telemetry is a separate evidence stream. The recovery helper defaults to pwsh
and accepts RUST_INTEL_POWERSHELL_EXECUTABLE=powershell.exe for the Windows PowerShell 5.1
runtime used by the .bat wrappers. No current-head CI, version bump, tag, push, or publication
is claimed.
Round-42 fixing disposition (partial; ordinary Windows validation passed locally).
ef20ca5 advances class-element state for private, computed, string, and numeric names and adds
declaration, completion, and workflow controls 461–484. 14a672a adds the isolated Windows
windows-validator CI lane and repoints the workflow validate steps at the coordinator
entrypoint, parameterizes the recovery helper, and exercises both pwsh and powershell.exe,
including the documented .bat paths. 49dd4f0 creates dev/validate-all.mjs and
dev/validate-lexer-observations.mjs — the two files the three earlier commits referenced before
either existed — repoints package.json's validate script and .github/workflows/npm-publish.yml
at the coordinator, corrects the 484-control execution breakdown, and implements the anti-vacuity
companion. These are partial fixes and matrix definitions. Acceptance of the sequential
coordinator also produced one successful ordinary Windows npm run validate run: core checks and
all 484 fixture controls passed. Independent review and CI on the resulting commit remain
required. At that fixing state the fixture header read 484 controls: 409 child-process controls
(386 validator-entrypoint and 23 focused lexer/helper children) plus 75 in-process controls — a
hand-maintained split the registry tally later showed to be off by one. Focused-helper
anti-vacuity, independent HS review, and exact-head CI remained release gates at that state; no
version bump, tag, push, or publication is claimed.
Round-43 fixing disposition (implemented; ordinary Windows validation re-run locally).
The round-43 review (docs/reviews/latest-commits-review-round-43-2026-09-07-1144.md) is disposed
as follows, committed as 6bc997b. P2-1: a validator check now extracts every
node <path> / node --check <path> argument from workflow run steps and asserts the file
exists, with regression control 485. P3-1: control 401's input embeds a causal marker —
';completeCurrentControlScope(902, true)' after 1,999,961 filler units, exactly 2,000,000 code
units — and requires {inputLength: 2000000, ids: [902]}; the size-conditional facade the review
demonstrated was re-tested against the new observation and is rejected. That closed the review's
specific facade, not the class: round 44's review demonstrated an early return placed above the
marker line that still passed all three gates, and the round-44 fixing pass (below) closes the
class. P3-A: dev/validate-all.mjs
joins runtimeGuardContracts, its phase wiring is source-pinned with negative control 486, and
package.json's validate script plus the three workflow invocations are pinned to the
coordinator path. P3-B: the registry tallies actual validator/focused child spawns against the
header and proved the reviewed head's own 386/23/75 prose off by one (actual 385/23/76); the
header now states the routed 387/23/76 split. P3-4: RUST_INTEL_VALIDATE_TIMEOUT_MS hard-errors
on a malformed value instead of silently restoring the 20-minute default, and the fixtures-phase
'0' entry documents that it only neutralizes an inherited =1. P3-2/P3-3/P3-C: this changelog,
the ledger, and the README Layout/Status text now credit 49dd4f0 with its actual contents, move
the coordinator attribution off 14a672a, state one anti-vacuity disposition, and document
RUST_INTEL_SKIP_NESTED_FIXTURES and RUST_INTEL_VALIDATE_TIMEOUT_MS. P2-2 remains an open
evidence item, not a closure claim: at the reviewed head (49dd4f0, before this fixing pass
landed) on Node v24.12.0 / Windows 10.0.19045 the fault did not reproduce in three attempts — one
progress-instrumented fixture run (246.310 s, 484/484 controls, peak heap ≈21.2 MB, last live
control 460) and two ordinary npm run validate coordinator runs (276.451 s, 285.209 s), all
exit 0. All three runs predate this fixing pass, and no measurement exists at 6bc997b, whose
fixture workload is materially larger; the 6bc997b commit body's '486/486 controls at this
head' figure for the same 246.310 s run is wrong for the same reason and is superseded by this
record. The coordinator stays a mitigation whose effect on the original fault is unconfirmed, and
the windows-validator lane's first real CI run remains the required proof. Independent HS review and exact-head CI remain release gates; no version bump,
tag, push, or publication is claimed.
Round-44 fixing disposition (implemented; ordinary Windows validation re-run locally).
The round-44 review (docs/reviews/latest-commits-review-round-44-2026-09-07-1418.md) is disposed
as follows, committed as 1ba3956 on top of c9a37cb (the round-44 report commit); push, bump,
tag, and publication remain separate, explicit, human-authorized actions. P3-1: control 401's marker id is now chosen
at run time and passed to the focused child in argv, the expected observation is derived from
that id and additionally requires the marker's source index, control 458 replaces its whole-file
substring search with an anchored contract — the scanner call must be the first statement of the
shared observation function, preceded only by whitespace or comments — and control 401 gains
measured work-was-done floors on the focused child's peak heap and peak RSS. Re-tested
adversarially: the round-44 early-return facade is rejected twice over (runtime-id oracle and
anchored contract), and a stronger argv-forging facade that defeats the runtime-id oracle is
still rejected by the anchored contract and the memory floors; the fixture suite exits non-zero
with either facade in place and passes 486/486 without them. P3-4: dev/validate.mjs now pins
the coordinator's actual spawn wiring (env: { ...process.env, ...phase.env } must reach the
spawnSync options), anchors each phase contract to its own object, pins core-before-fixtures
order, and control 486 applies both wiring mutations to one mutated copy and requires both
rejections. P3-A: the three CI lanes that run the coordinator are raised to 50-minute job
timeouts (repository-checks 30→50, windows-validator 30→50, node-floor 20→50) so the
coordinator's worst case — two sequential 20-minute phases — and its attributed ETIMEDOUT
diagnostic are reachable on every lane; dev/validate.mjs now pins each lane's timeout-minutes
at or above two per-phase defaults plus setup margin and treats a workflow-level
RUST_INTEL_VALIDATE_TIMEOUT_MS override as an error (the publish job's existing 45-minute
timeout satisfies the pinned minimum). P2-1: the three Windows non-reproduction runs are
attributed to one tree, 49dd4f0, in all release records, the fixture-only progress run is no
longer described as an ordinary npm run validate run, and the absence of any measurement at
6bc997b is stated. P3-2/P3-3: the README credits 49dd4f0 with the coordinator's creation,
and this changelog and the ledger now describe the round-43 fixing pass as committed (6bc997b)
instead of uncommitted. Fresh evidence for the open Windows gate: ordinary npm run validate at the round-44 fixing
tree immediately before its final documentation edits (Node v24.12.0, Windows 10.0.19045)
passed in 368 s with exit 0 and 486/486 controls. The measured tree is not the committed one:
CHANGELOG.md, README.md, and docs/reviews/README.md are validator inputs and were edited
after the run finished, so the committed tree differs from the measured one in exactly those
files; the figure is attributed to that pre-final-edit tree rather than to a SHA. This is local non-reproduction evidence for the current fixing state, not a
demonstrated fix; independent HS review and exact-head CI remain release gates, and no version
bump, tag, push, or publication is claimed.
Round-45 fixing disposition (implemented; ordinary Windows validation re-run locally).
The round-45 review (docs/reviews/latest-commits-review-round-45-2026-09-07-1626.md) is disposed
as follows, committed as the commit containing this row, on top of 997f1f3 (the round-45 report
commit); push, bump, tag, and publication remain separate, explicit, human-authorized actions —
phrased without naming this pass's own SHA, which is the row-authoring defect round 45 identified
(P3-2/P3-C), so the row stays true without an amend. P2-1: control 401's boundary-sampled
peakHeapUsed floor (32 MiB of uncollected garbage at the terminal sample) and the
single-host-calibrated 100 MiB peakRss floor are replaced with same-child causal signals
sampled immediately after the large scan returns, before the companion call evicts it from the
one-entry lexical cache: a scan-heap ratio (post-scan heapUsed over the pre-scan baseline;
floor 3) or a retained-array delta (arrayBuffers growth of at least half the 2,000,000-unit
input — GC-immune, derived from the scanner's own regex-bitmap allocation); either signal alone
passes, and a scan-eliding facade produces neither. Measured on the reference host
(Node v24.12.0, Windows 10.0.19045, --max-old-space-size=64): genuine scans ratio
13.13–13.16 with delta 1,954,792 across five marker ids; a reconstructed module-scope wrapper
facade ratios 1.49 with delta 0. The RSS assertion survives only as a conservative 32 MiB
tripwire, overridable via RUST_INTEL_CONTROL401_MIN_PEAK_RSS_MB and documented as not
validated on Linux. (Round-48 correction, inserted in place: the whole mechanism this
paragraph and its P3-1 item describe — the scan-heap-ratio and retained-array work floors, their
telemetry, the 32 MiB RSS tripwire with the RUST_INTEL_CONTROL401_MIN_PEAK_RSS_MB override, and
control 458's source-level callee-identity pin — was retired by the anti-vacuity gate rebuild in
6defc27 and exists in no current code path; the knob is read by no code, so setting it now does
nothing at all. This text is history, not current state.) P3-1: the "expected result is unknowable from source" claim is withdrawn as
false — the marker id is recoverable from the input string — and control 458 now pins callee
identity: the import line must be verbatim
import { literalTrueCompletionDiagnostics } from './js-lexer.mjs'; and the identifier must
appear exactly twice in the comment/string-masked module (import binding plus anchored call).
This rejected the specific as-aliased module-scope wrapper facade the review demonstrated.
The length-gated fast-path vector inside dev/js-lexer.mjs itself is NOT closed by the work
floors: an allocating fast path satisfies the retained-array delta with a single
new Uint8Array(source.length) while never scanning. (Round-46 correction, inserted in place:
the original text here claimed the floors covered that vector structurally; that was false as
stated — see the round-46 fixing disposition below. The floors close non-allocating eliding
facades, which is the only reconstruction actually run; an allocating fast path inside
dev/js-lexer.mjs is excluded by the stated threat model — deliberate forgery of the lexer —
not by the floors.) Closure prose in this changelog, the ledger, and the code comments is narrowed to what
was actually tested. P3-4/P3-B: the ...phase.env pin is anchored to the actual spawnSync(
options object instead of the whole file, the fixtures-arm phase slice is bounded at the phases
array's ]; instead of end-of-file, and new control 487 executes dev/validate-all.mjs
against a temp copy whose core phase exits 1 immediately, asserting the exit-status mapping
(status 1), the [validate-all] phase=core failed diagnostic with exit status 1, and that
the fixtures phase never starts. P3-5: three negative controls cover the previously
unexercised enforcement mechanisms — control 488 (fully reversed phase declaration order is
rejected), control 489 (a coordinator lane's timeout-minutes below the computed minimum 45 is
rejected), and control 490 (a workflow-level RUST_INTEL_VALIDATE_TIMEOUT_MS occurrence is
rejected, including in a comment). The fixture suite is 490 controls — 414 child-process
(390 validator-entrypoint and 24 focused lexer/helper) plus 76 in-process — with the header,
registry total, README, and this changelog in agreement. P3-A: the 0.7.0 MINOR classification
is re-derived against the packaged bin/ delta accumulated since the round-23 decision (see
the Unreleased note above) and holds. Fresh evidence for the open Windows gate: ordinary
npm run validate at this fixing pass's tree (Node v24.12.0, Windows 10.0.19045) passed
in 344 s with exit 0 and 490/490 controls; the recorded run predates only the
insertion of its own figures into this paragraph. This is local non-reproduction evidence for
the current fixing state, not a demonstrated fix; independent HS review and exact-head CI
remain release gates, and no version bump, tag, push, or publication is claimed.
Round-46 fixing disposition (implemented; ordinary Windows validation re-run locally).
The round-46 review (docs/reviews/latest-commits-review-round-46-2026-09-07-1826.md) is disposed
as follows, committed as the commit containing this row, on top of c2ee3fb (the round-46 report
commit); push, bump, tag, and publication remain separate, explicit, human-authorized actions.
Closure below is stated per reconstruction actually run, not per class. P2-1: control 458's
callee-identity pin is reworked so both halves agree on what counts as source — the pinned import
must appear verbatim in the raw text AND its comment/string-masked image must appear as a live
line (a copy inside a comment or a string satisfies nothing; a forged module path fails the raw
half; the pinned image is computed by masking the pinned line itself, because the line's own
'./js-lexer.mjs' string is blanked by the same masking) — and the brittle exactly-twice
occurrence count is replaced by binding-shape assertions (no function/const/let/var/class
declaration of the identifier, no as-alias in either direction), evaluated by a shared
evaluateObservationModuleContract function used verbatim by the positive and negative controls.
Reconstructions run and rejected: (1) the review's comment-hidden-import wrapper facade
(block-commented pinned import + same-named wrapper retaining new Uint8Array(source.length) +
tail recovery), inserted into the real module — the full fixture suite exits 1 with control 458's
pin message naming the violated check (observed twice); (2) the round-45 as-alias wrapper
spelling — flagged by the binding-shape assertions. Residual stated honestly: a binding that is
neither a declaration nor an alias (for example a same-named forged parameter default) is not
asserted against; deliberate source forgery remains outside the threat model, as the control 401
comment already states. P2-2: control 458's helperContract now pins the work-floor telemetry's
provenance at source level — initialMemory/scanMemory/companionMemory sampled from
process.memoryUsage(), the scan-then-sample adjacency, the scanHeapSample/
scanArrayBuffersSample/initialArrayBuffers telemetry expressions, and the peakHeapSource
label — and expectLexerProbe re-asserts peakHeapSource === 'sampled-around-scan-and-terminal'
plus peakRssSource being boundary-sample or process.resourceUsage.maxRSS (the round-45
deleted telemetry-provenance assertion is restored and extended). Recorded distinction: removing
the sampling statements outright already fails control 401 closed at runtime (both work signals
evaluate to null, so the floor OR is false) — observed via control 491's diagnostic — so that
removal was a regression-clarity gap, not a live bypass; the pins additionally close the
replacement forgery (a fabricated scanMemory value no longer passes control 458's source
contract). P3-1: the false fast-path closure claim in the round-45 paragraph above is corrected
in place; the code comment at the floors and control 459's comment now state that the floors close
non-allocating eliding facades only, and that an allocating fast path inside dev/js-lexer.mjs is
out of the stated threat model rather than floor-closed. P3-2: control 487's mutated core phase now
writes a stdout sentinel and exits 7 — a value that is not the coordinator's fallback — and the
control asserts the coordinator's own status is 7, the exit status 7 diagnostic, the sentinel's
arrival (the stdio: 'inherit' contract), phase=core failed, and the absence of both
phase=core passed and phase=fixtures. Discrimination observed: replacing the coordinator's
forwarding expression with a literal process.exit(1) makes control 487 the suite's only failure
(status 1 against the child's 7). P3-3: two negative controls close the gap for this round's own
mechanisms — control 491 removes the probe file's post-scan sampling in a temp copy and requires
the shared evaluateWorkFloors predicate to reject the resulting null telemetry while the
semantic oracle stays green (discrimination observed: neutering the shared predicate makes control
491 the only failure, with both samples null and the child still exiting 0); control 492 builds
both facade shapes from the real module source and requires the comment-hidden facade to pass the
child's full semantic oracle (which is why the parent-level pin must exist) while the shared
evaluateObservationModuleContract evaluation flags both facades and the unmutated module stays
clean (discrimination observed: neutering the shared evaluation's identityHeld makes control 492
the only failure). Control 492 also exposed a failure mode in this pass's first draft: a forged
module the lexer cannot scan crashed the suite with an unhandled maskJsNonCode exception; the
shared evaluation now returns a fail-closed lexable: false result and control 458 reports a
dedicated message. P3-4: RUST_INTEL_CONTROL401_MIN_PEAK_RSS_MB is documented in the README's
tooling paragraph beside the other knobs. (Round-48 correction, inserted in place: the knob was
then deleted outright by the 6defc27 gate rebuild — it is read by no code and setting it does
nothing — and this paragraph's P2-1/P2-2/P3-3 mechanisms (the helperContract pins, the telemetry
work floors, evaluateObservationModuleContract/evaluateWorkFloors, and the old controls
491/492 source-contract behavior) were retired with it; those control ids now belong to
lexer-mutation differentials.) P3-A: the semver paragraph above is re-anchored at
v0.6.0. Suite: the fixture suite is 492 controls — 416 child-process (390 validator-entrypoint
plus 26 focused) plus 76 in-process — with header, registry, README, and this changelog in
agreement. (Round-48 correction: "is" held only at that fixing state; the suite is now 494
controls — 419 child-process (390 validator-entrypoint plus 29 focused) plus 75 in-process —
under the behavioral-differential gate.) Fresh evidence for the open Windows gate: ordinary npm run validate at this fixing
pass's tree (Node v24.12.0, Windows 10.0.19045) passed with exit 0 and 492/492 controls in 432 s;
the recorded run predates only the insertion of its own figures into this sentence. This is
local non-reproduction evidence for the current fixing state, not a demonstrated fix; independent
HS review and exact-head CI remain release gates, and no version bump, tag, push, or publication
is claimed.
Round-48 fixing disposition (implemented; ordinary Windows validation re-run locally). The round-48 review (docs/reviews/latest-commits-review-round-48-2026-09-08-0044.md) is disposed as follows, committed as the commit containing this entry, on top of a971bc5 (the round-48 report commit); push, bump, tag, and publication remain separate, explicit, human-authorized actions. P2-1: the anti-vacuity differentials no longer disclose their expected outcomes through the invocation ordinal. The eight differential controls are registered, executed, and completed as one group in a per-run randomized order (Fisher-Yates shuffle driven by crypto.randomInt), so the number of prior invocations of a probe argv no longer predicts which mutation — or none — a vehicle's temp tree carries; each control still registers, spawns, and completes immediately around its own child, preserving the registry's one-spawn-per-control attribution. Where the expected outcome is an error, the mutation now also embeds a run-time-random nonce in the message thrown by the mutated lexer itself — controls 402 and 458 join control 459's random K (402's mutate rewrites only the budget throw's message text; its condition, constant, and every other byte of dev/js-lexer.mjs are untouched, and the 401∧458 / 402∧491 operation-count pairings are unchanged) — so a canned static message cannot match an error-valued expectation even by luck. Success-valued expectations (401, 491, 492, 493, 494) cannot carry a nonce without changing their verified semantics and rely on the shuffle. (Round-49 correction, inserted in place: this sentence was false — the shuffle protected the success-valued expectations from nothing, because the nonce had made 402 and 458 length-changing and completed a fs.statSync().size fingerprint one stat could read, while sub-50-unit behavioral probes identified 492/493/494 independently; closed by the round-49 fixing disposition below.) (Round-50 correction, inserted in place: the shuffle, the padding, and the branch draws were retired outright by the round-50 fixing disposition below — every success-valued expectation itself carries a run-time scan-completion nonce now, which makes identification worthless and this paragraph's protection machinery dead weight.) In the same pass, every differential mutate asserts its anchors occur exactly once at run time (P3-5), and expectLexerProbe's dead expected option is removed. Adversarial re-test, run for real: the review's reconstructed invocation-ordinal facade (a per-(ppid, argv) counter persisted in os.tmpdir(), canned answers for every (argv, ordinal) pair under the old fixed source order, a genuine call only where the answer was unknowable) fails the rebuilt suite — observed exit 1 with controls 402, 458, 459, 491, 492, 493, and 494 all failing (402 and 458 on the nonce their canned messages cannot carry, 459 on the unknown K, the rest because the shuffled order feeds each canned answer to the wrong control), control 401 being the only differential the canned table happened to satisfy that run. P3-1: the "re-verified by reconstruction" claim in the gate-rebuild paragraph above is corrected in place to what is actually recorded. P3-3: the superseded round-45 and round-46 paragraphs are annotated in place (the round-46 precedent), with RUST_INTEL_CONTROL401_MIN_PEAK_RSS_MB marked as read by no code; README's provenance sentence now qualifies that dev/validate-lexer-observations.mjs no longer exists. P3-4: the 64 MB heap-cap comment now describes the workloads that actually run under it, and the ragged control-485 comment is re-aligned with dev/validate.mjs's precise historical wording. P3-A/P3-B: the review ledger's blank-line breaks are repaired so it renders as a table again, the rounds 43-47 series row is re-dated to its commit's real date and commit-anchored, and the missing round-47 and round-48 review rows are added. P3-C (coordinator option surface), P3-D (two unreferenced dev scripts), and P3-E (the primary checkout's untracked hooks directory) remain open, unchanged. Fresh evidence for the open Windows gate: ordinary npm run validate at this fixing pass's tree (Node v24.12.0, Windows 10.0.19045) passed with exit 0 and 494/494 controls in 328 s; the recorded run predates only the insertion of its own figures into this sentence. This is local non-reproduction evidence for the current fixing state, not a demonstrated fix; independent HS review and exact-head CI remain release gates, and no version bump, tag, push, or publication is claimed.
Round-49 fixing disposition (implemented; ordinary Windows validation re-run locally). The round-49 review (docs/reviews/latest-commits-review-round-49-2026-09-08-0332.md) is disposed as follows, committed as the commit containing this entry, on top of 4811697 (the round-49 report commit); push, bump, tag, and publication remain separate, explicit, human-authorized actions. P2-1 — the round-48 nonce made controls 402 and 458 length-changing, completing the fs.statSync().size fingerprint the previous round had measured and bounded (one stat identified the mutation within every probe argv, and the five success-valued differentials, control 401 included, were canned-answerable with zero scanning), plus an independent sub-50-unit behavioral-probe channel identifying 492/493/494 — is closed structurally rather than by pinning the vehicle: (a) every mutated tree in a run, the identity runs of 401 and 493 included, is padded after all executable code to one common byte length computed from that run's own draws (measured in the re-test below: all eight invocations reported the same size delta, +286), so the size channel has nothing to read; (b) success-valued expectations now derive from run-time draws observable only through the mutated text (the accepted second-interpreter residual) or a full-cost scan — control 491 draws a 50/50 accept/reject branch (budget raised by a random headroom; the argv-carried input length is drawn so its distribution is identical on both branches, so argv leaks nothing; reject = one operation above the raised budget on a nonce'd message), control 492 always renames the completion constant but draws whether the scan runs at the exact budget (accepted, no diagnostics) or one operation short (rejected, nonce'd message), and control 494 gates the block-comment branch behind a random operation threshold T ∈ [2, 999,957] (the decoy's comment-branch check measured at operation 999,958, so the decoy is always live while every sub-decoy probe stays masked exactly as in the unmutated scanner), making 493 and 494 mutually ambiguous below ~1,000,000 charged operations. Controls 401 and 493 are identity runs with no mutable answer; their protection is that size-and-behavior ambiguity plus the per-run shuffle. Residual restated honestly (superseding the residual-list annotations above): the three error-valued differentials force a full-length scan outright; the five success-valued differentials force a scan up to a per-run guessing probability — a facade that scans nothing and answers its argv's most likely canned shape passes with probability ≈ 1/64 per run (401: 1/4; 491/492/493/494: 1/2 each) — and a vehicle that reads the mutated temp-tree lexer and simulates it remains the accepted second-interpreter forgery. (Round-50 correction, inserted in place: the ≈ 1/64 guessing-probability residual is moot — the round-50 fixing disposition below deleted the mechanism that had that residual (the shuffle, the padding, the branch draws) rather than fixing it in place, and the five success-valued differentials now carry a fresh per-run scan-completion nonce, so a canned, eliminating, or fingerprinting facade constructs no success observation at all.) Adversarial re-tests, run for real against this fixing pass (each a scratch replacement of dev/validate-lexer-probes.mjs, reverted afterwards): the reconstructed round-48 invocation-ordinal facade failed the suite — exit 1 with differential controls 402, 458, 459, and 491 failing (static canned messages cannot match the nonces, and the shuffled order fed the canned 401/493/494 answers and the genuine-run 459 slot to the wrong trees), controls 401, 493, and 494 passing only by shuffle luck and 492 via its genuine-run fall-through; the reconstructed round-49 size/behavior facade failed the suite — exit 1 with differential controls 402, 458, 459, 491, 492, and 494 failing, after its own stderr showed every invocation of all three argvs measuring the identical size delta (+286) so no bucket matched and it scanned nothing, controls 401 and 493 passing only because their identity defaults happened to be right. (Both scratch facades replaced the whole vehicle file, so focused controls 399/400/409-478 also reported unsupported-argument failures — scaffolding noise of the re-test harness, not gate evidence. A first evidence run for this entry failed control 491 because that same scratch-restore procedure — git checkout -- dev/validate-lexer-probes.mjs — had also reverted the vehicle's uncommitted argv-length transport; the failure was diagnosed from the new P3-4 reproduction line, the vehicle re-applied, and the suite re-measured; recorded here because the restore hazard is part of this pass's evidence.) P3-1: the heap-cap comment now states the real bounds (controls 409-478 scan the whole dev/validate-fixtures.mjs source, ~266,000 code units and growing with every control added; 493/494 run 1,000,000-unit inputs). P3-2: README's provenance sentence now credits the gate rebuild to the round-47 fixing pass (6defc27). P3-3: the load-bearing punctuation of the differential scope headers is documented next to the headers — the 401/402 parentheticals keep them out of the label inventory carried by the // Controls 400-402: label, while the colon-bearing 458/459/491/492 headers ARE those controls' inventory entries. P3-4: any differential failure now prints the per-run execution order and every draw to stderr (observed live in both re-tests and in the failed first evidence run above), so a red run is diagnosable without a seed. P3-5, P3-C, P3-D, P3-E: open, unchanged. Fresh evidence: ordinary npm run validate at this fixing pass's tree (Node v24.12.0, Windows 10.0.19045) passed with exit 0 and 494/494 controls in 293 s; the recorded run predates only the insertion of its own figures and this pass's documentation edits into the tree. This is local non-reproduction evidence, not a demonstrated fix; independent HS review and exact-head CI remain release gates, and no version bump, tag, push, or publication is claimed.
Round-50 fixing disposition (implemented; ordinary Windows validation re-run locally). The round-50 review (docs/reviews/latest-commits-review-round-50-2026-09-08-0721.md) is disposed as follows, committed as the commit containing this entry, on top of 56add5f (the round-50 report commit); push, bump, tag, and publication remain separate, explicit, human-authorized actions. A second design consultation rejected both of the review's proposed corrections before this pass was built: the variable-multiset P2-1 fix breaks the registry's one-spawn-per-control attribution and remains probabilistic rather than deterministic, and the move-entropy-into-the-input P2-2 fix was empirically disproved (built and measured: a forged vehicle answered the entropy-carrying input via String.prototype.indexOf in 0.2 ms against the real scan's 189 ms, so entropy embedded in the input does not force real work). P2-1 (elimination) is closed by making identification worthless instead of expensive: every success-valued differential's mutated dev/js-lexer.mjs now records a fresh per-run 48-bit nonce in a module binding when its scan's operation counter reaches that differential's measured final charged operation (measured against this tree: 2,000,000 for the 401/492 input and tree, 2,000,001 for 491's, 999,998 for 493's identity-plus-nonce tree — the /* and */ delimiters each consume two code units for one charged operation — and 1,000,000 for 494's fixed comment-disabled tree), and completionDiagnostics' return appends one trailing diagnostic entry { index: <input length>, id: <nonce> } only when the nonce fired and the completion pass ran at that differential's full input length; the expectation is the former diagnostics plus that entry. A vehicle that identifies its tree — by order, elimination within a probe argv group, os.tmpdir() memory, file size, or a cheap behavioral probe — still cannot construct the answer, so the round-48 Fisher-Yates shuffle, the round-49 length-equalization padding, and the round-49 491/492/494 branch draws are deleted as dead weight rather than kept alongside: 491 and 492 revert to their pre-round-49 always-accept shapes (budget raised by exactly one to a fixed 2,000,001 at a fixed 2,000,001 input length; rename-only), 494 reverts to the fixed false && comment-branch disable, 402/458/459 are byte-for-byte unchanged, and controls 401 and 493 stop being identity runs — each now carries only the nonce machinery. Deleting the padding pass also deletes its two top-level throws, so an anchor drift again fails the eight controls closed instead of aborting the fixture process (round-50 P3-4's mechanism is gone and the designed per-control message is reachable again), and the failure reproduction line now prints the eight per-run draws that still exist (three message nonces, K, five scan-completion nonces). Adversarial re-test, run for real against this fixing pass: round 50's elimination facade (a scratch dev/validate-lexer-probes.mjs persisting per-(ppid, argv-group) differential identity in os.tmpdir() and answering the last unidentified member of a probe argv group from its formerly-public canned answer without scanning) fails the suite — exit 1 with controls 491, 492, and 494 failing on the missing nonce entries, the fixed order making exactly those the last identified members of their groups, while every invocation the facade scanned honestly passed. P2-2 (the forged-lexer exclusion) is closed as a claim-narrowing, not a mechanism change: the gate-rebuild paragraph above and README's tooling paragraph now state the boundary the review asked for — the differentials prove the fixture's evidence is produced by executing the mutated scan through its final charged operation, a dev/js-lexer.mjs deliberately written to read its own mutation is outside the threat model by construction (the definitional boundary of mutation testing, for any code under test in any language), and the lexer's integrity is a review obligation. The two accidental-defect detection claims in that new wording are measured against this mechanism, not hand-traced: scratch copies of dev/js-lexer.mjs with (a) an early return above one million code units and (b) an identifier-continue loop that stops charging the operation counter were run against all eight differentials and each failed every one — 402/458/459 on the missing budget throw, the five success-valued controls on the missing nonce entry. P3-1, P3-2, P3-3, and P3-5, and the carried P2-A, P2-B, P3-C, P3-D, and P3-E, remain open, unchanged. Fresh evidence: ordinary npm run validate at this fixing pass's tree (Node v24.12.0, Windows 10.0.19045) passed with exit 0 and 494/494 controls in 496 s; the recorded run predates only the insertion of its own figures and this pass's documentation edits into the tree. This is local non-reproduction evidence, not a demonstrated fix; independent HS review and exact-head CI remain release gates, and no version bump, tag, push, or publication is claimed.
New category: §C12/§C12a — reinventing a solved problem instead of reaching for a world-recognized crate. MINOR: a new numbered category (with a lettered infrastructure-tier sub-section), a new §A1 bullet, two trigger rows, one 🔴 escalation. Numbered category count 58 → 59.
The class was already present piecemeal — §B12 (don't hand-roll crypto/zeroing), §B24 (subtle for constant-time compare), memchr in the Substitution catalog — but scattered across categories that each earned their own line through a narrow, evidenced acceptance gate, with no home for the general pattern. This release collects the pattern into its own category and applies the same gate explicitly: a row exists only when a concrete input can be named where the hand-rolled version compiles, passes a plausible test, and is silently wrong — never "less idiomatic" (style, out of scope) and never "slower" (→ the existing Substitution catalog, Tier E). Membership is the gate; the gate is stated in the category body so a future addition is held to the same bar, not to "is there a popular crate for this."
Process, stated for auditability. Candidates were sourced from three kinds of evidence, not one: blessed.rs (topical curation, read section by section by hand), crates.io's own API (?sort=downloads and per-crate downloads counts, fetched directly — every number in the catalog is a live figure, not a guess), and a five-way parallel sweep of awesome-rust's full README plus crates.io ranks #1–300, each sweep instructed with the same gate and the running list of already-accepted/already-covered/already-rejected domains so the five passes did not duplicate each other's ground. Independent convergence across sweeps (three of five independently proposed unicode-normalization; two independently proposed quick-xml and textwrap) is treated as corroboration, not as three separate rows.
Verification caught two agent errors before they shipped — recorded here because the failure mode (an agent's grounding claim sounding plausible but being wrong) is exactly what this spec exists to catch, so shipping it uncaught would have been a self-inflicted instance of the defect class the spec targets:
- A proposed
httpdaterow claimedchrono's%A/%bstrftime specifiers read the OS locale. Checked againstchrono's own docs: false —%b/%Aare always English; only%x/%care locale-dependent. The row is dropped, not shipped, and the false claim is recorded inreferences/sources.mdso a future pass does not re-propose it without re-checking. - A proposed
unicode-segmentationrow was already covered:data-and-types.md§B28 already names the same crate for grapheme-boundary handling. Dropped as a duplicate, not added.
§C12 (utility-level, 27 rows) — CSV, semver comparison, URL construction, money/decimal arithmetic, retry/backoff, rate limiting, glob matching, directory traversal, base64, temp files, HTML escaping (redirected to an auto-escaping template engine, not a manual-escape crate — the defect is manual call-sites, not a missing function), date/time DST, JSON construction, HTTP client, config/cache directory paths, Unicode NFC/NFD string equality, XML parsing, Unicode-width-aware text wrapping, form/query-string +-decoding, shell-word/argv splitting, byte-order integer decoding, CIDR containment, geospatial antimeridian/pole math, HTML sanitization, Markdown rendering, MIME/Content-Type parsing, and in-process cache eviction (explicitly distinguished in-body from §B14's unbounded-cache finding: this is a bounded cache whose eviction logic is wrong, not one with no eviction at all). Two rows — HTML sanitization and Markdown rendering — escalate to 🔴 (added to the Enforcement tiers list) because their silent-failure shape is XSS, not merely wrong output.
§C12a (infrastructure-level, 2 rows) — hand-rolled persistent storage (fs::write on every update has no crash-consistency; a killed process leaves a truncated file with no error, not the WAL/atomic-rename guarantee rusqlite/sled/redb/fjall provide) and a hand-rolled in-process coordination primitive (cache, dedup, rate limiter) that silently assumes a single instance and diverges the moment the process is horizontally scaled — redis cited as one verified externalization option, not a mandate.
§A1 gains a "default-of-an-earlier-era" bullet: a crate that still compiles and has no advisory, but stopped being the ecosystem's recognized default before the training corpus's cutoff caught up — structopt (RUSTSEC-2022-0104, folded into clap's derive API), serde_yaml (archived by its author), lazy_static/once_cell::sync::Lazy (superseded by std LazyLock/OnceLock, cross-referenced to the existing Version pins entry rather than duplicated). A chrono → jiff bullet was considered and deliberately not added: chrono carries no advisory and is actively maintained; "soft-deprecated" is one curated site's opinion, not a fact, and hardcoding a default-of-the-moment into the spec is the exact rot the iai-crate-name caveat (§E6, v0.5.1) already warns against.
Considered and explicitly excluded, so a future pass does not re-litigate them (recorded in the §C12 body itself, not just here): clap, anyhow/thiserror, logging-framework choice, itertools, bitflags, linear algebra, statistics, edit-distance, language/locale detection — no nameable silent-failure input, style; tokio — already the assumed runtime baseline, not an omitted alternative; full-text search (tantivy) — performance-shaped, redirected to the Substitution catalog; connection pooling, consistent-hashing/sharding, HTTP response caching with Vary handling, Bloom filters, UUID generation, HTML scraping, IP-range matching beyond CIDR — real candidate shapes with weak download evidence, an atypical LLM-authored trigger, or unfinished verification against neighboring categories (§B14/§C8); deferred, not rejected.
skill/deps-macros-ergonomics.md— new §C12/§C12a bodies (trap, membership gate, REQUIRED "propose per §A1, don't add silently — a documented zero-dependency decision is not a defect", BANNED, the two catalog tables, the excluded-candidates list); new §A1 "default-of-an-earlier-era" bullet.skill/SKILL.md— category count 58→59; Tier C overview sentence; category→module map; Enforcement tiers 🔴 list gains the two-row §C12 escalation; one phrase-trigger row and one code-pattern-trigger row.skill/references/sources.md— new "Recent sources (v0.6.0)" section: the blessed.rs/crates.io-API methodology, the RustSec-INFO-advisory grounding for the §A1 bullet, and the rejectedhttpdate/chrono claim kept on record.dev/validate-fixtures.mjs—moduleForgainsC12/C12aso the structural contract (section header exists, category is routed fromSKILL.md) covers the new category without a fragile textual pattern-detector for a 27-shape catalog.
cargo-semver-checks closes the one tier with no tool in Post-flight — plus a scope-line correction. PATCH-shaped: one Post-flight bullet, one scope-line clause, one source entry; no BANNED/REQUIRED wording moved, no category added, retired, or renumbered. Numbered category count unchanged (still 58).
Triggered by a first-party account of cargo-semver-checks gaining stdlib stability-attribute awareness (http://predr.ag/blog/protecting-the-rust-stdlib-from-breakage), citing three historical incidents: an unstable required trait method breaking async-std (2020), a BuildHasher method reaching beta before its object-safety break was caught (2021), and an Iterator soundness fix that silently dropped Send/Sync (2022). The stdlib-specific plumbing (rustdoc-JSON stability attributes) does not transfer — ordinary crates carry no #[unstable]/#[rustc_const_unstable] attributes — so no category was written from it. What transfers is narrower and was verified independently against the tool's own lint catalog (src/lints, ~200 files) before being cited: trait_method_added, trait_no_longer_dyn_compatible, and auto_trait_impl_removed exist as named lints today, so all three incident shapes are mechanically caught, not just plausible.
Every other tier in this spec has a tool in Post-flight — miri for §B5, loom for §B9/§B13, cargo audit/cargo deny for §A1, cargo-mutants for §D1a, tokio-console/heaptrack for §B9–§B11 — except semver (§C1/§C1a/§A3), which had none. Per the spec's own tier discipline, a mechanically-checked pattern is 🟢 (delegate, don't hand-report) — so the fix is a Post-flight gate, not a new §C-category; adding one would be exactly the noise the tier system exists to prevent.
skill/SKILL.mdPost-flight checklist —cargo semver-checksadded as a publish gate alongsidecargo audit/cargo deny, with the same honesty caveat those two already carry: the tool documents its own gaps (generics, lifetimes, feature/target-specific breakage), so §C1/§C1a/§A3's write-time discipline still covers what it doesn't.skill/SKILL.mdscope line — added a second stated exception (alongside the existing §B18a variance one): a trait method added withoutwhere Self: Sizedin a published library's public trait loses object-safety only at the consumer'sdyn Traitcall site, sorustccannot flag it in the author's own crate. The scope line's blanket claim ("rustc already catches them and the LLM cannot ship them") was inaccurate for this specific case; the exception corrects it without changing the omission's conclusion — this shape stays out of scope as a hand-written category, same as before, now for the right reason.skill/references/sources.md— new entry citing the tool's verified lint names and the blog post, with the transfer boundary stated explicitly (lint coverage and the delayed-blast framing transfer; the rustdoc-JSON stability-attribute mechanism does not).
Audit findings gain an evidence axis — pattern / traced / proven, plus honest unreachable matches. PATCH-shaped: audit-harness and command changes only; no category was added, changed, or retired, and no BANNED/REQUIRED wording moved. Numbered category count unchanged (still 58).
Imported — deliberately narrowly — from an external Rust project's review methodology (a logic-defect-prover agent whose verdicts are proven / disproven / unreachable / unprovable, and whose hard rule is that a test failing on compilation, on a setup panic, or on a wrong expectation is not proof). Nothing was taken from that project's architecture rules: they are one codebase's DDD/hexagon decisions (dependency direction, no &mut parameters, no flag arguments, straight-line logic), and this spec's own acceptance gate excludes rules where LLMs do not err more than humans — that is Rust style, not a defect class. Its one signal that is a Rust defect (unordered HashMap iteration reaching public output) was checked and is already covered: §B13's REQUIRED list carries HashMap::iter order being randomized per-process and per-rehash, and the Substitution catalog in data-and-types.md carries the output/snapshot-order row.
Compatibility note (consumer-visible, despite the patch shape). Two things change for anything downstream of the audit harness, and neither is a spec-wording change: (1) FINDINGS_SCHEMA gains required fields — a structured-output agent must now emit evidence, reachedFrom and unreachable, so an older producer fails validation; (2) the report's Found: line gains an evidence breakdown and a new top-level Unreachable matches section appears, so anything parsing the report header or section list needs updating. The spec's Major trigger is breaking BANNED/REQUIRED wording that tooling depends on, which is untouched — but the schema and report format are contracts too, and they moved.
What was missing here was not a rule but an output axis. The spec's central promise is noise reduction — "treating all 58 categories as equally critical produces noise that buries the few findings that matter" — and individual rules already carry reachability calibration (§B14 is 🔴 only when the accept source is attacker-extendable). But the audit report had nowhere to record how a finding was established, and §D1a demanded negative controls of the user's tests while exempting the audit's own findings from any comparable standard.
skill/audit-project.workflow.js—FINDINGS_SCHEMAgains a requiredevidenceenum (pattern|traced|proven) andreachedFromper finding, plus a top-levelunreachable[]array. The audit-unit prompt states the discipline:tracedrequires naming the entry point (and a hazard sitting inside an entry point counts, with that entry named); a path followed that dead-ends before an entry point ispattern, nottraced;provenrequires a run whose failure matches the predicted reason — "reading the code more carefully is not a run", and a failure on compilation, on a setup panic, or on a wrong expectation is not proof either.- Evidence is a label, not a ranking — and explicitly not a sort key. Findings stay ordered by severity, then tier. Many categories have no caller path by nature — manifests/lockfiles/CI (§A1, §C5–§C11), public-API and semver shape (§B1*/§C1*), test code (Tier D), documented guarantees (§F2) — and for those
patternis complete evidence, not a candidate: the artifact establishes the finding. Sorting by evidence would have ranked a fully-establishedCargo.tomlfinding below a traced async guess, conflating "no call graph applies" with "not established" — the exact confusion the field's own header disclaims. - Merge may not launder evidence across categories. When two units report one site under different §ids, the surviving entry keeps the evidence of the report whose category survived — never the stronger label from the discarded one, which was earned for a different category and proves nothing about this one. The other unit's category and label go in the "also flagged by" note.
- Unreachable matches are a result, not a failure — recorded with what was checked, kept out of the severity counts, never silently dropped. Four limits keep the bucket from swallowing real findings:
cargo testis an entry point (so a Tier D match in test code is never unreachable — "test-only path" means production code whose sole live caller is a test); in a library crate thepubsurface is an entry point (the caller is a downstream user you cannot see); it requires showing no path exists, not failing to find one, so undetermined reachability (dyn dispatch, macro-generated calls) stays a finding labelledpattern; and a 🔴 occurrence listed here still appears in the Post-flight 🔴 inventory — the bucket never weakens the "report EVERY occurrence" guarantee. commands/rust-intel-cc/audit.md— the same axis, vocabulary and limits on the serial (non-workflow) path, so this is not a fan-out-only feature. Plus a new behavioural principle ("report what you established, not what you suspect"), and a Limits section that now says plainly that a pure reading pass yields all-patternfindings by construction, and that some hazards (a race with no synchronization point) are decidable by neither reading nor a single run — name what would settle it rather than upgrading a guess.dev/validate.mjs— the coverage contract pins the wholerequired: [...]literals, not just property names: the realistic regression is a field quietly leavingrequired(making it optional for the structured-output agent, so the axis vanishes from real reports) while every property definition stays in place. Verified by removingevidencefromrequiredand watching the check go red. A second check pins the evidence vocabulary incommands/rust-intel-cc/audit.md, which has neither a mirror check nor schema validation of its own, so the two audit paths cannot drift apart on wording.
Performance gates measure the wrong thing — §E6 + §D1. PATCH-shaped: bullets, sub-clauses, two trigger rows and one source entry; numbered category count unchanged (still 58).
Triggered by an external profiling write-up, but the write-up's own figures are not the grounding — it is a vendor blog for a profiler crate, and its numbers are self-produced demos. Four of its six lessons were already covered, two of them by verbatim-matching examples (§E1 already illustrates serialized .awaits with let a = fetch_a().await; let b = fetch_b().await;; §E5 already routes Regex::new in a hot function to LazyLock; the Substitution catalog already lists Arc<str> for cloned immutable payloads; §E4 already bans a critical section "spanning I/O, allocation, or format!"). What survived verification was a contradiction inside this spec, grounded in first-party documentation:
skill/testing.md— §E6 "Lock the win". The rule told the reader to "guard it with acriterionbenchmark in CI that fails on regression" — which is precisely what Criterion's own FAQ advises against: cloud-CI virtualization (it names GitHub Actions) "introduces a great deal of noise into the benchmarking process, and Criterion.rs' statistical analysis can only do so much to mitigate that." Rewritten so what you gate on is the point: the failing gate must be a deterministic counter (allocation bytes/counts, query/syscall/comparison counts, or instruction counts from a Valgrind/callgrind harness — Linux CI only, Valgrind has no Windows support), with wall-clock kept as a trend rather than a red build unless it runs on dedicated hardware. Adds an §A1 pointer: Criterion's FAQ still recommendsiai, unreleased since 2021 — the technique is stable, the crate name is not.skill/testing.md— §D1, new BANNED bullet: a wall-clock threshold asserted as a test.assert!(t.elapsed() < Duration::from_millis(100))has no useful setting — tight flakes on a loaded runner or noisy co-tenant VM (thesleepfailure above, wearing an assertion), loose is structurally unable to fail (the vacuous-test/coverage-theater bullet below it). And neither reading describes the shipped binary, becausecargo testruns the debug profile (§D3) while production runs release. Calibrated against its legitimate twin: asserting that a timeout fires is a behavioural postcondition and stays allowed — driven bytokio::time::pause()/advance(), not real elapsed time. This is the §B26/§D3 shape reused — §E6 owns the discipline, §D1 is the testing-side enforcement.skill/SKILL.md— the "benchmark this / lock in the speedup / guard against regression" phrase row previously restated the advice Criterion warns against; it now routes to the deterministic-counter gate. New code-pattern row for a wall-clock threshold inside a#[test].skill/references/sources.md— new entry quoting Criterion's FAQ and analysis chapters verbatim, with theiaistaleness recorded as a live §A1 example of a documentation recommendation that rotted.
Second distribution channel (Codex), a review-of-reviews pass over v0.4.7, and the repo's first CI. Numbered category count is unchanged (still 58) — no category was added, split, or retired.
Why MINOR. The bump is earned by the distribution half, not the spec half: a second installable target (Codex plugin manifest, the rust-intel-codex binary, the skills/rust-intel/ layout) is a substantive addition under this repo's rule. The spec half is PATCH-shaped — corrections to existing bullets and their calibration, no new numbered categories. Explicitly not MAJOR: the corrected §B5/§B12/§B20 bullets narrow or sharpen claims that were overstated, and each keeps a mechanically-matchable form for the case tooling actually keys on (a tagged-union read with no tag match; a struct returned by value that holds key material; an untrusted-request struct without deny_unknown_fields). No BANNED/REQUIRED rule was withdrawn.
Five audits shipped in v0.4.7 in five independently-written releases; this is the merge-time review they never got as a set. The ledger in docs/reviews/README.md records what each correction was, and the historical gap-audit reports are now banner-marked non-normative.
skill/unsafe-and-ffi.md— §B5unionreads. v0.4.7 claimed a tag check is required for every union read; that is false for deliberate type-punning between mutually valid representations (u32↔[u8; 4]). The rule now demands a local validity proof, and names which proof applies where: for a tagged union — thebindgen-emitted C shape that dominates FFI — the proof is the tag check and a read with no precedingmatchis still the finding; for type-punning it is a stated bit-validity argument. The non-#[repr(C)]-union-for-FFI ban is retained verbatim.skill/unsafe-and-ffi.md— §B5transmute. (Superseded by the third-pass correction below — see that entry for the version that shipped.)skill/security.md— §B12 zeroize. v0.4.7 overstated the mechanism ("every Rust move is amemcpy"). Restated as best-effort memory hygiene, not a guarantee — the compiler may elide, duplicate, or combine copies — while keeping both worked failure modes (a secret returned by value fromload(); aStringsecret grown past its capacity) and theZeroizedocumentation's own reallocation caveat. Addszeroize's custom-Droppattern for types whose all-zero state would break an invariant.skill/data-and-types.md— §B20 duplicate keys. v0.4.7 asserted flat "JSON duplicate keys are last-wins". Corrected: a direct Serde struct visitor normally errors on a duplicate known field; last-wins is the behavior of an intermediateserde_json::Value/map path. The proxy-vs-service parser-differential hazard is preserved — the policy must be chosen and tested at the boundary.skill/data-and-types.md— §B26 shift counts. New BANNED bullet, stated to the module's own precision standard: a count at or beyond the type's bit width panics in debug and masks ton % BITSin release, sox << 32on au32yieldsx, not0— defined behavior, wrong answer, green tests. Notes that the masking is on the count, sowrapping_shldoes not mean "the bits shifted away".skill/lifetimes-and-api.md— §C1#[repr(transparent)]. No longer implies transmutability: it gives ABI compatibility, and §B5's value-validity/provenance/ownership proofs still apply on top.skill/unsafe-and-ffi.md§B7 +skill/security.md§C2 — archive extraction, split along the correct axis. v0.4.7 filed the whole of archive safety under §B7 (resource exhaustion). The traversal half is not exhaustion: zip-slip/tar-slip (attacker-authored entry names, absolute/rooted paths, and link entries extracted first so a later write escapes the tree — CWE-22) now lives in §C2 next to thePath::joinrule that already carries the component-rejection /canonicalize/openat-cap-stdrecipe. §B7 keeps the part that is exhaustion: no aggregate cap on extracted bytes, entry count, or nesting depth — per-entry limits do not bound the archive. Both bullets cross-reference each other; a traversal-safe extractor can still fill the disk, and a quota-bounded one can still write to/etc.serde_yamlwording made version-aware everywhere it appears — the uncontrolled-recursion abort was fixed in 0.8.4 (RUSTSEC-2018-0005) and the crate is now unmaintained, so the instruction is to verify the pinned version, not to assume "no limit". This corrects a residue incommands/rust-intel-cc/fix.mdthat the v0.4.9 pass had missed.skill/SKILL.md— trigger tables. Duplicatex << ncode-pattern row removed (two independently-added rows for one rule); theunionrow regained its grep anchor ("no preceding tag/discriminantmatch", "not#[repr(C)]"); archive extraction split into a §C2 traversal row and a §B7 quota row; toolchain-as-artifact added to Version pins;cargo audit/cargo denyadded to the command block as release/CI gates with an explicit "record the reason if you can't run them" clause.commands/rust-intel-cc/fix.mdgains matching zip-slip and aggregate-quota symptom rows.skill/SKILL.md— fan-out fallback. Rewritten host-neutrally (ClaudeWorkflow(...)/ Codex native delegation) without losing the hand-rolled fallback: the order is now explicitly workflow → one sub-agent per module written by hand → bounded single-context pass with the report marked incomplete.
An independent review of the second-pass commit (458e821) filed REQUEST CHANGES with 7 findings against the still-unpublished 0.5.0 tree, verified individually before acting on them (one — a .claude-plugin/npm claim drafted while writing this entry — was caught and corrected during that verification, not in the review itself).
skill/unsafe-and-ffi.md— §B5transmute(P1). The second-pass rewrite bannedtransmutewhenever either side lacked a pinned#[repr(...)]— but per the standard library's own documentation, primitive/array/pointer/fn-pointer conversions ([u8; 4]↔u32,*const ()↔fn() -> i32) have specified layout without a user-written repr, and the rule both rejected valid, idiomatic transmutes and contradicted the union bullet immediately below it (which correctly allows exactly this kind of type-punning with a bit-validity proof instead of a tag). (Superseded again by the fourth-pass correction below — see that entry for the version that shipped.)skill/audit-project.workflow.js— coverage status (P1). The second-passcoverageStatus.completedropped source-file sweep from its gate entirely, which meant a crate with 100 inventoried files and every unit returningsourceFilesReviewed: []reportedCoverage: COMPLETE — reviewed 0 of 100. Split into two independent signals so orchestration success can never read as audit coverage:orchestrationComplete(did every unit run under its label with its required inputs) and anoSourceEvidencefloor (a unit whose module had non-empty grep-candidate rows and whose scope had source files, yet reviewed none, is a per-unit failure regardless of orchestration status). The synthesis report now prints both lines separately. (noSourceEvidence's scope check was itself gapped — see the fourth-pass entry below.)- Release workflow inverted to verify-not-rewrite (P1).
npm-publish.ymlrewrote manifests in the runner's checkout after the tag already existed. A release run must publish the committed tree selected when the tag is resolved; rewriting that checkout would create an npm artifact not represented by that tree. The tag ref itself remains movable unless repository protection makes it immutable, so the workflow verifies the committed manifests against the resolved tag instead of rewriting them.dev/set-release-version.mjsis now a manual bump-before-tagging utility; a newdev/check-release-version.mjsverifies${GITHUB_REF_NAME#v}against all three committed manifests and fails the release on mismatch, in bothnpm-publish.yml(on tag) andci.yml(on every push, againstpackage.json's current version, so drift is caught before a tag ever exists). Also documented, having verified it directly:.claude-plugin/is intentionally absent from the npmfileslist (Claude installs via the git-based marketplace, not npm) and.codex-plugin/ships despitebin/install-codex.jsnot reading it (for a future Codex marketplace-style path). rust-cc-install.sh— portable overlap guard (P2). The overlap guard added in the second pass required GNU coreutils'realpath -m, which stock macOS/BSDrealpath, BusyBox, and minimal Linux do not support — breaking the installer on a platform the README advertises it for, unverified by CI (which only runsbash -non Ubuntu). Replaced with a POSIX-onlycanonical_candidate()(resolve the nearest existing ancestor viacd+pwd -P, re-append the missing tail) mirroring the equivalent helper already inbin/install.js/rust-cc-install.ps1. (That replacement had its own bypass — see the fourth-pass entry below.)dev/semver.mjs(new) — real SemVer grammar (P2). The "strict semver" regex indev/set-release-version.mjsanddev/validate.mjsaccepted01.2.3,1.2.3-alpha.01, and1.2.3-alpha..1— all invalid per SemVer 2.0.0's leading-zero and empty-identifier rules. Replaced with semver.org's own regex, shared by both scripts and the newdev/check-release-version.mjs, withdev/validate.mjsregression-asserting both the valid and invalid cases above.dev/validate.mjs— duplicate-trigger cell parsing (P2). The duplicate-trigger check split a table row on the first|, which truncates a cell containing a Markdown-escaped pipe inside inline code — exactly the shape of the existing`std::thread::scope(\|s\| ...)`row — silently missing duplicates in that class. Replaced with a splitter that respects the\|escape. Narrowed the CHANGELOG wording for this check from "trigger rows" to "code-pattern rows", since prose-only rows with no inline code were never in scope.dev/validate-fixtures.mjs— shift-count probe (P3). The runtime-shift detector exempted any SCREAMING_CASE identifier as "probably a constant" — naming convention, not proof; a lowercaseconstwould have evaded it either way. Removed the exemption; the probe is documented as what it is, a crude "any identifier on the right of<</>>" textual check, not a const-vs-runtime analysis.
A second independent review, of the commit that fixed the first review's 7 findings, filed REQUEST CHANGES with 4 more findings — two of them regressions surviving inside the very fixes meant to close the prior round. All four verified individually before acting (the canonical_candidate bypass was reproduced directly against real POSIX -e semantics, not just read as plausible — Windows/MSYS's lexical ..-normalizing -e masks the bug, so it had to be traced by hand and independently confirmed rather than trusted from a single local repro).
skill/unsafe-and-ffi.md— §B5transmute, corrected again (P1). The third-pass fix still overstated the contract in two ways. First, it required "every source bit pattern" to be a valid destination value — that is the obligation for a generic/unchecked conversion API accepting arbitrary source values (bytemuck::Pod/zerocopy::FromBytesbounds), not for an individualtransmutecall: au8statically known to be0or1transmutes soundly tobooleven though mostu8values do not, because the contract is about this call's actual argument and result, not the source type's whole value space. Second, it said struct-to-bytes needs "both sides on a matching pinned repr" — but a byte array has no representation attribute to match, and#[repr(C)]on the struct does not by itself guarantee the by-value operation preserves padding or that the destination bytes are initialized. Restated both obligations in value-specific, per-conversion terms, and pointed the struct-to-bytes case at the existing padding-byte bullet (the actual proof obligation there) instead of implying a repr attribute settles it.rust-cc-install.sh—canonical_candidatemissing-component/..bypass (P1). The portable replacement forrealpath -mresolved the nearest existing ancestor and re-appended the missing tail without normalizing./..inside that tail. A destination like<repo>/.does-not-exist/../skillnever reaches an existing path until it walks all the way up to the repo root (each intermediate component fails-ebecause.does-not-existdoesn't exist to resolve..through), so the entire remaining suffix — including the literal..— gets glued back on verbatim, whilemkdir -p/find/the kernel normalize it and land insideskill/, defeating the guard whose only job is to prevent exactly that. Addednormalize_path_components()— a stack-based./..collapse applied to the resolved-ancestor-plus-tail before comparison — written without bash 4.3+ features (namerefs, negative array indices) since stock macOS still ships bash 3.2. Added five execution tests toci.yml(normal install, existing-target overlap, fully-missing-target overlap, the missing-component-then-..bypass itself, and a symlink into the source tree), so the guard's actual runtime behavior is asserted on every push, not just its syntax. (Those ingredients turned out to be insufficient composed — see the fifth-pass entry below.)skill/audit-project.workflow.js—noSourceEvidenceaccepted out-of-scope paths (P2). The per-unit floor added in the third pass only checkedsourceFilesReviewed.length > 0, so a unit could dodge it by reportingREADME.md, a typo'd path, or a hallucinated filename — none of which is evidence that any scoped Rust source file was reviewed. Now intersects each unit's reported paths againstscoperResult.filesbefore counting, and surfaces any out-of-scope paths separately ascoverageStatus.invalidSourceEvidence(named in the report, never silently counted as evidence).- Trailing whitespace + a standing invariant (P3). The three metadata lines in
docs/reviews/commit-458e821-review.mdused Markdown hard-break trailing spaces — the only trailing whitespace in the entire tracked tree, and enough to failgit diff --check. Reformatted as a plain list (matching the format already used in the newer review doc). Added a whole-tree trailing-whitespace check toci.yml(git diff --checkagainst the empty tree, so every tracked line is checked, not just a PR's diff) so this becomes a standing invariant instead of a one-off cleanup.
A third independent review confirmed three of the previous four findings closed and found the installer fix still incomplete: the two path shapes it had newly tested were each rejected alone, but their composition was not.
rust-cc-install.sh—missing/..followed by a symlink (P1). The fourth-pass helper ran physical resolution and lexical normalization once each, in that order — and the two steps feed each other. Nothing can be traversed through a nonexistent directory, so the physical walk stops before it ever reaches a later component; the lexical pass then collapses themissing/..pair and exposes that component. If it is a symlink, it is returned unresolved, and a destination like<anywhere>/absent/../link(wherelink→ the source tree) compares as an unrelated path whilemkdir -pand the kernel follow it straight intoskill/— reinstating the recursive self-copy the guard exists to prevent. Restructuredcanonical_candidate()to iterate walk →cd+pwd -P→ normalize to a fixpoint rather than once; each iteration either converges or resolves at least one more symlink, with a 64-iteration cap as a backstop against symlink cycles. The inner walk also now tests-drather than-e, so a plain file on the path can never become acdtarget (which would abort the script underset -e). Added CI cases 6 and 6b: the composed traversal-plus-symlink against the skill tree, and the same construction against the commands tree — the latter passes the skill comparison and can only be caught by the commands comparison, so it exercises that second guard specifically.- Verification note. This class of bug is invisible on the maintainer's Windows/MSYS shell, where
-e/-dnormalize..lexically and silently "fix" the traversal before the guard sees it — the same masking that hid the fourth-pass bug. It was therefore confirmed twice: by hand-tracing the walk against real POSIX semantics, and by re-implementing the exact algorithm against a modelled POSIX filesystem where traversal through a nonexistent component fails, which reproduces the bypass on the single-pass version and shows the fixpoint version returning the physical path for all five shapes.bin/install.jsandbin/install-codex.jswere checked and are not affected:path.resolve()collapses./..before the existence walk, so the symlink is already inside the resolved prefix whenrealpathSyncruns.
.codex-plugin/plugin.json— Codex plugin manifest with aninterfaceblock (display name, descriptions, category, capabilities, default prompt), validated field-by-field in CI against the documented schema (allowed fields, length limits, HTTPS-only URLs, strict semver,./-relative paths without traversal).bin/install-codex.js— zero-dependency installer, exposed from the npm package asrust-intel-codex. Installs to$CODEX_HOME/skills/rust-intel, else~/.agents/skills/rust-intel;--user-dir <path>overrides,--uninstallremoves. Argument parsing is strict: unknown flags, repeated flags, and a missing--user-dirvalue are hard errors (CI asserts each).skills/rust-intel/— the Codex manifest requires askills/<name>/layout, so a byte-identical mirror ofskill/is checked in (a git-based plugin install has no build step). It is a derived artifact: generated bynpm run sync(dev/sync-mirror.mjs), byte-identity enforced in CI, never edited by hand. Documented as such inREADME.mdandcommands/README.md, which is where the repo's "duplication of knowledge is forbidden by design" rule is stated..claude-plugin/is intentionally absent frompackage.json'sfileslist (confirmed vianpm pack --dry-run) — the Claude Code plugin installs through the git-based marketplace (.claude-plugin/marketplace.json→/plugin marketplace add), never through npm, so it has nothing to do in the npm tarball..codex-plugin/is listed even thoughbin/install-codex.jsnever reads it (that installer only copiesskill/) — it ships so the manifest is available to a future Codex marketplace-style install path, the Codex analogue of.claude-plugin/marketplace.json.
docs/sources.md→skill/references/sources.md. The evidence base now ships with the skill instead of living one directory outside it, so every in-skill citation resolves after installation.docs/sources.mdremains as a compatibility page (the ~25 historical CHANGELOG links to it keep working), anddev/validate.mjsnow fails any skill link that escapes the installable tree. All three installers copyskill/**recursively, soreferences/actually lands at the target.
skill/audit-project.workflow.js— the scoper now inventories non-Rust artifacts by group (manifests, lockfiles, toolchains, configs, CI, scripts, FFI/bindgen/linker), and each unit declares which groups it must open: deps/macros gets all of manifests+lockfiles+toolchains+configs+CI+scripts, unsafe/FFI gets build scripts and headers, testing gets CI/config/scripts, semantics gets the project's docs. Units return the exact paths they actually opened (sourceFilesReviewed/artifactsReviewed/docsReviewed) plus their assignedlabel, and the synthesis prompt is told never to infer one unit's coverage from another's evidence.- Coverage gating is scoped to what the run owed each unit — missing scope fields, missing slices, missing per-unit required inputs, unrecognized labels, and dropped agents. It deliberately does not gate on a total sweep of every
*.rsfile: units grep for candidates rather than reading the whole tree, so that condition is false on every real crate, and an always-red flag would drown the per-unit gaps that actually invalidate a finding. Source depth is reported instead as "reviewed R of T source files (grep-candidate sampling)".
- Source/destination overlap is now a hard error in all three installers (
bin/install.js,bin/install-codex.js,rust-cc-install.ps1,rust-cc-install.sh) for both the skill and the commands directory, checked against canonicalized paths that tolerate not-yet-existing targets. Installing into the repo's ownskill/used to delete the source mid-copy. - Recursive copy of
skill/**/*.{md,js}replaces the flat*.mdsweep plus a one-offaudit-project.workflow.jsline (which the PowerShell installer had been copying twice).
.github/workflows/ci.yml— the repo had none. Runs on every push and PR: pinned Rust toolchain (1.97.0), Codex-mirror sync check,dev/validate.mjs,dev/validate-fixtures.mjs,rustccompilation of both fixtures,node --checkon every shipped script,bash -non the shell installers, andnpm pack --dry-run.dev/validate.mjs— required-file existence; markdown link integrity for both the canonical skill and the mirror, including a check that no skill link escapes the installable tree; the workflow's module list and coverage contract; full Codex-manifest schema validation; version agreement acrosspackage.json/.claude-plugin/.codex-plugin; mirror byte-identity; Codex-installer CLI rejection cases; and duplicate code-pattern trigger rows — two rows in oneSKILL.mdtable keyed off the same set of inline-code tokens are the same rule stated twice, which is exactly how thex << nduplicate got in. Table cells are split respecting Markdown's\|escape, so a row whose code span itself contains a pipe (`std::thread::scope(\|s\| ...)`) is compared correctly instead of being truncated. Scope is code-pattern rows only — prose-only trigger cells with no inline code have nothing mechanical to key off and are not covered.dev/validate-fixtures.mjs+examples/fixtures/— a two-case calibration seed (§B5 union validity, §B26 runtime shift) with positive and negative controls. Scope is stated honestly inexamples/README.md: a regression tripwire and a structural check that the cited categories still exist and are still routed — not a coverage figure. It deliberately asserts nothing about rule wording; pinning prose in CI would make every legitimate rewrite a red build and freeze whichever phrasing shipped first.dev/set-release-version.mjs(manual bump) +dev/check-release-version.mjs(CI gate) — the release version is bumped and committed locally before tagging so the published artifact comes from the committed tree selected when the tag is resolved; the tag ref remains movable unless protected, and rewriting the runner after tag resolution would publish a tree that the ref does not select.npm-publish.ymlonly verifies${GITHUB_REF_NAME#v}against the three committed manifests and fails the release on any mismatch.ci.ymlruns the same check againstpackage.json's current version on every push, so drift is caught before a tag is ever created.dev/semver.mjs— the full SemVer 2.0.0 grammar (semver.org's own regex), shared by the manifest bump/check scripts anddev/validate.mjs's Codex-manifest schema check; rejects leading-zero and empty-identifier forms (01.2.3,1.2.3-alpha.01,1.2.3-alpha..1) that a looser pattern let through, with regression cases for both valid and invalid inputs indev/validate.mjs.dev/sync-mirror.mjs— regenerates (or with--check, verifies) the Codex mirror, including removal of files stale after a rename inskill/.
Gap-cluster release: five dedicated audits close 25 findings across crypto, FFI/unsafe, deserialization, concurrency, and supply-chain. Sourced from five parallel gap audits (docs/reviews/gap-audit-*.md); each finding was verified still-uncovered against the exact existing bullet text, grounded in a citable primary source, and independently reviewed before merge. Numbered category count is unchanged (still 58) — one new lettered sub-category (§B25a) is counted under §B25, exactly as §B18a is under §B18 ("the count is of numbered categories; lettered sub-sections are counted under their parent"). This is a PATCH bump: bullets/sub-clauses/documentation plus one lettered sub-category that leaves the numbered count untouched is this repo's established PATCH-shaped event (precedent: §B18a/§C1a shipped PATCH in v0.3.2), and Major = breaking BANNED/REQUIRED wording tooling depends on does not apply.
The words "salt", "certificate", and "aud" appeared nowhere in the skill before this release — the three highest-frequency crypto mistakes in LLM-generated web-service code.
skill/security.md— §B12 gains five bullets:- JWT claims beyond
alg— verification that pins the algorithm but leavesaud/issat theirNonedefault authenticates a token minted by the same IdP key for a different service/tenant;jsonwebtokenchecksaudonly when the token contains one. REQUIRED:set_audience/set_issuer/set_required_spec_claims. Plus a BANNED bullet onvalidate_exp = false/insecure_disable_signature_validation()outside#[cfg(test)]. - TLS validation bypass —
danger_accept_invalid_certs(true),danger_accept_invalid_hostnames(true), or a no-oprustlsServerCertVerifier, outside#[cfg(test)]→ silent full MITM (CWE-295). REQUIRED: pin the internal CA viatls_certs_only/tls_certs_merge(the current, non-deprecated form). Calibration preserved: a#[cfg(test)]-scopeddanger_accept_invalid_certsis fine. - KDF salt misuse + below-floor parameters — fixed/hardcoded/per-username salts (identical passwords → identical hashes) and degenerate Argon2/PBKDF2 work factors chosen for test speed, reaching production hashing. REQUIRED: per-credential
SaltString::generate(&mut OsRng)and parameters at/above the OWASP floor, cited as a living reference (Argon2id m ≥ 19 MiB/t ≥ 2/p = 1; PBKDF2-HMAC-SHA256 ≥ 600 000 as of citation) so the numbers can be re-verified rather than silently rotting. Calibration: a#[cfg(test)]-scoped reduced-cost config is fine. - Zeroize defeated by moves/reallocation — the derive zeroes only the final location, and Rust moves are
memcpy, so a returned/moved secret or a realloc'dString/Vecleaves a live copy. REQUIRED:Box/secrecy::SecretBoxat creation; pre-size secret buffers. cargo audit/ RUSTSEC before adding crypto deps — a maintained, correctly-used crate can carry an open-by-design advisory;rsa's non-constant-time private-key ops are network-observable (Marvin, RUSTSEC-2023-0071). Keep advisory-carrying ops off attacker-timed paths.
- JWT claims beyond
skill/security.md— §B24 widened from "timing of==" to "side channels on secret-dependent branches" (title + trap intro): one BANNED bullet (distinguishable decrypt-failure errors — padding vs MAC vs post-decrypt-parse — across a trust boundary are a padding oracle, CWE-208/209) and one REQUIRED bullet (collapse to one opaque error; AEAD crates'aead::Erroris opaque by design — preserve it). 🔴 across a network boundary, 🟡 logs-only.- §C2/§B24 calibration carve-out (internal-consistency fix). §C2's "carry error context, don't collapse
?-errors" is good general practice but the opposite of correct at a crypto boundary. Added a cross-referenced carve-out both ways: §C2's#[from]bullet now states "error context stops at the crypto boundary — see §B24", and §B24's REQUIRED bullet cites the §C2 carve-out back.
The word "union" appeared nowhere in the skill before this release. Why §B25a is its own section (the §B18a split test): §B18a was split from §B18 because "there is no unsafe token at the site that decides variance" — a separate failure locus. §B25a passes the same test: §B25 is about the boundary code you write (panics, ABI, pointer ownership); §B25a is about a contract of code you did not write — whether the C library is thread-safe/reentrant and what it demands of init/globals. The reflexive §B18 per-handle Arc<Mutex<_>> fix actively masks it: two independently-locked handles still race on the library's process-global state — so it is its own 🔴 section, cross-referenced from §B18's body at the point a reader would misapply that fix.
skill/unsafe-and-ffi.md— new §B25a "The C library's own concurrency contract" (🔴). BANNED:unsafe impl Send/Syncfor an FFI handle without citing the library's documented thread-safety level; giving a non-reentrant library per-handle locks (the §B18 fix misapplied); readinglast_os_error()/errnoafter an intervening call. REQUIRED: audit the library's contract first (per-handle thread-safe → §B18 per-handle lock; global-lock-required → one process-global lock or a dedicated owning thread; init-once →Once/OnceLock); captureerrnoimmediately. Grounded in CVE-2020-26235 / RUSTSEC-2020-0071/0159 (time/chronolocaltime_r↔getenv/setenvrace, shipped for years).skill/unsafe-and-ffi.md— §B25 gains three BANNED and three REQUIRED bullets: callback/context UAF (freeing a callback context before unregistration+in-flight-drain is confirmed; reclaiminguser_dataas an owningBox<F>in a re-invocable trampoline → UAF+double-free; REQUIRED unregister → drain → free, trampoline borrows); exported entry points trusting the type system (a#[no_mangle] extern "C"fn taking&T/&str/bool/enumfrom the foreign caller → immediate UB on NULL/non-UTF-8/bad-discriminant; REQUIRED take*const/*mut+primitives and validate first — the read-direction mirror of §B5's validate-before-mint); per-function ownership audit (the// SAFETY:block states copies/takes/returns-borrowed/returns-owned with a citation to the C doc line, since libraries are inconsistent across functions).skill/unsafe-and-ffi.md— §B5 gains one BANNED and one REQUIRED bullet onunionfield reads: reading a union field without checking the tag/discriminant is instant UB (no active-field tracking); a non-#[repr(C)]union for FFI has unspecified offsets. REQUIRED — a tag-checked safe accessor with// SAFETY:; and if the union only pairs with a Rust flag, use anenum.skill/audit-project.workflow.js—unsafe-and-ffi.mdcategory list gainsB25a.skill/SKILL.md— §B25 added to the lettered-split parent list, §B25a to the sub-section enumeration, the category→module map, and the 🔴 Enforcement-tiers list.
skill/unsafe-and-ffi.md— §B7 gains a decompression-bomb BANNED bullet (🔴 — capping the compressed input does not cap the decompressed output;GzDecoder::read_to_endamplifies ~1 KiB → multi-GiB with no clampablen; thezipcrate's internal.take(compressed_size)is active false safety; fix:.take(MAX+1)on the decoder, CWE-409), a parser-recursion sub-clause on the existing depth bullet (serde_json's 128 limit is parse-phase only — re-deserializing an already-builtValueviafrom_value/IgnoredAny/flatten/untaggedbypasses it → uncatchable stack overflow; serde_yaml's alias/anchor recursion was fixed in 0.8.4/RUSTSEC-2018-0005 but the crate is unmaintained; mitigate withserde_stacker), and a one-linechecked_mulcross-ref (a wrappingcount * item_sizein release passes a size clamp, then the original count OOMs, CWE-190→789; mirrored from §B26).skill/data-and-types.md— §B20 extends the existingflattenbullet with the buffer-everything cliff (flatten/tagged enums flip serde from streaming into materialize-the-whole-Content-tree — ~2× slower per serde #2363's ~50–86%, allocates the full body, errors lose position; cross-ref §E2) and adds the missingdeny_unknown_fieldsdirection (REQUIRED on untrusted request structs so unknown/typo'd/mass-assignment fields are rejected — the opposite of the existing self-typo warning; OWASP API8:2023) plus JSON duplicate-key last-wins (RFC 8259 §4, a parser-differential smuggling vector). Calibration:flattenis not banned;deny_unknown_fieldsis REQUIRED only where rejection matters, not on a forward-compatible API.
Concurrency & admission-control exhaustion — §B14/§B3a/§B13/§B17 (docs/reviews/gap-audit-concurrency-exhaustion.md)
Thesis: both concurrency modules are dense on unbounded data growth (§B14) and leaked resources (§F3/§B21) but blind to unbounded admission — object counts, key cardinality, synchronized load.
skill/concurrency-and-state.md— §B14 gains unbounded task/connection admission (🔴 peer-extendable — an accept loop spawning one task per connection with no cap; the task/socket count is the resource, CWE-770; fix:Arc<Semaphore>+acquire_owned().awaitbeforetokio::spawn, permit moved into the task — the two LLM-typical wrong fixes are named), accept-error classification (🟡 —?onaccept()kills the loop;warn!;continuebusy-spins onEMFILE; classify fatal vs transient-with-backoff, explicitly not §B3a's release-and-return), and insert-only unbounded caches (🔴 attacker keys — a cache has a consumer so theVec-no-consumer bullet doesn't match; the defect is no eviction; bound vialru/moka, CWE-770/400), plus a warning line on §B13's endorsed per-keyArc<OnceCell>map.skill/async.md— §B3a REQUIRED backoff clause amended in place: back off exponentially with jitter — deterministic backoff synchronizes N instances into thundering-herd waves; add a retry budget when the caller fans out.skill/concurrency-and-state.md— §B17 gains a third reentrance flavor (RefCell→panic, tokio Mutex→deadlock,RwLockreentrant read→deadlock — std's priority policy is OS-dependent, tokio's is documented write-preferring), with an §E4 cross-ref. Bans undisciplined reentrant reads through a hidden call graph, notRwLockitself. (The two cache CVEs CVE-2026-33012/CVE-2026-41310 are cited as cross-language pattern grounding, not Rust incidents.)
§A1 was dense on which crate name (slopsquatting, dependency confusion, the malicious-dep build-time-execution vector, the cargo-deny/cargo-vet mitigation stack) but blind to what happens to a trusted dependency's bytes over time.
skill/deps-macros-ergonomics.md— §A1 gains yanked-version handling under--locked(🟡 —cargo build --lockedagainst a yanked pin warns-not-errors, green in CI for months; REQUIREDcargo deny check advisories/cargo audit -D warnings, targetedcargo update -p <crate>not a blanketcargo update), network access in your ownbuild.rs(🟡 — the write-direction twin of the existing read-direction malicious-dependency rule:curl/git clone/reqwestin your build script pulls unpinned bytes outside the lockfile, so--locked/--offline/cargo vendorgive zero integrity; REQUIRED vendor or fetch in a separate CI step with a hardcoded SHA-256; local codegen is fine), and unpinned[patch]/git overrides (🔴 unpinned-HEAD/unnamed fork, 🟡 rev-pinned user-approved patch — a git/[patch]override with norevfollows HEAD and walks around the registry-keyedcargo audit/vet/RUSTSEC stack; REQUIRED a full-commitrev, an approved repo, a post-flight flag, a removal condition). The incident base is refreshed (evm-units/uniswap-utils,finch-rust/sha-rust, the 2026-02 crates.io policy update, TrapDoor) and §A1's proc-macro-trust sentence now cites the serde_derive precompiled-binary episode (2023, reverted v1.0.184).
skill/SKILL.md— ~21 phrase-trigger + ~20 code-pattern rows added across the five clusters.commands/rust-intel-cc/fix.md— 27 routing rows added.docs/sources.md— five new grounding subsections (crypto, FFI, deserialization, concurrency, supply-chain) plus four refreshed incident entries; every new claim carries a citable source, the two non-Rust CVEs are flagged as cross-language pattern grounding, and the one numeric anchor (serde #2363's ~50–86%, the OWASP floors) is cited as re-verifiable.package.json+.claude-plugin/plugin.json+README.md— version 0.4.6 → 0.4.7.- Each of the five clusters was implemented and independently reviewed in isolation, then verified end-to-end for cross-cluster consistency: every
§-reference across all added lines resolves to a real category; no duplicate trigger/routing rows across clusters; the 58-count invariant and the §B25a threading hold in every location.
§B5 gains the padding-byte info-leak on serialize. One bullet-pair, no new categories (still 58), no tier change. The write-direction dual of §B5's existing "validate bytes → Result before minting" rule: turning a struct into &[u8] by a raw cast copies its inter-field padding, which is uninitialized — reading it is UB (miri flags it) and it carries stale memory out of the process (Heartbleed-class disclosure; a §B12 secret-leak when those bytes are sensitive). Compiles and passes cargo test because the value round-trips fine locally. Surfaced by miri-assisted LLM audits of real crates (Levin/Shnatsel 2026).
skill/unsafe-and-ffi.md— §B5 gains one BANNED bullet (serializing a padded struct to bytes via a raw cast —from_raw_parts(&t as *const _ as *const u8, size_of)/bytes_of/transmute— across a trust boundary; and theunsafe impl NoUninit/Pod-to-force-it anti-pattern) and one REQUIRED bullet (derivebytemuck::NoUninit/zerocopy::IntoBytesand let them reject padded types at compile time — the rejection is the leak being caught; else make the layout padding-free with explicit zero-initialized_padfields or serialize field-by-field;#[repr(packed)]removes padding but reintroduces the unaligned-access hazard). Calibration: scoped to bytes that cross a trust boundary (network/disk/IPC/log), not every struct-to-bytes.skill/SKILL.md— one code-pattern trigger row (&t as *const _ as *const u8+from_raw_parts, orbytes_of/transmuteon a struct, written to a socket/file/log → §B5 padding leak).docs/sources.md— one grounding entry:bytemuck::NoUninit/zerocopy::IntoBytesreject-padding-at-compile-time contracts, CWE-212 (sensitive-info exposure) + CWE-908 (uninitialized resource), and the miri-assisted-audit field observation. Documented-mechanism grounding; no numeric claims.
Distribution: Claude Code plugin marketplace + npm. Two install paths that need no clone, alongside the existing shell installers.
.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.mdfrontmatter gains thename: rust-intelfield 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 explicitversiongates plugin updates — bump it in every release alongside README/CHANGELOG.- npm package
rust-intel-cc—npx rust-intel-ccinstalls project-locally (./.claude/),--userfor global,--uninstallfor the inverse; honorsCLAUDE_CONFIG_DIR. Zero-dependency Node installer (bin/install.js) mirroring the shell installers' layout (flat/rust-cc-*command names). Package shipsskill/,commands/rust-intel-cc/, both licenses, README, CHANGELOG. .github/workflows/npm-publish.yml— publishes to npm on everyv*tag with--provenance; the package version is derived from the tag at publish time (npm version "${GITHUB_REF_NAME#v}"), sopackage.json's version field can never drift from the release tag. Pipeline sanity-checks (node --checkboth JS files, file-presence asserts) and smoke-tests the installer in a sandbox before publishing. Requires theNPM_TOKENrepository secret.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.
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.
skill/data-and-types.md— Substitution catalog (after §E3), four groups, ~22 rows: ownership & allocation (Cowinstead of clone,Arc<T>/Arc<str>for shared immutables,bytes::Bytesfor zero-copy pipelines,compact_str/smol_str,SmallVec/arrayvec/tinyvec,write!+itoa/ryuinstead of hotformat!); lookup & complexity (hoist toHashSet,VecDeque, sort-once/BinaryHeap, dense-keyVec/slab/slotmapinstead ofHashMap<usize,_>, bitsets,match/phffor fixed key sets, linear-scanVec<(K,V)>for tiny dynamic maps,BTreeMapfor range/order,indexmapfor deterministic iteration — also the honest fix for iteration-order-dependent tests (§D1),memchr); keys & hashing (the hasher ladder —FxHashMapfor integer keys,foldhashas the balanced string default,ahash;fnvmarked obsolete,gxhash/wyhashniche behind §A1 + target-CPU; all behind §B16's trust boundary;Borrow<str>lookup instead ofget(&key.to_string()),entry()instead ofcontains_key+insert); concurrent maps by access shape (the three architectural families: sharded locks —dashmap; per-bucket/lock-free reads —scc,papaya/flurrybehind §A1; read-copy snapshots —arc-swap,evmap/left-right with the consistency-model gate; ordered concurrent —crossbeam-skiplist/scc::TreeIndex; plus the "globalMutex<HashMap>→ first ask §A2/§E4's design question" row). Calibration built in: every row 🟡 under §E6, new crates via §A1, §B2's guard-across-.awaitrule 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).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).skill/concurrency-and-state.md— §E4 gains a pointer to the catalog's concurrent-map selection table and hasher ladder.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'sBorrow<str>/entry()contracts; no numeric claims, per the Tier E precedent.
§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).
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.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).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).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 thatcargo testgreen ≠ correct.README.md— status block for v0.4.4.
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).
skill/security.md— §C2 path-traversal recipe gains a TOCTOU caveat (CWE-367). The existing recipe (rejectComponent::Prefix/RootDir/ParentDir; join;canonicalize+ re-verifystarts_with(base)) closes the static symlink case (a pre-existing link insidebasepointing out) — but step (3) is not race-free against an attacker who can swap a component between thecanonicalizeand the subsequentopen(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 +openis a TOCTOU, and the race-free answer is component-wiseopenat+O_NOFOLLOW(or Linuxopenat2(RESOLVE_BENEATH)) — in Rust, thecap-stdcrate.skill/concurrency-and-state.md— §B9 gainsstd::thread::scope(sync mirror of §B21). Wheretokio::spawndetaches 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 eachScopedJoinHandle::join()when cleanup must run regardless. 🟡.skill/data-and-types.md— §B16 gains ReDoS as the sibling of HashDoS (CWE-1333). Same "untrusted input × non-linear primitive" shape, different primitive. Theregexcrate is linear by construction (RE2-style automaton; why it lacks lookaround/backreferences); LLMs reach forfancy-regex/onig/pcre2to 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 theregexcrate; if a backtracking-only feature is genuinely needed, size-cap input and wrap the match in a hard timeout.
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+openunder attacker-mutable dir → §C2;std::thread::scopechild awaiting parent-controlled resource → §B9;fancy_regex::Regex::new/onig::Regex::new/pcre2on untrusted input → §B16).commands/rust-intel-cc/fix.md— three routing rows added (canonicalize+open TOCTOU → §C2;thread::scopehang / cleanup-skipped-by-panic → §B9; worker pinned at 100% CPU on regex match → §B16 ReDoS).docs/sources.md— new "Siblings of safe-looking primitives" subsection grounding §C2 TOCTOU (CWE-367 +openat/openat2/cap-std), §B9thread::scope(std docs: auto-join + panic propagation), §B16 ReDoS (regexcrate's untrusted-input section + CWE-1333). Documented-mechanism + observed-pattern; no numeric claims.docs/roadmap.md— new "Backlog — confirmed siblings, lower frequency" subsection holds thetokio::sync::Semaphorepermit-lifecycle sibling (drafted, grounded, frequency too low to ship inline; promote on incident or reviewer flag).README.md— recall-honesty note after the "every category is backed by a source" paragraph: per-rule grounding ≠ measured coverage; anexamples/regression corpus is the next infrastructure step (roadmap §4). Status block for v0.4.3.
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.
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 withErrbut never exiting — livelocks on a persistent failure. REQUIRED: release leadership andreturnon a write/sync error so a fresh attempt re-enters from clean state; the leadership flag is released on every exit path, error included. 🟡.skill/testing.md— §D4 Filtering test-runner output throughgrep/headhides hangs: the filter silently dropsSLOW/TIMEOUTlines (the only lines naming a hanging test) and, withoutset -o pipefail, masks the runner's non-zero exit so a deadlock reads as green. Thepipefailfoot-gun is generic; the| grep-on-every-test-run reflex is the agentic-Rust angle. BANNED: gating "passed" on a filtered live pipe withoutpipefail; raising a timeout to silenceSLOW/TIMEOUT. REQUIRED: gate on the runner directly or tee-to-file-then-grep-the-file; root-cause everySLOW/TIMEOUTas a deadlock. 🟡.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>.exeat run start. 🟡.
skill/async.md— §B2 extended: adashmap::DashMap/scc::HashMapRef/RefMutheld across.awaitis a synchronous per-shardRwLockinvisible toclippy::await_holding_lock; holding it across an init.awaitdeadlocks the shard. REQUIRED for async lazy-init in a concurrent map: storeArc<OnceCell<T>>, clone out of the map, drop the shard guard before awaiting the initializer. Cross-note added at §B13'sentry().or_insert_with()endorsement ("synchronous only — see §B2").skill/async.md— §B21 extended: a spawned periodic task (timer-driven flush/reaper/sweeper) must hold aWeak<T>, not a strongArc<T>, to its owner and exit onupgrade() == None— a strong clone inverts RAII. Pair with a..._exits_on_droptest.skill/testing.md— §D1 extended: forinterval-driven background tasks understart_paused,advance(period)once per tick needed (the first tick fires immediately) andyield_now().awaitbetween advances so the spawned task runs between virtual-time steps.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).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).docs/sources.md— new "Concurrency liveness & test-harness honesty" subsection grounding §B3a (circuit-breaker / bounded retry — Fowler, AWS Builders' Library), §D4 (pipefailBash semantics +cargo nextestslow/timeout reporting), and §D5 (MSVCLNK1104+ Windows image locking). Documented-mechanism + observed-pattern, no numeric claims — on the Tier E/F precedent.
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.
skill/semantics-and-conformance.md— new (10th) theme module holding Tier F:- §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. - §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.
- §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. - §F4 Round-trip obligations for inverse pairs —
encode/decode,parse/Display,encrypt/decryptwritten together but never proven inverse over the domain; oneproptestround-trip property per pair, shipped in the same change; knows the difference betweendecode(encode(x)) == xand the canonical-form law. 🟡.
- §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;
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?skill/testing.md— §D3 Test/prod divergence:cargo testruns 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.)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).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.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-mutantsfor §D1a; CWE-404/772/400 + slowloris-class and the std/tokioOk(0)-EOF contracts for §F3; Cargo Book profile defaults +loomfor §D3; §F2 marked definitional. CRUST-Bench "Used in" extended with the external-oracle premise (§F1/§D1a).
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.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.skill/SKILL.md— the fan-out section's theme enumeration now includes semantics/conformance (was missing the 10th theme).skill/semantics-and-conformance.md— header tier-line aligned to the other modules' template ("Derived from SKILL.md → Enforcement tiers (canonical)").README.md— six tiers + Tier F row in the spec-architecture table; Verify range §A1–§F4.
Fan-out audit workflow + module header enrichment for agent audit ergonomics. No category changes (still 51).
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-auditreport format with a Post-flight 🔴-summary. Closes G1 (trigger-table slicing), G3 (canonical finding schema), G5 (artifact-vs-process semantics), G6 (async split). Launched viaWorkflow({scriptPath: '<skill-dir>/audit-project.workflow.js', args: {target, skillDir}}).- Module headers enriched (all 9 theme modules). Each module's blockquote header now carries a
Tiers in this moduleline (derived from SKILL.md's Enforcement tiers — the canonical source) and anAudit semanticsline (🔴/🟡/🟢 meaning + artifact-vs-process rule). Closes G2 (tier badges) and G4 (dangling operational references).
skill/SKILL.md— "Running a full pass" section now points to the shippedaudit-project.workflow.jsinstead of telling the reader to "write the equivalent fan-out".dev/review-modules.workflow.js(maintainer self-review) reference preserved.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.- Installers (
rust-cc-install.sh,rust-cc-install.ps1) now copyaudit-project.workflow.jsalongside the*.mdmodules.
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.
SKILL.mdversion pins —clippy::await_holding_lockgroup history. Previously claimed "warn-by-default (suspicious group) since clippy 1.45". Corrected: introduced in 1.45 (correctness, deny), downgraded topedantic(allow) around 1.50, promoted tosuspicious(warn-by-default) only in Rust 1.61 — so on a 1.50–1.60 toolchain the barecargo clippydoes not emit it and the explicit-Wis required. The present-day "bare clippy emits it" claim was already right; only the dating was wrong.SKILL.mdMSRV floor1.84→1.85. Edition 2024 (which the spec targets) stabilized in Rust 1.85; a crate withedition = "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. TheVec::into_raw_partspin's "MSRV floor is 1.84" line updated to match.- §B4a (
drop-and-raii.md) —never_type_fallback_flowing_into_unsafedating. 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. - §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.
- §B9 (
concurrency-and-state.md) — note thatparking_lot::deadlock::check_deadlock()requires thedeadlock_detectioncargo feature (the module is absent without it). - §B12 (
security.md) — flagged thatrust-crypto(in the API-hallucination example list) is itself unmaintained since 2016 (RUSTSEC-2022-0011) and must never be proposed; pointed to the maintainedRustCryptocrates. - §B2 / §B15a (
async.md) — trimmed two internal duplications (theawait_holding_lockblind-spot list; the RPITIT-vs-AFIT desugar explanation) to single statements with back-references.
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).
- §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. - §B18a — variance &
PhantomDatasoundness in raw-pointer wrappers (unsafe-and-ffi.md, 🔴). Wrong/absentPhantomDataon a hand-written*const T/*mut T/NonNull<T>type silently picks the wrong variance (covariance where invariance is needed → use-after-free with nounsafeat the call site), drop-check, or auto-traits. The hole is in the type declaration, not anyunsafeblock, so a per-block// SAFETY:(§B5) cannot catch it — hence its own 🔴 check, withPhantomData-selection guidance and acompile_fail-doctest recommendation.
- §B4 expanded (
drop-and-raii.md) with three drop hazards: (1) the memory-vs-resource fork at process exit — memory-onlyDrop(a huge tree/map/arena) is wasted work at shutdown and may be skipped viamem::forget/process::exit, while resource-cleanupDropmust still run (resolves the apparent tension with the existingprocess::exit-skips-Drop ban); (2) recursive auto-Dropoverflows the stack on deep self-owning structures (the §B7 DoS shape via the destructor) — write an iterativeDrop; (3) the drop-order shutdown deadlock (aJoinHandlejoined before itsSendercloses →join()blocks forever) made concrete with the field/local drop-order rule and fix. - §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. 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).docs/sources.md— added normative references (the Rustonomicon variance/PhantomData/dropck chapters, the Cargo Book SemVer chapter, the Rust Reference destructors page, thefrom_utf8/TryFromBytes/Podconstructors) plus the Microsoft RustTraining book for provenance.
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.
- 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-filerust-intel.mdis retired (kept in git history underv0.3.0); cross-references between modules (e.g.§B22 → §B4) are navigational by design. SKILL.mdgained acategory→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.- Installers now copy
skill/*.md→<target>/skills/rust-intel/(the single-file reference is no longer installed); a prior monolithicSKILL.mdis swept like any prior layout.install.sh/install.ps1updated and prove-tested;uninstall.*unchanged (already removes the whole directory). - Docs —
README.md,commands/README.md,docs/roadmap.md,docs/sources.mdupdated for the modular layout.
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.
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.
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.
- §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. - §B18 —
NonNull<T>removed as a phantomSend/Syncremedy (it is!Send/!Synclike the raw pointer and closes no race); the real fix (Arc<Mutex<RawHandle>>, or a cited happens-before viajoin) is now the only one offered. - §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). - §B5 —
mem::uninitializeddeprecation date corrected October → November 2019.
- §B4 — panic-in-
Dropnow namesstd::thread::panicking()as the primary guard (skip the fallible work while already unwinding);catch_unwinddemoted to the secondary, non-panicking-path guard. - §C9 — the
spawn_blockingspan-restore fix now notes thattokio::task_local!values do not cross thespawn_blockingboundary, so request context (tenant/auth/request-id) must be captured explicitly before the call — otherwise the closure reads whatever last touched that pool thread. - §B13 — the "insert if absent" remedy split into a sync form (
entry+&mut-borrow onstd::HashMap— the false "bucket lock" claim removed — or aDashMapshard lock) and an async form (OnceCell-per-key), the latter now primary for the lazy-cache example the category opens with. - §B16 — the BANNED manual-
PartialEq-without-Hashbullet scoped to types that actually implement/deriveHashor are used as keys (a never-hashed manualPartialEqis sound). - §B7 —
Box::<[u8]>::new_uninit_sliceflagged as UB on the partial-read pattern (areadreturningn < Nleaves an uninit tail that must never be sliced);vec![0u8; N]remains the safe form for partial-read buffers. - §C2 — the
Path::jointraversal guard rewritten as a sequence (reject a leadingPrefix/RootDirand any..; join; thencanonicalize+starts_with(base)only on the read path, to defeat symlink escape) rather than "reject..or canonicalize".
- §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). - §B12 — the secret-field-name
Debugtrigger scoped by role: a lexerToken, a map/cachekey, a deterministic-PRNGseedare not secrets — require a second crypto/auth signal before redacting (mirrors the §B24"HS256"carve-out). - §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. - §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. - trigger table / version pins — the "trait object" row now reads §B15a AFIT/RPITIT with the "not
dyn-compatible withoutasync-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".
- §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. - §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. - §B7 — attacker-controlled allocation size:
Vec::with_capacity(attacker_n)/vec![_; n]→ OOM/abort DoS; clamp before allocating and preferRead::take(limit). Plus phrase + code-pattern triggers. - §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))]. - §A1 —
cargo-vet(human-audit attestations) added to the supply-chain defenses alongsidecargo-deny/cargo-audit.
Shipped as part of release 0.3.0 (2026-05-29).
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.
- §B26 —
dbg!removed from the "compiled out in release" list:debug_assert!/debug_assert_eq!are stripped, butdbg!evaluates and prints in release too (a forgottendbg!leaks to stderr) — now noted as the converse trap. - §B19 —
mem::replacecorrected: it leaves the passed-in value, not aDefault(onlymem::take/Option::takeleave aDefault); the data-loss hazard is unchanged. - README — spec-architecture table Tier B range
§B1–§B28→§B1–§B29(missed when §B29 landed).
- §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 bycargo-deny/cargo-audit/committedCargo.lock/--locked, not by hand); the "code you are about to run" aphorism dropped. - §B29 — the
Vec::dedupBANNED bullet reframed from "on an unsorted collection" (hard to prove locally) to "nosort/grouping visibly preceding it"; the chunk-size trigger now notes literal sizes are fine. - §B16 — the float-sort bullet gained a provably-non-
NaNexception (NotNan/OrderedFloat/Duration/validated). - §C9 — log-injection scoped to plain-text/terminal sinks (a structured/JSON sink is largely immune).
Shipped as part of release 0.3.0 (2026-05-29).
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.
- §B29 — Iterator and slice adapter traps (new Tier B category; count 50 → 51).
ziptruncates to the shorter side (silent data loss);Vec::dedupremoves only consecutive duplicates (not a set);chunks(0)/windows(0)/step_by(0)panic;collectinto aHashMap/HashSetsilently overwrites duplicate keys (last wins). The highest-frequency LLM surface, previously under-covered. - §A1 — build-time supply-chain vector. A dependency's
build.rsand proc-macros execute arbitrary code atcargo build(before any runtime guard), reading credentials/keys/CI secrets; plus-↔_typosquats and dependency confusion. Defenses: pin + commitCargo.lock, auditbuild.rs/proc-macro deps,cargo-deny/cargo-audit,--locked/vendored builds. - Within-category bullets: §B4a (edition-2024
impl Traitlifetime capture /+ use<>), §B16 (partial_cmp().unwrap()NaN-panic in float sort →total_cmp; inconsistent comparator now panics), §B19 (mem::take/replace/Option::takeleave aDefaulton early-return/?/panic), §B26 (debug_assert!/dbg!compiled out in release), §C4 (BufWriter/BufReaderdrop-flush discardsio::Result), §C9 (log/ANSI/control-char injection from untrusted input), §B14 (long sync step insideFuturesUnordered/buffer_unorderedburies siblings → spurious timeout / semaphore self-deadlock).
- 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). - Calibration (anti-dogmatism): §A2/§B2
Rc/RefCell"forbidden" narrowed (legitimate underspawn_local/LocalSetand in synchronous single-threaded code; cross-thread misuse is already a compiler error); §B24 trigger narrowed to secret operands (no longer flagsalgo == "HS256") plus a scope note; Post-flight drops the noisyclippy::expect_usedand annotatesunwrap_usedas hand-triaged; §B26 overflow-checks flagged as a binary-crate lever (libraries use per-sitechecked_*) with softened tone; §B5 strict-provenance scoped to genuine address↔integer round-trips (not a blanket cast replacement); §B3 titleTHE BIG ONE→invisible in signatures; §B10 OOM phrasing softened.
- §C2 — corrected the UNC-path claim:
\\server\shareparses as[Component::Prefix, Component::RootDir](sohas_root()/is_absolute()aretrue, notfalse); the first-componentPrefix/RootDirguard is unchanged and correct. - 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_decimaldownloads~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".
Shipped as part of release 0.3.0 (2026-05-29).
Discipline hardening — vacuous-test ban, workspace boundaries, benchmark-as-guard (bullets within existing categories; count stays 50)
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.
- §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. - §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. - §E6 — benchmark as regression-guard. Lock a measured win with a
criterionCI 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".
- 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).
Shipped as part of release 0.3.0 (2026-05-29).
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.
- 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.
- §E1 — Serialism that need not exist. Independent
.awaits run sequentially →join!/try_join!/buffer_unordered/JoinSet; CPU-bound work on the async runtime →spawn_blocking/rayon. - §E2 — Allocation that need not happen. Reflexive
clone/collect/format!;Vec::with_capacityfor known sizes;Cow/&strover ownedString; reuse buffers;bytesfor shared/zero-copy slices. - §E3 — Complexity that compounds. Accidental O(n²) (the one always-fix case); the wrong container (
HashSet/VecDeque/SmallVec/BTreeMap/phfchosen by access pattern). - §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 (fastFxHashMap/foldhash/ahashfor trusted keys, DoS-resistant default for untrusted — see §B16); false sharing →CachePadded. - §E5 — Work already done.
Regex::new/ parsing repeated per call →LazyLock/OnceLock; unbuffered I/O →BufReader/BufWriter; reuse scratch buffers; lazytracingevaluation;dyndispatch in a hot path → generics / enum. - §E6 — Measure before you spend. Profile-first discipline:
cargo flamegraph/perf,dhat/heaptrack,tokio-console,criterion. §E3 and theclippy::perf-obvious wins are proactive; the rest is profile-gated — do not optimize on a guess.
- 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).
- Front-matter
descriptionextended so the skill matches performance/scale queries (systemic-cost / latency / allocation / contention), without disturbing the existing correctness-hazard list. - Both trigger tables route performance symptoms to §E*: the phrase table (slow at scale, two sequential
.awaits, too many allocations, lock contention, "faster HashMap", recompilesRegex) and the code-pattern table map onto §E1–§E6, with the hasher row split by trust boundary (§E4 + §B16). 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.commands/rust-intel-cc/fix.md— routing table gains performance rows (slow/high-latency-at-scale, sequential.awaits, 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).docs/roadmap.mdanddocs/sources.md— Tier E logged as shipped content; normative performance sources added under §E* (see the sources.md entry in this batch).
This is a MINOR change by SemVer (new categories) — shipped in 0.3.0.
Shipped as part of release 0.3.0 (2026-05-29).
Corrective pass (eighth review pass — external review: one verified bug, a 🔴-propagation gap, three undisclosed-precondition gaps, meta-layer recalibration)
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.
- §C2 — verified factual error. The path-traversal guard for
Path::joinwashas_root(), which lets a bare\\server\sharethrough (on Windows that is aComponent::Prefixwith noRootDir→has_root() == false), even thoughjoindiscards the base anyway. The primary guard is now: reject a leadingComponent::Prefix/RootDircomponent. - §B3 —
write_all_bufreclassified from "cancel-safe-with-caveat" to cancel-safe (on cancellation the buffer is partially advanced — resume from the remainder). - 🔴 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 inaudit.mdgained a §B13 line;commands/rust-intel-cc/fix.mdgained a routing row for the atomic-ordering (ARM) symptom → §B13. - §B13 trigger-gap closed — new row in the code-pattern table for
Ordering::Relaxed-publication. - 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.
- Version pins —
extern "C-unwind"marked stable 1.71 (distinct from 1.81 = abort-by-default for plainextern "C");clippy::await_holding_lockmarked warn-by-default (suspiciousgroup, since 1.45) — a manual-Wis redundant. 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).
- §B4 + §B25 —
catch_unwindis inert underpanic = "abort"(it catches nothing, and the guard code behind it never runs) and requiresUnwindSafe; this was an unstated precondition of the spec's own recommendations. - §C9 — a
thread_local!read after.awaitreads another worker's value or the default on a multi-thread runtime (the task can migrate between threads) → usetokio::task_local!. - §B11 —
spawn_blockingpool starvation (default 512). - §B15c —
block_in_placepanics on a current-thread runtime. - §B16 — HashDoS: for keys from untrusted input, do not swap the default
RandomStatefor a fixed-seedFxHashMap/fnv/ahash. - §B20 —
#[serde(flatten)]silently disablesdeny_unknown_fieldsand breaksu128/ non-string map keys. - §B21 — a panic in a detached task (dropped
JoinHandle) is silently swallowed. - §B26 —
saturating_subonusize(lengths / cursors) masks a logic bug. - §B5 — strict-provenance list extended (
map_addr/dangling/without_provenance, stable 1.84);Vec::into_raw_partspreferred on ≥ 1.93. - §B4a — let-chains have spread to
if letmatch-guards (stable 1.95); the one silent-runtime never-type-fallback case is deny-by-default (edition 2024 / 1.92) and out of focus. - Version pins — recent tokio APIs:
biasedinjoin!/try_join!(1.46),SetOnce(1.47), the coop module (1.44).
- The "Principle" section was condensed (~13 → 4 lines).
- Operating mode step 3 (text-first for traits) scoped to the public API of a published library.
- Operating mode step 5 (
/// cancel-safe:) narrowed to functions actually in a cancellation context. - Enforcement tiers: narrowing
asstays 🟢 but with a trust-boundary caveat; the canonical "inline-flag policy" is stated once, in 🟡. - §A2 — "
Box<T>for a smallSizedis almost always wrong" softened (recursion /Pin/dynare exceptions). - §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".
- §B12 — "mandatory human cryptographer review" reserved for custom / protocol-level crypto.
- §B16 — the inline flag for a manual
PartialEq/Ordnarrowed to non-trivial contracts. - §C4 — algorithmic O(n²) (always fix) separated from micro-allocations (profile-gated).
- Trigger table: three duplicated phrase rows merged. Tier A intro: the residual compile-only list collapsed to a pointer.
commands/rust-intel-cc/audit.md,commands/rust-intel-cc/fix.md,README.md,docs/roadmap.md— see above.
Shipped as part of release 0.3.0 (2026-05-29).
Final fix pass (seventh review pass — resolve refactor seams, close one currency gap, then freeze content)
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.
- §B26 under-flagging regression. The refactor had made
overflow-checks = truethe primary defense and gated manualchecked_*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 whenoverflow-checks = trueis not guaranteed in the build profile. Routine boundedi + 1/(lo + hi) / 2remain explicitly out (no return to over-flagging). - 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.
- 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.
- §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.
- §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 theelseblock (if_let_rescope; the canonical case is anRwLockdeadlock 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. (RPITuse<>capture,unsafe extern,genblocks, 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.)
- 🔴 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.
- §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.
- §B13 atomic
Relaxed-publish promoted to 🔴. ARelaxedstore/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.
- §A2/§B2 — note that a
LazyLock/OnceLockinit closure that panics poisons the cell (every later access panics, not just the first); don't panic in lazy init. - §B3 — the
/// cancel-safe:annotation requirement aligned with the softened Operating mode step 5 ("every non-trivial async fn"). - §B8 —
async ||closures (stable 1.85) added to the list of future-producing forms that are inert until polled. - §C1 — fixed a dangling "§B5/T4" cross-reference to "§B5".
commands/rust-intel-cc/audit.md— the report-format example aligned to the 🔴-only post-flight (it had still listedunwrap/Arc<Mutex<_>>/.lock().unwrap()counts as mandatory summary lines).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.
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.
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.
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.
- "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, droppedJoinHandle), 🟢 delegate to clippy (narrowingas,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.
- 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 fnlifetimes,RefCell::borrow_mut,Path::join,read/write, …) remain as rules in their category bodies — only the noisy re-surfacing mandate was removed. - §B26 over-flagging softened.
overflow-checks = true(release profile) is now the primary defense; manualchecked_*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; routinei + 1/(lo + hi) / 2are explicitly out. - Operating mode mandates narrowed.
/// cancel-safe:annotation is required only for anasync fnwith more than one side-effecting.awaitor one documented to run underselect!/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. - 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),
unsafewith 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. - §B15 split into labeled subsections §B15a (AFIT vs RPITIT) / §B15b (Pin, Waker) / §B15c (sync↔async bridging) / §B15d (
StreamvsIterator) / §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. - 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.mdfor the empirical figures.
- §B26 —
clippy::arithmetic_side_effectsis in therestrictiongroup, notpedantic. 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 enablearithmetic_side_effectsexplicitly. Reworded, and the flag added to the post-flight clippy command. - §C2 —
Path::joinguard corrected for Windows.is_absolute()is the wrong check:joindiscards the base onhas_root(), and/etc/passwdor\\server\\sharegiveis_absolute() == falsewhile still dropping the base. Now recommendshas_root()(or rejecting a leadingRootDir/Prefixcomponent). - §B26 —
overflow-checks = truehot-path caveat. Noted the global runtime cost (~5–15%, inhibits autovectorization); for numeric hot paths, prefer targetedchecked_*at the few real overflow sites over the global flag. - §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.
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.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 ("oneSKILL.md; consolidate internally instead"); infrastructure (examples/corpus, CI link-checker) promoted to highest-value-next.
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.
- §B15 —
watch::Receiverchanged()claim corrected. v0.4.0 saidchanged().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, sochanged().awaitis pending until the nextsend— it does not fire for the initial value.borrow()-returns-initial is correct and kept; the loop example now usesborrow_and_update(). - §B28 —
ßcase-mapping example was backwards. The length-changing exampleß → sswas attributed to the wrong direction:ßis unchanged byto_lowercase()and becomesSSunderto_uppercase(). Corrected toß → SSunderto_uppercase; the Turkishİ → i̇example correctly illustratesto_lowercase. - §B27 —
Instant::saturating_adddoes not exist on stable. The overflow bullet recommended it;Instanthaschecked_addandsaturating_duration_sincebut nosaturating_add(that is aDurationmethod). Reworded to split the two types.
- §B26 (renamed "Lossy numeric conversions and integer overflow") — integer overflow + div/rem-by-zero + index OOB. The headline addition: bare integer
+/-/*/pow/sumon untrusted or accumulating values panics in debug but silently wraps in release (overflow-checks = falseis the release default), socargo 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. Plusa / b/a % bpanic on a zero divisor (debug and release), andslice[i]/split_atpanic on an untrusted out-of-bounds index. REQUIRED:checked_*/saturating_*/wrapping_*,overflow-checks = truefor prod release builds,slice.get(..). - §C4 — partial
Read/Write. A singleread/writemay transfer fewer bytes than requested even without EOF (sockets, pipes); useread_exact/write_all/read_to_endor loop. - §C2 —
Path::joinwith an absolute segment.base.join(untrusted)silently discardsbaseif the segment is absolute — a path-traversal hazard; validate withPath::is_absolute/ reject../ canonicalize-and-check.
- 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).
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.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 boundarypanic / mid-character truncation).README.md— stale Layout comment forroadmap.md("Planned commands and category expansions" → "Roadmap: open directions and structural notes").
Shipped as part of release 0.3.0 (2026-05-29).
The post-compilation taxonomy is now near-saturated under the spec's scope. See 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.
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.
-
§B26. Lossy numeric conversions.
as-casts silently truncate, wrap, or saturate with no panic and no default warning (clippy::cast_possible_truncationis pedantic / off-by-default). Covers narrowing/sign-changing integer casts (u64 as u32,len() as u32), theusize-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_fromfor narrowing; explicit range checks before float→int. This is the backing rule for the long-orphanedas-cast line in the post-flight checklist. -
§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 returnResultfor exactly that reason. REQUIRED:Instant::now()for all durations/deadlines/benchmarks;SystemTimeonly for absolute timestamps; handle theError usesaturating_duration_since. -
§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-segmentationfor graphemes;eq_ignore_ascii_casefor protocol strings.
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:
- §A2 —
Box::leak(Box::new(...))for globals (leaks on every re-init path; useOnceLock/LazyLock, stable ≥ 1.80);RefCellwhereCellsuffices forCopy/replace-whole interiors (avoids the §B17BorrowMutErrorpanic surface). - §B4 —
mem::forget/ManuallyDropwithout a manual drop silently disables RAII (fd/connection/lock never released) — the §C5 reflexive-.clone()reflex applied toDrop. - §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. - §B14 —
FuturesUnordered/JoinSetgrown unbounded (same hazard as an unbounded channel), and an emptyFuturesUnorderedin aselect!arm returnsPoll::Ready(None)immediately → 100% CPU busy-loop. - §B15 —
watch::Receiver::borrow()returns the initial value before anysend, and the firstchanged().awaitreturns immediately; useborrow_and_update()to avoid re-processing. - §B16 —
sort_unstable*when the relative order of equal elements matters silently breaks a multi-key sort's secondary order; use stablesort/sort_by_keywhen the tie-break is load-bearing. - §B20 — deserializing a large integer (snowflake ID, ns timestamp,
u64> 2^53) into anf64field or viaValue::as_f64()silently loses precision (53-bit mantissa). - §C2 —
env::var("X").unwrap()panics both on a missing var and on a non-UTF8 value (common on Windows); usevar_os/ handleVarError::NotPresent. - §C4 —
Vec::remove(0)/insert(0, _)/containsin a loop is O(n²) (useVecDeque/swap_remove/HashSet);{:?}on&[u8]/Vec<u8>prints a decimal array, not hex (usehex::encodefor non-secret bytes). - §C9 — logging PII (email, name, phone, address, government ID, card, IP) through
Debug/tracingis a compliance leak (GDPR/PCI) distinct from §B12's crypto-secret coverage; classify and redact PII fields.
- §B15 —
Notifypattern wording corrected. v0.3.2's comment said.enable()"registers the waker"; per tokio's docsenable()does not register the taskWaker(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.
- 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,SystemTimeduration,&s[..]/len()-as-chars,Box::leak,mem::forget,FuturesUnordered,watch::channel,Vecfront-mutation,{:?}-on-bytes,sort_unstable*). - Post-flight checklist gains surface-able items for the new categories and bullets (narrowing casts,
SystemTime-for-duration, computed&s[..],Box::leak,mem::forget, unboundedFuturesUnordered,env::var().unwrap(),sort_unstable*,Vecfront-mutation, depth-unbounded recursion, PII-through-Debug). - Version pins — float→int saturating cast pinned to Rust 1.45;
LazyLockto 1.80 (alongsideOnceLock).
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.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.docs/sources.md— normative-source entries added for the three new categories (Rust Reference onas-cast semantics,std::timeon monotonic vs wall-clock,strUTF-8 docs).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 undereol=lf).
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.
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.
-
§B15 — the
Notifylost-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, aNotifiedfuture does not receive wakeups until it is polled or explicitly armed. The corrected pattern arms the waker withnotified.as_mut().enable();betweenpin!and the check, so anotify_one()landing between check and await is not lost. Variable renamedpermit→notified(it is aNotifiedfuture, not a semaphore permit). -
§B11 + Version pins —
tokio::task::coop::consume_budgetwas pinned to the wrong version. v0.3.1 claimed thecoop::path was stable since tokio 1.39.1. In fact the function is stable since 1.39.1 attokio::task::consume_budget; thetokio::task::coopmodule 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_budgetbelow 1.44,tokio::task::coop::consume_budgeton 1.44+). -
§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 hardE0119compile 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::Errorcollapses 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.
-
§B8 —
tokio::sync::oneshot::Receiverhas no.recv()method. The bullet's variable was namedrecv, falsely implying a.recv()call (whichmpsc::Receiverhas, butoneshot::Receiverdoes not — it is aFuture, awaited directly). Renamed torxand added a parenthetical noting the receiver is awaited directly. -
§B15 —
block_in_placewas 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.awaitinside it without aHandle. Reworded to distinguish it fromspawn_blockingand from a sync→async bridge. -
§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.
v0.3.1 added rules but no triggers for them, so the self-monitoring layer never surfaced them proactively. Added:
- 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. - Code-pattern triggers (8):
std::process::exitbelow a live guard → §B4;Arc::strong_count/Rc::strong_countin a conditional → §B13;assert_eq!with anf32/f64operand → §D1;notify.notified()→ §B15;#[derive(Debug)]on a struct with apassword/secret/token/key/seedfield → §B12;impl Dropwhose body can panic → §B4;tokio::time::interval(...)→ §B15;oneshot::channel()with the result discarded/.unwrap()-ed → §B8.
-
§B15 —
tokio::time::intervalfirst-tick semantics. The first.tick().awaitreturns immediately (at creation), not after one period; the defaultMissedTickBehavior::Burstfires missed ticks back-to-back to "catch up", producing a load spike. REQUIRED: discard the first tick or useinterval_at(Instant::now() + period, period), and setMissedTickBehavior::Delay/Skipexplicitly. -
§B13 — atomic memory ordering.
Ordering::Relaxedon 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. UseRelease/Acquire(orAcqRel/SeqCstfor RMW) when the atomic gates other memory;Relaxedonly for standalone counters; don't blanket-SeqCst; model-check withloom. -
§B14 —
broadcast::RecvError::Lagged(n)is data loss, not a transient error.Lagged(n)meansnmessages are gone forever and the receiver has skipped to the oldest still-buffered one; amatch { Err(Lagged(_)) => continue }loop recovers nothing and masks the loss. Log/metric the skipped count and decide explicitly whether dropping is acceptable. -
§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 tocargo 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 undercargo test's default parallelism.
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–§C11to§A1–§D2so Tier D is visible.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_jsonfidelity,watch::Receiver,FuturesUnordered,{:?}-on-bytes,CellvsRefCell) plus structural notes (possible §B15 split, section-length rebalancing).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.)
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.
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).
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.
-
§B23 —
tokio::sync::mpsc::Sender::sendis NOT cancel-safe inselect!. v0.3.0 text claimed it was; per tokio's own documentation, whensendis cancelled in aselect!arm, the message is dropped and lost. The two-stepSender::reserve().await→Permit::send(value)is the canonical cancel-safe pattern (reserve acquires capacity asynchronously and is cancel-safe; the synchronousPermit::sendcannot fail at that point). Section rewritten to remove the false claim and document the correct pattern. -
§B25 —
cargo expand --type-sizesdoes not exist. v0.3.0 text recommended this fictional invocation for FFI layout verification.cargo expandis a third-party macro-expansion plugin with no such flag. Replaced with the real nightly toolcargo +nightly rustc --lib -- -Zprint-type-sizesplus a stable-toolchain fallback usingstd::mem::size_of,std::mem::align_of, andstd::mem::offset_of!in a unit test asserted against expected C-side values. -
§B11 + Version pins —
tokio::task::consume_budgetpath is deprecated. The canonical location istokio::task::coop::consume_budget; the oldertokio::task::consume_budgetre-export is now#[deprecated]. Spec text and version pins updated. Stable since tokio 1.39.1 (1.39.0 was yanked). -
§B24 —
subtle::ConstantTimeEq::ct_eqreturnsChoice, notbool. v0.3.0 phrasing "x.ct_eq(&y).into()returnsbool" was technically correct but invited readers to writeif x.ct_eq(&y) { ... }(which does not compile). Reworded to be explicit:ct_eqreturnssubtle::Choiceand must be converted viabool::from(choice)orchoice.into(). Also flagged: never branch directly onChoice— the whole point is to keep the comparison branch-free until the explicit conversion. -
§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
DerefandDerefMut(C-DEREF). The traits should be used only for that purpose."
-
§B12 (Crypto) — Debug leakage, JWT
alg: none, AEAD nonce width, key zeroization. New BANNED bullets cover#[derive(Debug)]on structs withpassword/secret/token/api_key/private_key/seed/mnemonic/cookiefields (printed by{:?}in logs); JWT verification that acceptsalg: 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 coverszeroizediscipline (#[derive(Zeroize, ZeroizeOnDrop)]) for key material. -
§C2 (Error handling) —
Box<dyn Error>in libraries, ambiguous#[from]. New BANNED bullets coverResult<T, Box<dyn Error>>as the return type of anypub fnin a published library crate (callers can't match), andthiserror::Errorenums with two or more#[from]variants over interconvertible source types (the?operator's resolution becomes ambiguous). -
§D1 (Tests by luck) — floating-point exact equality. New BANNED bullet:
assert_eq!on computedf32/f64values flakes between debug/release, architectures, and compiler versions. Useapprox::assert_relative_eq!/assert_abs_diff_eq!or manual epsilon comparison. -
§B4 (Drop and RAII) —
process::exitskips Drop, panic-in-Drop. New BANNED bullets:std::process::exit(...)from code paths with stack-local guards (transactions, file handles, lock guards) —process::exitdoes not unwind;Drop::dropbody that can itself panic during a panic unwind (double-panic aborts the process). Cross-link added pointing to §B22 for the async cleanup constraint. -
§B8 (Silent task dropping) —
oneshotchannel drop cascades. New BANNED bullets:let _ = tx.send(value);on atokio::sync::oneshot::Sender(discarding theErr(value)when the receiver is gone makes the producer's work invisible), andrecv.await.unwrap()on aoneshot::Receiverwhen the producer can fail or be dropped. Cross-link added pointing to §B21 for the work-runs-but-can't-be-observed case. -
§B15 (Advanced async) —
Notifylost-wakeup, half-consumedStream,select! biased. Three new BANNED bullets:notify.notified().awaitwithout first checking the represented condition (the canonical fix is thenotified() → pin! → check → awaitfour-step); dropping a half-consumedStreamwithout explicit acknowledgement that buffered items are lost;tokio::select! { ... }withoutbiased;when arm priority matters (default per-poll pseudo-random can starve a low-priority arm). One REQUIRED bullet: usebiased;for deterministic left-to-right arm priority. -
§B13 (TOCTOU) —
Arccount races, HashMap iter order. New BANNED bullets:if Arc::strong_count(&arc) == 1 { ... }is a TOCTOU race — useArc::into_inner(arc)(returnsOption<T>) orArc::try_unwrap(arc). Restated that the same TOCTOU pattern viaHashMap::iter+HashMap::insertis broken. New REQUIRED bullet: for ordered iteration, useBTreeMapor collect-then-sort —HashMap::iterorder is randomized per-process and per-rehash, and tests that depend on it flake across machines.
- §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. - §B21 ↔ §B8 — opening of §B21 now distinguishes "future never polled" (§B8) from "work ran but you can't cancel/observe" (§B21).
- §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".
- §B23 ↔ §B3 — opening of §B23 now states explicitly that it is the
select!-specific application of §B3's general cancel-safety rule.
descriptionextended 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.
README.mdLayout —.gitattributesand.gitignorenow 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.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/§C9shipments and silently omitted the new tier.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..gitattributes— deduplication pass. Removed seven explicittext eol=lfrules for*.md,*.rs,*.toml,*.lock,*.json,*.yml,*.yamlsince 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.rust-intel.md— working-tree line endings renormalized to LF (the v0.3.0 commit landed withi/lf w/crlf, which would have re-triggered the CRLF warning on the next edit). Nowi/lf w/crlf attr/text=auto eol=lf— git no longer warns because the canonical eol is explicit.
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.
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.
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.
-
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_stdreflexivestd::*, self-referential structs,From/Intocycles, MSRV mismatch) are deliberately omitted — the compiler is sufficient, the LLM cannot ship them. This spec covers what survivesrustc,clippy, andcargo testand still breaks. The opening section, the front-matterdescription, and the README "What this is" / "Spec architecture" sections all reflect the new scope. -
§B3 —
AsyncWriteExt::write_bufcancel-safety corrected (technical error). v0.2.x text listedwrite_bufas cancel-UNSAFE; per tokio's documented cancel-safety contract,write_bufis cancel-safe (single-shot). The actually-unsafe variant iswrite_all_buf(safe-with-caveat: the buffer may be partially advanced) andwrite_all(unsafe). Text now distinguishes all three. -
§B8 —
tokio::spawn(async_fn())"future-of-future" claim removed (technical error). v0.2.x text asserted thattokio::spawn(async_fn())creates a future-of-future and spawns the outer wrapper, dropping the inner. That is wrong: anasync fnreturnsimpl Futuredirectly, andtokio::spawnpolls 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). -
§B9 —
tokio::sync::Mutex"detects deadlock undertokio-console" claim corrected (technical error).tokio-consoleprovides visibility (which task holds which lock, who is waiting), not detection. Deadlock detection isparking_lot::deadlock::check_deadlock()for sync sections or human review of documented lock-acquisition orders. Reworded accordingly. -
§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)). -
§B5 —
slice::align_toremoved from "safe abstractions" list (technical error).<[T]>::align_to::<U>isunsafe fn; v0.2.x had it in the safe-defaults list alongsidebytemuck::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 samePod-style invariants astransmuteand a// SAFETY:block. -
§B7 —
Box::new_uninit_slicenightly 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 tovec![0u8; N].into_boxed_slice()for zero-init-wasted scenarios, withassume_initflagged asunsafe. -
§B7 — stack-overflow threshold rationale clarified. The v0.2.x
N * size_of::<T>() > 4096line conflated page size with stack budget. Replaced with the real numbers — 8 MiB on Linux main thread, 2 MiB onstd::thread::spawn, ~2 MiB on tokio tasks — and the ~64 KiB practical rule of thumb. TheBox::new([0u8; N])placement trap (array built on stack before being moved to heap) is now called out explicitly. -
§B5 —
Vec::into_raw_partspinned to Rust 1.93. Verified via the stdlib docs: stable since 1.93.0. The spec's MSRV is 1.84, so theManuallyDrop<Vec<T>>+ manual(ptr, len, cap)decomposition (stable since 1.0) is the default; theVec::into_raw_partsconvenience is opt-in on a bumped MSRV. The version pins section reflects this. -
§B5 —
mem::uninitialized/mem::zeroedpromoted to BANNED list. Previously surfaced inside a REQUIRED bullet aboutMaybeUninitdiscipline; now each has its own BANNED line spelling out the UB conditions (mem::uninitializeddeprecated since 1.39 and UB for any type with invariants;mem::zeroedUB forbool/&T/Box<T>/NonZero*/restricted-discriminant enums/#[repr(transparent)]wrappers over those). The compiler does not stop either call. -
§A1 — repositioned as "stale APIs and slopsquatting" (scope reframe). Pure
E0599hallucinations 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). -
§A3 — repositioned as "
pubas a hammer forE0603" (scope reframe). Now framed as "LLM reflexively makes thingspubto 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.) -
§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 gainsMaybeUninitdiscipline, strict provenance API rules (Rust 2024+),slice::from_raw_partsinvariant list. §B11 gainstokio::task::consume_budget. §B12 cross-links to the new §B24 for constant-time comparison. §B15 gainsStreamvsIteratorfailure modes. §C1 gains#[repr(transparent)]zero-cost newtype guidance. -
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 fnwithMutex<...>,Rc<RefCell<>>,unsafe impl Send/Sync, untrackedJoinHandle,impl Dropwith.await,impl Derefon non-pointer wrappers,#[serde(untagged)], untagged TOCTOU patterns, raw-bytes comparisons in security contexts,select!with arm side effects,tokio::spawnunder active spans,mem::transmute/ptr::read/slice::from_raw_parts, large stack arrays. -
Post-flight checklist — extended. New surface-able items: manual
Send/Syncimpl (§B18),#[serde(untagged)]enums and string-keyed JSON (§B20), untrackedJoinHandles (§B21),impl Dropwith async-looking work (§B22),==on secrets (§B24), everyextern "C" fnandBox::into_raw/Box::from_raw/Vec::into_raw_parts/Vec::from_raw_partspair (§B25), unbounded channels by runtime (§C8), spawn without.in_current_span()under instrumented contexts (§C9), default features that pull heavy deps (§C10),impl Derefon non-pointer wrappers (§C11),thread::sleepin tests (§D1),#[should_panic]withoutexpected(§D1). -
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."
Tier B — Silent correctness bugs. Ten new categories.
- §B16. Equality and hashing contracts. Manual
PartialEqwithout matchingHash, manualPartialOrdwithout total-orderOrd,f64/f32keys withoutOrderedFloat/NotNan. Failure mode:HashMapsilently loses keys,BTreeMapbehaves nondeterministically. Compiles, often passes thin tests, corrupts data at contention. - §B17.
RefCell/Mutexruntime borrow panics.Rc<RefCell<T>>in callback/traversal chains, reentrantborrow_mut(), undocumented borrow-disjointness invariants. Compiles, tests pass at low concurrency, production panics. REQUIRED:try_borrow_mut()withBorrowMutErrorhandling for tree traversals. - §B18. Manual
unsafe impl Send/unsafe impl Sync. Reflexiveunsafe impl Sendto silencetokio::spawnbound errors. Now requires explicit// SAFETY:citing the synchronization invariant; impls without one are BANNED. - §B19. Iterator invalidation through indirection. Borrow checker catches
Vecinvalidation at compile time; it does not catch invalidation throughRefCell<Vec<T>>,unsafe, orfor i in 0..vec.len()loops that mutatevec.len()mid-loop. Now covered. - §B20.
serdefield-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. - §B21.
JoinHandlesemantics: drop ≠ abort. Dropping atokio::task::JoinHandledetaches the task; it does not abort it. Spawning fire-and-forget without explicit// fire-and-forget: detached by designannotation is now BANNED.JoinSetrecommended for joinable fan-in. - §B22.
async Dropis not real.impl Dropcallingtokio::spawn-ing an async cleanup is fire-and-forget and may not run before runtime shutdown;block_oninsideDropre-enters the runtime and deadlocks. Resources requiring async cleanup must expose an explicitasync fn close(self). - §B23.
select!arm side effects under cancellation. Side effects (DB writes, channel sends, file flushes) inside atokio::select!arm may not be observed if another branch wins. Each arm must be cancel-safe or guarded; side effects belong after theselect!returns on the winning branch. - §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::ConstantTimeEqorconstant_time_eqcrate. Cross-linked from §B12. - §B25. Panic and ownership across
extern "C"ABI. Panics escapingextern "C"boundaries (UB pre-1.81, process abort since),Box/Vec/String/Rc/Arcpassed directly through FFI (no stable ABI), allocator-mismatchedBox::from_raw,cap-mismatchedVec::from_raw_parts, missing paired free functions, gratuitous#[no_mangle]. REQUIRED:catch_unwindwrapping, pairedextern "C" fn rust_drop_T(p: *mut T),ManuallyDrop<Vec<T>>orVec::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).
Tier C — Architecture and ergonomics. Four new categories.
- §C8. Channel-and-runtime mismatch.
std::sync::mpscin async (blocks executor),tokio::sync::mpscfor MPMC (only first receiver gets messages),crossbeam::channelin async (await around recv blocks the worker). Now mapped explicitly. - §C9.
tracingspan leakage acrosstokio::spawn. Spawning without.in_current_span()(requirestracing::Instrumentin scope) loses span context.spawn_blockingrequires explicitspan.enter()inside the closure. - §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-depsin CI now recommended. - §C11.
Derefpolymorphism antipattern.impl Deref<Target = Inner> for Wrapperfor inheritance-style composition. Rust API Guidelines C-DEREF rule cited; explicit-accessor pattern (fn user(&self) -> &User) given as the right shape.
Tier D — Testing and CI gaps. New tier. Two categories.
- §D1. Tests that pass by luck.
thread::sleepwaiting for async work (flaky),#[should_panic]withoutexpected = "..."(any panic passes including in test setup), tests asserting absence of panic instead of postconditions. REQUIRED:tokio::time::pause/advance, explicitNotify/oneshotsynchronization,expectedsubstring pinning. - §D2. Integration vs unit test placement drift.
#[cfg(test)] mod testsreferencing private items that are later split into siblings; integration tests intests/depending onpub(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.
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).
- 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 (
pubas 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. - 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.
README.mdStatus 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).docs/roadmap.mdfully refreshed: all/rust-{audit,fix,plan}references corrected to/rust-cc-*, broken relative paths tocommands/rust-{audit,fix,plan}.mdcorrected tocommands/rust-intel-cc/{audit,fix,plan}.md. Categories that shipped into the spec marked✅ shipped in v0.3.0. Out-of-scope note added.docs/sources.md— the single/rust-fixreference 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.commands/rust-intel-cc/{audit,fix,plan}.md— references to "rust-intel.md" reworded to "therust-intelskill" (decouples command files from the on-disk filename, which isSKILL.mdafter install).audit.mdexample header updated fromrust-audit reporttorust-cc-audit report.26 categoriesreferences removed.fix.mdrouting table E0277/E0308 row updated to point atout-of-scope (compile-only)with a check for §A2/§A3/§C5 residue from the reflexive fix.commands/README.md— "26 categories" wording dropped.- Line endings.
.batand.ps1files were stored in the working tree as LF despite.gitattributesdeclaringeol=crlf. Working tree now matches the attribute. (Index was correct; only the working copy needed renormalization.) - Windows symlink note added to README:
--symlinkis bash-only; PowerShell and cmd.exe installers always copy.
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.
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).
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.
- Install and uninstall scripts gained the
rust-cc-prefix. Generic names likeinstall.bat/install.share a footgun: if the repo lives on the user'sPATH, or if multiple tooling repos share a common convention, an unprefixedinstallshadows other things in the system. Renamed all six scripts to be project-specific:install.sh→rust-cc-install.shinstall.ps1→rust-cc-install.ps1install.bat→rust-cc-install.batuninstall.sh→rust-cc-uninstall.shuninstall.ps1→rust-cc-uninstall.ps1uninstall.bat→rust-cc-uninstall.batInternal references (.bat→ sibling.ps1,--helptext) and external docs (README,commands/README.md) updated to match.
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.
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.
- Slash commands flattened from
/rust-intel-cc:*to/rust-cc-*. v0.2.0 misread the original intent: the repo's nestedcommands/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:- Repo source (unchanged from v0.2.0):
commands/rust-intel-cc/{audit,fix,plan}.md. - Installed target (new):
<claude>/commands/rust-cc-{audit,fix,plan}.md— flat, with arust-cc-prefix, no subdirectory. - Slash commands (new):
/rust-intel-cc:audit→/rust-cc-audit/rust-intel-cc:fix→/rust-cc-fix/rust-intel-cc:plan→/rust-cc-planThe 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).
- Repo source (unchanged from v0.2.0):
- Installers and uninstallers sweep every prior layout before copying:
- v0.2.1+ flat-with-prefix (
rust-cc-{audit,fix,plan}.md) - v0.2.0 namespace dir (
rust-intel-cc/) - v0.1.x legacy flat-no-prefix (
{rust-audit,rust-fix,rust-plan,rust-intel}.md)
- v0.2.1+ flat-with-prefix (
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.
Same as v0.2.0's migration — re-running the installer sweeps the old /rust-audit, /rust-fix, /rust-plan automatically.
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.
- Slash commands moved into the
rust-intel-ccnamespace. The three top-level commands are gone; they now live undercommands/rust-intel-cc/and are invoked with the colon-namespace syntax Claude Code uses for nested commands:/rust-audit→/rust-intel-cc:audit/rust-fix→/rust-intel-cc:fix/rust-plan→/rust-intel-cc:planRationale: a singlerust-intel-ccumbrella is easier to remember, easier to grep, and isolates the three sub-commands into one Claude Code namespace instead of three top-level slots.
- 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_DIRenv 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. - 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 earlycommands/rust-intel.md) and the entirecommands/rust-intel-cc/directory before copying. Re-running the installer cleanly migrates from any previous version.
install.bat/uninstall.bat— thin wrappers around the corresponding.ps1scripts for users incmd.exe. Pass-through arguments work as expected (install.bat -User, etc.)..gitattributesnow pins*.batto CRLF (cmd.exe will not parse LF-terminated batch files reliably)./.claude/added to.gitignoreso running the installer from the repo root does not pollute the working tree.
For anyone upgrading from v0.1.x:
- Pull the new repo state.
- Re-run the installer (
./install.sh,.\install.ps1, orinstall.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. - If you previously installed to
~/.claude/(the v0.1.x default), pass--user/-Useron the new install — otherwise the installer will treat your current directory as the install target. - Update any tooling or notes that invoked the old slash commands to use the new namespaced names.
The skill itself activates the same way as before. Only the slash-command names changed.
Tooling-only patch. No changes to rust-intel.md (the skill itself); no new categories.
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 legacyrust-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.- README "Uninstall" section documenting both scripts.
install.sh/install.ps1also remove the legacycommands/rust-intel.mdbefore 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 stalecommands/rust-intel.mdleft 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.
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.
- §B15 AFIT vs RPITIT — terminology rewrite (technical correction). The previous text described
fn bar(&self) -> impl Future + Sendas "native AFIT with a Send bound via RPITIT". This conflates two distinct syntactic forms: AFIT isasync fn bar(&self) -> T, RPITIT isfn 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). - §B11 —
yield_nowno longer presented as alternative tospawn_blockingfor CPU-bound work (technical correction).yield_nowonly schedules other tasks already on the same worker thread; the worker itself remains occupied.spawn_blockinguses 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. - §C2 — anyhow rule narrowed. "Never
anyhow::Errorinlib.rspublic 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 apubAPI other authors consume). Internal/workspace libraries may useanyhowas a deliberate trade-off. - §B5 —
~55% UB rateheadline 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-generatedunsafeis significantly more dangerous than LLM-generated safe code. - §B14 — folk numbers "typically 100–10000" replaced with a sizing formula. Size
Nfrom expected producer burst over one consumer cycle, capped by memory budget per pending message. If the rightNcannot be reasoned about, that is itself a signal to design the backpressure policy before writing the channel. - §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.
- §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 yourrustc --version" rather than asserting it as a fixed property. - §B14
Vec::pushexample clarified. Previous wording read as ifVec::pushitself was the failure. Reworded to "aVecthat ispush-ed in a hot loop with no consumer or cap" — the failure is the missing drain or bound, not the call. - §B12 — rand 0.8 / 0.9 API gap noted.
thread_rng()was renamed torng()inrand0.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 therandversion assumed. - §B15 Pin reorientation. The "you cannot hold a reference through
.awaitand expect Pin to fix it" bullet was a strawman — that confusion is rare in practice. Replaced with the actual LLM-typical confusions: mixing upPin<&mut T>(borrowing, stack) withPin<Box<T>>(owning, heap), and the fact thatUnpinis an auto-trait so most uses ofPinare incidental and add no real constraint. - 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.
- §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
'ainpub fnsignatures is not the same as binding too many things to one'ainside 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". install.sh/install.ps1clean-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.
Initial release. 26 categories plus a meta-layer.
Meta-layer:
- "Prove, don't guess" principle.
- Blocking protocol — explicit refusal format when context is insufficient.
- Operating mode — 7 mandatory steps before generating any Rust.
- Self-monitoring — "user-phrase → activated category" trigger table.
- Pre-flight checklist (7 questions) and Post-flight checklist (what to surface in the summary).
Tier A — Mass compilation failures:
- §A1. API hallucinations and stale APIs (+ slopsquatting with documented attacks: CrateDepression 2022,
faster_log/async_println2025). - §A2. Trait bounds and type mismatches (E0277 / E0308).
- §A3. Smart pointer misuse.
- §A4. Module visibility and pub leaks.
Tier B — Silent correctness bugs:
- §B1. Lifetime laundering (+ lifetime leaking through public APIs).
- §B2.
std::sync::Mutexacross.await(+ Mutex poisoning cascade, oversized critical section). - §B3. Async cancellation.
- §B4. Drop order and RAII contracts.
- §B5. Unsafe that looks safe.
- §B6. Pattern matching exhaustiveness drift.
- §B7. Large stack allocations and arena pitfalls.
- §B8. Silent task dropping (forgotten
.await). - §B9. Lock ordering and ABBA deadlock.
- §B10. Reference cycles in
Rc/Arcgraphs. - §B11. Blocking the async executor.
- §B12. Cryptographic code (silent insecurity).
- §B13. Check-then-act races in concurrent collections (TOCTOU).
- §B14. Unbounded channels and backpressure neglect.
- §B15. Advanced async pitfalls (AFIT, Pin, Waker, block_on).
Tier C — Architecture and ergonomics:
- §C1. Blanket impls in public APIs (semver hazard).
- §C2. Error handling discipline.
- §C3. Async runtime and ecosystem coherence.
- §C4. Iterator and allocation discipline.
- §C5. Reflexive
.clone()as a borrow-checker silencer. - §C6. Procedural macro hygiene.
- §C7. Cargo feature flag hygiene.
Tooling:
commands/rust-audit.md— scan existing Rust against all 26 categories.commands/rust-fix.md— map an error symptom to a category and propose a root-cause fix.commands/rust-plan.md— pre-flight a new task through the trigger table and 7-question checklist.
Repository scaffolding:
LICENSE(MIT) at repo root.install.sh/install.ps1for~/.claude/installation..gitattributespinning LF on.sh/.mdand CRLF on.ps1.docs/sources.mdwith verified URLs for every empirical claim, including the published 2026-05-16 uproger.com field report.docs/roadmap.mdlisting planned categories and tooling.
- "6-month production study" relabeled to "published field report" throughout
rust-intel.mdanddocs/sources.md— the source is a public article, not unattributed internal observation. - 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."
- AFIT recommendation in §B15 reordered to lead with native AFIT + RPITIT +
+ Send;trait-variantsecond;async-traitonly fordyn Trait. (Terminology further corrected in 0.1.1.) cargo check --check-cfginstruction replaced with the actual Rust 1.80+ behavior (automaticunexpected_cfgslint fromCargo.tomldeclarations).bytes::Bytesremoved from the §B5 "safe abstractions" list; replaced withbytemuck::Pod/bytemuck::cast_slice, with a note thatBytesis a buffer container, not a safe-transmute abstraction.rand::random()"not cryptographically secure" claim corrected:ThreadRngis a CSPRNG; the actual ban targetsSmallRng/StdRngfor security work and recommendsOsRngas the default for keys and nonces.- Slopsquatting "~45%" figure in §A1 hedged to match the verification status in
docs/sources.md. - Tier B intro rewritten to motivate via §B2 (46%→19%) and SafeGenBench (~57%) instead of the unverifiable RustPrint citation.
- "The eleven categories below" in Tier B intro → "fifteen" (matches §B1–§B15).
- README slash-command misnaming: skills aren't invoked with
/; clarified that the skill activates automatically. commands/rust-audit.mdandcommands/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.commands/rust-fix.md: clarified the symptom→category table is a routing layer, not duplicated rule knowledge.RustPrintbenchmark entry removed fromdocs/sources.mdand its citation inrust-intel.md(no verifiable source under that name).LICENSE-APACHEdropped (dual-licensing not needed for a prose-first repo; MIT alone is sufficient).
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.