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,375 @@
1
+ # Concurrency Primitives
2
+
3
+ Locks, atomics, channels, and the loom model checker. The decision tree that keeps the agent out of soundness trouble.
4
+
5
+ ## The pyramid
6
+
7
+ ```
8
+ Highest level tokio::sync::mpsc / broadcast / watch
9
+ (message passing — default for new code)
10
+
11
+ Arc<Mutex<T>> / Arc<RwLock<T>>
12
+ (shared mutable state — common, easy to get right)
13
+
14
+ parking_lot::{Mutex, RwLock, Condvar}
15
+ (faster sync locks, no poisoning)
16
+
17
+ Atomics (AtomicUsize, AtomicBool, AtomicPtr)
18
+ (single-word lock-free state)
19
+
20
+ Lowest level UnsafeCell + unsafe + loom + miri
21
+ (custom lock-free / wait-free primitives)
22
+ ```
23
+
24
+ **Always start at the top.** Drop a level only when you have measured a real bottleneck.
25
+
26
+ ## Decision tree
27
+
28
+ ```
29
+ Need to share state between tasks?
30
+ ├── State is configuration (read-only after start)
31
+ │ └── Arc<Config> (no lock needed)
32
+ ├── State is a queue of work
33
+ │ └── tokio::sync::mpsc::channel(cap)
34
+ ├── State is "latest value" published to many readers
35
+ │ └── tokio::sync::watch::channel(initial)
36
+ ├── State is broadcast (every consumer sees every value)
37
+ │ └── tokio::sync::broadcast::channel(cap)
38
+ ├── State is request-response within one task tree
39
+ │ └── tokio::sync::oneshot::channel()
40
+ ├── State is a counter
41
+ │ └── AtomicU64 (or AtomicUsize)
42
+ ├── State is a flag / set-once
43
+ │ └── AtomicBool / OnceLock<T> / OnceCell<T>
44
+ ├── State needs mutation across many tasks/threads, cheap critical sections
45
+ │ ├── async context → tokio::sync::Mutex<T>
46
+ │ └── sync context (no .await held) → parking_lot::Mutex<T>
47
+ ├── State needs mutation, many readers, few writers
48
+ │ ├── async context → tokio::sync::RwLock<T>
49
+ │ └── sync context → parking_lot::RwLock<T>
50
+ └── State is a custom lock-free primitive (channels, hazard pointers)
51
+ └── UnsafeCell + atomics + loom-tested + miri-tested + a co-author
52
+ ```
53
+
54
+ ## Atomics — when and how
55
+
56
+ Use atomics for:
57
+ - Counters incremented from many threads (`AtomicU64`).
58
+ - Single-shot flags (`AtomicBool`).
59
+ - Pointer publication (`AtomicPtr<T>`).
60
+
61
+ ### Memory orderings
62
+
63
+ ```rust
64
+ use std::sync::atomic::{AtomicUsize, Ordering};
65
+
66
+ let c = AtomicUsize::new(0);
67
+
68
+ // Just need a count, no synchronization with other data
69
+ c.fetch_add(1, Ordering::Relaxed);
70
+
71
+ // Reading a counter that was incremented from elsewhere
72
+ let n = c.load(Ordering::Relaxed);
73
+ ```
74
+
75
+ | Ordering | When |
76
+ |---|---|
77
+ | `Relaxed` | Standalone counters, no other memory needs to be synchronized. |
78
+ | `Acquire` (loads) / `Release` (stores) | Publish/consume pattern: you write some data then release a flag, readers acquire the flag then read the data. |
79
+ | `AcqRel` | RMW that both reads-and-publishes (e.g., `fetch_add` on a sequence number). |
80
+ | `SeqCst` | Total ordering across all `SeqCst` ops. Strongest, slowest. Use when in doubt and switch to a weaker ordering after testing under loom. |
81
+
82
+ **Default to `SeqCst` if unsure.** Performance difference is usually negligible. Going weaker requires loom.
83
+
84
+ ### Publish-then-load pattern
85
+
86
+ ```rust
87
+ static READY: AtomicBool = AtomicBool::new(false);
88
+ static mut DATA: Option<Config> = None;
89
+
90
+ // Producer thread:
91
+ unsafe { DATA = Some(load_config()); }
92
+ READY.store(true, Ordering::Release);
93
+
94
+ // Consumer thread:
95
+ if READY.load(Ordering::Acquire) {
96
+ // SAFETY: producer's Release pairs with our Acquire; if we see READY=true,
97
+ // we are guaranteed to also see the DATA write that happened-before it.
98
+ let cfg = unsafe { DATA.as_ref().unwrap() };
99
+ }
100
+ ```
101
+
102
+ This is the canonical Release/Acquire pattern. **Use `OnceLock<Config>` instead** in new code — it encapsulates exactly this with safe API.
103
+
104
+ ## Std vs parking_lot vs tokio for locks
105
+
106
+ | | std::sync::Mutex | parking_lot::Mutex | tokio::sync::Mutex |
107
+ |---|---|---|---|
108
+ | Speed | Slowest (OS futex direct) | Fastest (smarter parking) | Slow (await-aware) |
109
+ | Poisoning | Yes (`PoisonError`) | No | No |
110
+ | Hold across `.await` | Dangerous (deadlock under current-thread runtime) | Dangerous | Safe |
111
+ | Drop guard releases | Yes | Yes | Yes |
112
+ | RAII | Yes (`MutexGuard`) | Yes | Yes |
113
+ | Const constructor | Yes (since 1.63) | Yes | No |
114
+ | Async | No | No | Yes |
115
+
116
+ **Rule of thumb:**
117
+
118
+ - Hot, short critical section, no await inside → `parking_lot::Mutex`.
119
+ - Shared state held across `.await` → `tokio::sync::Mutex`.
120
+ - Static init / app config → `OnceLock` or `LazyLock`.
121
+ - Avoid `std::sync::Mutex` for new code; the poisoning behavior is more annoying than useful and `parking_lot` is strictly faster.
122
+
123
+ ### Common deadlock — async + sync mutex
124
+
125
+ ```rust
126
+ let m = std::sync::Mutex::new(0u64);
127
+ let guard = m.lock().unwrap();
128
+ something_async().await; // ❌ guard is held across await
129
+ *guard += 1;
130
+ ```
131
+
132
+ Under `current_thread` runtime this deadlocks (the future suspends while holding the lock; another future on the same thread tries to acquire, blocks the executor). Under `multi_thread` it works but serializes the system.
133
+
134
+ Fix:
135
+
136
+ ```rust
137
+ {
138
+ let mut guard = m.lock().unwrap();
139
+ *guard += 1;
140
+ } // guard released
141
+ something_async().await;
142
+ ```
143
+
144
+ Or switch to `tokio::sync::Mutex` whose guard is `Send` across awaits.
145
+
146
+ ## Channels
147
+
148
+ ### Mpsc — the workhorse
149
+
150
+ ```rust
151
+ let (tx, mut rx) = tokio::sync::mpsc::channel::<Job>(256);
152
+
153
+ tokio::spawn(async move {
154
+ while let Some(job) = rx.recv().await {
155
+ process(job).await;
156
+ }
157
+ });
158
+
159
+ tx.send(Job::new()).await?; // backpressure: awaits if full
160
+ ```
161
+
162
+ Capacity is the backpressure budget. **Never `unbounded_channel()`** unless you have a hard upper bound elsewhere; otherwise it is a slow-leak memory bomb.
163
+
164
+ ### Watch — latest-value pubsub
165
+
166
+ ```rust
167
+ let (tx, mut rx) = tokio::sync::watch::channel(Config::default());
168
+
169
+ // Producer:
170
+ tx.send(new_config)?;
171
+
172
+ // Consumer:
173
+ loop {
174
+ rx.changed().await?;
175
+ let cfg = rx.borrow();
176
+ apply(&cfg);
177
+ }
178
+ ```
179
+
180
+ Receivers see only the latest value (older updates are dropped). Perfect for config reload, leadership changes, "current time" propagation.
181
+
182
+ ### Broadcast — fanout queue
183
+
184
+ ```rust
185
+ let (tx, _) = tokio::sync::broadcast::channel::<Event>(1024);
186
+ let mut rx1 = tx.subscribe();
187
+ let mut rx2 = tx.subscribe();
188
+
189
+ while let Ok(event) = rx1.recv().await {
190
+ // ...
191
+ }
192
+ ```
193
+
194
+ Each subscriber has its own buffer. If a subscriber falls behind by more than the buffer size, it gets `RecvError::Lagged(n)` and skips messages. Decide explicitly: log + continue, or drop the subscriber and reconnect.
195
+
196
+ ### Oneshot — single value
197
+
198
+ ```rust
199
+ let (tx, rx) = tokio::sync::oneshot::channel::<Response>();
200
+ worker.send(Request { reply: tx }).await?;
201
+ let response = rx.await?;
202
+ ```
203
+
204
+ The standard request/response pattern over an actor.
205
+
206
+ ## Semaphores
207
+
208
+ Bound concurrent operations:
209
+
210
+ ```rust
211
+ let sem = Arc::new(tokio::sync::Semaphore::new(10));
212
+
213
+ for task in tasks {
214
+ let permit = sem.clone().acquire_owned().await?;
215
+ tokio::spawn(async move {
216
+ let _hold = permit; // released when task exits
217
+ process(task).await
218
+ });
219
+ }
220
+ ```
221
+
222
+ Use cases:
223
+ - "Max 10 outbound HTTP requests in flight."
224
+ - "Max 3 DB connections doing writes."
225
+ - "Max N tokio tasks running heavy CPU."
226
+
227
+ A semaphore with `permits=1` is a mutex. Use the actual `Mutex` for that — clearer intent.
228
+
229
+ ## Arc and Rc
230
+
231
+ `Arc<T>` for cross-thread shared ownership, `Rc<T>` for single-thread (never spans threads).
232
+
233
+ ```rust
234
+ let shared = Arc::new(BigData::new());
235
+ for _ in 0..workers {
236
+ let s = shared.clone();
237
+ tokio::spawn(async move { use_data(&s).await });
238
+ }
239
+ ```
240
+
241
+ `Arc::clone(&s)` is just a reference-count increment; the data is not copied.
242
+
243
+ **Do not clone in hot loops** if you can pass a reference. `&Arc<T>` is fine to pass; only call `Arc::clone` when you need to move ownership across a thread/task boundary.
244
+
245
+ `Weak<T>` for back-references in graphs / parent pointers to avoid cycles.
246
+
247
+ ## Once-init primitives
248
+
249
+ ```rust
250
+ use std::sync::{OnceLock, LazyLock};
251
+
252
+ // Lazy initialization, computed on first read
253
+ static CONFIG: LazyLock<Config> = LazyLock::new(|| Config::load_from_env().unwrap());
254
+
255
+ fn get_config() -> &'static Config {
256
+ &CONFIG
257
+ }
258
+
259
+ // One-shot publication, set explicitly
260
+ static DB: OnceLock<sqlx::PgPool> = OnceLock::new();
261
+
262
+ #[tokio::main]
263
+ async fn main() {
264
+ let pool = sqlx::PgPool::connect(&env_url()).await.unwrap();
265
+ DB.set(pool).expect("only set once");
266
+ // Now everywhere: DB.get().unwrap()
267
+ }
268
+ ```
269
+
270
+ `OnceLock` is `std::sync` and stable. `LazyLock` is in `std::sync` since 1.80. Avoid the older `once_cell` crate for new code.
271
+
272
+ ## Loom — model-checking lock-free code
273
+
274
+ When `unsafe` participates in a concurrent algorithm, miri's single-thread model is insufficient. Loom exhaustively explores thread interleavings.
275
+
276
+ `Cargo.toml`:
277
+
278
+ ```toml
279
+ [target.'cfg(loom)'.dev-dependencies]
280
+ loom = "0.7"
281
+ ```
282
+
283
+ In code, switch between real and loom primitives:
284
+
285
+ ```rust
286
+ #[cfg(loom)]
287
+ use loom::sync::atomic::{AtomicUsize, Ordering};
288
+ #[cfg(not(loom))]
289
+ use std::sync::atomic::{AtomicUsize, Ordering};
290
+
291
+ #[cfg(loom)]
292
+ use loom::sync::Arc;
293
+ #[cfg(not(loom))]
294
+ use std::sync::Arc;
295
+ ```
296
+
297
+ Write a test:
298
+
299
+ ```rust
300
+ #[cfg(loom)]
301
+ mod loom_tests {
302
+ use super::*;
303
+ use loom::thread;
304
+
305
+ #[test]
306
+ fn concurrent_push_pop_preserves_order() {
307
+ loom::model(|| {
308
+ let queue = Arc::new(MyQueue::new());
309
+ let q1 = queue.clone();
310
+ let q2 = queue.clone();
311
+ let h1 = thread::spawn(move || q1.push(1));
312
+ let h2 = thread::spawn(move || q2.pop());
313
+ h1.join().unwrap();
314
+ h2.join().unwrap();
315
+ // Assert the invariant: queue is in a coherent state.
316
+ });
317
+ }
318
+ }
319
+ ```
320
+
321
+ Run:
322
+
323
+ ```bash
324
+ RUSTFLAGS="--cfg loom" cargo test --release -- --test-threads 1
325
+ ```
326
+
327
+ Loom explores every legal scheduling of the threads, including those a real scheduler would rarely produce. If your code has a race, loom will find it deterministically.
328
+
329
+ ### Loom's limits
330
+
331
+ - Slow. Each `loom::model` invocation explores many schedules; keep tests tiny (2-3 threads, a few operations each).
332
+ - Single-machine only. Doesn't model distributed systems.
333
+ - Doesn't catch UB inside `unsafe` blocks the way miri does. **Run both: miri for memory safety, loom for thread schedules.**
334
+ - Doesn't handle `tokio` directly. Loom replaces stdlib's sync primitives; tokio's are independent.
335
+
336
+ ## Send and Sync — what they mean
337
+
338
+ - `T: Send` — `T` can be moved between threads safely.
339
+ - `T: Sync` — `&T` can be shared between threads safely.
340
+
341
+ These are auto-derived for composite types if all components implement them. Manual `unsafe impl Send/Sync` is required only for raw pointer types and FFI handles.
342
+
343
+ ```rust
344
+ struct MyHandle { raw: *mut FfiObject }
345
+
346
+ // SAFETY: FfiObject's documented contract states that move-between-threads
347
+ // is safe as long as concurrent use is externally synchronized. We do not
348
+ // implement Sync because the FFI object is single-threaded once obtained.
349
+ unsafe impl Send for MyHandle {}
350
+ // Do NOT impl Sync — the FFI is not thread-safe.
351
+ ```
352
+
353
+ When the compiler complains that "T: Send is not satisfied", the cause is usually a raw pointer, an `Rc` (not `Arc`), or a `RefCell` (use `Mutex`).
354
+
355
+ ## Common mistakes
356
+
357
+ 1. **Holding a `std::sync::Mutex` guard across `.await`.** Compiles, deadlocks at runtime under `current_thread`.
358
+ 2. **`Arc::clone` in a tight loop.** Refcount bump is cheap but not free; pass `&Arc<T>` when possible.
359
+ 3. **`Mutex<HashMap<K, V>>` for hot reads.** Switch to `RwLock` or `Arc<dashmap::DashMap>`.
360
+ 4. **Atomic operations with `Ordering::Relaxed` for happens-before publication.** You need `Release`/`Acquire`. Run under loom to be sure.
361
+ 5. **Unbounded channels.** Always set capacity. If you "know it won't backlog", you don't, and it will.
362
+ 6. **Spawning detached tokio tasks for fire-and-forget cleanup.** Use `JoinSet` so panics surface.
363
+ 7. **`std::mem::transmute` to fake `Send`/`Sync`.** Use `unsafe impl` with a SAFETY comment instead. Transmute breaks Stacked Borrows and miri.
364
+ 8. **Locking order inversion across two mutexes.** Always acquire in a globally consistent order. For more than three locks, switch to a single mutex around a struct.
365
+
366
+ ## When to escape to lock-free
367
+
368
+ You should reach for atomics + `UnsafeCell` only when:
369
+ 1. The hot path is **measured** to be bottlenecked on lock contention.
370
+ 2. There is no existing library (crossbeam, atomic-queue, hazardous) that solves your problem.
371
+ 3. You can write loom tests that pass.
372
+ 4. You can write miri tests that pass.
373
+ 5. You have at least one other engineer who can review the algorithm.
374
+
375
+ Practically all "I want to write a lock-free queue" projects fail (3) or (4). When in doubt, take the lock and move on.