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.
- package/CHANGELOG.md +155 -0
- package/LICENSE +21 -0
- package/README.md +369 -0
- package/README_ko-KR.md +374 -0
- package/RELEASE_CHECKLIST.md +165 -0
- package/bin/litclaude-ai.js +643 -0
- package/cover.png +0 -0
- package/docs/agents.md +67 -0
- package/docs/hooks.md +134 -0
- package/docs/lsp.md +40 -0
- package/docs/migration.md +209 -0
- package/docs/workflow-compatibility-audit.md +119 -0
- package/generate_cover.py +123 -0
- package/package.json +48 -0
- package/plugins/litclaude/.claude-plugin/plugin.json +25 -0
- package/plugins/litclaude/.lsp.json +13 -0
- package/plugins/litclaude/.mcp.json +9 -0
- package/plugins/litclaude/agents/boulder-executor.md +12 -0
- package/plugins/litclaude/agents/librarian-researcher.md +15 -0
- package/plugins/litclaude/agents/oracle-verifier.md +16 -0
- package/plugins/litclaude/agents/prometheus-planner.md +13 -0
- package/plugins/litclaude/agents/qa-runner.md +16 -0
- package/plugins/litclaude/agents/quality-reviewer.md +17 -0
- package/plugins/litclaude/bin/litclaude-hook.js +110 -0
- package/plugins/litclaude/bin/litclaude-hud.js +271 -0
- package/plugins/litclaude/bin/litclaude-lsp-doctor.js +15 -0
- package/plugins/litclaude/bin/litclaude-mcp.js +70 -0
- package/plugins/litclaude/commands/deep-interview.md +21 -0
- package/plugins/litclaude/commands/dynamic-workflow.md +36 -0
- package/plugins/litclaude/commands/lit-loop.md +40 -0
- package/plugins/litclaude/commands/lit-plan.md +35 -0
- package/plugins/litclaude/commands/litgoal.md +30 -0
- package/plugins/litclaude/commands/review-work.md +35 -0
- package/plugins/litclaude/commands/start-work.md +36 -0
- package/plugins/litclaude/hooks/hooks.json +54 -0
- package/plugins/litclaude/lib/context-pressure.mjs +25 -0
- package/plugins/litclaude/lib/hud-accent-palette.mjs +58 -0
- package/plugins/litclaude/lib/litgoal/cli.mjs +266 -0
- package/plugins/litclaude/lib/litgoal/ledger.mjs +16 -0
- package/plugins/litclaude/lib/litgoal/paths.mjs +7 -0
- package/plugins/litclaude/lib/litgoal/state.mjs +67 -0
- package/plugins/litclaude/lib/mutated-file-paths.mjs +63 -0
- package/plugins/litclaude/lib/start-work-continuation.mjs +99 -0
- package/plugins/litclaude/lib/workflow-check.mjs +83 -0
- package/plugins/litclaude/skills/ai-slop-remover/SKILL.md +142 -0
- package/plugins/litclaude/skills/comment-checker/SKILL.md +55 -0
- package/plugins/litclaude/skills/debugging/SKILL.md +70 -0
- package/plugins/litclaude/skills/debugging/references/methodology/00-setup.md +108 -0
- package/plugins/litclaude/skills/debugging/references/methodology/02-investigate.md +126 -0
- package/plugins/litclaude/skills/debugging/references/methodology/04-oracle-triple.md +106 -0
- package/plugins/litclaude/skills/debugging/references/methodology/05-escalate.md +69 -0
- package/plugins/litclaude/skills/debugging/references/methodology/06-fix.md +116 -0
- package/plugins/litclaude/skills/debugging/references/methodology/08-qa.md +94 -0
- package/plugins/litclaude/skills/debugging/references/methodology/09-cleanup.md +164 -0
- package/plugins/litclaude/skills/debugging/references/methodology/partial-runtime-evidence.md +228 -0
- package/plugins/litclaude/skills/debugging/references/runtimes/bundled-js-binary.md +415 -0
- package/plugins/litclaude/skills/debugging/references/runtimes/go.md +252 -0
- package/plugins/litclaude/skills/debugging/references/runtimes/native-binary.md +484 -0
- package/plugins/litclaude/skills/debugging/references/runtimes/node.md +260 -0
- package/plugins/litclaude/skills/debugging/references/runtimes/python.md +248 -0
- package/plugins/litclaude/skills/debugging/references/runtimes/rust.md +234 -0
- package/plugins/litclaude/skills/debugging/references/tools/ghidra.md +212 -0
- package/plugins/litclaude/skills/debugging/references/tools/playwright-cli.md +194 -0
- package/plugins/litclaude/skills/debugging/references/tools/pwndbg.md +263 -0
- package/plugins/litclaude/skills/debugging/references/tools/pwntools.md +265 -0
- package/plugins/litclaude/skills/deep-interview/SKILL.md +323 -0
- package/plugins/litclaude/skills/deep-interview/scripts/render_progress.py +193 -0
- package/plugins/litclaude/skills/frontend-ui-ux/SKILL.md +62 -0
- package/plugins/litclaude/skills/lit-loop/SKILL.md +144 -0
- package/plugins/litclaude/skills/lit-plan/SKILL.md +125 -0
- package/plugins/litclaude/skills/litgoal/SKILL.md +219 -0
- package/plugins/litclaude/skills/lsp/SKILL.md +63 -0
- package/plugins/litclaude/skills/programming/SKILL.md +106 -0
- package/plugins/litclaude/skills/programming/references/go/README.md +90 -0
- package/plugins/litclaude/skills/programming/references/go/backend-stack.md +641 -0
- package/plugins/litclaude/skills/programming/references/go/bootstrap.md +328 -0
- package/plugins/litclaude/skills/programming/references/go/bubbletea-v2.md +360 -0
- package/plugins/litclaude/skills/programming/references/go/cobra-stack.md +468 -0
- package/plugins/litclaude/skills/programming/references/go/concurrency.md +362 -0
- package/plugins/litclaude/skills/programming/references/go/data-modeling.md +329 -0
- package/plugins/litclaude/skills/programming/references/go/error-handling.md +359 -0
- package/plugins/litclaude/skills/programming/references/go/golangci-strict.md +236 -0
- package/plugins/litclaude/skills/programming/references/go/grpc-connect.md +375 -0
- package/plugins/litclaude/skills/programming/references/go/libraries.md +337 -0
- package/plugins/litclaude/skills/programming/references/go/one-liners.md +202 -0
- package/plugins/litclaude/skills/programming/references/go/sqlc-pgx.md +471 -0
- package/plugins/litclaude/skills/programming/references/go/testing.md +467 -0
- package/plugins/litclaude/skills/programming/references/go/type-patterns.md +298 -0
- package/plugins/litclaude/skills/programming/references/python/README.md +314 -0
- package/plugins/litclaude/skills/programming/references/python/async-anyio.md +442 -0
- package/plugins/litclaude/skills/programming/references/python/data-modeling.md +233 -0
- package/plugins/litclaude/skills/programming/references/python/data-processing.md +133 -0
- package/plugins/litclaude/skills/programming/references/python/error-handling.md +218 -0
- package/plugins/litclaude/skills/programming/references/python/fastapi-stack.md +316 -0
- package/plugins/litclaude/skills/programming/references/python/httpx2-optimization.md +360 -0
- package/plugins/litclaude/skills/programming/references/python/libraries.md +307 -0
- package/plugins/litclaude/skills/programming/references/python/one-liners.md +268 -0
- package/plugins/litclaude/skills/programming/references/python/orjson-stack.md +378 -0
- package/plugins/litclaude/skills/programming/references/python/pydantic-ai.md +285 -0
- package/plugins/litclaude/skills/programming/references/python/pyproject-strict.md +232 -0
- package/plugins/litclaude/skills/programming/references/python/textual-tui.md +201 -0
- package/plugins/litclaude/skills/programming/references/python/type-patterns.md +176 -0
- package/plugins/litclaude/skills/programming/references/rust/README.md +317 -0
- package/plugins/litclaude/skills/programming/references/rust/async-tokio.md +299 -0
- package/plugins/litclaude/skills/programming/references/rust/axum-stack.md +467 -0
- package/plugins/litclaude/skills/programming/references/rust/cargo-strict.md +317 -0
- package/plugins/litclaude/skills/programming/references/rust/clap-stack.md +409 -0
- package/plugins/litclaude/skills/programming/references/rust/concurrency.md +375 -0
- package/plugins/litclaude/skills/programming/references/rust/libraries.md +439 -0
- package/plugins/litclaude/skills/programming/references/rust/one-liners.md +291 -0
- package/plugins/litclaude/skills/programming/references/rust/proptest-insta.md +429 -0
- package/plugins/litclaude/skills/programming/references/rust/type-state.md +354 -0
- package/plugins/litclaude/skills/programming/references/rust/unsafe-discipline.md +250 -0
- package/plugins/litclaude/skills/programming/references/rust/zero-cost-safety.md +527 -0
- package/plugins/litclaude/skills/programming/references/rust-ub/README.md +289 -0
- package/plugins/litclaude/skills/programming/references/rust-ub/miri-sanitizers-loom.md +411 -0
- package/plugins/litclaude/skills/programming/references/rust-ub/ub-taxonomy.md +269 -0
- package/plugins/litclaude/skills/programming/references/typescript/README.md +195 -0
- package/plugins/litclaude/skills/programming/references/typescript/backend-hono.md +672 -0
- package/plugins/litclaude/skills/programming/references/typescript/bootstrap.md +199 -0
- package/plugins/litclaude/skills/programming/references/typescript/data-modeling.md +202 -0
- package/plugins/litclaude/skills/programming/references/typescript/error-handling.md +169 -0
- package/plugins/litclaude/skills/programming/references/typescript/tsconfig-strict.md +152 -0
- package/plugins/litclaude/skills/programming/references/typescript/type-patterns.md +196 -0
- package/plugins/litclaude/skills/programming/scripts/go/check-no-excuse-rules.sh +173 -0
- package/plugins/litclaude/skills/programming/scripts/go/new-project.py +138 -0
- package/plugins/litclaude/skills/programming/scripts/go/templates/.editorconfig +13 -0
- package/plugins/litclaude/skills/programming/scripts/go/templates/.golangci.yml +95 -0
- package/plugins/litclaude/skills/programming/scripts/go/templates/AGENTS.md.tmpl +24 -0
- package/plugins/litclaude/skills/programming/scripts/go/templates/README.md.tmpl +12 -0
- package/plugins/litclaude/skills/programming/scripts/go/templates/Taskfile.yml +40 -0
- package/plugins/litclaude/skills/programming/scripts/go/templates/ci.yml +37 -0
- package/plugins/litclaude/skills/programming/scripts/go/templates/config.go +24 -0
- package/plugins/litclaude/skills/programming/scripts/go/templates/gitignore +15 -0
- package/plugins/litclaude/skills/programming/scripts/go/templates/main.go.tmpl +22 -0
- package/plugins/litclaude/skills/programming/scripts/go/templates/run.go +15 -0
- package/plugins/litclaude/skills/programming/scripts/python/check-no-excuse-rules.py +687 -0
- package/plugins/litclaude/skills/programming/scripts/python/new-project.py +172 -0
- package/plugins/litclaude/skills/programming/scripts/python/new-script.py +116 -0
- package/plugins/litclaude/skills/programming/scripts/rust/check-no-excuse-rules.py +296 -0
- package/plugins/litclaude/skills/programming/scripts/rust/check-no-excuse-rules.sh +158 -0
- package/plugins/litclaude/skills/programming/scripts/rust/new-project.py +175 -0
- package/plugins/litclaude/skills/programming/scripts/typescript/check-no-excuse-rules.ts +282 -0
- package/plugins/litclaude/skills/programming/scripts/typescript/new-project.ts +177 -0
- package/plugins/litclaude/skills/refactor/SKILL.md +73 -0
- package/plugins/litclaude/skills/remove-ai-slops/SKILL.md +52 -0
- package/plugins/litclaude/skills/review-work/SKILL.md +331 -0
- package/plugins/litclaude/skills/rules/SKILL.md +66 -0
- package/plugins/litclaude/skills/start-work/SKILL.md +132 -0
- package/scripts/audit-plan-checkboxes.mjs +37 -0
- package/scripts/doctor.mjs +41 -0
- package/scripts/inspect-agent-tools.mjs +27 -0
- package/scripts/postinstall.mjs +50 -0
- package/scripts/qa-claude-plugin-smoke.sh +60 -0
- package/scripts/qa-portable-install.sh +136 -0
- package/scripts/validate-plugin.mjs +72 -0
|
@@ -0,0 +1,269 @@
|
|
|
1
|
+
# Rust Undefined Behavior Taxonomy
|
|
2
|
+
|
|
3
|
+
Every category of UB the Rust compiler, Miri, and the language specification recognize. The agent must know the full surface to hunt systematically. Each entry names the UB class, its root cause, canonical trigger, Miri detection status, and the canonical fix.
|
|
4
|
+
|
|
5
|
+
## 1. Aliasing Violations (Stacked Borrows / Tree Borrows)
|
|
6
|
+
|
|
7
|
+
**Root cause:** Two pointers access the same memory in ways that violate Rust's borrowing model — even through raw pointers inside `unsafe`.
|
|
8
|
+
|
|
9
|
+
**Canonical triggers:**
|
|
10
|
+
- Creating a `&mut T` while another `&T` or `&mut T` to the same location exists.
|
|
11
|
+
- Dereferencing a raw pointer derived from a reference after that reference was invalidated (e.g., `&mut` was retaken).
|
|
12
|
+
- Calling `slice::from_raw_parts_mut` on overlapping regions.
|
|
13
|
+
- Interior mutability through `UnsafeCell` without going through the `UnsafeCell` API.
|
|
14
|
+
- Casting `&T` to `*mut T` and writing through it (even via FFI).
|
|
15
|
+
|
|
16
|
+
**Miri detection:** YES — Stacked Borrows is the default model. Tree Borrows (`-Zmiri-tree-borrows`) is the newer, more permissive model. Run both:
|
|
17
|
+
```bash
|
|
18
|
+
cargo +nightly miri test # Stacked Borrows (stricter)
|
|
19
|
+
MIRIFLAGS="-Zmiri-tree-borrows" cargo +nightly miri test # Tree Borrows (relaxed)
|
|
20
|
+
```
|
|
21
|
+
If code passes Tree Borrows but fails Stacked Borrows, it is *likely* sound but *possibly* relying on unspecified behavior. Fix it anyway — Stacked Borrows is the conservative bet.
|
|
22
|
+
|
|
23
|
+
**Fix pattern:** Use `UnsafeCell` for all interior mutability. Never cast `&T` to `*mut T`. Derive mutable pointers from `*mut T` obtained via `UnsafeCell::get()` or `addr_of_mut!()`.
|
|
24
|
+
|
|
25
|
+
---
|
|
26
|
+
|
|
27
|
+
## 2. Data Races
|
|
28
|
+
|
|
29
|
+
**Root cause:** Two threads access the same non-atomic memory location, at least one is a write, and there is no happens-before ordering between them.
|
|
30
|
+
|
|
31
|
+
**Canonical triggers:**
|
|
32
|
+
- `unsafe impl Send for T` on a type containing `*mut U` without synchronization.
|
|
33
|
+
- `unsafe impl Sync for T` on a type containing `Cell<T>` or `UnsafeCell<T>` without a lock.
|
|
34
|
+
- Using `std::ptr::write` from multiple threads to the same allocation.
|
|
35
|
+
- Shared `&T` where `T` has interior mutability but no atomic/lock guard.
|
|
36
|
+
|
|
37
|
+
**Miri detection:** YES — Miri's data-race detector is on by default. It detects races on non-atomic accesses. For **preemptive scheduling** stress, use:
|
|
38
|
+
```bash
|
|
39
|
+
MIRIFLAGS="-Zmiri-preemption-rate=0.1" cargo +nightly miri test
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
**Complementary tools:** `loom` for exhaustive interleaving exploration on lock-free algorithms. ThreadSanitizer (TSAN) for integration tests Miri cannot run (I/O, FFI).
|
|
43
|
+
|
|
44
|
+
**Fix pattern:** Wrap in `Mutex`/`RwLock`/`AtomicXxx`. Never `unsafe impl Sync` unless you can name the synchronization primitive guarding every mutable field.
|
|
45
|
+
|
|
46
|
+
---
|
|
47
|
+
|
|
48
|
+
## 3. Use After Free / Dangling Pointers
|
|
49
|
+
|
|
50
|
+
**Root cause:** A pointer or reference outlives the allocation it points to.
|
|
51
|
+
|
|
52
|
+
**Canonical triggers:**
|
|
53
|
+
- Returning a reference to a local variable (compiler catches most, but raw pointers escape).
|
|
54
|
+
- `Box::into_raw` → manual `Box::from_raw` with wrong lifetime.
|
|
55
|
+
- `Vec` reallocation invalidating raw pointers obtained from `as_ptr()` / `as_mut_ptr()`.
|
|
56
|
+
- `Pin<Box<T>>` unpinned and moved after self-referential pointers were set up.
|
|
57
|
+
|
|
58
|
+
**Miri detection:** YES — allocation tracking catches use-after-free on the exact operation.
|
|
59
|
+
|
|
60
|
+
**Fix pattern:** Borrow checker for references. For raw pointers: tie pointer validity to an explicit lifetime via a `PhantomData<&'a T>` in the wrapper, or use arena allocation (`bumpalo`) so all pointers share one lifetime.
|
|
61
|
+
|
|
62
|
+
---
|
|
63
|
+
|
|
64
|
+
## 4. Uninitialized Memory
|
|
65
|
+
|
|
66
|
+
**Root cause:** Reading a value from memory that was never written to.
|
|
67
|
+
|
|
68
|
+
**Canonical triggers:**
|
|
69
|
+
- `MaybeUninit::assume_init()` before all bytes are written.
|
|
70
|
+
- `mem::uninitialized()` (deprecated, still compiles).
|
|
71
|
+
- `alloc::alloc(layout)` returns uninitialized memory — reading it before writing is UB.
|
|
72
|
+
- Padding bytes in structs read via `transmute` or raw pointer casts.
|
|
73
|
+
- `read_unaligned` on uninitialized memory.
|
|
74
|
+
|
|
75
|
+
**Miri detection:** YES — tracks initialization state per byte. Catches partial-init structs, padding reads, and premature `assume_init`.
|
|
76
|
+
|
|
77
|
+
**Fix pattern:** Use `MaybeUninit::zeroed()` when zero-init is acceptable. Write every field before calling `assume_init()`. Use `MaybeUninit::write()` instead of raw pointer writes. Never `transmute` structs with padding unless you zeroed the padding.
|
|
78
|
+
|
|
79
|
+
---
|
|
80
|
+
|
|
81
|
+
## 5. Invalid Values (Type Invariant Violations)
|
|
82
|
+
|
|
83
|
+
**Root cause:** Producing a value that violates the type's validity invariant.
|
|
84
|
+
|
|
85
|
+
**Canonical triggers:**
|
|
86
|
+
- `bool` not 0 or 1.
|
|
87
|
+
- `char` outside Unicode scalar range.
|
|
88
|
+
- Enum discriminant not matching any variant.
|
|
89
|
+
- `NonZeroU32` containing 0.
|
|
90
|
+
- `&T` or `&mut T` that is null or dangling.
|
|
91
|
+
- `str` containing non-UTF-8 bytes.
|
|
92
|
+
- `fn` pointer that is null.
|
|
93
|
+
|
|
94
|
+
**Miri detection:** YES — validity checks are on by default. Extra strictness:
|
|
95
|
+
```bash
|
|
96
|
+
MIRIFLAGS="-Zmiri-strict-provenance" cargo +nightly miri test
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
**Fix pattern:** Validate before transmuting. Use `TryFrom` at boundaries. Never `transmute` to enum types — use a checked conversion function.
|
|
100
|
+
|
|
101
|
+
---
|
|
102
|
+
|
|
103
|
+
## 6. Misaligned Pointer Access
|
|
104
|
+
|
|
105
|
+
**Root cause:** Dereferencing a pointer that is not aligned to the type's required alignment.
|
|
106
|
+
|
|
107
|
+
**Canonical triggers:**
|
|
108
|
+
- Casting `*const u8` to `*const u64` and dereferencing (alignment goes from 1 to 8).
|
|
109
|
+
- `#[repr(packed)]` struct field references (the compiler warns, but raw pointers bypass the warning).
|
|
110
|
+
- Network buffer parsing where offsets are arbitrary.
|
|
111
|
+
|
|
112
|
+
**Miri detection:** YES — immediate trap on misaligned read/write.
|
|
113
|
+
|
|
114
|
+
**Fix pattern:** Use `read_unaligned` / `write_unaligned` for packed data. Use `bytemuck` or `zerocopy` for safe reinterpretation with alignment checks.
|
|
115
|
+
|
|
116
|
+
---
|
|
117
|
+
|
|
118
|
+
## 7. Violating `Pin` Invariants
|
|
119
|
+
|
|
120
|
+
**Root cause:** Moving a value that was pinned and relied on its address stability (self-referential types, intrusive linked lists).
|
|
121
|
+
|
|
122
|
+
**Canonical triggers:**
|
|
123
|
+
- `mem::swap` on a `Pin<&mut T>` after `unsafe` deref.
|
|
124
|
+
- Implementing `Unpin` for a type that contains self-referential pointers.
|
|
125
|
+
- Manually calling `Pin::new_unchecked` on a movable allocation.
|
|
126
|
+
|
|
127
|
+
**Miri detection:** PARTIAL — Miri detects the resulting aliasing/use-after-free if the self-referential pointer is actually used. It does not detect "Pin contract violated but pointer was never dereferenced."
|
|
128
|
+
|
|
129
|
+
**Fix pattern:** Never `impl Unpin` for self-referential types. Use `pin_project` or `pin_project_lite` for safe pin projections. Review every `Pin::new_unchecked` call.
|
|
130
|
+
|
|
131
|
+
---
|
|
132
|
+
|
|
133
|
+
## 8. FFI Boundary UB
|
|
134
|
+
|
|
135
|
+
**Root cause:** Mismatch between Rust's ABI expectations and the foreign code's actual behavior.
|
|
136
|
+
|
|
137
|
+
**Canonical triggers:**
|
|
138
|
+
- C function returning uninitialized memory into a Rust `&T`.
|
|
139
|
+
- Wrong `#[repr(C)]` layout (padding differs between platforms).
|
|
140
|
+
- Passing a Rust `enum` to C without `#[repr(C)]` or `#[repr(i32)]`.
|
|
141
|
+
- Null pointer passed where C expects non-null (and Rust wraps it in `&T`).
|
|
142
|
+
- C code writing to Rust-owned memory through a pointer Rust considers immutable.
|
|
143
|
+
- Forgetting to mark FFI functions as `unsafe extern "C"`.
|
|
144
|
+
- longjmp/setjmp across Rust frames (unwinding UB).
|
|
145
|
+
|
|
146
|
+
**Miri detection:** LIMITED — Miri cannot execute foreign code. It detects UB in the Rust-side handling of FFI return values.
|
|
147
|
+
|
|
148
|
+
**Complementary tools:** AddressSanitizer (ASAN), MemorySanitizer (MSAN) for detecting actual FFI-side corruption. Valgrind as a last resort.
|
|
149
|
+
|
|
150
|
+
**Fix pattern:** Validate every FFI return at the boundary. Use `Option<NonNull<T>>` for nullable pointers. Use `CStr`/`CString` for strings. Add `cbindgen` to CI to verify layout agreement. Wrap every FFI call in a safe Rust function that checks preconditions.
|
|
151
|
+
|
|
152
|
+
---
|
|
153
|
+
|
|
154
|
+
## 9. Incorrect `Send` / `Sync` Implementations
|
|
155
|
+
|
|
156
|
+
**Root cause:** Manually implementing `Send` or `Sync` for a type that does not actually uphold the required invariant.
|
|
157
|
+
|
|
158
|
+
**Canonical triggers:**
|
|
159
|
+
- `unsafe impl Send for Wrapper(*mut T)` when `T` is not `Send`.
|
|
160
|
+
- `unsafe impl Sync for Wrapper(UnsafeCell<T>)` without a lock, atomic, or other synchronization.
|
|
161
|
+
- Types containing `Rc<T>` with a manual `Send` impl (Rc is explicitly !Send).
|
|
162
|
+
|
|
163
|
+
**Miri detection:** YES for the *resulting* data race if exercised. Miri's data-race detector will fire when two threads access the same location unsynchronized.
|
|
164
|
+
|
|
165
|
+
**Fix pattern:** Never manually implement `Send`/`Sync` unless you can write a SAFETY proof naming the synchronization mechanism. Use `PhantomData<*const ()>` to opt-out of auto-`Send`/`Sync` when in doubt.
|
|
166
|
+
|
|
167
|
+
---
|
|
168
|
+
|
|
169
|
+
## 10. Out-of-Bounds Memory Access
|
|
170
|
+
|
|
171
|
+
**Root cause:** Pointer arithmetic or indexing that escapes the allocation.
|
|
172
|
+
|
|
173
|
+
**Canonical triggers:**
|
|
174
|
+
- `ptr.offset(n)` where `n` exceeds the allocation size.
|
|
175
|
+
- `slice::from_raw_parts(ptr, len)` where `len` is too large.
|
|
176
|
+
- Off-by-one in manual buffer management.
|
|
177
|
+
- Integer overflow in size calculations leading to undersized allocation.
|
|
178
|
+
|
|
179
|
+
**Miri detection:** YES — allocation-precise bounds checking.
|
|
180
|
+
|
|
181
|
+
**Fix pattern:** Use checked arithmetic (`checked_add`, `checked_mul`) for size calculations. Use `slice::from_raw_parts` only with validated lengths. Prefer safe indexing (`get()`, iterators) over raw pointer arithmetic.
|
|
182
|
+
|
|
183
|
+
---
|
|
184
|
+
|
|
185
|
+
## 11. Provenance Violations
|
|
186
|
+
|
|
187
|
+
**Root cause:** Using a pointer whose provenance does not grant access to the target memory, even if the address is numerically correct.
|
|
188
|
+
|
|
189
|
+
**Canonical triggers:**
|
|
190
|
+
- Casting an integer to a pointer and dereferencing it (`addr as *const T`).
|
|
191
|
+
- Roundtripping a pointer through `usize` and back (`ptr as usize as *const T`) — the provenance is lost.
|
|
192
|
+
- Using `ptr::from_exposed_addr` without a corresponding `ptr.expose_provenance()`.
|
|
193
|
+
|
|
194
|
+
**Miri detection:** YES with strict provenance:
|
|
195
|
+
```bash
|
|
196
|
+
MIRIFLAGS="-Zmiri-strict-provenance" cargo +nightly miri test
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
**Fix pattern:** Use `ptr::with_exposed_provenance` / `ptr.expose_provenance()` for legitimate int-to-ptr roundtrips. Avoid `as usize as *const T` entirely. Use `sptr` crate for provenance-safe pointer manipulation on stable.
|
|
200
|
+
|
|
201
|
+
---
|
|
202
|
+
|
|
203
|
+
## 12. Double Free / Invalid Free
|
|
204
|
+
|
|
205
|
+
**Root cause:** Freeing the same allocation twice, or freeing memory not obtained from the allocator.
|
|
206
|
+
|
|
207
|
+
**Canonical triggers:**
|
|
208
|
+
- `Box::from_raw` called twice on the same pointer.
|
|
209
|
+
- Manual `dealloc` on a pointer already freed.
|
|
210
|
+
- `ManuallyDrop` dropped explicitly then the outer type also drops it.
|
|
211
|
+
|
|
212
|
+
**Miri detection:** YES — immediate trap.
|
|
213
|
+
|
|
214
|
+
**Fix pattern:** Enforce single ownership via RAII. Use `ManuallyDrop` with extreme care — document who is responsible for the drop. Never clone a raw pointer and `Box::from_raw` both copies.
|
|
215
|
+
|
|
216
|
+
---
|
|
217
|
+
|
|
218
|
+
## 13. Library / Unsafe Contract Violations
|
|
219
|
+
|
|
220
|
+
**Root cause:** Violating the documented safety invariant of a safe or unsafe API, where the library author relied on the invariant for soundness.
|
|
221
|
+
|
|
222
|
+
**Canonical triggers:**
|
|
223
|
+
- `Vec::set_len(n)` where the first `n` elements are not initialized.
|
|
224
|
+
- `String::from_utf8_unchecked` on non-UTF-8 bytes.
|
|
225
|
+
- `HashMap` key mutated after insertion (violates hash invariant — not UB per se, but unsound and Miri may detect downstream effects).
|
|
226
|
+
- `BTreeMap` key with broken `Ord` impl (the standard library assumes a total order).
|
|
227
|
+
|
|
228
|
+
**Miri detection:** DEPENDS — Miri catches the downstream UB (e.g., reading uninitialized bytes from a `Vec` with inflated len). It does not catch "you violated the documented contract" if no memory-level UB results.
|
|
229
|
+
|
|
230
|
+
**Fix pattern:** Read the `# Safety` section of every `unsafe fn` you call. Document the invariant in your SAFETY comment. When in doubt, use the safe API and pay the cost.
|
|
231
|
+
|
|
232
|
+
---
|
|
233
|
+
|
|
234
|
+
## 14. Unwinding Across `extern "C"` Boundaries
|
|
235
|
+
|
|
236
|
+
**Root cause:** A Rust panic unwinding through a frame that uses the C calling convention.
|
|
237
|
+
|
|
238
|
+
**Canonical triggers:**
|
|
239
|
+
- `panic!()` inside a `#[no_mangle] extern "C" fn` callback passed to C code.
|
|
240
|
+
- `unwrap()` inside FFI callbacks.
|
|
241
|
+
|
|
242
|
+
**Miri detection:** PARTIAL — Miri does not model foreign unwinding, but it can detect the immediate UB if the panic reaches the FFI boundary.
|
|
243
|
+
|
|
244
|
+
**Fix pattern:** Use `std::panic::catch_unwind` at every FFI entry point. Mark FFI callbacks as `extern "C-unwind"` when panic propagation is intentional (nightly). Prefer returning `Result`-like error codes from FFI callbacks.
|
|
245
|
+
|
|
246
|
+
---
|
|
247
|
+
|
|
248
|
+
## Summary Table
|
|
249
|
+
|
|
250
|
+
| # | Category | Miri Detects? | Complementary Tool |
|
|
251
|
+
|---|----------|--------------|-------------------|
|
|
252
|
+
| 1 | Aliasing (Stacked/Tree Borrows) | YES | — |
|
|
253
|
+
| 2 | Data races | YES | loom, TSAN |
|
|
254
|
+
| 3 | Use-after-free / dangling | YES | ASAN |
|
|
255
|
+
| 4 | Uninitialized memory | YES | MSAN |
|
|
256
|
+
| 5 | Invalid values | YES | — |
|
|
257
|
+
| 6 | Misaligned access | YES | UBSAN |
|
|
258
|
+
| 7 | Pin invariant violation | PARTIAL | manual review |
|
|
259
|
+
| 8 | FFI boundary UB | LIMITED | ASAN, MSAN, Valgrind |
|
|
260
|
+
| 9 | Incorrect Send/Sync | YES (via race) | loom |
|
|
261
|
+
| 10 | Out-of-bounds access | YES | ASAN |
|
|
262
|
+
| 11 | Provenance violations | YES (strict mode) | — |
|
|
263
|
+
| 12 | Double free | YES | ASAN |
|
|
264
|
+
| 13 | Library contract violations | PARTIAL | proptest, fuzzing |
|
|
265
|
+
| 14 | Unwinding across FFI | PARTIAL | — |
|
|
266
|
+
|
|
267
|
+
## Miri Coverage Assessment
|
|
268
|
+
|
|
269
|
+
Miri catches categories 1-6, 9-12 with high confidence. Categories 7, 8, 13, 14 require supplementary tools or manual audit. **Miri is the single highest-leverage tool** — it should run on every PR that touches `unsafe`, and ideally on the full test suite regularly.
|
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
|
|
2
|
+
# TypeScript Programmer
|
|
3
|
+
|
|
4
|
+
Modern TypeScript. Type-strict, stack-first, async-correct.
|
|
5
|
+
|
|
6
|
+
## Philosophy
|
|
7
|
+
|
|
8
|
+
The compiler is your proof system. Make illegal states unrepresentable. Parse at boundaries. Every function has a contract; the type system enforces it.
|
|
9
|
+
|
|
10
|
+
## Hard rules
|
|
11
|
+
|
|
12
|
+
These are deliberate project choices. Violations are always wrong, not "style preferences".
|
|
13
|
+
|
|
14
|
+
### Tooling
|
|
15
|
+
|
|
16
|
+
| Category | Use | Never |
|
|
17
|
+
|---|---|---|
|
|
18
|
+
| Runtime | Bun (native TS, single binary) | ts-node, tsx |
|
|
19
|
+
| Package manager | `pnpm` | npm, yarn (unless workspace requires it) |
|
|
20
|
+
| Linter + formatter | Biome | ESLint, Prettier |
|
|
21
|
+
| Type checker | `tsc --noEmit` with strict config | skip type checking |
|
|
22
|
+
| Web framework | Hono | Express |
|
|
23
|
+
| Validation | Zod | joi, yup, class-validator |
|
|
24
|
+
| Testing | `bun test` or vitest | jest |
|
|
25
|
+
| ORM | Drizzle | TypeORM, Prisma (unless already in project) |
|
|
26
|
+
|
|
27
|
+
### The iron list
|
|
28
|
+
|
|
29
|
+
1. **Readonly by default** — all `type`/`interface` properties are `readonly`. Arrays are `readonly T[]`. Mutable only when mutation is the documented purpose.
|
|
30
|
+
2. **Branded types for distinct IDs** — `type UserId = Brand<string, "UserId">`. Never pass raw `string` where a branded type exists.
|
|
31
|
+
3. **Exhaustive switch** — every `switch` on a discriminated union ends with `default: assertNever(x)`. No fall-through.
|
|
32
|
+
4. **No any** — `any` is banned in annotations, returns, and parameters. Use `unknown` and narrow.
|
|
33
|
+
5. **No type assertions** — `as any`, `as unknown` banned. `as const` and `satisfies` are fine.
|
|
34
|
+
6. **No non-null assertion** — `x!` is banned. Use narrowing or optional chaining (`x?.y`).
|
|
35
|
+
7. **No @ts-ignore / @ts-expect-error** — fix the type.
|
|
36
|
+
8. **No enum** — use `as const` objects + literal union types.
|
|
37
|
+
9. **Zod at boundaries** — external input (API, user, file) → Zod schema. Internal → plain types.
|
|
38
|
+
10. **Typed errors** — Error subclasses with typed fields. No `throw new Error("bare string")` for domain errors. Use Result for expected failures within 1-2 call levels; throw for propagation across many layers.
|
|
39
|
+
11. **as const for constants** — module-level constant objects and arrays use `as const`.
|
|
40
|
+
12. **import type** — type-only imports use `import type`. Enforced by `verbatimModuleSyntax`.
|
|
41
|
+
13. **Named exports only** — no `export default`. Exception: framework requirement (Next.js pages, etc.).
|
|
42
|
+
14. **No empty catch, no catch-and-swallow** — every `catch` block must either (a) narrow the error with `instanceof` and handle each case, or (b) re-throw. Empty catch blocks and `catch (e) { console.error(e) }` without narrowing or re-throw are banned — they hide bugs. At top-level boundaries (CLI entry, HTTP handler), opt out with `// no-excuse-ok: catch`.
|
|
43
|
+
|
|
44
|
+
### Data modeling — which construct, when
|
|
45
|
+
|
|
46
|
+
| Situation | Use |
|
|
47
|
+
|---|---|
|
|
48
|
+
| User input, API request/response | Zod schema + `z.infer` |
|
|
49
|
+
| Internal value object | `type` with `readonly` properties |
|
|
50
|
+
| Function with multiple outcomes | Discriminated union (`kind` field) |
|
|
51
|
+
| Contract for implementations | `interface` |
|
|
52
|
+
| Fixed constants | `as const` + literal union |
|
|
53
|
+
| Distinct primitive (UserId vs OrderId) | Branded type |
|
|
54
|
+
| Key-value map | `Record<K, V>` or index signature |
|
|
55
|
+
|
|
56
|
+
**The one rule**: data crosses trust boundary → Zod. Everything else → plain `type` with `readonly`.
|
|
57
|
+
|
|
58
|
+
Load `data-modeling.md` for the full decision flowchart and comparison.
|
|
59
|
+
|
|
60
|
+
### When readonly does not apply
|
|
61
|
+
|
|
62
|
+
- **Framework state** (React `useState`, signals) — managed by framework.
|
|
63
|
+
- **Builder / accumulator** — object exists to be mutated (buffer, cache). Document why.
|
|
64
|
+
- **ORM mutations** — Drizzle insert/update objects.
|
|
65
|
+
|
|
66
|
+
### Why empty/unhandled catch is banned
|
|
67
|
+
|
|
68
|
+
In TypeScript, every `catch` receives `unknown`. The language gives you no type safety in catch blocks — you must earn it with `instanceof`. A bare `catch (e) { console.error(e) }` swallows `TypeError`, `RangeError`, and your domain errors identically. When a new error type appears, nothing warns you.
|
|
69
|
+
|
|
70
|
+
```typescript
|
|
71
|
+
// BANNED — empty catch
|
|
72
|
+
try { await fetchData() } catch {}
|
|
73
|
+
try { await fetchData() } catch (e) { /* will fix later */ }
|
|
74
|
+
|
|
75
|
+
// BANNED — catch-and-swallow (no narrowing, no rethrow)
|
|
76
|
+
try {
|
|
77
|
+
const data = await api.get("/users")
|
|
78
|
+
} catch (e) {
|
|
79
|
+
console.error("failed", e)
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// GOOD — narrow with instanceof
|
|
83
|
+
try {
|
|
84
|
+
const data = await api.get("/users")
|
|
85
|
+
} catch (e) {
|
|
86
|
+
if (e instanceof HttpError) {
|
|
87
|
+
logger.warn(`API ${e.status}: ${e.message}`)
|
|
88
|
+
return fallback
|
|
89
|
+
}
|
|
90
|
+
throw e // unknown errors propagate
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
// GOOD — top-level boundary (only place catch-all is acceptable)
|
|
94
|
+
async function main(): Promise<void> { // no-excuse-ok: catch
|
|
95
|
+
try {
|
|
96
|
+
await run()
|
|
97
|
+
} catch (e) {
|
|
98
|
+
console.error("unhandled:", e)
|
|
99
|
+
process.exit(1)
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
### Libraries
|
|
105
|
+
|
|
106
|
+
| Domain | Library | Why |
|
|
107
|
+
|---|---|---|
|
|
108
|
+
| HTTP framework | Hono | Lightweight, multi-runtime, middleware, OpenAPI |
|
|
109
|
+
| Validation | Zod | Runtime validation + type inference |
|
|
110
|
+
| ORM | Drizzle | Type-safe SQL, no codegen |
|
|
111
|
+
| HTTP client | `ky` | Thin fetch wrapper (5KB); auto-throw on non-2xx, retry, timeout, hooks, prefixUrl. Browser + Node + Bun + Deno |
|
|
112
|
+
| HTTP client (perf) | `undici` (direct API) | Node backend에서 connection pooling, HTTP/2, pipelining 필요 시 |
|
|
113
|
+
|
|
114
|
+
> **HTTP client 규칙** — 프로덕션 코드에서 bare `fetch()`는 사용 금지. retry·timeout·에러 핸들링이 전무하여 장애 시 silent failure를 유발한다. **`ky`** 를 기본으로 설치하고, Node backend에서 대량 요청·커넥션 풀·HTTP/2 파이프라이닝이 필요하면 **`undici`** direct API를 쓴다. ~~`axios`~~ 는 supply-chain compromise(2026-03) 이후 사용 금지. `node-fetch`는 Node 18+ 내장 fetch로 대체되어 불필요.
|
|
115
|
+
| Testing | `bun test` / vitest | Fast, ESM-native |
|
|
116
|
+
| Logging | `pino` | Structured JSON, fast |
|
|
117
|
+
| CLI | `@clack/prompts` + `commander` | Interactive + parsing |
|
|
118
|
+
|
|
119
|
+
## tsconfig — the one true config
|
|
120
|
+
|
|
121
|
+
Scaffold a new project with all strict defaults pre-configured:
|
|
122
|
+
|
|
123
|
+
```bash
|
|
124
|
+
bun run ../../scripts/typescript/new-project.ts my-api
|
|
125
|
+
bun run ../../scripts/typescript/new-project.ts my-api --path ./projects
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
Creates: `package.json` (Hono + Zod + Biome), `tsconfig.json` (ultra-strict), `biome.json`, `src/index.ts`, `.gitignore`. Works on macOS, Linux, Windows.
|
|
129
|
+
|
|
130
|
+
For manual setup: `bunx tsc --init`, then load `tsconfig-strict.md` for the full strict config.
|
|
131
|
+
|
|
132
|
+
Key flags beyond `"strict": true`:
|
|
133
|
+
|
|
134
|
+
| Flag | What it catches |
|
|
135
|
+
|---|---|
|
|
136
|
+
| `noUncheckedIndexedAccess` | `arr[0]` is `T \| undefined`, forces check |
|
|
137
|
+
| `exactOptionalPropertyTypes` | `{ x?: string }` ≠ `{ x: string \| undefined }` |
|
|
138
|
+
| `verbatimModuleSyntax` | Forces `import type` for type-only imports |
|
|
139
|
+
| `noFallthroughCasesInSwitch` | Forgotten `break` / `return` |
|
|
140
|
+
| `noPropertyAccessFromIndexSignature` | `.key` on index sig → bracket notation |
|
|
141
|
+
|
|
142
|
+
## Reference loading
|
|
143
|
+
|
|
144
|
+
Load on demand — not all at once.
|
|
145
|
+
|
|
146
|
+
| Need | Load |
|
|
147
|
+
|---|---|
|
|
148
|
+
| Strict tsconfig + Biome config | `tsconfig-strict.md` |
|
|
149
|
+
| Type patterns (branded, as const, satisfies, narrowing, assertNever) | `type-patterns.md` |
|
|
150
|
+
| Data modeling (type vs interface vs Zod, readonly, parse-don't-validate) | `data-modeling.md` |
|
|
151
|
+
| Error handling (Result, typed errors, union vs throw) | `error-handling.md` |
|
|
152
|
+
| Bootstrapping a new project (Bun, pnpm, Hono, Vite) | `bootstrap.md` |
|
|
153
|
+
| Hono backend stack (hono-openapi, Scalar, Swagger) | `backend-hono.md` |
|
|
154
|
+
|
|
155
|
+
## No-excuse audit
|
|
156
|
+
|
|
157
|
+
Violations caught by `../../scripts/typescript/check-no-excuse-rules.ts`. Run after every edit session.
|
|
158
|
+
|
|
159
|
+
| Rule ID | Catches | Opt-out |
|
|
160
|
+
|---|---|---|
|
|
161
|
+
| `no-any-assertion` | `as any` | None — redesign types |
|
|
162
|
+
| `no-unknown-assertion` | `as unknown` | None — redesign types |
|
|
163
|
+
| `no-ts-ignore` | `@ts-ignore` | None — fix the type |
|
|
164
|
+
| `no-ts-expect-error` | `@ts-expect-error` | None — fix the type |
|
|
165
|
+
| `no-enum` | `enum` declarations | None — use `as const` |
|
|
166
|
+
| `no-non-null-assertion` | `x!` postfix | None — narrow or `?.` |
|
|
167
|
+
| `no-throw-literal` | `throw "string"` / `throw 123` | None — throw Error subclass |
|
|
168
|
+
| `no-mutable-export` | `export let` / `export var` | None — use `export const` |
|
|
169
|
+
| `no-any-annotation` | `: any` in parameter/return/variable types | `// no-excuse-ok: any` |
|
|
170
|
+
| `no-explicit-any-return` | `(): any` or `(): Promise<any>` return types | `// no-excuse-ok: any` |
|
|
171
|
+
| `empty-catch` | `catch { }` or `catch (e) { }` with empty body | `// no-excuse-ok: catch` |
|
|
172
|
+
| `catch-without-narrowing` | `catch (e)` used without `instanceof` or re-throw | `// no-excuse-ok: catch` |
|
|
173
|
+
|
|
174
|
+
Biome enforces additional rules (noExplicitAny, noNonNullAssertion, noDefaultExport, useImportType). The script catches what Biome cannot.
|
|
175
|
+
|
|
176
|
+
## In tests
|
|
177
|
+
|
|
178
|
+
Tests are strict too, with these exceptions (configure in `biome.jsonc` overrides):
|
|
179
|
+
|
|
180
|
+
| In tests you may | Why |
|
|
181
|
+
|---|---|
|
|
182
|
+
| Use `expect()` assertions | That's how testing works |
|
|
183
|
+
| Use magic numbers | Test data |
|
|
184
|
+
| Access private members via bracket notation | Testing internals |
|
|
185
|
+
| Skip readonly on test fixtures | Mutable setup/teardown |
|
|
186
|
+
|
|
187
|
+
Tests still follow the iron list — branded types, typed errors, exhaustive switch.
|
|
188
|
+
|
|
189
|
+
## Existing codebases
|
|
190
|
+
|
|
191
|
+
When editing an existing file that doesn't follow these rules: **write new code in strict style, don't refactor existing code in the same change.**
|
|
192
|
+
|
|
193
|
+
## Activation
|
|
194
|
+
|
|
195
|
+
This skill activates whenever you are writing or modifying any `.ts` or `.tsx` file. Even one-off scripts get the strict treatment.
|