litclaude-ai 0.3.7 → 0.3.8
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 +43 -0
- package/README.md +11 -7
- package/README_ko-KR.md +5 -5
- package/RELEASE_CHECKLIST.md +12 -8
- package/package.json +1 -1
- package/plugins/litclaude/.claude-plugin/plugin.json +1 -1
- package/plugins/litclaude/bin/litclaude-hook.js +1 -1
- package/plugins/litclaude/commands/dynamic-workflow.md +6 -3
- package/plugins/litclaude/commands/lit-loop.md +4 -2
- package/plugins/litclaude/skills/debugging/SKILL.md +152 -15
- package/plugins/litclaude/skills/lit-loop/SKILL.md +23 -3
- package/plugins/litclaude/skills/lit-plan/SKILL.md +168 -1
- package/plugins/litclaude/skills/litresearch/SKILL.md +101 -21
- package/plugins/litclaude/skills/programming/SKILL.md +382 -51
- package/plugins/litclaude/skills/refactor/SKILL.md +390 -26
- package/plugins/litclaude/skills/remove-ai-slops/SKILL.md +328 -33
- package/plugins/litclaude/skills/review-work/SKILL.md +217 -29
- package/plugins/litclaude/skills/start-work/SKILL.md +140 -19
|
@@ -10,22 +10,61 @@ Go, shell glue, plugin manifests, and test harnesses. It is the Claude-native
|
|
|
10
10
|
port of LitClaude programming discipline: strong types, small units, explicit
|
|
11
11
|
boundaries, and tests that prove behavior before code is accepted.
|
|
12
12
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
13
|
+
This SKILL.md is an index. The hard per-language rules live under `references/`.
|
|
14
|
+
Treat it as a routing table: read the matching language reference **before**
|
|
15
|
+
writing a single line of code, then apply the shared philosophy on top.
|
|
16
|
+
|
|
17
|
+
---
|
|
18
|
+
|
|
19
|
+
## PHASE 0 — LANGUAGE GATE (RUN THIS FIRST, EVERY TIME)
|
|
20
|
+
|
|
21
|
+
**Do not write or edit a single line of code before completing this gate.**
|
|
22
|
+
|
|
23
|
+
1. **Identify the language** from the file extension or the user's request.
|
|
24
|
+
2. **Stop** and read the matching reference set with the `Read` tool:
|
|
25
|
+
|
|
26
|
+
| File / Language | Mandatory reading |
|
|
27
|
+
|---|---|
|
|
28
|
+
| `.py`, `.pyi`, "Python" | `references/python/README.md` + every file under `references/python/` the README routes you to |
|
|
29
|
+
| `.rs`, `Cargo.toml`, "Rust" | `references/rust/README.md` + every file under `references/rust/` the README routes you to. **If the change touches `unsafe`, `*mut`, `*const`, `MaybeUninit`, FFI, `unsafe impl Send/Sync`, or a custom lock-free primitive, ALSO read `references/rust-ub/README.md` and every file under `references/rust-ub/`.** |
|
|
30
|
+
| `.ts`, `.tsx`, `.mts`, `.cts`, "TypeScript" | `references/typescript/README.md` + every file under `references/typescript/` the README routes you to |
|
|
31
|
+
| `.go`, `go.mod`, `go.sum`, `.golangci.yml`, `*.proto` next to a Go module, "Go" / "Golang" | `references/go/README.md` + every file under `references/go/` the README routes you to |
|
|
32
|
+
|
|
33
|
+
3. Only after the references are loaded, apply the shared philosophy below plus
|
|
34
|
+
the per-language iron list from the reference.
|
|
35
|
+
|
|
36
|
+
Prefer the repository's existing tools over introducing a new stack. If the task
|
|
37
|
+
spans multiple languages, write the boundary between them down before coding.
|
|
38
|
+
**No exceptions for "small" or "one-off" code** — disposable scripts (`uv run`
|
|
39
|
+
+ PEP 723, `rust-script`, `bun run`, `go run` + `//go:build ignore`) cost
|
|
40
|
+
nothing to write with full discipline, so they get the full treatment too.
|
|
41
|
+
|
|
42
|
+
---
|
|
43
|
+
|
|
44
|
+
## Shared philosophy
|
|
45
|
+
|
|
46
|
+
These are not style preferences. They are the axioms every recipe in
|
|
47
|
+
`references/` derives from.
|
|
48
|
+
|
|
49
|
+
1. **The type system is your proof system.** Make illegal states unrepresentable.
|
|
50
|
+
The type checker is the cheapest test you will ever run. If a bug can be
|
|
51
|
+
expressed as a type error, express it as a type error.
|
|
52
|
+
2. **Parse, don't validate.** Untrusted input crosses a boundary exactly once;
|
|
53
|
+
there it is parsed into a typed value. Inside the boundary, code receives
|
|
54
|
+
typed values and never re-validates. The boundary owns trust; the interior
|
|
55
|
+
owns logic.
|
|
56
|
+
3. **One name = one concept.** A `UserId` is not a `string`; `Seconds` is not
|
|
57
|
+
`Milliseconds`. Use a branded primitive for every distinct semantic unit so
|
|
58
|
+
the compiler refuses to let two units mix.
|
|
59
|
+
4. **Exhaustive variant matching, always.** Discriminated unions and enums are
|
|
60
|
+
matched exhaustively with a never-reached guard. `if`/`elif`/`else` to
|
|
61
|
+
discriminate a tagged variant is forbidden — it silently swallows new cases.
|
|
62
|
+
5. **Trust framework guarantees; validate only at boundaries.** No null checks
|
|
63
|
+
for values the type system already proves non-null. No `try`/`catch` around
|
|
64
|
+
code that cannot raise. No escape hatch papering over a contract you should
|
|
65
|
+
have encoded in types.
|
|
66
|
+
6. **Test-driven, with the right shape of test.** No production line ships
|
|
67
|
+
without a failing test that proves it was needed.
|
|
29
68
|
|
|
30
69
|
## Parse, don't validate
|
|
31
70
|
|
|
@@ -41,38 +80,217 @@ Examples:
|
|
|
41
80
|
- Environment variables are read at the edge and converted into explicit paths,
|
|
42
81
|
booleans, or enums.
|
|
43
82
|
|
|
44
|
-
|
|
83
|
+
---
|
|
84
|
+
|
|
85
|
+
## TDD DISCIPLINE — NON-NEGOTIABLE
|
|
86
|
+
|
|
87
|
+
For production behavior, follow red -> green -> refactor. The order is mandatory;
|
|
88
|
+
reverse it and you have written speculative code.
|
|
89
|
+
|
|
90
|
+
1. **Red.** Add a failing test that names the missing behavior and *fails for the
|
|
91
|
+
right reason* — not a typo, not an import error. A test that fails because the
|
|
92
|
+
function does not exist yet is the right reason; a missing import is not.
|
|
93
|
+
2. **Green.** Write the smallest change that passes that test. Resist adding the
|
|
94
|
+
second case until the first passes — the second case is the next red.
|
|
95
|
+
3. **Refactor.** Simplify with the tests still green. If the test is hard to
|
|
96
|
+
refactor against, the test is bad; fix the test before the code.
|
|
97
|
+
|
|
98
|
+
### The test pyramid
|
|
99
|
+
|
|
100
|
+
Every feature ships with all three rungs, sized in this proportion:
|
|
101
|
+
|
|
102
|
+
| Rung | Count | Purpose | Speed budget |
|
|
103
|
+
|---|---|---|---|
|
|
104
|
+
| **Unit** | many | Pure-function correctness for every meaningful input class (happy + edges + boundaries + error paths) | < 10 ms each |
|
|
105
|
+
| **Integration** | some | The real adapter against the real downstream (file system, plugin manifest, CLI, DB, queue, HTTP). Never a unit test pretending to be integration. | < 1 s each |
|
|
106
|
+
| **E2E scenario** | few | One narrative per user-visible outcome: tmux transcript, HTTP status/body, browser screenshot, desktop automation log. Asserts the observable outcome, not internal state. | seconds, on CI |
|
|
107
|
+
|
|
108
|
+
Tests alone are not completion proof when the change affects user workflows.
|
|
109
|
+
Pair green tests with one real scenario artifact. If a user-facing feature has
|
|
110
|
+
zero E2E coverage, it is undone even when every unit test passes.
|
|
111
|
+
|
|
112
|
+
### Given / When / Then is mandatory
|
|
113
|
+
|
|
114
|
+
Every test is structured by three blocks:
|
|
45
115
|
|
|
46
|
-
|
|
116
|
+
```
|
|
117
|
+
Given: the exact fixture and preconditions
|
|
118
|
+
When: the single action under test
|
|
119
|
+
Then: one observable outcome AND only that outcome
|
|
120
|
+
```
|
|
47
121
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
2. Green: write the smallest change that passes that test.
|
|
51
|
-
3. Refactor: simplify with the tests still green.
|
|
122
|
+
One `When` per test. Multiple `When`s = multiple tests. The `Then` asserts only
|
|
123
|
+
what changed because of the `When`, not unrelated invariants.
|
|
52
124
|
|
|
53
|
-
|
|
125
|
+
### Mock priority ladder — less mock, the better
|
|
54
126
|
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
127
|
+
Mocks are a last resort, not a default. Walk down this ladder and stop at the
|
|
128
|
+
first rung that works:
|
|
129
|
+
|
|
130
|
+
1. **Real object** — when constructable in under 1 ms (most domain types, pure
|
|
131
|
+
functions, value objects).
|
|
132
|
+
2. **In-memory fake** — a real implementation of the interface backed by a
|
|
133
|
+
map/slice for stores, caches, queues. The fake has its OWN test proving it
|
|
134
|
+
behaves like the real one.
|
|
135
|
+
3. **Sandbox / container** — real Postgres, real Redis, real S3-compatible, via
|
|
136
|
+
the project's container harness. Slow but truthful.
|
|
137
|
+
4. **Wire-level fake** — fake at the HTTP wire, not at the SDK.
|
|
138
|
+
5. **Mock** — only when 1–4 are genuinely infeasible (clock, randomness,
|
|
139
|
+
external SaaS with no sandbox). Mock the narrowest seam, never a whole
|
|
140
|
+
service. A mock that returns whatever the test wants is a tautology.
|
|
141
|
+
|
|
142
|
+
**The rule:** if your test fails when the production code's *implementation*
|
|
143
|
+
changes but its *behavior* did not, the test is over-mocked. Delete the mock and
|
|
144
|
+
assert on observable outputs.
|
|
145
|
+
|
|
146
|
+
### Prompt and skill tests follow the same rule
|
|
58
147
|
|
|
59
148
|
For prompt and skill text, assert on behavior-bearing rules and structure, not
|
|
60
149
|
on fragile full-string snapshots. A good prompt test checks that a required
|
|
61
|
-
decision, tool boundary, or safety rule is present
|
|
150
|
+
decision, tool boundary, or safety rule is present — never that an exact
|
|
151
|
+
sentence survives verbatim.
|
|
62
152
|
|
|
63
|
-
|
|
153
|
+
---
|
|
64
154
|
|
|
65
|
-
|
|
155
|
+
## Cross-language iron list
|
|
66
156
|
|
|
67
|
-
|
|
68
|
-
- Integration tests for file-system state, plugin manifests, and CLI behavior.
|
|
69
|
-
- Manual QA for the user-facing surface: tmux transcript, HTTP status/body,
|
|
70
|
-
browser screenshot, or desktop automation log.
|
|
157
|
+
Apply unless the per-language reference overrides with something stricter.
|
|
71
158
|
|
|
72
|
-
|
|
73
|
-
|
|
159
|
+
| Rule | Python | Rust | TypeScript | Go |
|
|
160
|
+
|---|---|---|---|---|
|
|
161
|
+
| Immutable by default | `@dataclass(frozen=True, slots=True)` / Pydantic `frozen=True` | every binding is `let` unless mutation is the documented purpose | every field is `readonly`; arrays are `readonly T[]` | value types, unexported fields, no mutation methods unless mutation is the purpose |
|
|
162
|
+
| Branded primitives | `UserId = NewType("UserId", int)` | `struct UserId(u64);` newtype tuple | `type UserId = Brand<string, "UserId">` | `type UserID string` + smart constructor with unexported field |
|
|
163
|
+
| Exhaustive variant matching | `match` + `assert_never` | `match` (compiler-enforced) | `switch` + `assertNever` | sealed interface + type switch + exhaustiveness linter |
|
|
164
|
+
| No untyped escape hatches | no `Any` in public sigs, no `cast`, no `# type: ignore` | no `unwrap`/`expect` outside `main`/tests, no `as` for narrowing | no `any`, no `as` (except `as const`, `satisfies`), no `!`, no `@ts-ignore` | no bare `any` in domain sigs; no `_ = err`; no unexplained lint suppression |
|
|
165
|
+
| No bare error strings | typed exception dataclass | `thiserror` enum (lib) or `anyhow` + `.context(...)` (app) | `Error` subclass with typed fields | sentinel + typed error struct; wrap with `%w`; check via `errors.Is/As` |
|
|
166
|
+
| Boundary catch only | catch the exact exception; broad catch only in `main()` with re-raise | `?` everywhere; never `panic!` in library code | `catch` narrows with `instanceof` then re-throws/converts; no empty catch | every `(T, error)` checked; `panic` only in `main`/tests |
|
|
167
|
+
| Resources via RAII / ownership | `with` / `async with` | `Drop` impl or RAII guard | `using` / `await using` | `defer x.Close()` immediately after acquisition |
|
|
168
|
+
| Async runtime choice | `anyio` (never bare `asyncio`) | `tokio` | platform-native async with `AbortSignal` cancellation | `context.Context` first param + `errgroup`; `-race` on every test |
|
|
169
|
+
| Modern HTTP client | typed HTTP/2 client with brotli + zstd | `reqwest` with rustls | `ky` / `undici` — never bare `fetch` in prod | stdlib `net/http.Client` with tuned `Transport` + retry/backoff |
|
|
170
|
+
| No parameter mutation | params are inputs; produce a new value | `&mut` only when mutation is documented | parameters never reassigned | value receivers unless mutation is the purpose |
|
|
171
|
+
| No helpers for one-off | inline a 3-line operation until the second caller | same | same | same |
|
|
172
|
+
|
|
173
|
+
---
|
|
174
|
+
|
|
175
|
+
## Canonical libraries by domain
|
|
176
|
+
|
|
177
|
+
Use these unless the project's manifest explicitly picks something else. A bare
|
|
178
|
+
default constructor (no timeouts, no pool tuning, no schema) is a bug — see the
|
|
179
|
+
per-language reference for production defaults.
|
|
180
|
+
|
|
181
|
+
| Domain | Python | Rust | TypeScript | Go |
|
|
182
|
+
|---|---|---|---|---|
|
|
183
|
+
| Boundary parse | Pydantic v2 | `serde` + `#[derive(Deserialize)]` | Zod | smart constructors + request validator |
|
|
184
|
+
| Internal value object | frozen dataclass | newtype tuple struct | `readonly` type alias | struct with unexported fields + constructor |
|
|
185
|
+
| Error types | typed exception dataclass | `thiserror` (lib) + `anyhow` (app) | `Error` subclass + Result | sentinel + typed error struct + `%w` wrap |
|
|
186
|
+
| Web framework | FastAPI | axum | Hono | gin / chi / connect-go |
|
|
187
|
+
| DB access | SQLAlchemy 2.x async | `sqlx` (compile-time checked) | Drizzle | sqlc + pgx + migrations |
|
|
188
|
+
| CLI | typer + rich | clap (derive) | commander | cobra + slog |
|
|
189
|
+
| Logging | structlog | tracing | pino | stdlib `log/slog` |
|
|
190
|
+
| Testing | pytest | nextest + proptest + insta | bun test / vitest | stdlib `testing` + container harness |
|
|
191
|
+
| Config from env | pydantic-settings | figment / config | zod + `process.env` | struct-tag env parser |
|
|
192
|
+
|
|
193
|
+
## Modern toolchain
|
|
194
|
+
|
|
195
|
+
| Tool category | Python | Rust | TypeScript | Go |
|
|
196
|
+
|---|---|---|---|---|
|
|
197
|
+
| Project manager | uv | cargo + nextest + deny | Bun (pnpm if Node is forced) | go modules + `go work` |
|
|
198
|
+
| Type checker | strict pyright/basedpyright | rustc `-D warnings` + clippy pedantic | `tsc --noEmit` with the strict flag set | go compiler + strict golangci-lint + nil-deref static analysis |
|
|
199
|
+
| Linter + formatter | ruff | clippy + rustfmt | Biome | gofumpt + goimports + golangci-lint |
|
|
200
|
+
| Test runner | pytest | nextest | bun test / vitest | `go test -race -shuffle=on -count=1` |
|
|
201
|
+
| Soundness gate | (n/a) | nightly miri with strict provenance | (n/a) | `-race` + leak detector |
|
|
202
|
+
|
|
203
|
+
CI-gate one-liners — the command a pre-commit hook or CI lane runs:
|
|
204
|
+
|
|
205
|
+
- Python: `ruff check . && basedpyright && pytest`
|
|
206
|
+
- Rust: `cargo clippy -- -D warnings && cargo nextest run && cargo +nightly miri test`
|
|
207
|
+
- TypeScript: `bunx biome check . && bunx tsc --noEmit && bun test`
|
|
208
|
+
- Go: `gofumpt -l . && golangci-lint run ./... && go test -race -shuffle=on -count=1 ./...`
|
|
209
|
+
|
|
210
|
+
A `tsconfig.json` with `"strict": true` alone is not strict — the reference
|
|
211
|
+
enumerates the additional flags. The same holds for `pyproject.toml` and
|
|
212
|
+
`Cargo.toml`: the references carry the canonical full configuration.
|
|
213
|
+
|
|
214
|
+
---
|
|
215
|
+
|
|
216
|
+
## THE 250 PURE LOC CEILING (NON-NEGOTIABLE)
|
|
217
|
+
|
|
218
|
+
Treat 250 non-comment, non-blank lines in a source file as a hard design
|
|
219
|
+
warning. A file past this line is telling you the module does more than one
|
|
220
|
+
thing, that cohesive units got fused "to save a file", and that every future
|
|
221
|
+
reader pays a tax to find what they need.
|
|
222
|
+
|
|
223
|
+
### Why 250
|
|
224
|
+
|
|
225
|
+
At 250 pure LOC a file still fits in one screen at a readable font. A reviewer
|
|
226
|
+
can hold the whole thing in working memory and spot a cross-cutting bug. The
|
|
227
|
+
number is the cognitive ceiling of a single reviewer who has not memorized the
|
|
228
|
+
file.
|
|
229
|
+
|
|
230
|
+
### Measuring pure LOC
|
|
231
|
+
|
|
232
|
+
```bash
|
|
233
|
+
# Quick (line-comment + blank exclusion):
|
|
234
|
+
awk '!/^[[:space:]]*$/ && !/^[[:space:]]*(\/\/|#|--)/' <file> | wc -l
|
|
235
|
+
|
|
236
|
+
# Authoritative (handles block comments):
|
|
237
|
+
cloc --by-file <file> # the "code" column is the number that matters
|
|
238
|
+
```
|
|
74
239
|
|
|
75
|
-
|
|
240
|
+
### Required behavior
|
|
241
|
+
|
|
242
|
+
- **Creating a file that will exceed 250 pure LOC:** split it before the first
|
|
243
|
+
commit. Carve by responsibility, one cohesive unit per file. Use a barrel
|
|
244
|
+
(`__init__.py`, `mod.rs`, `index.ts`) for re-exports only, never for logic.
|
|
245
|
+
- **Editing a file already over 250 pure LOC when your edit adds lines:**
|
|
246
|
+
refactor the unit you are touching into its own file *before* adding the new
|
|
247
|
+
lines. The split is part of THIS task, not a follow-up nobody does.
|
|
248
|
+
- **Reading a file over 250 pure LOC while implementing a feature:** surface the
|
|
249
|
+
smell in your reply, propose a concrete split (which functions go where, one
|
|
250
|
+
line each), and ask whether to split now. Do not silently keep going.
|
|
251
|
+
|
|
252
|
+
### Forbidden escapes
|
|
253
|
+
|
|
254
|
+
- Counting comments and blanks toward the budget. Pure LOC means code lines.
|
|
255
|
+
- Splitting by token count (`module_part_a`, `service-2`). Split by what each
|
|
256
|
+
file does and name it after the concept it owns.
|
|
257
|
+
- Catch-all dumps (`utils`, `helpers`, `common`, `shared`). They relocate the
|
|
258
|
+
smell, not remove it.
|
|
259
|
+
- "It's a test file with many cases." Split by subject-under-test or behavior
|
|
260
|
+
cluster, one file per cohesive group.
|
|
261
|
+
- "230 LOC, close enough." A 230-LOC file about to grow is already over the
|
|
262
|
+
line. Split now; do not race to the ceiling.
|
|
263
|
+
|
|
264
|
+
### Before / after split example
|
|
265
|
+
|
|
266
|
+
Before — `user_service.py`, ~412 pure LOC, doing too much:
|
|
267
|
+
|
|
268
|
+
```python
|
|
269
|
+
class UserRepository: ... # ~90 LOC of DB access
|
|
270
|
+
class UserValidator: ... # ~60 LOC of boundary parse + rules
|
|
271
|
+
class PasswordHasher: ... # ~40 LOC of hashing wrapper
|
|
272
|
+
class EmailSender: ... # ~50 LOC of HTTP client
|
|
273
|
+
class UserService: ... # ~130 LOC orchestrating the four above
|
|
274
|
+
```
|
|
275
|
+
|
|
276
|
+
After — split by responsibility, every file under the ceiling:
|
|
277
|
+
|
|
278
|
+
```
|
|
279
|
+
src/myapp/users/
|
|
280
|
+
├── __init__.py # barrel: re-exports UserService only (~5 LOC)
|
|
281
|
+
├── repository.py # UserRepository (~95 LOC)
|
|
282
|
+
├── validator.py # UserValidator (~65 LOC)
|
|
283
|
+
├── password.py # PasswordHasher (~45 LOC)
|
|
284
|
+
├── notifier.py # EmailSender (named for the role) (~55 LOC)
|
|
285
|
+
└── service.py # UserService (thin orchestrator) (~135 LOC)
|
|
286
|
+
```
|
|
287
|
+
|
|
288
|
+
Each file owns one concept. The barrel exposes the only public name. The
|
|
289
|
+
reviewer never scrolls through password hashing to understand mail retry policy.
|
|
290
|
+
|
|
291
|
+
---
|
|
292
|
+
|
|
293
|
+
## Type and error discipline
|
|
76
294
|
|
|
77
295
|
- Make illegal states difficult to represent.
|
|
78
296
|
- Use semantic names for IDs, paths, versions, plugin keys, and command names.
|
|
@@ -82,25 +300,138 @@ Pair green tests with one real scenario artifact.
|
|
|
82
300
|
- Avoid defensive code that checks impossibilities. If a case is possible, name
|
|
83
301
|
it and test it. If it is impossible, encode that in parsing or types.
|
|
84
302
|
|
|
85
|
-
##
|
|
86
|
-
|
|
87
|
-
Treat 250 non-comment lines in a source file as a design warning. If a touched
|
|
88
|
-
file is already too large and the change adds logic, split the cohesive unit you
|
|
89
|
-
are modifying before adding more. Keep orchestrators thin, parsing isolated,
|
|
90
|
-
and command implementations focused.
|
|
91
|
-
|
|
92
|
-
## Resource and State Hygiene
|
|
303
|
+
## Resource and state hygiene
|
|
93
304
|
|
|
94
305
|
Every spawned process, tmux session, temp directory, port, browser context, or
|
|
95
306
|
container needs a cleanup receipt. Register cleanup when the resource is
|
|
96
307
|
created, not after the test passes. A leftover process means the work is not
|
|
97
308
|
done.
|
|
98
309
|
|
|
99
|
-
|
|
310
|
+
---
|
|
311
|
+
|
|
312
|
+
## MANDATORY POST-WRITE SELF-REVIEW LOOP
|
|
313
|
+
|
|
314
|
+
This runs every time you finish writing or substantively editing code, before
|
|
315
|
+
you claim the task is done.
|
|
316
|
+
|
|
317
|
+
### Step 1 — measure
|
|
318
|
+
|
|
319
|
+
For every file you created or modified, run the pure-LOC count above.
|
|
320
|
+
|
|
321
|
+
### Step 2 — interpret
|
|
322
|
+
|
|
323
|
+
| Pure LOC | Verdict | Required action |
|
|
324
|
+
|---|---|---|
|
|
325
|
+
| ≤ 200 | Healthy | continue |
|
|
326
|
+
| 200–250 | Warning band | state the fact explicitly and propose a split if the next edit adds lines |
|
|
327
|
+
| > 250 | Defect | do not commit; refactor into smaller cohesive units now, in this same task |
|
|
328
|
+
|
|
329
|
+
### Step 3 — architectural self-review (always, even at 80 LOC)
|
|
330
|
+
|
|
331
|
+
Answer these in your reply before declaring done:
|
|
332
|
+
|
|
333
|
+
1. **Single responsibility?** Can you name what the file owns in one noun
|
|
334
|
+
phrase? If the answer needs "and", split.
|
|
335
|
+
2. **Boundary purity?** Did you parse untrusted input into a typed value, or
|
|
336
|
+
pass an untyped blob past the boundary?
|
|
337
|
+
3. **Variant discrimination?** Any non-exhaustive branch on a tagged type?
|
|
338
|
+
4. **Escape hatches?** Any untyped cast, suppression, or `unwrap`/`!` outside
|
|
339
|
+
`main`/tests?
|
|
340
|
+
5. **Defensive layer?** Any guard for a value the type system already proves?
|
|
341
|
+
6. **Helpers for one-off?** Any abstraction with a single caller that will never
|
|
342
|
+
get a second?
|
|
343
|
+
7. **Tests?** Is the new behavior locked by a test that fails if you revert it?
|
|
344
|
+
|
|
345
|
+
If any answer fails, fix it before declaring done.
|
|
346
|
+
|
|
347
|
+
### Step 4 — hand off to the recovery skills
|
|
348
|
+
|
|
349
|
+
- The file you just wrote (or an adjacent one) is over 250 pure LOC, or step 3
|
|
350
|
+
surfaced more than two issues: **load the `refactor` skill** and run its
|
|
351
|
+
safe-refactor protocol (codemap, plan, characterization tests, edits with
|
|
352
|
+
tests after each step). Do not improvise a structural change under pressure.
|
|
353
|
+
- You inherited code with AI-generated patterns (broad catches, redundant null
|
|
354
|
+
checks, vague TODOs, oversized modules, dead helpers): **load the
|
|
355
|
+
`remove-ai-slops` skill** to do a categorized cleanup with regression tests
|
|
356
|
+
pinned first.
|
|
357
|
+
|
|
358
|
+
These are the recovery path for the defects this loop is designed to catch, not
|
|
359
|
+
optional cosmetics.
|
|
360
|
+
|
|
361
|
+
---
|
|
362
|
+
|
|
363
|
+
## Per-language jump table
|
|
364
|
+
|
|
365
|
+
Stop. Read the matching reference fully before writing code.
|
|
366
|
+
|
|
367
|
+
### Python (`.py`, `.pyi`)
|
|
368
|
+
|
|
369
|
+
Read `references/python/README.md` first, then load on demand:
|
|
370
|
+
|
|
371
|
+
| Need | Load |
|
|
372
|
+
|---|---|
|
|
373
|
+
| Strict pyproject / type checker / ruff config | `references/python/pyproject-strict.md` |
|
|
374
|
+
| Type patterns (`NewType`, `Final`, `Protocol`) | `references/python/type-patterns.md` |
|
|
375
|
+
| Data modeling (Pydantic vs dataclass vs TypedDict) | `references/python/data-modeling.md` |
|
|
376
|
+
| Error handling (typed exceptions, exhaustive match) | `references/python/error-handling.md` |
|
|
377
|
+
| Async with anyio | `references/python/async-anyio.md` |
|
|
378
|
+
| FastAPI + async DB stack | `references/python/fastapi-stack.md` |
|
|
379
|
+
| Canonical library defaults | `references/python/libraries.md` |
|
|
380
|
+
| Disposable PEP 723 scripts | `references/python/one-liners.md` |
|
|
381
|
+
|
|
382
|
+
### Rust (`.rs`, `Cargo.toml`)
|
|
383
|
+
|
|
384
|
+
Read `references/rust/README.md` first, then load on demand:
|
|
385
|
+
|
|
386
|
+
| Need | Load |
|
|
387
|
+
|---|---|
|
|
388
|
+
| Strict `Cargo.toml` lints + profile | `references/rust/cargo-strict.md` |
|
|
389
|
+
| Type-state and newtype patterns | `references/rust/type-state.md` |
|
|
390
|
+
| `unsafe` discipline (safe wrapper + SAFETY comment + miri proof) | `references/rust/unsafe-discipline.md` |
|
|
391
|
+
| Async with tokio | `references/rust/async-tokio.md` |
|
|
392
|
+
| Concurrency primitives | `references/rust/concurrency.md` |
|
|
393
|
+
| axum HTTP stack | `references/rust/axum-stack.md` |
|
|
394
|
+
| Canonical library defaults | `references/rust/libraries.md` |
|
|
395
|
+
| Any `unsafe` / FFI / `MaybeUninit` / lock-free work | `references/rust-ub/` (full directory) |
|
|
396
|
+
|
|
397
|
+
### TypeScript (`.ts`, `.tsx`, `.mts`, `.cts`)
|
|
398
|
+
|
|
399
|
+
Read `references/typescript/README.md` first, then load on demand:
|
|
400
|
+
|
|
401
|
+
| Need | Load |
|
|
402
|
+
|---|---|
|
|
403
|
+
| Strict tsconfig + Biome config | `references/typescript/tsconfig-strict.md` |
|
|
404
|
+
| Type patterns (branded types, `satisfies`, `assertNever`) | `references/typescript/type-patterns.md` |
|
|
405
|
+
| Data modeling (type vs interface vs Zod, parse-don't-validate) | `references/typescript/data-modeling.md` |
|
|
406
|
+
| Error handling (Result, typed errors, AbortSignal timeouts) | `references/typescript/error-handling.md` |
|
|
407
|
+
| Bootstrapping a new project | `references/typescript/bootstrap.md` |
|
|
408
|
+
| Hono backend stack | `references/typescript/backend-hono.md` |
|
|
409
|
+
|
|
410
|
+
### Go (`.go`, `go.mod`, `go.sum`, `.golangci.yml`, `*.proto`)
|
|
411
|
+
|
|
412
|
+
Read `references/go/README.md` first, then load on demand:
|
|
413
|
+
|
|
414
|
+
| Need | Load |
|
|
415
|
+
|---|---|
|
|
416
|
+
| Library defaults (gin vs chi, sqlc, slog) | `references/go/libraries.md` |
|
|
417
|
+
| Canonical strict `.golangci.yml` | `references/go/golangci-strict.md` |
|
|
418
|
+
| Project layout, CI, `go.mod` template | `references/go/bootstrap.md` |
|
|
419
|
+
| Type patterns (named types, smart constructors, sealed interfaces) | `references/go/type-patterns.md` |
|
|
420
|
+
| Error handling (`errors.Is/As`, `%w` wrapping, no panic) | `references/go/error-handling.md` |
|
|
421
|
+
| Concurrency (`context.Context`, `errgroup`, `-race`) | `references/go/concurrency.md` |
|
|
422
|
+
| HTTP backend stack | `references/go/backend-stack.md` |
|
|
423
|
+
| Testing (Given/When/Then, table-driven, fakes-over-mocks) | `references/go/testing.md` |
|
|
424
|
+
|
|
425
|
+
---
|
|
426
|
+
|
|
427
|
+
## Claude Code adaptation
|
|
428
|
+
|
|
429
|
+
Prefer Claude Code's available tools and project conventions. If a private
|
|
430
|
+
reference instruction names a tool from another agent runtime, translate the
|
|
431
|
+
principle instead of copying the tool name. For example, goal tools become
|
|
432
|
+
conditional `get_goal` / `create_goal` / `update_goal` guidance only when
|
|
433
|
+
exposed; workflow isolation uses `Workflow`, `EnterWorktree`, or an explicit
|
|
434
|
+
`claude --worktree ...` operator path when that is the available Claude surface.
|
|
100
435
|
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
the tool name. For example, goal tools become conditional `get_goal` /
|
|
104
|
-
`create_goal` / `update_goal` guidance only when exposed; workflow isolation
|
|
105
|
-
uses `Workflow`, `EnterWorktree`, or an explicit `claude --worktree ...`
|
|
106
|
-
operator path when that is the available Claude surface.
|
|
436
|
+
The references contain the recipes. Read them before writing code, and re-read
|
|
437
|
+
them when the model drifts. The post-write self-review loop is non-negotiable.
|