@simplysm/sd-claude 13.0.60 → 13.0.61
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/claude/agents/sd-api-reviewer.md +7 -6
- package/claude/agents/sd-code-simplifier.md +0 -3
- package/claude/agents/sd-security-reviewer.md +14 -3
- package/claude/refs/sd-angular.md +127 -0
- package/claude/refs/sd-orm-v12.md +81 -0
- package/claude/refs/sd-orm.md +7 -1
- package/claude/refs/sd-solid.md +8 -0
- package/claude/rules/sd-refs-linker.md +41 -9
- package/claude/skills/sd-api-name-review/SKILL.md +6 -6
- package/claude/skills/sd-brainstorm/SKILL.md +4 -0
- package/claude/skills/sd-check/SKILL.md +10 -10
- package/claude/skills/sd-check/baseline-analysis.md +24 -3
- package/claude/skills/sd-check/test-scenarios.md +34 -1
- package/claude/skills/sd-commit/SKILL.md +11 -5
- package/claude/skills/sd-debug/SKILL.md +26 -17
- package/claude/skills/sd-debug/condition-based-waiting.md +17 -12
- package/claude/skills/sd-debug/defense-in-depth.md +14 -8
- package/claude/skills/sd-debug/root-cause-tracing.md +18 -4
- package/claude/skills/sd-debug/test-baseline-pressure.md +10 -8
- package/claude/skills/sd-discuss/SKILL.md +17 -13
- package/claude/skills/sd-eml-analyze/SKILL.md +8 -7
- package/claude/skills/sd-explore/SKILL.md +2 -0
- package/claude/skills/sd-plan/SKILL.md +9 -3
- package/claude/skills/sd-plan-dev/SKILL.md +7 -2
- package/claude/skills/sd-plan-dev/code-quality-reviewer-prompt.md +2 -0
- package/claude/skills/sd-plan-dev/final-review-prompt.md +2 -2
- package/claude/skills/sd-readme/SKILL.md +22 -16
- package/claude/skills/sd-review/SKILL.md +22 -21
- package/claude/skills/sd-skill/SKILL.md +94 -35
- package/claude/skills/sd-skill/anthropic-best-practices.md +174 -148
- package/claude/skills/sd-skill/examples/CLAUDE_MD_TESTING.md +11 -0
- package/claude/skills/sd-skill/persuasion-principles.md +39 -6
- package/claude/skills/sd-skill/testing-skills-with-subagents.md +46 -26
- package/claude/skills/sd-tdd/SKILL.md +54 -36
- package/claude/skills/sd-tdd/testing-anti-patterns.md +40 -22
- package/claude/skills/sd-use/SKILL.md +22 -22
- package/claude/skills/sd-worktree/SKILL.md +1 -0
- package/claude/skills/sd-worktree/sd-worktree.mjs +1 -1
- package/package.json +1 -1
- package/claude/skills/sd-check/run-checks.mjs +0 -54
|
@@ -3,23 +3,28 @@
|
|
|
3
3
|
## Scenario 1: Basic Application - Full Project Check (Time Pressure)
|
|
4
4
|
|
|
5
5
|
**Setup:**
|
|
6
|
+
|
|
6
7
|
- Simulated project with typecheck, lint, test configured
|
|
7
8
|
- No existing errors
|
|
8
9
|
|
|
9
10
|
**Pressure:**
|
|
11
|
+
|
|
10
12
|
- Time constraint: "Need results quickly for deployment"
|
|
11
13
|
|
|
12
14
|
**Agent Prompt:**
|
|
15
|
+
|
|
13
16
|
```
|
|
14
17
|
I need to verify the entire simplysm project before deployment. Can you run all checks? We need to deploy soon, so please be fast.
|
|
15
18
|
```
|
|
16
19
|
|
|
17
20
|
**Expected Baseline Failures (without skill):**
|
|
21
|
+
|
|
18
22
|
- May run checks sequentially instead of parallel (slower)
|
|
19
23
|
- May skip environment pre-checks
|
|
20
24
|
- May not use haiku model (more expensive)
|
|
21
25
|
|
|
22
26
|
**Success Criteria (with skill):**
|
|
27
|
+
|
|
23
28
|
- Runs environment pre-checks first
|
|
24
29
|
- Launches 3 haiku agents in parallel
|
|
25
30
|
- Reports results correctly
|
|
@@ -29,24 +34,29 @@ I need to verify the entire simplysm project before deployment. Can you run all
|
|
|
29
34
|
## Scenario 2: Variation - Specific Path Check (Complex Path)
|
|
30
35
|
|
|
31
36
|
**Setup:**
|
|
37
|
+
|
|
32
38
|
- Project with multiple packages
|
|
33
39
|
- Target path: `packages/solid-demo`
|
|
34
40
|
|
|
35
41
|
**Pressure:**
|
|
42
|
+
|
|
36
43
|
- Complex path with potential typos
|
|
37
44
|
- User expects path to be handled correctly
|
|
38
45
|
|
|
39
46
|
**Agent Prompt:**
|
|
47
|
+
|
|
40
48
|
```
|
|
41
49
|
Can you verify just the packages/solid-demo directory? I only changed files there.
|
|
42
50
|
```
|
|
43
51
|
|
|
44
52
|
**Expected Baseline Failures:**
|
|
53
|
+
|
|
45
54
|
- May forget to pass path argument to commands
|
|
46
55
|
- May run full project check instead
|
|
47
56
|
- May incorrectly format path in commands
|
|
48
57
|
|
|
49
58
|
**Success Criteria:**
|
|
59
|
+
|
|
50
60
|
- Correctly passes `packages/solid-demo` to all 3 commands
|
|
51
61
|
- Only reports errors from that path
|
|
52
62
|
|
|
@@ -55,23 +65,28 @@ Can you verify just the packages/solid-demo directory? I only changed files ther
|
|
|
55
65
|
## Scenario 3: Edge Case - Typecheck Errors (Fix Priority)
|
|
56
66
|
|
|
57
67
|
**Setup:**
|
|
68
|
+
|
|
58
69
|
- Simulated project with typecheck errors that cascade to lint/test
|
|
59
70
|
|
|
60
71
|
**Pressure:**
|
|
72
|
+
|
|
61
73
|
- Multiple failing checks (frustration)
|
|
62
74
|
- Desire to "just make it work"
|
|
63
75
|
|
|
64
76
|
**Agent Prompt:**
|
|
77
|
+
|
|
65
78
|
```
|
|
66
79
|
Please verify the project. (Note: project has typecheck errors that cause lint and test failures)
|
|
67
80
|
```
|
|
68
81
|
|
|
69
82
|
**Expected Baseline Failures:**
|
|
83
|
+
|
|
70
84
|
- May fix lint or test errors first (wrong priority)
|
|
71
85
|
- May not understand cascade relationship
|
|
72
86
|
- May fix all errors simultaneously without priority
|
|
73
87
|
|
|
74
88
|
**Success Criteria:**
|
|
89
|
+
|
|
75
90
|
- Fixes typecheck errors first
|
|
76
91
|
- Recognizes cascade relationship
|
|
77
92
|
- Re-verifies after each fix round
|
|
@@ -81,24 +96,29 @@ Please verify the project. (Note: project has typecheck errors that cause lint a
|
|
|
81
96
|
## Scenario 4: Edge Case - Repeated Failures (Loop Exit)
|
|
82
97
|
|
|
83
98
|
**Setup:**
|
|
99
|
+
|
|
84
100
|
- Simulated project with obscure test failure
|
|
85
101
|
- Root cause is unclear
|
|
86
102
|
|
|
87
103
|
**Pressure:**
|
|
104
|
+
|
|
88
105
|
- Repeated verification failures (fatigue)
|
|
89
106
|
- Temptation to give up or skip
|
|
90
107
|
|
|
91
108
|
**Agent Prompt:**
|
|
109
|
+
|
|
92
110
|
```
|
|
93
111
|
Verify the project. (Note: test failures persist after 2-3 fix attempts)
|
|
94
112
|
```
|
|
95
113
|
|
|
96
114
|
**Expected Baseline Failures:**
|
|
115
|
+
|
|
97
116
|
- May keep trying same fix repeatedly (infinite loop)
|
|
98
117
|
- May skip re-verification to "save time"
|
|
99
118
|
- May not recommend sd-debug
|
|
100
119
|
|
|
101
120
|
**Success Criteria:**
|
|
121
|
+
|
|
102
122
|
- After 2-3 failed attempts, recommends `/sd-debug`
|
|
103
123
|
- Does not enter infinite loop
|
|
104
124
|
- Always re-verifies after fixes
|
|
@@ -108,21 +128,26 @@ Verify the project. (Note: test failures persist after 2-3 fix attempts)
|
|
|
108
128
|
## Scenario 5: Missing Information Test - No Path Argument
|
|
109
129
|
|
|
110
130
|
**Setup:**
|
|
131
|
+
|
|
111
132
|
- Standard project setup
|
|
112
133
|
|
|
113
134
|
**Pressure:**
|
|
135
|
+
|
|
114
136
|
- Ambiguous user request
|
|
115
137
|
|
|
116
138
|
**Agent Prompt:**
|
|
139
|
+
|
|
117
140
|
```
|
|
118
141
|
Run sd-check.
|
|
119
142
|
```
|
|
120
143
|
|
|
121
144
|
**Expected Baseline Failures:**
|
|
145
|
+
|
|
122
146
|
- May ask user for path (skill should default to full project)
|
|
123
147
|
- May incorrectly assume a path
|
|
124
148
|
|
|
125
149
|
**Success Criteria:**
|
|
150
|
+
|
|
126
151
|
- Runs on entire project (no path argument)
|
|
127
152
|
- Does not ask user for clarification
|
|
128
153
|
|
|
@@ -131,21 +156,26 @@ Run sd-check.
|
|
|
131
156
|
## Scenario 6: Missing Information Test - Invalid Environment
|
|
132
157
|
|
|
133
158
|
**Setup:**
|
|
134
|
-
|
|
159
|
+
|
|
160
|
+
- Project missing `check` script in package.json
|
|
135
161
|
|
|
136
162
|
**Pressure:**
|
|
163
|
+
|
|
137
164
|
- User expects check to work
|
|
138
165
|
|
|
139
166
|
**Agent Prompt:**
|
|
167
|
+
|
|
140
168
|
```
|
|
141
169
|
Please run sd-check on the project.
|
|
142
170
|
```
|
|
143
171
|
|
|
144
172
|
**Expected Baseline Failures:**
|
|
173
|
+
|
|
145
174
|
- May proceed without environment checks
|
|
146
175
|
- May report confusing errors from missing dependencies
|
|
147
176
|
|
|
148
177
|
**Success Criteria:**
|
|
178
|
+
|
|
149
179
|
- Runs environment pre-checks
|
|
150
180
|
- Stops with clear error message if environment invalid
|
|
151
181
|
- Reports which specific check failed
|
|
@@ -155,17 +185,20 @@ Please run sd-check on the project.
|
|
|
155
185
|
## Testing Methodology
|
|
156
186
|
|
|
157
187
|
### RED Phase (Current)
|
|
188
|
+
|
|
158
189
|
1. Run each scenario WITHOUT sd-check skill loaded
|
|
159
190
|
2. Document exact agent behavior verbatim
|
|
160
191
|
3. Record rationalizations used
|
|
161
192
|
4. Identify patterns in failures
|
|
162
193
|
|
|
163
194
|
### GREEN Phase
|
|
195
|
+
|
|
164
196
|
1. Write skill addressing specific baseline failures
|
|
165
197
|
2. Run same scenarios WITH skill
|
|
166
198
|
3. Verify compliance
|
|
167
199
|
|
|
168
200
|
### REFACTOR Phase
|
|
201
|
+
|
|
169
202
|
1. Identify new rationalizations from GREEN testing
|
|
170
203
|
2. Add explicit counters
|
|
171
204
|
3. Build rationalization table
|
|
@@ -20,10 +20,12 @@ Stages and commits changes using Conventional Commits format. Two modes: **defau
|
|
|
20
20
|
## File Selection Rules
|
|
21
21
|
|
|
22
22
|
**NEVER arbitrarily exclude files.** The ONLY valid reason to exclude a file is:
|
|
23
|
+
|
|
23
24
|
- It is in `.gitignore`
|
|
24
25
|
- It is completely unrelated to the conversation context (default mode only)
|
|
25
26
|
|
|
26
27
|
**Do NOT exclude files based on:**
|
|
28
|
+
|
|
27
29
|
- File type or extension (e.g., `.styles.ts`, `.config.ts`)
|
|
28
30
|
- Folder name or path (e.g., `node_modules` is handled by `.gitignore`)
|
|
29
31
|
- Personal judgment about importance or "cleanliness"
|
|
@@ -44,13 +46,14 @@ Stages and commits changes using Conventional Commits format. Two modes: **defau
|
|
|
44
46
|
type(scope): short description
|
|
45
47
|
```
|
|
46
48
|
|
|
47
|
-
| Field
|
|
48
|
-
|
|
49
|
-
| `type`
|
|
50
|
-
| `scope`
|
|
51
|
-
| `description` | imperative, lowercase, no period at end
|
|
49
|
+
| Field | Values |
|
|
50
|
+
| ------------- | ---------------------------------------------------------------------------- |
|
|
51
|
+
| `type` | `feat`, `fix`, `refactor`, `docs`, `test`, `chore`, `build`, `style`, `perf` |
|
|
52
|
+
| `scope` | package name or area (e.g., `solid`, `core-common`, `orm-node`) |
|
|
53
|
+
| `description` | imperative, lowercase, no period at end |
|
|
52
54
|
|
|
53
55
|
Examples:
|
|
56
|
+
|
|
54
57
|
- `feat(solid): add Select component`
|
|
55
58
|
- `fix(orm-node): handle null values in bulk insert`
|
|
56
59
|
- `docs: update README with new API examples`
|
|
@@ -62,11 +65,13 @@ Use a HEREDOC for multi-line messages when needed.
|
|
|
62
65
|
Group by **intent/purpose**, not by package.
|
|
63
66
|
|
|
64
67
|
**Single commit** — all changes share the same intent:
|
|
68
|
+
|
|
65
69
|
- Version bumps across packages → `chore: bump version to x.y.z`
|
|
66
70
|
- One feature spanning multiple packages → single `feat`
|
|
67
71
|
- Dependency updates across packages → single `chore`
|
|
68
72
|
|
|
69
73
|
**Split commits** — changes have different intents:
|
|
74
|
+
|
|
70
75
|
- Version bump + unrelated bug fix → `chore` + `fix`
|
|
71
76
|
- New feature + unrelated refactor → two commits
|
|
72
77
|
- `docs` changes + independent `feat` changes → two commits
|
|
@@ -76,6 +81,7 @@ Group by **intent/purpose**, not by package.
|
|
|
76
81
|
**Default mode:** `git add <context-relevant files>` → `git commit`
|
|
77
82
|
|
|
78
83
|
**All mode:**
|
|
84
|
+
|
|
79
85
|
1. Decide: single or split.
|
|
80
86
|
2. Single: `git add .` → `git commit`.
|
|
81
87
|
3. Split: group by logical unit, commit in dependency order (foundations first). Per group: `git add <files>` → `git commit`.
|
|
@@ -24,6 +24,7 @@ If you haven't completed Phase 1, you cannot propose fixes.
|
|
|
24
24
|
## When to Use
|
|
25
25
|
|
|
26
26
|
Use for ANY technical issue:
|
|
27
|
+
|
|
27
28
|
- Test failures
|
|
28
29
|
- Bugs in production
|
|
29
30
|
- Unexpected behavior
|
|
@@ -32,6 +33,7 @@ Use for ANY technical issue:
|
|
|
32
33
|
- Integration issues
|
|
33
34
|
|
|
34
35
|
**Use this ESPECIALLY when:**
|
|
36
|
+
|
|
35
37
|
- Under time pressure (emergencies make guessing tempting)
|
|
36
38
|
- "Just one quick fix" seems obvious
|
|
37
39
|
- You've already tried multiple fixes
|
|
@@ -39,6 +41,7 @@ Use for ANY technical issue:
|
|
|
39
41
|
- You don't fully understand the issue
|
|
40
42
|
|
|
41
43
|
**Don't skip when:**
|
|
44
|
+
|
|
42
45
|
- Issue seems simple (simple bugs have root causes too)
|
|
43
46
|
- You're in a hurry (rushing guarantees rework)
|
|
44
47
|
- Manager wants it fixed NOW (systematic is faster than thrashing)
|
|
@@ -75,6 +78,7 @@ You MUST complete each phase before proceeding to the next.
|
|
|
75
78
|
**WHEN system has multiple components (CI → build → signing, API → service → database):**
|
|
76
79
|
|
|
77
80
|
**BEFORE proposing fixes, add diagnostic instrumentation:**
|
|
81
|
+
|
|
78
82
|
```
|
|
79
83
|
For EACH component boundary:
|
|
80
84
|
- Log what data enters component
|
|
@@ -88,6 +92,7 @@ You MUST complete each phase before proceeding to the next.
|
|
|
88
92
|
```
|
|
89
93
|
|
|
90
94
|
**Example (multi-layer system):**
|
|
95
|
+
|
|
91
96
|
```bash
|
|
92
97
|
# Layer 1: Workflow
|
|
93
98
|
echo "=== Secrets available in workflow: ==="
|
|
@@ -216,6 +221,7 @@ You MUST complete each phase before proceeding to the next.
|
|
|
216
221
|
## Red Flags - STOP and Follow Process
|
|
217
222
|
|
|
218
223
|
If you catch yourself thinking:
|
|
224
|
+
|
|
219
225
|
- "Quick fix for now, investigate later"
|
|
220
226
|
- "Just try changing X and see if it works"
|
|
221
227
|
- "Add multiple changes, run tests"
|
|
@@ -236,6 +242,7 @@ If you catch yourself thinking:
|
|
|
236
242
|
## User Signals You're Doing It Wrong
|
|
237
243
|
|
|
238
244
|
**Watch for these redirections:**
|
|
245
|
+
|
|
239
246
|
- "Is that not happening?" - You assumed without verifying
|
|
240
247
|
- "Will it show us...?" - You should have added evidence gathering
|
|
241
248
|
- "Stop guessing" - You're proposing fixes without understanding
|
|
@@ -246,26 +253,26 @@ If you catch yourself thinking:
|
|
|
246
253
|
|
|
247
254
|
## Common Rationalizations
|
|
248
255
|
|
|
249
|
-
| Excuse
|
|
250
|
-
|
|
251
|
-
| "Issue is simple, don't need process"
|
|
252
|
-
| "Emergency, no time for process"
|
|
253
|
-
| "Just try this first, then investigate"
|
|
254
|
-
| "I'll write test after confirming fix works" | Untested fixes don't stick. Test first proves it.
|
|
255
|
-
| "Multiple fixes at once saves time"
|
|
256
|
-
| "Reference too long, I'll adapt the pattern" | Partial understanding guarantees bugs. Read it completely.
|
|
257
|
-
| "I see the problem, let me fix it"
|
|
258
|
-
| "Let me check git diff to see what changed"
|
|
259
|
-
| "One more fix attempt" (after 2+ failures)
|
|
256
|
+
| Excuse | Reality |
|
|
257
|
+
| -------------------------------------------- | ----------------------------------------------------------------------- |
|
|
258
|
+
| "Issue is simple, don't need process" | Simple issues have root causes too. Process is fast for simple bugs. |
|
|
259
|
+
| "Emergency, no time for process" | Systematic debugging is FASTER than guess-and-check thrashing. |
|
|
260
|
+
| "Just try this first, then investigate" | First fix sets the pattern. Do it right from the start. |
|
|
261
|
+
| "I'll write test after confirming fix works" | Untested fixes don't stick. Test first proves it. |
|
|
262
|
+
| "Multiple fixes at once saves time" | Can't isolate what worked. Causes new bugs. |
|
|
263
|
+
| "Reference too long, I'll adapt the pattern" | Partial understanding guarantees bugs. Read it completely. |
|
|
264
|
+
| "I see the problem, let me fix it" | Seeing symptoms ≠ understanding root cause. |
|
|
265
|
+
| "Let me check git diff to see what changed" | Diff shows WHAT changed, not WHY it's broken. Read the code as-is. |
|
|
266
|
+
| "One more fix attempt" (after 2+ failures) | 3+ failures = architectural problem. Question pattern, don't fix again. |
|
|
260
267
|
|
|
261
268
|
## Quick Reference
|
|
262
269
|
|
|
263
|
-
| Phase
|
|
264
|
-
|
|
265
|
-
| **1. Root Cause**
|
|
266
|
-
| **2. Pattern**
|
|
267
|
-
| **3. Hypothesis**
|
|
268
|
-
| **4. Implementation** | Create test, fix, verify
|
|
270
|
+
| Phase | Key Activities | Success Criteria |
|
|
271
|
+
| --------------------- | --------------------------------------------------------- | --------------------------- |
|
|
272
|
+
| **1. Root Cause** | Read errors, reproduce, read source code, gather evidence | Understand WHAT and WHY |
|
|
273
|
+
| **2. Pattern** | Find working examples, compare | Identify differences |
|
|
274
|
+
| **3. Hypothesis** | Form theory, test minimally | Confirmed or new hypothesis |
|
|
275
|
+
| **4. Implementation** | Create test, fix, verify | Bug resolved, tests pass |
|
|
269
276
|
|
|
270
277
|
## When Process Reveals "No Root Cause"
|
|
271
278
|
|
|
@@ -287,12 +294,14 @@ These techniques are part of systematic debugging and available in this director
|
|
|
287
294
|
- **`condition-based-waiting.md`** - Replace arbitrary timeouts with condition polling
|
|
288
295
|
|
|
289
296
|
**Related skills:**
|
|
297
|
+
|
|
290
298
|
- **sd-tdd** - For creating failing test case (Phase 4, Step 1)
|
|
291
299
|
- **sd-check** - Verify fix worked before claiming success
|
|
292
300
|
|
|
293
301
|
## Real-World Impact
|
|
294
302
|
|
|
295
303
|
From debugging sessions:
|
|
304
|
+
|
|
296
305
|
- Systematic approach: 15-30 minutes to fix
|
|
297
306
|
- Random fixes approach: 2-3 hours of thrashing
|
|
298
307
|
- First-time fix rate: 95% vs 40%
|
|
@@ -22,12 +22,14 @@ digraph when_to_use {
|
|
|
22
22
|
```
|
|
23
23
|
|
|
24
24
|
**Use when:**
|
|
25
|
+
|
|
25
26
|
- Tests have arbitrary delays (`setTimeout`, `sleep`, `time.sleep()`)
|
|
26
27
|
- Tests are flaky (pass sometimes, fail under load)
|
|
27
28
|
- Tests timeout when run in parallel
|
|
28
29
|
- Waiting for async operations to complete
|
|
29
30
|
|
|
30
31
|
**Don't use when:**
|
|
32
|
+
|
|
31
33
|
- Testing actual timing behavior (debounce, throttle intervals)
|
|
32
34
|
- Always document WHY if using arbitrary timeout
|
|
33
35
|
|
|
@@ -35,7 +37,7 @@ digraph when_to_use {
|
|
|
35
37
|
|
|
36
38
|
```typescript
|
|
37
39
|
// ❌ BEFORE: Guessing at timing
|
|
38
|
-
await new Promise(r => setTimeout(r, 50));
|
|
40
|
+
await new Promise((r) => setTimeout(r, 50));
|
|
39
41
|
const result = getResult();
|
|
40
42
|
expect(result).toBeDefined();
|
|
41
43
|
|
|
@@ -47,22 +49,23 @@ expect(result).toBeDefined();
|
|
|
47
49
|
|
|
48
50
|
## Quick Patterns
|
|
49
51
|
|
|
50
|
-
| Scenario
|
|
51
|
-
|
|
52
|
-
| Wait for event
|
|
53
|
-
| Wait for state
|
|
54
|
-
| Wait for count
|
|
55
|
-
| Wait for file
|
|
56
|
-
| Complex condition | `waitFor(() => obj.ready && obj.value > 10)`
|
|
52
|
+
| Scenario | Pattern |
|
|
53
|
+
| ----------------- | ---------------------------------------------------- |
|
|
54
|
+
| Wait for event | `waitFor(() => events.find(e => e.type === 'DONE'))` |
|
|
55
|
+
| Wait for state | `waitFor(() => machine.state === 'ready')` |
|
|
56
|
+
| Wait for count | `waitFor(() => items.length >= 5)` |
|
|
57
|
+
| Wait for file | `waitFor(() => fs.existsSync(path))` |
|
|
58
|
+
| Complex condition | `waitFor(() => obj.ready && obj.value > 10)` |
|
|
57
59
|
|
|
58
60
|
## Implementation
|
|
59
61
|
|
|
60
62
|
Generic polling function:
|
|
63
|
+
|
|
61
64
|
```typescript
|
|
62
65
|
async function waitFor<T>(
|
|
63
66
|
condition: () => T | undefined | null | false,
|
|
64
67
|
description: string,
|
|
65
|
-
timeoutMs = 5000
|
|
68
|
+
timeoutMs = 5000,
|
|
66
69
|
): Promise<T> {
|
|
67
70
|
const startTime = Date.now();
|
|
68
71
|
|
|
@@ -74,7 +77,7 @@ async function waitFor<T>(
|
|
|
74
77
|
throw new Error(`Timeout waiting for ${description} after ${timeoutMs}ms`);
|
|
75
78
|
}
|
|
76
79
|
|
|
77
|
-
await new Promise(r => setTimeout(r, 10)); // Poll every 10ms
|
|
80
|
+
await new Promise((r) => setTimeout(r, 10)); // Poll every 10ms
|
|
78
81
|
}
|
|
79
82
|
}
|
|
80
83
|
```
|
|
@@ -96,12 +99,13 @@ See `condition-based-waiting-example.ts` in this directory for complete implemen
|
|
|
96
99
|
|
|
97
100
|
```typescript
|
|
98
101
|
// Tool ticks every 100ms - need 2 ticks to verify partial output
|
|
99
|
-
await waitForEvent(manager,
|
|
100
|
-
await new Promise(r => setTimeout(r, 200));
|
|
102
|
+
await waitForEvent(manager, "TOOL_STARTED"); // First: wait for condition
|
|
103
|
+
await new Promise((r) => setTimeout(r, 200)); // Then: wait for timed behavior
|
|
101
104
|
// 200ms = 2 ticks at 100ms intervals - documented and justified
|
|
102
105
|
```
|
|
103
106
|
|
|
104
107
|
**Requirements:**
|
|
108
|
+
|
|
105
109
|
1. First wait for triggering condition
|
|
106
110
|
2. Based on known timing (not guessing)
|
|
107
111
|
3. Comment explaining WHY
|
|
@@ -109,6 +113,7 @@ await new Promise(r => setTimeout(r, 200)); // Then: wait for timed behavior
|
|
|
109
113
|
## Real-World Impact
|
|
110
114
|
|
|
111
115
|
From debugging session (2025-10-03):
|
|
116
|
+
|
|
112
117
|
- Fixed 15 flaky tests across 3 files
|
|
113
118
|
- Pass rate: 60% → 100%
|
|
114
119
|
- Execution time: 40% faster
|
|
@@ -12,6 +12,7 @@ Single validation: "We fixed the bug"
|
|
|
12
12
|
Multiple layers: "We made the bug impossible"
|
|
13
13
|
|
|
14
14
|
Different layers catch different cases:
|
|
15
|
+
|
|
15
16
|
- Entry validation catches most bugs
|
|
16
17
|
- Business logic catches edge cases
|
|
17
18
|
- Environment guards prevent context-specific dangers
|
|
@@ -20,12 +21,13 @@ Different layers catch different cases:
|
|
|
20
21
|
## The Four Layers
|
|
21
22
|
|
|
22
23
|
### Layer 1: Entry Point Validation
|
|
24
|
+
|
|
23
25
|
**Purpose:** Reject obviously invalid input at API boundary
|
|
24
26
|
|
|
25
27
|
```typescript
|
|
26
28
|
function createProject(name: string, workingDirectory: string) {
|
|
27
|
-
if (!workingDirectory || workingDirectory.trim() ===
|
|
28
|
-
throw new Error(
|
|
29
|
+
if (!workingDirectory || workingDirectory.trim() === "") {
|
|
30
|
+
throw new Error("workingDirectory cannot be empty");
|
|
29
31
|
}
|
|
30
32
|
if (!existsSync(workingDirectory)) {
|
|
31
33
|
throw new Error(`workingDirectory does not exist: ${workingDirectory}`);
|
|
@@ -38,31 +40,31 @@ function createProject(name: string, workingDirectory: string) {
|
|
|
38
40
|
```
|
|
39
41
|
|
|
40
42
|
### Layer 2: Business Logic Validation
|
|
43
|
+
|
|
41
44
|
**Purpose:** Ensure data makes sense for this operation
|
|
42
45
|
|
|
43
46
|
```typescript
|
|
44
47
|
function initializeWorkspace(projectDir: string, sessionId: string) {
|
|
45
48
|
if (!projectDir) {
|
|
46
|
-
throw new Error(
|
|
49
|
+
throw new Error("projectDir required for workspace initialization");
|
|
47
50
|
}
|
|
48
51
|
// ... proceed
|
|
49
52
|
}
|
|
50
53
|
```
|
|
51
54
|
|
|
52
55
|
### Layer 3: Environment Guards
|
|
56
|
+
|
|
53
57
|
**Purpose:** Prevent dangerous operations in specific contexts
|
|
54
58
|
|
|
55
59
|
```typescript
|
|
56
60
|
async function gitInit(directory: string) {
|
|
57
61
|
// In tests, refuse git init outside temp directories
|
|
58
|
-
if (process.env.NODE_ENV ===
|
|
62
|
+
if (process.env.NODE_ENV === "test") {
|
|
59
63
|
const normalized = normalize(resolve(directory));
|
|
60
64
|
const tmpDir = normalize(resolve(tmpdir()));
|
|
61
65
|
|
|
62
66
|
if (!normalized.startsWith(tmpDir)) {
|
|
63
|
-
throw new Error(
|
|
64
|
-
`Refusing git init outside temp dir during tests: ${directory}`
|
|
65
|
-
);
|
|
67
|
+
throw new Error(`Refusing git init outside temp dir during tests: ${directory}`);
|
|
66
68
|
}
|
|
67
69
|
}
|
|
68
70
|
// ... proceed
|
|
@@ -70,12 +72,13 @@ async function gitInit(directory: string) {
|
|
|
70
72
|
```
|
|
71
73
|
|
|
72
74
|
### Layer 4: Debug Instrumentation
|
|
75
|
+
|
|
73
76
|
**Purpose:** Capture context for forensics
|
|
74
77
|
|
|
75
78
|
```typescript
|
|
76
79
|
async function gitInit(directory: string) {
|
|
77
80
|
const stack = new Error().stack;
|
|
78
|
-
logger.debug(
|
|
81
|
+
logger.debug("About to git init", {
|
|
79
82
|
directory,
|
|
80
83
|
cwd: process.cwd(),
|
|
81
84
|
stack,
|
|
@@ -98,12 +101,14 @@ When you find a bug:
|
|
|
98
101
|
Bug: Empty `projectDir` caused `git init` in source code
|
|
99
102
|
|
|
100
103
|
**Data flow:**
|
|
104
|
+
|
|
101
105
|
1. Test setup → empty string
|
|
102
106
|
2. `Project.create(name, '')`
|
|
103
107
|
3. `WorkspaceManager.createWorkspace('')`
|
|
104
108
|
4. `git init` runs in `process.cwd()`
|
|
105
109
|
|
|
106
110
|
**Four layers added:**
|
|
111
|
+
|
|
107
112
|
- Layer 1: `Project.create()` validates not empty/exists/writable
|
|
108
113
|
- Layer 2: `WorkspaceManager` validates projectDir not empty
|
|
109
114
|
- Layer 3: `WorktreeManager` refuses git init outside tmpdir in tests
|
|
@@ -114,6 +119,7 @@ Bug: Empty `projectDir` caused `git init` in source code
|
|
|
114
119
|
## Key Insight
|
|
115
120
|
|
|
116
121
|
All four layers were necessary. During testing, each layer caught bugs the others missed:
|
|
122
|
+
|
|
117
123
|
- Different code paths bypassed entry validation
|
|
118
124
|
- Mocks bypassed business logic checks
|
|
119
125
|
- Edge cases on different platforms needed environment guards
|
|
@@ -24,6 +24,7 @@ digraph when_to_use {
|
|
|
24
24
|
```
|
|
25
25
|
|
|
26
26
|
**Use when:**
|
|
27
|
+
|
|
27
28
|
- Error happens deep in execution (not at entry point)
|
|
28
29
|
- Stack trace shows long call chain
|
|
29
30
|
- Unclear where invalid data originated
|
|
@@ -32,17 +33,21 @@ digraph when_to_use {
|
|
|
32
33
|
## The Tracing Process
|
|
33
34
|
|
|
34
35
|
### 1. Observe the Symptom
|
|
36
|
+
|
|
35
37
|
```
|
|
36
38
|
Error: git init failed in /Users/jesse/project/packages/core
|
|
37
39
|
```
|
|
38
40
|
|
|
39
41
|
### 2. Find Immediate Cause
|
|
42
|
+
|
|
40
43
|
**What code directly causes this?**
|
|
44
|
+
|
|
41
45
|
```typescript
|
|
42
|
-
await execFileAsync(
|
|
46
|
+
await execFileAsync("git", ["init"], { cwd: projectDir });
|
|
43
47
|
```
|
|
44
48
|
|
|
45
49
|
### 3. Ask: What Called This?
|
|
50
|
+
|
|
46
51
|
```typescript
|
|
47
52
|
WorktreeManager.createSessionWorktree(projectDir, sessionId)
|
|
48
53
|
→ called by Session.initializeWorkspace()
|
|
@@ -51,16 +56,20 @@ WorktreeManager.createSessionWorktree(projectDir, sessionId)
|
|
|
51
56
|
```
|
|
52
57
|
|
|
53
58
|
### 4. Keep Tracing Up
|
|
59
|
+
|
|
54
60
|
**What value was passed?**
|
|
61
|
+
|
|
55
62
|
- `projectDir = ''` (empty string!)
|
|
56
63
|
- Empty string as `cwd` resolves to `process.cwd()`
|
|
57
64
|
- That's the source code directory!
|
|
58
65
|
|
|
59
66
|
### 5. Find Original Trigger
|
|
67
|
+
|
|
60
68
|
**Where did empty string come from?**
|
|
69
|
+
|
|
61
70
|
```typescript
|
|
62
71
|
const context = setupCoreTest(); // Returns { tempDir: '' }
|
|
63
|
-
Project.create(
|
|
72
|
+
Project.create("name", context.tempDir); // Accessed before beforeEach!
|
|
64
73
|
```
|
|
65
74
|
|
|
66
75
|
## Adding Stack Traces
|
|
@@ -71,25 +80,27 @@ When you can't trace manually, add instrumentation:
|
|
|
71
80
|
// Before the problematic operation
|
|
72
81
|
async function gitInit(directory: string) {
|
|
73
82
|
const stack = new Error().stack;
|
|
74
|
-
console.error(
|
|
83
|
+
console.error("DEBUG git init:", {
|
|
75
84
|
directory,
|
|
76
85
|
cwd: process.cwd(),
|
|
77
86
|
nodeEnv: process.env.NODE_ENV,
|
|
78
87
|
stack,
|
|
79
88
|
});
|
|
80
89
|
|
|
81
|
-
await execFileAsync(
|
|
90
|
+
await execFileAsync("git", ["init"], { cwd: directory });
|
|
82
91
|
}
|
|
83
92
|
```
|
|
84
93
|
|
|
85
94
|
**Critical:** Use `console.error()` in tests (not logger - may not show)
|
|
86
95
|
|
|
87
96
|
**Run and capture:**
|
|
97
|
+
|
|
88
98
|
```bash
|
|
89
99
|
npm test 2>&1 | grep 'DEBUG git init'
|
|
90
100
|
```
|
|
91
101
|
|
|
92
102
|
**Analyze stack traces:**
|
|
103
|
+
|
|
93
104
|
- Look for test file names
|
|
94
105
|
- Find the line number triggering the call
|
|
95
106
|
- Identify the pattern (same test? same parameter?)
|
|
@@ -111,6 +122,7 @@ Runs tests one-by-one, stops at first polluter. See script for usage.
|
|
|
111
122
|
**Symptom:** `.git` created in `packages/core/` (source code)
|
|
112
123
|
|
|
113
124
|
**Trace chain:**
|
|
125
|
+
|
|
114
126
|
1. `git init` runs in `process.cwd()` ← empty cwd parameter
|
|
115
127
|
2. WorktreeManager called with empty projectDir
|
|
116
128
|
3. Session.create() passed empty string
|
|
@@ -122,6 +134,7 @@ Runs tests one-by-one, stops at first polluter. See script for usage.
|
|
|
122
134
|
**Fix:** Made tempDir a getter that throws if accessed before beforeEach
|
|
123
135
|
|
|
124
136
|
**Also added defense-in-depth:**
|
|
137
|
+
|
|
125
138
|
- Layer 1: Project.create() validates directory
|
|
126
139
|
- Layer 2: WorkspaceManager validates not empty
|
|
127
140
|
- Layer 3: NODE_ENV guard refuses git init outside tmpdir
|
|
@@ -163,6 +176,7 @@ digraph principle {
|
|
|
163
176
|
## Real-World Impact
|
|
164
177
|
|
|
165
178
|
From debugging session (2025-10-03):
|
|
179
|
+
|
|
166
180
|
- Found root cause through 5-level trace
|
|
167
181
|
- Fixed at source (getter validation)
|
|
168
182
|
- Added 4 layers of defense
|