litclaude-ai 0.2.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (156) hide show
  1. package/CHANGELOG.md +155 -0
  2. package/LICENSE +21 -0
  3. package/README.md +369 -0
  4. package/README_ko-KR.md +374 -0
  5. package/RELEASE_CHECKLIST.md +165 -0
  6. package/bin/litclaude-ai.js +643 -0
  7. package/cover.png +0 -0
  8. package/docs/agents.md +67 -0
  9. package/docs/hooks.md +134 -0
  10. package/docs/lsp.md +40 -0
  11. package/docs/migration.md +209 -0
  12. package/docs/workflow-compatibility-audit.md +119 -0
  13. package/generate_cover.py +123 -0
  14. package/package.json +48 -0
  15. package/plugins/litclaude/.claude-plugin/plugin.json +25 -0
  16. package/plugins/litclaude/.lsp.json +13 -0
  17. package/plugins/litclaude/.mcp.json +9 -0
  18. package/plugins/litclaude/agents/boulder-executor.md +12 -0
  19. package/plugins/litclaude/agents/librarian-researcher.md +15 -0
  20. package/plugins/litclaude/agents/oracle-verifier.md +16 -0
  21. package/plugins/litclaude/agents/prometheus-planner.md +13 -0
  22. package/plugins/litclaude/agents/qa-runner.md +16 -0
  23. package/plugins/litclaude/agents/quality-reviewer.md +17 -0
  24. package/plugins/litclaude/bin/litclaude-hook.js +110 -0
  25. package/plugins/litclaude/bin/litclaude-hud.js +271 -0
  26. package/plugins/litclaude/bin/litclaude-lsp-doctor.js +15 -0
  27. package/plugins/litclaude/bin/litclaude-mcp.js +70 -0
  28. package/plugins/litclaude/commands/deep-interview.md +21 -0
  29. package/plugins/litclaude/commands/dynamic-workflow.md +36 -0
  30. package/plugins/litclaude/commands/lit-loop.md +40 -0
  31. package/plugins/litclaude/commands/lit-plan.md +35 -0
  32. package/plugins/litclaude/commands/litgoal.md +30 -0
  33. package/plugins/litclaude/commands/review-work.md +35 -0
  34. package/plugins/litclaude/commands/start-work.md +36 -0
  35. package/plugins/litclaude/hooks/hooks.json +54 -0
  36. package/plugins/litclaude/lib/context-pressure.mjs +25 -0
  37. package/plugins/litclaude/lib/hud-accent-palette.mjs +58 -0
  38. package/plugins/litclaude/lib/litgoal/cli.mjs +266 -0
  39. package/plugins/litclaude/lib/litgoal/ledger.mjs +16 -0
  40. package/plugins/litclaude/lib/litgoal/paths.mjs +7 -0
  41. package/plugins/litclaude/lib/litgoal/state.mjs +67 -0
  42. package/plugins/litclaude/lib/mutated-file-paths.mjs +63 -0
  43. package/plugins/litclaude/lib/start-work-continuation.mjs +99 -0
  44. package/plugins/litclaude/lib/workflow-check.mjs +83 -0
  45. package/plugins/litclaude/skills/ai-slop-remover/SKILL.md +142 -0
  46. package/plugins/litclaude/skills/comment-checker/SKILL.md +55 -0
  47. package/plugins/litclaude/skills/debugging/SKILL.md +70 -0
  48. package/plugins/litclaude/skills/debugging/references/methodology/00-setup.md +108 -0
  49. package/plugins/litclaude/skills/debugging/references/methodology/02-investigate.md +126 -0
  50. package/plugins/litclaude/skills/debugging/references/methodology/04-oracle-triple.md +106 -0
  51. package/plugins/litclaude/skills/debugging/references/methodology/05-escalate.md +69 -0
  52. package/plugins/litclaude/skills/debugging/references/methodology/06-fix.md +116 -0
  53. package/plugins/litclaude/skills/debugging/references/methodology/08-qa.md +94 -0
  54. package/plugins/litclaude/skills/debugging/references/methodology/09-cleanup.md +164 -0
  55. package/plugins/litclaude/skills/debugging/references/methodology/partial-runtime-evidence.md +228 -0
  56. package/plugins/litclaude/skills/debugging/references/runtimes/bundled-js-binary.md +415 -0
  57. package/plugins/litclaude/skills/debugging/references/runtimes/go.md +252 -0
  58. package/plugins/litclaude/skills/debugging/references/runtimes/native-binary.md +484 -0
  59. package/plugins/litclaude/skills/debugging/references/runtimes/node.md +260 -0
  60. package/plugins/litclaude/skills/debugging/references/runtimes/python.md +248 -0
  61. package/plugins/litclaude/skills/debugging/references/runtimes/rust.md +234 -0
  62. package/plugins/litclaude/skills/debugging/references/tools/ghidra.md +212 -0
  63. package/plugins/litclaude/skills/debugging/references/tools/playwright-cli.md +194 -0
  64. package/plugins/litclaude/skills/debugging/references/tools/pwndbg.md +263 -0
  65. package/plugins/litclaude/skills/debugging/references/tools/pwntools.md +265 -0
  66. package/plugins/litclaude/skills/deep-interview/SKILL.md +323 -0
  67. package/plugins/litclaude/skills/deep-interview/scripts/render_progress.py +193 -0
  68. package/plugins/litclaude/skills/frontend-ui-ux/SKILL.md +62 -0
  69. package/plugins/litclaude/skills/lit-loop/SKILL.md +144 -0
  70. package/plugins/litclaude/skills/lit-plan/SKILL.md +125 -0
  71. package/plugins/litclaude/skills/litgoal/SKILL.md +219 -0
  72. package/plugins/litclaude/skills/lsp/SKILL.md +63 -0
  73. package/plugins/litclaude/skills/programming/SKILL.md +106 -0
  74. package/plugins/litclaude/skills/programming/references/go/README.md +90 -0
  75. package/plugins/litclaude/skills/programming/references/go/backend-stack.md +641 -0
  76. package/plugins/litclaude/skills/programming/references/go/bootstrap.md +328 -0
  77. package/plugins/litclaude/skills/programming/references/go/bubbletea-v2.md +360 -0
  78. package/plugins/litclaude/skills/programming/references/go/cobra-stack.md +468 -0
  79. package/plugins/litclaude/skills/programming/references/go/concurrency.md +362 -0
  80. package/plugins/litclaude/skills/programming/references/go/data-modeling.md +329 -0
  81. package/plugins/litclaude/skills/programming/references/go/error-handling.md +359 -0
  82. package/plugins/litclaude/skills/programming/references/go/golangci-strict.md +236 -0
  83. package/plugins/litclaude/skills/programming/references/go/grpc-connect.md +375 -0
  84. package/plugins/litclaude/skills/programming/references/go/libraries.md +337 -0
  85. package/plugins/litclaude/skills/programming/references/go/one-liners.md +202 -0
  86. package/plugins/litclaude/skills/programming/references/go/sqlc-pgx.md +471 -0
  87. package/plugins/litclaude/skills/programming/references/go/testing.md +467 -0
  88. package/plugins/litclaude/skills/programming/references/go/type-patterns.md +298 -0
  89. package/plugins/litclaude/skills/programming/references/python/README.md +314 -0
  90. package/plugins/litclaude/skills/programming/references/python/async-anyio.md +442 -0
  91. package/plugins/litclaude/skills/programming/references/python/data-modeling.md +233 -0
  92. package/plugins/litclaude/skills/programming/references/python/data-processing.md +133 -0
  93. package/plugins/litclaude/skills/programming/references/python/error-handling.md +218 -0
  94. package/plugins/litclaude/skills/programming/references/python/fastapi-stack.md +316 -0
  95. package/plugins/litclaude/skills/programming/references/python/httpx2-optimization.md +360 -0
  96. package/plugins/litclaude/skills/programming/references/python/libraries.md +307 -0
  97. package/plugins/litclaude/skills/programming/references/python/one-liners.md +268 -0
  98. package/plugins/litclaude/skills/programming/references/python/orjson-stack.md +378 -0
  99. package/plugins/litclaude/skills/programming/references/python/pydantic-ai.md +285 -0
  100. package/plugins/litclaude/skills/programming/references/python/pyproject-strict.md +232 -0
  101. package/plugins/litclaude/skills/programming/references/python/textual-tui.md +201 -0
  102. package/plugins/litclaude/skills/programming/references/python/type-patterns.md +176 -0
  103. package/plugins/litclaude/skills/programming/references/rust/README.md +317 -0
  104. package/plugins/litclaude/skills/programming/references/rust/async-tokio.md +299 -0
  105. package/plugins/litclaude/skills/programming/references/rust/axum-stack.md +467 -0
  106. package/plugins/litclaude/skills/programming/references/rust/cargo-strict.md +317 -0
  107. package/plugins/litclaude/skills/programming/references/rust/clap-stack.md +409 -0
  108. package/plugins/litclaude/skills/programming/references/rust/concurrency.md +375 -0
  109. package/plugins/litclaude/skills/programming/references/rust/libraries.md +439 -0
  110. package/plugins/litclaude/skills/programming/references/rust/one-liners.md +291 -0
  111. package/plugins/litclaude/skills/programming/references/rust/proptest-insta.md +429 -0
  112. package/plugins/litclaude/skills/programming/references/rust/type-state.md +354 -0
  113. package/plugins/litclaude/skills/programming/references/rust/unsafe-discipline.md +250 -0
  114. package/plugins/litclaude/skills/programming/references/rust/zero-cost-safety.md +527 -0
  115. package/plugins/litclaude/skills/programming/references/rust-ub/README.md +289 -0
  116. package/plugins/litclaude/skills/programming/references/rust-ub/miri-sanitizers-loom.md +411 -0
  117. package/plugins/litclaude/skills/programming/references/rust-ub/ub-taxonomy.md +269 -0
  118. package/plugins/litclaude/skills/programming/references/typescript/README.md +195 -0
  119. package/plugins/litclaude/skills/programming/references/typescript/backend-hono.md +672 -0
  120. package/plugins/litclaude/skills/programming/references/typescript/bootstrap.md +199 -0
  121. package/plugins/litclaude/skills/programming/references/typescript/data-modeling.md +202 -0
  122. package/plugins/litclaude/skills/programming/references/typescript/error-handling.md +169 -0
  123. package/plugins/litclaude/skills/programming/references/typescript/tsconfig-strict.md +152 -0
  124. package/plugins/litclaude/skills/programming/references/typescript/type-patterns.md +196 -0
  125. package/plugins/litclaude/skills/programming/scripts/go/check-no-excuse-rules.sh +173 -0
  126. package/plugins/litclaude/skills/programming/scripts/go/new-project.py +138 -0
  127. package/plugins/litclaude/skills/programming/scripts/go/templates/.editorconfig +13 -0
  128. package/plugins/litclaude/skills/programming/scripts/go/templates/.golangci.yml +95 -0
  129. package/plugins/litclaude/skills/programming/scripts/go/templates/AGENTS.md.tmpl +24 -0
  130. package/plugins/litclaude/skills/programming/scripts/go/templates/README.md.tmpl +12 -0
  131. package/plugins/litclaude/skills/programming/scripts/go/templates/Taskfile.yml +40 -0
  132. package/plugins/litclaude/skills/programming/scripts/go/templates/ci.yml +37 -0
  133. package/plugins/litclaude/skills/programming/scripts/go/templates/config.go +24 -0
  134. package/plugins/litclaude/skills/programming/scripts/go/templates/gitignore +15 -0
  135. package/plugins/litclaude/skills/programming/scripts/go/templates/main.go.tmpl +22 -0
  136. package/plugins/litclaude/skills/programming/scripts/go/templates/run.go +15 -0
  137. package/plugins/litclaude/skills/programming/scripts/python/check-no-excuse-rules.py +687 -0
  138. package/plugins/litclaude/skills/programming/scripts/python/new-project.py +172 -0
  139. package/plugins/litclaude/skills/programming/scripts/python/new-script.py +116 -0
  140. package/plugins/litclaude/skills/programming/scripts/rust/check-no-excuse-rules.py +296 -0
  141. package/plugins/litclaude/skills/programming/scripts/rust/check-no-excuse-rules.sh +158 -0
  142. package/plugins/litclaude/skills/programming/scripts/rust/new-project.py +175 -0
  143. package/plugins/litclaude/skills/programming/scripts/typescript/check-no-excuse-rules.ts +282 -0
  144. package/plugins/litclaude/skills/programming/scripts/typescript/new-project.ts +177 -0
  145. package/plugins/litclaude/skills/refactor/SKILL.md +73 -0
  146. package/plugins/litclaude/skills/remove-ai-slops/SKILL.md +52 -0
  147. package/plugins/litclaude/skills/review-work/SKILL.md +331 -0
  148. package/plugins/litclaude/skills/rules/SKILL.md +66 -0
  149. package/plugins/litclaude/skills/start-work/SKILL.md +132 -0
  150. package/scripts/audit-plan-checkboxes.mjs +37 -0
  151. package/scripts/doctor.mjs +41 -0
  152. package/scripts/inspect-agent-tools.mjs +27 -0
  153. package/scripts/postinstall.mjs +50 -0
  154. package/scripts/qa-claude-plugin-smoke.sh +60 -0
  155. package/scripts/qa-portable-install.sh +136 -0
  156. package/scripts/validate-plugin.mjs +72 -0
@@ -0,0 +1,289 @@
1
+
2
+ # Rust Undefined Behavior Exorcist
3
+
4
+ You are a UB hunter. Your job is to find, classify, prove, and eliminate every instance of undefined behavior in Rust code. **Miri is your primary weapon** — everything else supplements where Miri cannot reach.
5
+
6
+ ## Core Philosophy
7
+
8
+ 1. **Miri first, always.** Before reading a single line of `unsafe`, run Miri. Before proposing a fix, run Miri. After applying a fix, run Miri. Miri is the oracle.
9
+ 2. **Classify before fixing.** Every UB finding gets classified against the 14-category taxonomy (see [ub-taxonomy.md](ub-taxonomy.md)). This prevents misdiagnosis and ensures the fix targets the root cause, not a symptom.
10
+ 3. **Prove the fix.** A fix is not done until Miri passes with full paranoia flags. If Miri cannot run the test (FFI, I/O), the fix is not done until the appropriate sanitizer passes.
11
+ 4. **Bead handoff.** Each resolved UB instance is a "bead" — a discrete, documented, proven fix. Hand it off with: the UB category, the root cause, the fix, and the Miri proof.
12
+
13
+ ## The UB Taxonomy
14
+
15
+ 14 categories. The full reference is in [ub-taxonomy.md](ub-taxonomy.md). Memorize the categories; classify every finding:
16
+
17
+ | # | Category | Miri? |
18
+ |---|----------|-------|
19
+ | 1 | Aliasing violations (Stacked/Tree Borrows) | YES |
20
+ | 2 | Data races | YES |
21
+ | 3 | Use-after-free / dangling pointers | YES |
22
+ | 4 | Uninitialized memory | YES |
23
+ | 5 | Invalid values (type invariant violations) | YES |
24
+ | 6 | Misaligned pointer access | YES |
25
+ | 7 | Pin invariant violations | PARTIAL |
26
+ | 8 | FFI boundary UB | LIMITED |
27
+ | 9 | Incorrect Send/Sync implementations | YES (via race) |
28
+ | 10 | Out-of-bounds memory access | YES |
29
+ | 11 | Provenance violations | YES (strict mode) |
30
+ | 12 | Double free / invalid free | YES |
31
+ | 13 | Library / unsafe contract violations | PARTIAL |
32
+ | 14 | Unwinding across extern "C" | PARTIAL |
33
+
34
+ ## The Hunt Workflow
35
+
36
+ ### Phase 1: Reconnaissance
37
+
38
+ 1. **Find all `unsafe` blocks and `unsafe impl`s:**
39
+ ```bash
40
+ rg 'unsafe\s*(fn|impl|{|\{)' --type rust -n
41
+ ```
42
+
43
+ 2. **Find all `unsafe` trait implementations:**
44
+ ```bash
45
+ rg 'unsafe\s+impl\s+(Send|Sync)' --type rust -n
46
+ ```
47
+
48
+ 3. **Find transmute / pointer casts / raw pointer derefs:**
49
+ ```bash
50
+ rg '(transmute|transmute_copy|from_raw|into_raw|as_ptr|as_mut_ptr|offset|add|sub|read|write|copy|ptr::null)' --type rust -n
51
+ ```
52
+
53
+ 4. **Find FFI boundaries:**
54
+ ```bash
55
+ rg 'extern\s+"C"' --type rust -n
56
+ ```
57
+
58
+ 5. **Count and catalog.** Create a hit list: file, line, `unsafe` category, initial risk assessment (high/medium/low based on the UB taxonomy).
59
+
60
+ ### Phase 2: Miri Sweep (THE CRITICAL PHASE)
61
+
62
+ Run Miri with escalating strictness. **Do not skip any level.**
63
+
64
+ **Level 1 — Default (Stacked Borrows):**
65
+ ```bash
66
+ cargo +nightly miri test 2>&1
67
+ ```
68
+
69
+ **Level 2 — Strict Provenance + Symbolic Alignment:**
70
+ ```bash
71
+ MIRIFLAGS="-Zmiri-strict-provenance -Zmiri-symbolic-alignment-check -Zmiri-backtrace=full" \
72
+ cargo +nightly miri test 2>&1
73
+ ```
74
+
75
+ **Level 3 — Full Paranoia (the audit standard):**
76
+ ```bash
77
+ MIRIFLAGS="\
78
+ -Zmiri-strict-provenance \
79
+ -Zmiri-symbolic-alignment-check \
80
+ -Zmiri-preemption-rate=0.1 \
81
+ -Zmiri-backtrace=full \
82
+ -Zmiri-disable-isolation" \
83
+ cargo +nightly miri test 2>&1
84
+ ```
85
+
86
+ **Level 4 — Tree Borrows (second model confirmation):**
87
+ ```bash
88
+ MIRIFLAGS="\
89
+ -Zmiri-tree-borrows \
90
+ -Zmiri-strict-provenance \
91
+ -Zmiri-symbolic-alignment-check \
92
+ -Zmiri-preemption-rate=0.1 \
93
+ -Zmiri-backtrace=full \
94
+ -Zmiri-disable-isolation" \
95
+ cargo +nightly miri test 2>&1
96
+ ```
97
+
98
+ **Interpret results:**
99
+ - Fails at Level 1 → Definite UB. Fix immediately.
100
+ - Passes Level 1, fails Level 2 → Provenance or alignment UB. Fix.
101
+ - Passes Levels 1-3, fails Level 4 → Tree Borrows found something Stacked Borrows missed (unusual). Investigate — may be a Tree Borrows false positive, but usually indicates fragile aliasing.
102
+ - Passes all 4 → Miri-clean. Proceed to supplementary tools.
103
+
104
+ ### Phase 3: Supplementary Scans
105
+
106
+ For code Miri cannot fully cover:
107
+
108
+ **Concurrent code with custom atomics:**
109
+ ```bash
110
+ RUSTFLAGS="--cfg loom" cargo test --lib --release -- loom_tests 2>&1
111
+ ```
112
+
113
+ **FFI-heavy code:**
114
+ ```bash
115
+ RUSTFLAGS="-Zsanitizer=address" cargo +nightly test -Zbuild-std --target $(rustc -vV | rg host | awk '{print $2}') 2>&1
116
+ ```
117
+
118
+ **Untrusted input parsing:**
119
+ ```bash
120
+ cargo +nightly fuzz run <target> -- -max_total_time=300 2>&1
121
+ ```
122
+
123
+ ### Phase 4: Fix-and-Prove Loop
124
+
125
+ For each UB finding:
126
+
127
+ 1. **Classify** against the 14-category taxonomy.
128
+ 2. **Write the SAFETY comment** explaining what is wrong and what the fix must achieve.
129
+ 3. **Apply the minimal fix.** Do not refactor — fix the UB and nothing else.
130
+ 4. **Run Miri (Level 3 minimum) on the specific test that triggered the UB.**
131
+ 5. **Run Miri (Level 3) on the full test suite** to check for regressions.
132
+ 6. **Document the bead:**
133
+ ```
134
+ BEAD: [Category #] [Short description]
135
+ FILE: [path:line]
136
+ ROOT CAUSE: [one sentence]
137
+ FIX: [one sentence]
138
+ PROOF: Miri Level [N] pass — [command used]
139
+ ```
140
+
141
+ ### Phase 5: Hardening (Post-Fix)
142
+
143
+ After all beads are resolved:
144
+
145
+ 1. **Add Miri to CI** if not already present (see [miri-sanitizers-loom.md](miri-sanitizers-loom.md) for the GitHub Actions config).
146
+ 2. **Add `#[cfg(miri)]` regression tests** for each bead — these are the tests that originally caught the UB, locked in so it never returns.
147
+ 3. **Review SAFETY comments** on every remaining `unsafe` block. Each must name the specific invariant from the taxonomy.
148
+ 4. **Run the full paranoia sweep one final time** to confirm clean.
149
+
150
+ ## Miri-First Decision Protocol
151
+
152
+ When the agent encounters `unsafe` code during ANY Rust task (not just audits):
153
+
154
+ ```
155
+ Is there unsafe code in the changeset?
156
+ YES → Run Miri Level 1 before proceeding.
157
+ │ Miri fails?
158
+ │ YES → Stop. Classify. Fix. Prove. Then continue.
159
+ │ NO → Run Miri Level 2 (strict provenance).
160
+ │ Miri fails?
161
+ │ YES → Stop. Classify. Fix. Prove. Then continue.
162
+ │ NO → Proceed with the original task.
163
+ NO → Proceed normally.
164
+ ```
165
+
166
+ This is not optional. **Every `unsafe` block gets Miri'd before it ships.**
167
+
168
+ ## SAFETY Comment Standard
169
+
170
+ Every `unsafe` block requires a SAFETY comment within 5 lines above it. The comment must:
171
+
172
+ 1. **Name the UB category** it could trigger (from the taxonomy).
173
+ 2. **State the invariant** that makes this safe.
174
+ 3. **Name who/what guarantees** the invariant (caller contract, type system, runtime check).
175
+
176
+ ```rust
177
+ // SAFETY: [Category 4 — Uninitialized Memory]
178
+ // All N elements have been written to via `ptr::write` in the loop above.
179
+ // The loop runs exactly `len` times, and `len` was validated against the
180
+ // allocation size at line 42. MaybeUninit::assume_init is therefore sound.
181
+ unsafe { buf.assume_init() }
182
+ ```
183
+
184
+ Bad SAFETY comments that must be rejected:
185
+ - `// SAFETY: we know this is safe` — Says nothing.
186
+ - `// SAFETY: this is fine because we tested it` — Testing does not prove absence of UB.
187
+ - `// SAFETY: the caller ensures correctness` — Which invariant? What is the contract?
188
+ - No SAFETY comment at all — Immediate failure.
189
+
190
+ ## Audit Report Format
191
+
192
+ When completing a UB audit, produce a summary:
193
+
194
+ ```markdown
195
+ ## UB Audit Report
196
+
197
+ **Scope:** [crate/module/file]
198
+ **Miri version:** [output of `cargo +nightly miri --version`]
199
+ **Date:** [date]
200
+
201
+ ### Findings
202
+
203
+ | # | Category | File:Line | Severity | Status |
204
+ |---|----------|-----------|----------|--------|
205
+ | 1 | Aliasing | src/buf.rs:42 | High | Fixed (Bead #1) |
206
+ | 2 | Uninit | src/ffi.rs:98 | High | Fixed (Bead #2) |
207
+
208
+ ### Beads
209
+
210
+ #### Bead #1: Aliasing violation in buffer resize
211
+ - **Root cause:** `&mut` created while `&` to same slice existed
212
+ - **Fix:** Restructured to drop shared ref before taking mutable
213
+ - **Proof:** `cargo +nightly miri test -- test_buffer_resize` passes Level 3
214
+
215
+ ### Miri CI Status
216
+ - [ ] Miri added to CI (Level 2 minimum)
217
+ - [ ] All SAFETY comments reviewed
218
+ - [ ] Regression tests added for each bead
219
+ ```
220
+
221
+ ## Common Fix Patterns
222
+
223
+ ### Aliasing → Use `UnsafeCell` or restructure borrows
224
+ ```rust
225
+ // BEFORE (UB: &mut while & exists)
226
+ let ptr = slice.as_ptr();
227
+ let mut_ref = &mut slice[0]; // UB: ptr still usable
228
+
229
+ // AFTER
230
+ let mut_ref = &mut slice[0];
231
+ // ptr is never created / used across the mutable borrow
232
+ ```
233
+
234
+ ### Uninitialized → Use `MaybeUninit::write` + `assume_init`
235
+ ```rust
236
+ // BEFORE (UB: mem::uninitialized)
237
+ let x: T = unsafe { std::mem::uninitialized() };
238
+
239
+ // AFTER
240
+ let x: T = unsafe {
241
+ let mut uninit = MaybeUninit::<T>::uninit();
242
+ uninit.write(initial_value);
243
+ uninit.assume_init()
244
+ };
245
+ ```
246
+
247
+ ### Provenance → Use `expose_provenance` / `with_exposed_provenance`
248
+ ```rust
249
+ // BEFORE (UB: provenance lost)
250
+ let addr = ptr as usize;
251
+ let recovered = addr as *const T;
252
+
253
+ // AFTER
254
+ let addr = ptr.expose_provenance();
255
+ let recovered = std::ptr::with_exposed_provenance::<T>(addr);
256
+ ```
257
+
258
+ ### Send/Sync → Remove manual impl, use PhantomData
259
+ ```rust
260
+ // BEFORE (unsound)
261
+ unsafe impl Send for MyType {}
262
+
263
+ // AFTER — if MyType truly needs Send, prove it:
264
+ // SAFETY: [Category 9 — Send/Sync]
265
+ // MyType's only non-Send field is `*mut Buffer`. Access to the buffer
266
+ // is guarded by `self.lock: Mutex<()>`, which provides the
267
+ // happens-before guarantee required by Send.
268
+ unsafe impl Send for MyType {}
269
+ ```
270
+
271
+ ### FFI → Validate at boundary
272
+ ```rust
273
+ // BEFORE (UB: null pointer from C becomes &T)
274
+ let result = unsafe { ffi_call() };
275
+
276
+ // AFTER
277
+ let raw = unsafe { ffi_call() };
278
+ let result = NonNull::new(raw).ok_or(Error::NullFromFfi)?;
279
+ ```
280
+
281
+ ## Activation
282
+
283
+ This skill activates when:
284
+ - The user requests a "UB audit", "miri sweep", "unsafe audit", "soundness check", "rustonomicon audit", "race hunt"
285
+ - The agent encounters `unsafe` code during a Rust task and needs to verify it
286
+ - Miri reports a failure and the agent needs to classify and fix it
287
+ - The user asks "is this sound?" about Rust code
288
+
289
+ **Miri is not optional. Miri is the proof. Ship nothing `unsafe` without Miri's blessing.**
@@ -0,0 +1,411 @@
1
+ # Miri, Sanitizers, Loom, and Fuzzing — The UB Detection Arsenal
2
+
3
+ Miri is the **primary weapon**. Everything else is supplementary for the gaps Miri cannot reach.
4
+
5
+ ---
6
+
7
+ ## Miri — The First and Last Line of Defense
8
+
9
+ ### What Miri Is
10
+
11
+ Miri is an interpreter for Rust's MIR (Mid-level IR). It executes your test suite inside a virtual machine that tracks every byte of memory for validity, provenance, alignment, initialization, and aliasing. It is **deterministic** — same inputs, same result — and it can find UB that no amount of testing on real hardware will ever trigger.
12
+
13
+ ### Why Miri Is Non-Negotiable
14
+
15
+ - Detects 12 of 14 UB categories (see `ub-taxonomy.md`).
16
+ - Catches aliasing violations that compile and run correctly on every platform today but are UB that future compiler optimizations will exploit.
17
+ - Catches data races under a configurable scheduling model.
18
+ - Catches provenance violations that are impossible to observe on real hardware.
19
+ - **Zero false positives** — if Miri says it is UB, it is UB. Period.
20
+
21
+ ### Installation
22
+
23
+ ```bash
24
+ rustup install nightly
25
+ rustup component add miri rust-src --toolchain nightly
26
+ ```
27
+
28
+ Verify:
29
+ ```bash
30
+ cargo +nightly miri --version
31
+ ```
32
+
33
+ ### Running Miri
34
+
35
+ **Default run (Stacked Borrows, standard checks):**
36
+ ```bash
37
+ cargo +nightly miri test
38
+ ```
39
+
40
+ **With nextest (recommended for projects already using nextest):**
41
+ ```bash
42
+ cargo +nightly miri nextest run
43
+ ```
44
+
45
+ **Specific test:**
46
+ ```bash
47
+ cargo +nightly miri test -- test_name
48
+ ```
49
+
50
+ **Run a binary:**
51
+ ```bash
52
+ cargo +nightly miri run
53
+ ```
54
+
55
+ ### MIRIFLAGS — The Dial-Up Knobs
56
+
57
+ These flags are set via the `MIRIFLAGS` environment variable. The agent should use ALL of the strictness flags during a UB audit.
58
+
59
+ #### Aliasing Model
60
+
61
+ ```bash
62
+ # Default: Stacked Borrows (strict)
63
+ cargo +nightly miri test
64
+
65
+ # Tree Borrows (newer, more permissive — use as a second pass)
66
+ MIRIFLAGS="-Zmiri-tree-borrows" cargo +nightly miri test
67
+ ```
68
+
69
+ **Protocol:** Run Stacked Borrows first. If it fails, fix it. Then run Tree Borrows to confirm. Code that passes Stacked Borrows is sound under both models.
70
+
71
+ #### Strict Provenance
72
+
73
+ ```bash
74
+ MIRIFLAGS="-Zmiri-strict-provenance" cargo +nightly miri test
75
+ ```
76
+
77
+ Catches `ptr as usize as *const T` roundtrips where provenance is lost. **Should be ON for every audit.**
78
+
79
+ #### Symbolic Alignment Checks
80
+
81
+ ```bash
82
+ MIRIFLAGS="-Zmiri-symbolic-alignment-check" cargo +nightly miri test
83
+ ```
84
+
85
+ Catches alignment UB that happens to be aligned on your machine but is not guaranteed by the type system.
86
+
87
+ #### Data Race Detection Tuning
88
+
89
+ ```bash
90
+ # Increase preemption rate to stress-test race conditions
91
+ MIRIFLAGS="-Zmiri-preemption-rate=0.5" cargo +nightly miri test
92
+
93
+ # Disable preemption (sequential scheduling — fewer races found but deterministic)
94
+ MIRIFLAGS="-Zmiri-preemption-rate=0" cargo +nightly miri test
95
+ ```
96
+
97
+ #### The Full Paranoia Sweep (Use This for Audits)
98
+
99
+ ```bash
100
+ MIRIFLAGS="\
101
+ -Zmiri-strict-provenance \
102
+ -Zmiri-symbolic-alignment-check \
103
+ -Zmiri-preemption-rate=0.1 \
104
+ -Zmiri-backtrace=full \
105
+ -Zmiri-disable-isolation" \
106
+ cargo +nightly miri test
107
+ ```
108
+
109
+ Then a second pass with Tree Borrows:
110
+ ```bash
111
+ MIRIFLAGS="\
112
+ -Zmiri-tree-borrows \
113
+ -Zmiri-strict-provenance \
114
+ -Zmiri-symbolic-alignment-check \
115
+ -Zmiri-preemption-rate=0.1 \
116
+ -Zmiri-backtrace=full \
117
+ -Zmiri-disable-isolation" \
118
+ cargo +nightly miri test
119
+ ```
120
+
121
+ #### Isolation and I/O
122
+
123
+ Miri runs in isolation by default — no file I/O, no network, no system calls. If your tests need the filesystem:
124
+ ```bash
125
+ MIRIFLAGS="-Zmiri-disable-isolation" cargo +nightly miri test
126
+ ```
127
+
128
+ Use sparingly — isolation is a feature, not a limitation. Tests that need I/O should have a separate `#[cfg(not(miri))]` path.
129
+
130
+ ### Miri Limitations
131
+
132
+ | Cannot do | Workaround |
133
+ |-----------|-----------|
134
+ | Execute FFI / C code | ASAN, MSAN, Valgrind |
135
+ | Run I/O-heavy tests (default) | `-Zmiri-disable-isolation` or `#[cfg(not(miri))]` |
136
+ | Exhaustive interleaving exploration | loom |
137
+ | Find performance bugs | criterion, flamegraph |
138
+ | Run inline assembly | skip with `#[cfg(not(miri))]` |
139
+ | Test OS-specific behavior | real hardware + sanitizers |
140
+
141
+ ### Miri in CI
142
+
143
+ ```yaml
144
+ # GitHub Actions example
145
+ miri:
146
+ runs-on: ubuntu-latest
147
+ steps:
148
+ - uses: actions/checkout@v4
149
+ - uses: dtolnay/rust-toolchain@nightly
150
+ with:
151
+ components: miri, rust-src
152
+ - name: Miri test (Stacked Borrows + strict provenance)
153
+ run: |
154
+ MIRIFLAGS="-Zmiri-strict-provenance -Zmiri-symbolic-alignment-check -Zmiri-backtrace=full" \
155
+ cargo +nightly miri test
156
+ - name: Miri test (Tree Borrows)
157
+ run: |
158
+ MIRIFLAGS="-Zmiri-tree-borrows -Zmiri-strict-provenance -Zmiri-symbolic-alignment-check -Zmiri-backtrace=full" \
159
+ cargo +nightly miri test
160
+ ```
161
+
162
+ ### Miri-Incompatible Test Gating
163
+
164
+ ```rust
165
+ #[test]
166
+ #[cfg_attr(miri, ignore)] // Miri cannot run this (FFI, I/O, inline asm)
167
+ fn test_requires_real_hardware() {
168
+ // ...
169
+ }
170
+
171
+ // Or conditionally compile the test body:
172
+ #[test]
173
+ fn test_with_miri_fallback() {
174
+ #[cfg(miri)]
175
+ {
176
+ // Simplified version that avoids FFI
177
+ }
178
+ #[cfg(not(miri))]
179
+ {
180
+ // Full version with FFI
181
+ }
182
+ }
183
+ ```
184
+
185
+ ---
186
+
187
+ ## Sanitizers — Where Miri Cannot Reach
188
+
189
+ Sanitizers are compiler instrumentation passes. They run your actual binary on real hardware with extra checks injected. Use them for FFI, I/O-heavy code, and integration tests.
190
+
191
+ ### AddressSanitizer (ASAN)
192
+
193
+ Detects: use-after-free, buffer overflow, stack-use-after-return, double-free, memory leaks.
194
+
195
+ ```bash
196
+ RUSTFLAGS="-Zsanitizer=address" cargo +nightly test -Zbuild-std --target x86_64-unknown-linux-gnu
197
+ ```
198
+
199
+ On macOS:
200
+ ```bash
201
+ RUSTFLAGS="-Zsanitizer=address" cargo +nightly test -Zbuild-std --target aarch64-apple-darwin
202
+ ```
203
+
204
+ ### ThreadSanitizer (TSAN)
205
+
206
+ Detects: data races on non-atomic accesses across threads.
207
+
208
+ ```bash
209
+ RUSTFLAGS="-Zsanitizer=thread" cargo +nightly test -Zbuild-std --target x86_64-unknown-linux-gnu
210
+ ```
211
+
212
+ **When to use over Miri:** Integration tests involving real threads + real I/O + FFI. Miri's data-race detector is superior for pure-Rust code.
213
+
214
+ ### MemorySanitizer (MSAN)
215
+
216
+ Detects: reads of uninitialized memory.
217
+
218
+ ```bash
219
+ RUSTFLAGS="-Zsanitizer=memory -Zsanitizer-memory-track-origins" cargo +nightly test -Zbuild-std --target x86_64-unknown-linux-gnu
220
+ ```
221
+
222
+ **When to use over Miri:** FFI code where C/C++ may return uninitialized memory into Rust.
223
+
224
+ ### UndefinedBehaviorSanitizer (UBSAN)
225
+
226
+ Detects: integer overflow, misaligned access, null dereference, and other C/C++-style UB at the LLVM level.
227
+
228
+ ```bash
229
+ RUSTFLAGS="-Zsanitizer=undefined" cargo +nightly test -Zbuild-std --target x86_64-unknown-linux-gnu
230
+ ```
231
+
232
+ ### Sanitizer Limitations
233
+
234
+ - Require nightly + `-Zbuild-std` (rebuilds the standard library with instrumentation).
235
+ - MSAN requires ALL dependencies (including C libs) to be instrumented — practically hard.
236
+ - Cannot catch aliasing violations (that is Miri's domain).
237
+ - Significant runtime overhead (2-15x slower).
238
+ - Linux has the best support; macOS works for ASAN; Windows support is minimal.
239
+
240
+ ---
241
+
242
+ ## Loom — Exhaustive Concurrency Testing
243
+
244
+ Loom explores all possible thread interleavings of a bounded concurrent program. It is mandatory for lock-free and wait-free primitives.
245
+
246
+ ### When to Use Loom
247
+
248
+ - Any `unsafe` code involving atomics with ordering weaker than `SeqCst`.
249
+ - Custom lock implementations.
250
+ - Lock-free queues, stacks, or other concurrent data structures.
251
+ - Any code where you chose `Relaxed`, `Acquire`, or `Release` ordering.
252
+
253
+ ### When NOT to Use Loom
254
+
255
+ - Code using only `Mutex`/`RwLock` from std or `parking_lot` — the locks are sound, your usage is the question, and Miri + TSAN cover that.
256
+ - Async code (loom does not model async runtimes — use `tokio::test` + Miri instead).
257
+
258
+ ### Setup
259
+
260
+ ```toml
261
+ [dev-dependencies]
262
+ loom = "0.7"
263
+ ```
264
+
265
+ ### Loom Test Pattern
266
+
267
+ ```rust
268
+ #[cfg(loom)]
269
+ mod loom_tests {
270
+ use loom::sync::atomic::{AtomicUsize, Ordering};
271
+ use loom::sync::Arc;
272
+ use loom::thread;
273
+
274
+ #[test]
275
+ fn concurrent_increment_is_sound() {
276
+ loom::model(|| {
277
+ let counter = Arc::new(AtomicUsize::new(0));
278
+
279
+ let threads: Vec<_> = (0..2).map(|_| {
280
+ let c = counter.clone();
281
+ thread::spawn(move || {
282
+ c.fetch_add(1, Ordering::SeqCst);
283
+ })
284
+ }).collect();
285
+
286
+ for t in threads {
287
+ t.join().unwrap();
288
+ }
289
+
290
+ assert_eq!(counter.load(Ordering::SeqCst), 2);
291
+ });
292
+ }
293
+ }
294
+ ```
295
+
296
+ ### Conditional Compilation for Loom
297
+
298
+ ```rust
299
+ #[cfg(loom)]
300
+ use loom::sync::atomic::{AtomicUsize, Ordering};
301
+ #[cfg(not(loom))]
302
+ use std::sync::atomic::{AtomicUsize, Ordering};
303
+ ```
304
+
305
+ ### Running Loom Tests
306
+
307
+ ```bash
308
+ # Loom tests only (use cfg flag)
309
+ RUSTFLAGS="--cfg loom" cargo test --lib -- loom_tests
310
+
311
+ # With release optimizations (loom is slow)
312
+ RUSTFLAGS="--cfg loom" cargo test --lib --release -- loom_tests
313
+ ```
314
+
315
+ ### Loom + Miri Interaction
316
+
317
+ Loom and Miri solve different problems:
318
+ - **Miri** checks a single execution for UB (aliasing, validity, provenance).
319
+ - **Loom** checks all interleavings for correctness (ordering, atomicity).
320
+
321
+ Run BOTH on lock-free code:
322
+ ```bash
323
+ # Step 1: loom for interleaving correctness
324
+ RUSTFLAGS="--cfg loom" cargo test --lib --release -- loom_tests
325
+
326
+ # Step 2: Miri for UB in each path
327
+ cargo +nightly miri test -- concurrent_tests
328
+ ```
329
+
330
+ ---
331
+
332
+ ## Cargo-Fuzz — Property-Based UB Hunting
333
+
334
+ Fuzzing generates random inputs to maximize code coverage and find crashes, panics, and UB.
335
+
336
+ ### Setup
337
+
338
+ ```bash
339
+ cargo install cargo-fuzz
340
+ cargo fuzz init
341
+ ```
342
+
343
+ ### Fuzz Target
344
+
345
+ ```rust
346
+ // fuzz/fuzz_targets/parse_input.rs
347
+ #![no_main]
348
+ use libfuzzer_sys::fuzz_target;
349
+
350
+ fuzz_target!(|data: &[u8]| {
351
+ // Your parsing/deserialization/processing code here.
352
+ // If it panics or triggers UB, the fuzzer catches it.
353
+ let _ = my_crate::parse(data);
354
+ });
355
+ ```
356
+
357
+ ### Running
358
+
359
+ ```bash
360
+ # Run until interrupted
361
+ cargo +nightly fuzz run parse_input
362
+
363
+ # Run with ASAN (catches memory bugs in unsafe code)
364
+ cargo +nightly fuzz run parse_input -- -rss_limit_mb=4096
365
+
366
+ # Minimize a crashing input
367
+ cargo +nightly fuzz tmin parse_input artifacts/parse_input/crash-xxxxx
368
+ ```
369
+
370
+ ### Fuzz + Miri Pipeline
371
+
372
+ When the fuzzer finds a crashing input:
373
+ 1. Minimize it with `cargo fuzz tmin`.
374
+ 2. Add it as a regression test.
375
+ 3. Run the regression test under Miri to classify whether it is a panic (safe) or UB (must fix).
376
+
377
+ ```bash
378
+ # After adding the input as a test case:
379
+ cargo +nightly miri test -- test_fuzz_regression_001
380
+ ```
381
+
382
+ ---
383
+
384
+ ## Tool Selection Decision Tree
385
+
386
+ ```
387
+ Start
388
+
389
+ ├── Is it pure Rust (no FFI, no I/O)?
390
+ │ YES → Miri (full paranoia flags)
391
+ │ │ └── Also: loom (if atomics/lock-free)
392
+ │ │ └── Also: proptest (if parsing/serialization)
393
+ │ │ └── Also: cargo-fuzz (if untrusted input)
394
+ │ │
395
+ │ NO → Does it involve FFI?
396
+ │ YES → ASAN + MSAN on integration tests
397
+ │ │ └── Miri on the Rust-side handling
398
+ │ │ └── cbindgen in CI for layout verification
399
+ │ │
400
+ │ NO → Is it I/O-heavy?
401
+ │ YES → TSAN for thread safety
402
+ │ │ └── Miri with -Zmiri-disable-isolation where possible
403
+ │ │
404
+ │ NO → Miri (full paranoia flags)
405
+
406
+ └── Always: Miri is the default. Other tools supplement.
407
+ ```
408
+
409
+ ## The One Rule
410
+
411
+ > **When in doubt, run Miri.** If Miri cannot run it, write a version it can run, and test that under Miri. Then test the real version under sanitizers. Never ship `unsafe` code that has not passed Miri.