opencodekit 0.20.7 → 0.20.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/dist/index.js +1 -1
- package/dist/template/.opencode/AGENTS.md +48 -0
- package/dist/template/.opencode/agent/build.md +3 -2
- package/dist/template/.opencode/agent/explore.md +14 -14
- package/dist/template/.opencode/agent/general.md +1 -1
- package/dist/template/.opencode/agent/plan.md +1 -1
- package/dist/template/.opencode/agent/review.md +1 -1
- package/dist/template/.opencode/agent/vision.md +0 -9
- package/dist/template/.opencode/memory.db +0 -0
- package/dist/template/.opencode/memory.db-shm +0 -0
- package/dist/template/.opencode/memory.db-wal +0 -0
- package/dist/template/.opencode/opencode.json +0 -5
- package/dist/template/.opencode/package.json +1 -1
- package/dist/template/.opencode/pnpm-lock.yaml +791 -9
- package/dist/template/.opencode/skill/api-and-interface-design/SKILL.md +162 -0
- package/dist/template/.opencode/skill/beads/SKILL.md +10 -9
- package/dist/template/.opencode/skill/beads/references/MULTI_AGENT.md +10 -10
- package/dist/template/.opencode/skill/ci-cd-and-automation/SKILL.md +202 -0
- package/dist/template/.opencode/skill/code-search-patterns/SKILL.md +253 -0
- package/dist/template/.opencode/skill/code-simplification/SKILL.md +211 -0
- package/dist/template/.opencode/skill/condition-based-waiting/SKILL.md +12 -0
- package/dist/template/.opencode/skill/defense-in-depth/SKILL.md +16 -6
- package/dist/template/.opencode/skill/deprecation-and-migration/SKILL.md +189 -0
- package/dist/template/.opencode/skill/development-lifecycle/SKILL.md +12 -48
- package/dist/template/.opencode/skill/documentation-and-adrs/SKILL.md +220 -0
- package/dist/template/.opencode/skill/incremental-implementation/SKILL.md +191 -0
- package/dist/template/.opencode/skill/performance-optimization/SKILL.md +236 -0
- package/dist/template/.opencode/skill/receiving-code-review/SKILL.md +11 -0
- package/dist/template/.opencode/skill/security-and-hardening/SKILL.md +296 -0
- package/dist/template/.opencode/skill/structured-edit/SKILL.md +10 -0
- package/dist/template/.opencode/skill/swarm-coordination/SKILL.md +66 -1
- package/package.json +1 -1
- package/dist/template/.opencode/skill/beads-bridge/SKILL.md +0 -321
- package/dist/template/.opencode/skill/code-navigation/SKILL.md +0 -130
- package/dist/template/.opencode/skill/mqdh/SKILL.md +0 -171
- package/dist/template/.opencode/skill/obsidian/SKILL.md +0 -192
- package/dist/template/.opencode/skill/obsidian/mcp.json +0 -22
- package/dist/template/.opencode/skill/pencil/SKILL.md +0 -72
- package/dist/template/.opencode/skill/ralph/SKILL.md +0 -296
- package/dist/template/.opencode/skill/tilth-cli/SKILL.md +0 -207
- package/dist/template/.opencode/skill/tool-priority/SKILL.md +0 -299
|
@@ -0,0 +1,253 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: code-search-patterns
|
|
3
|
+
description: Use when navigating unfamiliar code with search-first patterns, combining built-in/LSP navigation with tilth MCP (main agent) and tilth CLI (subagents)
|
|
4
|
+
version: 1.0.0
|
|
5
|
+
tags: [workflow, code-quality, context, subagent]
|
|
6
|
+
dependencies: []
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# Code Search Patterns
|
|
10
|
+
|
|
11
|
+
Unified navigation skill for fast, low-waste code understanding across both execution modes:
|
|
12
|
+
|
|
13
|
+
- **Main agent**: built-in tools + LSP + tilth MCP
|
|
14
|
+
- **Subagents**: Bash + `npx -y tilth` CLI
|
|
15
|
+
|
|
16
|
+
This skill merges practical navigation heuristics with concrete tool usage so you can locate behavior, trace impact, and edit safely with fewer calls.
|
|
17
|
+
|
|
18
|
+
## When to Use
|
|
19
|
+
|
|
20
|
+
- Exploring unfamiliar modules before editing
|
|
21
|
+
- Tracing call chains across files and directories
|
|
22
|
+
- Checking blast radius before signature/export changes
|
|
23
|
+
- Dispatching subagents that need structural code navigation
|
|
24
|
+
- Reducing token/tool-call waste during implementation
|
|
25
|
+
|
|
26
|
+
## When NOT to Use
|
|
27
|
+
|
|
28
|
+
- Trivial single-file edits where symbol location is already known
|
|
29
|
+
- Pure docs/config reads with no dependency tracing
|
|
30
|
+
- Tasks focused on external web research (use web research tools instead)
|
|
31
|
+
|
|
32
|
+
## Core Principle
|
|
33
|
+
|
|
34
|
+
> Collapse multiple tool calls into fewer, smarter ones. Every unnecessary read or search wastes tokens and turns.
|
|
35
|
+
|
|
36
|
+
## Navigation Patterns
|
|
37
|
+
|
|
38
|
+
### 1) Search First, Read Second
|
|
39
|
+
|
|
40
|
+
Start with symbol/content search to find exact locations, then read only the needed slice.
|
|
41
|
+
|
|
42
|
+
- **Good**: `search -> targeted read`
|
|
43
|
+
- **Avoid**: `read many files -> search later`
|
|
44
|
+
|
|
45
|
+
Use LSP (`findReferences`, `outgoingCalls`) or tilth search first; read deep only after narrowing scope.
|
|
46
|
+
|
|
47
|
+
### 2) Multi-Symbol Search
|
|
48
|
+
|
|
49
|
+
When flow spans multiple functions, query them together (`A,B,C`) instead of serial one-by-one lookups.
|
|
50
|
+
|
|
51
|
+
- Faster call-chain reconstruction
|
|
52
|
+
- Fewer repeated scans of the same files
|
|
53
|
+
|
|
54
|
+
### 3) Don’t Re-Read What Search Already Returned
|
|
55
|
+
|
|
56
|
+
If search output already includes definition body/context, proceed from it.
|
|
57
|
+
Re-read only when you need:
|
|
58
|
+
|
|
59
|
+
- exact edit anchors,
|
|
60
|
+
- additional surrounding lines,
|
|
61
|
+
- or untruncated content.
|
|
62
|
+
|
|
63
|
+
### 4) Blast Radius Check (Before Breaking Changes)
|
|
64
|
+
|
|
65
|
+
Before renaming/removing/changing signatures, inspect downstream impact first.
|
|
66
|
+
|
|
67
|
+
- LSP: `findReferences`, `incomingCalls`
|
|
68
|
+
- tilth: `tilth_tilth_deps` or `--deps`
|
|
69
|
+
|
|
70
|
+
Then apply edits from dependents inward.
|
|
71
|
+
|
|
72
|
+
### 5) Context Locality
|
|
73
|
+
|
|
74
|
+
Prefer nearby package/module scope first.
|
|
75
|
+
|
|
76
|
+
- built-in search: constrain `path`
|
|
77
|
+
- tilth: pass `scope` / `context`
|
|
78
|
+
|
|
79
|
+
Locality reduces irrelevant matches and token churn.
|
|
80
|
+
|
|
81
|
+
### 6) Outline Before Deep Read
|
|
82
|
+
|
|
83
|
+
For large files, inspect structure first (symbols/outline), then read only target sections.
|
|
84
|
+
|
|
85
|
+
- LSP: `documentSymbol`
|
|
86
|
+
- tilth read: smart outline by default, then section drill-in
|
|
87
|
+
|
|
88
|
+
### 7) Follow Call Chain, Not File Tree
|
|
89
|
+
|
|
90
|
+
Start at entry behavior and walk calls (`definition -> outgoing -> next definition`) instead of reading folders linearly.
|
|
91
|
+
|
|
92
|
+
This exposes real execution flow with fewer reads.
|
|
93
|
+
|
|
94
|
+
## tilth MCP (Main Agent)
|
|
95
|
+
|
|
96
|
+
Use MCP variants when available in the main agent session:
|
|
97
|
+
|
|
98
|
+
- `tilth_tilth_search`
|
|
99
|
+
- `tilth_tilth_read`
|
|
100
|
+
- `tilth_tilth_files`
|
|
101
|
+
- `tilth_tilth_deps`
|
|
102
|
+
|
|
103
|
+
### Built-in/LSP vs tilth MCP (when to choose which)
|
|
104
|
+
|
|
105
|
+
| Need | Built-in / LSP | Prefer tilth MCP when | Why tilth helps |
|
|
106
|
+
| ----------------------- | -------------------------------------- | ----------------------------------------------------------- | ----------------------------------- |
|
|
107
|
+
| Find symbols/usages | `grep` / `lsp.findReferences` | You want definitions + usages + expanded source in one call | Reduces search+read round trips |
|
|
108
|
+
| Read file content | `read` | File is large or you only need structure first | Smart outline + section targeting |
|
|
109
|
+
| List candidate files | `glob` | You want quick glob results with token-aware relevance | Faster triage for large directories |
|
|
110
|
+
| Pre-change impact check | `lsp.incomingCalls` + manual follow-up | You need import + dependent view before breaking change | Single blast-radius view via deps |
|
|
111
|
+
|
|
112
|
+
Guideline: if tilth MCP is active, use it as default for navigation; fall back to built-in/LSP when you need language-server-specific semantics or exact editor-position operations.
|
|
113
|
+
|
|
114
|
+
## tilth CLI (Subagents)
|
|
115
|
+
|
|
116
|
+
### Why this exists
|
|
117
|
+
|
|
118
|
+
Subagents typically cannot call MCP tools directly. They can still use tilth through Bash:
|
|
119
|
+
|
|
120
|
+
```bash
|
|
121
|
+
npx -y tilth <query> [flags]
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
### Auto-detection
|
|
125
|
+
|
|
126
|
+
| Query shape | Auto-detected action |
|
|
127
|
+
| --------------------------------- | ------------------------------------ |
|
|
128
|
+
| Existing file path (`src/foo.ts`) | Read file (smart outline/full) |
|
|
129
|
+
| Identifier (`initCommand`) | Symbol search (definitions + usages) |
|
|
130
|
+
| Glob (`*.test.ts`) | List files |
|
|
131
|
+
| Plain text / phrase | Content search |
|
|
132
|
+
|
|
133
|
+
Use `--kind` to force a specific mode when needed.
|
|
134
|
+
|
|
135
|
+
### Core operations
|
|
136
|
+
|
|
137
|
+
#### 1) Read file
|
|
138
|
+
|
|
139
|
+
```bash
|
|
140
|
+
npx -y tilth src/index.ts
|
|
141
|
+
npx -y tilth src/index.ts --full
|
|
142
|
+
npx -y tilth src/index.ts --section 45-89
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
#### 2) Search symbols
|
|
146
|
+
|
|
147
|
+
```bash
|
|
148
|
+
npx -y tilth initCommand --scope src/
|
|
149
|
+
npx -y tilth "initCommand,detectMode" --scope src/
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
#### 3) Search text/regex
|
|
153
|
+
|
|
154
|
+
```bash
|
|
155
|
+
npx -y tilth --kind content "TODO" --scope src/
|
|
156
|
+
npx -y tilth --kind regex "/TODO.*fix/" --scope src/
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
#### 4) Find callers
|
|
160
|
+
|
|
161
|
+
```bash
|
|
162
|
+
npx -y tilth --kind callers initCommand --scope src/
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
#### 5) List files
|
|
166
|
+
|
|
167
|
+
```bash
|
|
168
|
+
npx -y tilth "*.test.ts" --scope src/
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
#### 6) Blast radius / deps
|
|
172
|
+
|
|
173
|
+
```bash
|
|
174
|
+
npx -y tilth --deps src/commands/init.ts --scope src/
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
#### 7) Codebase map
|
|
178
|
+
|
|
179
|
+
```bash
|
|
180
|
+
npx -y tilth --map --scope src/
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
### Useful flags
|
|
184
|
+
|
|
185
|
+
| Flag | Purpose | Example |
|
|
186
|
+
| ----------------- | ------------------------------ | ---------------------------- | ------------------- | ----------------- | ---------------- |
|
|
187
|
+
| `--scope <dir>` | Restrict scan area | `--scope src/commands/` |
|
|
188
|
+
| `--section <range | heading>` | Targeted file slice | `--section 120-180` |
|
|
189
|
+
| `--full` | Force full file output | `--full` |
|
|
190
|
+
| `--budget <n>` | Limit output size | `--budget 2000` |
|
|
191
|
+
| `--json` | Machine-readable output | `--json` |
|
|
192
|
+
| `--map` | Structural project skeleton | `--map --scope src/` |
|
|
193
|
+
| `--kind <symbol | content | regex | callers>` | Force search mode | `--kind callers` |
|
|
194
|
+
| `--deps <file>` | Import/dependent blast radius | `--deps src/utils/errors.ts` |
|
|
195
|
+
| `--expand <n>` | Expand top matches with source | `--expand 3` |
|
|
196
|
+
|
|
197
|
+
### MCP vs CLI
|
|
198
|
+
|
|
199
|
+
| Capability | MCP (main agent) | CLI (subagents) |
|
|
200
|
+
| ---------------------------- | -------------------------------------------------- | ------------------------------------------ |
|
|
201
|
+
| Access mode | Tool call | Bash command |
|
|
202
|
+
| Session dedup/context carry | Yes | No |
|
|
203
|
+
| Hash-anchored edit flow | Yes (via MCP edit tools) | No |
|
|
204
|
+
| Symbol/content/regex/callers | Yes | Yes |
|
|
205
|
+
| Deps/blast-radius | Yes | Yes |
|
|
206
|
+
| Codebase map | Limited by toolset | Yes (`--map`) |
|
|
207
|
+
| Best for | Interactive main-agent navigation + edit workflows | Subagent discovery/exploration without MCP |
|
|
208
|
+
|
|
209
|
+
### Example subagent dispatch
|
|
210
|
+
|
|
211
|
+
```ts
|
|
212
|
+
task({
|
|
213
|
+
subagent_type: "general",
|
|
214
|
+
prompt: `Use tilth CLI via Bash for navigation.
|
|
215
|
+
|
|
216
|
+
1) Locate symbol and usages:
|
|
217
|
+
npx -y tilth initCommand --scope src/
|
|
218
|
+
|
|
219
|
+
2) Find callers:
|
|
220
|
+
npx -y tilth --kind callers initCommand --scope src/
|
|
221
|
+
|
|
222
|
+
3) Check blast radius before edits:
|
|
223
|
+
npx -y tilth --deps src/commands/init.ts --scope src/
|
|
224
|
+
|
|
225
|
+
4) Read only the relevant section:
|
|
226
|
+
npx -y tilth src/commands/init.ts --section 500-620
|
|
227
|
+
|
|
228
|
+
Then implement the requested change with minimal file edits.`,
|
|
229
|
+
});
|
|
230
|
+
```
|
|
231
|
+
|
|
232
|
+
## Cost Awareness
|
|
233
|
+
|
|
234
|
+
Navigation cost compounds quickly. Optimize for fewer, richer calls.
|
|
235
|
+
|
|
236
|
+
- Prefer one structural search over multiple blind reads
|
|
237
|
+
- Reuse search output; avoid duplicate reads of the same symbol body
|
|
238
|
+
- Scope aggressively (`path`, `scope`) to cut noise
|
|
239
|
+
- Use section reads for large files instead of full-file pulls
|
|
240
|
+
|
|
241
|
+
Target heuristic: understand a symbol and its direct impact in **≤3 calls** whenever possible.
|
|
242
|
+
|
|
243
|
+
## Common Mistakes
|
|
244
|
+
|
|
245
|
+
| Mistake | Better pattern |
|
|
246
|
+
| ------------------------------------------------ | ------------------------------------------------------------------- |
|
|
247
|
+
| Reading big files before locating symbol | Search first, then section read |
|
|
248
|
+
| Re-reading code already shown in search output | Work from returned snippet; re-read only if needed for edit anchors |
|
|
249
|
+
| Serially tracing one function at a time | Multi-symbol search + callers/deps |
|
|
250
|
+
| Ignoring blast radius before API/signature edits | Run references/incoming/deps first |
|
|
251
|
+
| Unscoped repository-wide search | Use `path`/`--scope` to localize |
|
|
252
|
+
| Using CLI defaults when mode is ambiguous | Force with `--kind` |
|
|
253
|
+
| Overusing `--full` on large files | Outline first, then `--section` |
|
|
@@ -0,0 +1,211 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: code-simplification
|
|
3
|
+
description: Use when reducing code complexity, eliminating dead code, or refactoring for clarity — enforces measure-before-cutting discipline to prevent breaking changes disguised as cleanup
|
|
4
|
+
version: 1.0.0
|
|
5
|
+
tags: [code-quality, refactoring]
|
|
6
|
+
dependencies: [verification-before-completion]
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# Code Simplification
|
|
10
|
+
|
|
11
|
+
> **Replaces** ad-hoc "cleanup" refactors that introduce bugs — enforces systematic simplification with verification at every step
|
|
12
|
+
|
|
13
|
+
## When to Use
|
|
14
|
+
|
|
15
|
+
- Code is harder to understand than it needs to be
|
|
16
|
+
- Functions are too long (>50 lines), files are too large (>500 lines)
|
|
17
|
+
- Dead code, unused imports, or unnecessary abstractions exist
|
|
18
|
+
- You're asked to "clean up" or "simplify" a module
|
|
19
|
+
- Complexity is making bugs harder to fix
|
|
20
|
+
|
|
21
|
+
## When NOT to Use
|
|
22
|
+
|
|
23
|
+
- The code works, is readable, and isn't blocking anything — leave it alone
|
|
24
|
+
- You're implementing a new feature (use incremental-implementation instead)
|
|
25
|
+
- The "simplification" is actually a rewrite with different behavior
|
|
26
|
+
|
|
27
|
+
## Common Rationalizations
|
|
28
|
+
|
|
29
|
+
| Rationalization | Rebuttal |
|
|
30
|
+
| -------------------------------------- | ---------------------------------------------------------------------------------------- |
|
|
31
|
+
| "This code is ugly, let me rewrite it" | Ugly but working > beautiful but broken. Simplify incrementally, not wholesale |
|
|
32
|
+
| "Nobody uses this, I'll delete it" | Verify with grep/find-references FIRST. "Nobody uses this" is the #1 cause of breakage |
|
|
33
|
+
| "I'll simplify it while I'm in here" | Mixing feature work with refactoring makes both harder to review and revert |
|
|
34
|
+
| "This abstraction isn't needed" | Check if it serves a testing, extension, or boundary purpose before removing |
|
|
35
|
+
| "I can make this more elegant" | Elegant for whom? Optimize for the next reader, not for cleverness |
|
|
36
|
+
| "The tests will catch any issues" | Tests cover known behavior. Simplification can change behavior in ways tests don't cover |
|
|
37
|
+
|
|
38
|
+
## Overview
|
|
39
|
+
|
|
40
|
+
Code simplification is the discipline of making code easier to understand and maintain WITHOUT changing its behavior. The key word is discipline — undisciplined simplification introduces bugs.
|
|
41
|
+
|
|
42
|
+
**Core principle:** Measure complexity, simplify the worst offender, verify nothing broke, repeat.
|
|
43
|
+
|
|
44
|
+
## The Process
|
|
45
|
+
|
|
46
|
+
```
|
|
47
|
+
1. MEASURE — Identify what's actually complex (not just what feels complex)
|
|
48
|
+
2. ISOLATE — Pick ONE simplification target
|
|
49
|
+
3. VERIFY — Ensure tests exist for current behavior
|
|
50
|
+
4. SIMPLIFY — Apply the smallest change that reduces complexity
|
|
51
|
+
5. CONFIRM — Run full verification to prove behavior is unchanged
|
|
52
|
+
6. REPEAT — Pick the next target
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
## Complexity Signals
|
|
56
|
+
|
|
57
|
+
| Signal | Threshold | Action |
|
|
58
|
+
| --------------------- | ---------------- | ------------------------------------------- |
|
|
59
|
+
| Function length | >50 lines | Extract helper functions |
|
|
60
|
+
| File length | >500 lines | Split into modules |
|
|
61
|
+
| Nesting depth | >3 levels | Flatten with early returns or extract |
|
|
62
|
+
| Parameter count | >4 params | Use an options object |
|
|
63
|
+
| Cyclomatic complexity | >10 per function | Break into smaller functions |
|
|
64
|
+
| Dead code | Any | Remove after verifying with find-references |
|
|
65
|
+
| Unused imports | Any | Remove (linter usually catches these) |
|
|
66
|
+
| Duplicate code | 3+ copies | Extract shared function |
|
|
67
|
+
|
|
68
|
+
## Simplification Patterns
|
|
69
|
+
|
|
70
|
+
### Extract Function
|
|
71
|
+
|
|
72
|
+
```typescript
|
|
73
|
+
// BEFORE: Long function with embedded logic
|
|
74
|
+
function processOrder(order: Order) {
|
|
75
|
+
// 20 lines of validation
|
|
76
|
+
// 15 lines of pricing
|
|
77
|
+
// 10 lines of notification
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// AFTER: Named steps
|
|
81
|
+
function processOrder(order: Order) {
|
|
82
|
+
validateOrder(order);
|
|
83
|
+
const total = calculateTotal(order);
|
|
84
|
+
notifyCustomer(order, total);
|
|
85
|
+
}
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
### Early Return (Flatten Nesting)
|
|
89
|
+
|
|
90
|
+
```typescript
|
|
91
|
+
// BEFORE: Deep nesting
|
|
92
|
+
function getUser(id: string) {
|
|
93
|
+
if (id) {
|
|
94
|
+
const user = db.find(id);
|
|
95
|
+
if (user) {
|
|
96
|
+
if (user.active) {
|
|
97
|
+
return user;
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
return null;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// AFTER: Guard clauses
|
|
105
|
+
function getUser(id: string) {
|
|
106
|
+
if (!id) return null;
|
|
107
|
+
const user = db.find(id);
|
|
108
|
+
if (!user) return null;
|
|
109
|
+
if (!user.active) return null;
|
|
110
|
+
return user;
|
|
111
|
+
}
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
### Remove Dead Code
|
|
115
|
+
|
|
116
|
+
```
|
|
117
|
+
1. Search: grep/find-references for the symbol
|
|
118
|
+
2. Verify: No callers exist (check tests too)
|
|
119
|
+
3. Remove: Delete the code
|
|
120
|
+
4. Confirm: All tests still pass
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
**NEVER assume code is dead without searching.** Check:
|
|
124
|
+
|
|
125
|
+
- Direct calls
|
|
126
|
+
- Dynamic references (string-based lookups, reflection)
|
|
127
|
+
- Test-only usage
|
|
128
|
+
- Configuration references
|
|
129
|
+
|
|
130
|
+
### Inline Unnecessary Abstraction
|
|
131
|
+
|
|
132
|
+
```typescript
|
|
133
|
+
// BEFORE: Wrapper that adds nothing
|
|
134
|
+
function getUserName(user: User): string {
|
|
135
|
+
return user.name;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
// AFTER: Just use the property directly
|
|
139
|
+
user.name;
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
Only inline if the abstraction doesn't serve a testing, boundary, or extension purpose.
|
|
143
|
+
|
|
144
|
+
### Replace Conditional with Early Exit
|
|
145
|
+
|
|
146
|
+
```typescript
|
|
147
|
+
// BEFORE
|
|
148
|
+
function handle(input: string) {
|
|
149
|
+
let result = "";
|
|
150
|
+
if (isValid(input)) {
|
|
151
|
+
result = transform(input);
|
|
152
|
+
} else {
|
|
153
|
+
throw new Error("Invalid");
|
|
154
|
+
}
|
|
155
|
+
return result;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
// AFTER
|
|
159
|
+
function handle(input: string) {
|
|
160
|
+
if (!isValid(input)) throw new Error("Invalid");
|
|
161
|
+
return transform(input);
|
|
162
|
+
}
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
## What NOT to Simplify
|
|
166
|
+
|
|
167
|
+
- **Working error handling** — even if verbose, it's there for a reason
|
|
168
|
+
- **Compatibility shims** — they exist because something needs them
|
|
169
|
+
- **Performance-critical paths** — "simpler" may mean "slower"
|
|
170
|
+
- **Code with extensive test coverage pointing at specific behavior** — the tests document WHY it's complex
|
|
171
|
+
- **Other people's current work** — don't simplify files with active PRs
|
|
172
|
+
|
|
173
|
+
## Red Flags — STOP
|
|
174
|
+
|
|
175
|
+
If you catch yourself:
|
|
176
|
+
|
|
177
|
+
- Changing behavior while "simplifying"
|
|
178
|
+
- Removing code without checking references first
|
|
179
|
+
- Simplifying more than one thing per commit
|
|
180
|
+
- "Cleaning up" files you weren't asked to touch
|
|
181
|
+
- Making the code "more elegant" without a clear readability improvement
|
|
182
|
+
|
|
183
|
+
**STOP.** Revert the current change and pick a smaller target.
|
|
184
|
+
|
|
185
|
+
## Verification
|
|
186
|
+
|
|
187
|
+
Before each simplification:
|
|
188
|
+
|
|
189
|
+
```bash
|
|
190
|
+
# Ensure tests exist for current behavior
|
|
191
|
+
npm test -- --related [file]
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
After each simplification:
|
|
195
|
+
|
|
196
|
+
```bash
|
|
197
|
+
npm run typecheck
|
|
198
|
+
npm run lint
|
|
199
|
+
npm test
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
**If ANY test fails, the simplification changed behavior.** Either:
|
|
203
|
+
|
|
204
|
+
1. The simplification is wrong — revert it
|
|
205
|
+
2. The test is testing implementation details — fix the test, but document WHY
|
|
206
|
+
|
|
207
|
+
## See Also
|
|
208
|
+
|
|
209
|
+
- **incremental-implementation** — Build new features in slices; simplify afterward
|
|
210
|
+
- **systematic-debugging** — When simplification reveals hidden bugs
|
|
211
|
+
- **defense-in-depth** — When simplifying validation, ensure all layers still hold
|
|
@@ -9,6 +9,7 @@ dependencies: []
|
|
|
9
9
|
# Condition-Based Waiting
|
|
10
10
|
|
|
11
11
|
> **Replaces** arbitrary `sleep()` / `setTimeout()` calls and hardcoded delays that cause flaky tests and slow CI
|
|
12
|
+
|
|
12
13
|
## When to Use
|
|
13
14
|
|
|
14
15
|
- Tests are flaky due to arbitrary delays or timing guesses
|
|
@@ -19,6 +20,17 @@ dependencies: []
|
|
|
19
20
|
- You are explicitly testing timing behavior (debounce, throttle, intervals)
|
|
20
21
|
- A fixed, documented timeout is part of the requirement
|
|
21
22
|
|
|
23
|
+
## Common Rationalizations
|
|
24
|
+
|
|
25
|
+
| Rationalization | Rebuttal |
|
|
26
|
+
| --------------------------------------------- | ------------------------------------------------------------------------------ |
|
|
27
|
+
| "50ms is plenty of time" | It's plenty on YOUR machine. CI runners under load disagree |
|
|
28
|
+
| "The sleep worked in local testing" | Local = fast SSD, idle CPU. CI = shared resources, variable latency |
|
|
29
|
+
| "Adding a waitFor is more complex than sleep" | A 5-line waitFor is simpler than debugging a flaky test 10 times |
|
|
30
|
+
| "This operation is always fast" | "Always" until garbage collection, disk I/O, or network latency says otherwise |
|
|
31
|
+
| "I'll increase the timeout if it flakes" | Increasing timeouts slows the entire suite and masks the real problem |
|
|
32
|
+
| "It only fails sometimes" | "Sometimes" = race condition. Condition-based waiting eliminates it entirely |
|
|
33
|
+
|
|
22
34
|
## Overview
|
|
23
35
|
|
|
24
36
|
Flaky tests often guess at timing with arbitrary delays. This creates race conditions where tests pass on fast machines but fail under load or in CI.
|
|
@@ -20,14 +20,24 @@ dependencies: []
|
|
|
20
20
|
- Simple, single-layer validation at an obvious entry point is enough
|
|
21
21
|
- The issue is unrelated to invalid data or boundary checks
|
|
22
22
|
|
|
23
|
+
## Common Rationalizations
|
|
24
|
+
|
|
25
|
+
| Rationalization | Rebuttal |
|
|
26
|
+
| ------------------------------------------------ | --------------------------------------------------------------------------------------------------------- |
|
|
27
|
+
| "One validation at the entry point is enough" | Different code paths, refactors, and mocks all bypass a single gate — each layer catches what others miss |
|
|
28
|
+
| "This adds too much boilerplate" | Each validation is 2-3 lines. The bug it prevents costs hours of debugging |
|
|
29
|
+
| "The caller already validates this" | You don't control the caller. New callers won't know your assumptions |
|
|
30
|
+
| "Tests will catch it" | Tests run after the fact. Validation prevents the bug from existing |
|
|
31
|
+
| "This is an internal function, input is trusted" | Internal functions get called from new paths during refactors. Trust no input |
|
|
32
|
+
|
|
23
33
|
## Anti-Patterns
|
|
24
34
|
|
|
25
|
-
| Anti-Pattern
|
|
26
|
-
|
|
|
27
|
-
| Validating only at the entry point (trusting downstream) | Alternate paths and refactors bypass one gate | Add independent checks at each boundary
|
|
28
|
-
| Duplicating identical validation at every layer
|
|
29
|
-
| Catching and swallowing errors silently
|
|
30
|
-
| Mixing validation with business logic
|
|
35
|
+
| Anti-Pattern | Why It Fails | Instead |
|
|
36
|
+
| -------------------------------------------------------- | --------------------------------------------- | ------------------------------------------------------------ |
|
|
37
|
+
| Validating only at the entry point (trusting downstream) | Alternate paths and refactors bypass one gate | Add independent checks at each boundary |
|
|
38
|
+
| Duplicating identical validation at every layer | Creates noise without improving safety | Tailor each layer to boundary-specific invariants |
|
|
39
|
+
| Catching and swallowing errors silently | Hides failures and delays detection | Raise explicit errors with actionable context |
|
|
40
|
+
| Mixing validation with business logic | Makes behavior hard to reason about and test | Keep validation checks explicit and separate from core logic |
|
|
31
41
|
|
|
32
42
|
## Overview
|
|
33
43
|
|