skills/role-expert-debug_dervish/SKILL.md
Systematic debugging methodology — runtime errors, test failures, logic bugs, performance issues, production incidents. Five-step framework, root-cause analysis, browser/Node/Svelte tooling, and common bug patterns.
npx skillsauth add jasonwarrenuk/goblin-mode DebuggingInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Methodical approach to identifying and fixing software issues. Emphasises reproducibility, isolation, and verification over trial-and-error debugging. Covers browser DevTools, Node debugging, logging strategies, and Svelte-specific debugging techniques.
Use this skill when:
Five-step process: Reproduce → Isolate → Diagnose → Fix → Verify
This applies to every debugging scenario without exception.
Make the bug happen reliably
Can't fix what you can't reproduce. First priority is finding reliable steps to trigger the issue.
Questions to ask:
Document reproduction steps:
## To Reproduce
1. Navigate to `/dashboard`
2. Click "Load More" button
3. Scroll to bottom of page
4. Click "Load More" again
**Expected**: More items load
**Actual**: Page freezes, console shows error
**Frequency**: Happens every time on 2nd click
If intermittent:
Narrow down the cause — then go deeper.
Once reproducible, determine exactly where the problem originates. But don't stop at the first explanation. The first "cause" is often a symptom. Ask why repeatedly until you reach the structural root.
The Five Whys:
Bug: Users see stale data after updating their profile.
Why? → The cache isn't invalidated after the update.
Why? → The update function doesn't call cache.invalidate().
Why? → The caching layer was added after the update function was written.
Why? → There's no pattern ensuring new writes invalidate related caches.
Root: Missing cache invalidation convention. Fix the convention, not just this instance.
Depth vs speed: Not every bug warrants five whys. Use your judgement:
Isolation techniques:
Binary search approach:
// Working at line 50?
console.log('Check 1:', data); // ✓ Data good here
// Working at line 75?
console.log('Check 2:', result); // ✗ Result undefined here
// Problem is between lines 50-75
Comment out code:
// Does removing this fix it?
// await someAsyncFunction();
// If yes, problem is in someAsyncFunction
Minimal reproduction:
// Strip away everything non-essential
// Original: 300 lines, complex state, multiple API calls
// Minimal: 20 lines that show the exact issue
async function minimalRepro() {
const data = await fetch('/api/items');
console.log(data); // undefined when expected array
}
Check assumptions:
// Assumption: API returns array
console.log(typeof data); // "object" - it's null!
// Assumption: User is logged in
console.log(user); // undefined - not logged in!
Understand why it's happening
Implement targeted solution
Now that you know the cause, fix it specifically.
Fix patterns:
Null/undefined checks:
// Before (crashes)
const count = items.length;
// After
const count = items?.length ?? 0;
Async timing:
// Before (race condition)
fetch('/api/data');
renderUI(); // Renders before data arrives
// After
const data = await fetch('/api/data');
renderUI(data);
State initialisation:
// Before (undefined on first render)
let items;
// After
let items = [];
Error boundaries:
// Before (crashes entire app)
const result = riskyOperation();
// After
try {
const result = riskyOperation();
} catch (error) {
console.error('Operation failed:', error);
showErrorToUser('Something went wrong');
}
Confirm the fix works
Don't assume it's fixed. Test thoroughly.
Verification checklist:
Test edge cases:
// Fixed for normal case, but what about:
- Empty array
- Null values
- Very large datasets
- Network failures
- Simultaneous requests
Write regression test (see testing-obsessive skill):
it('should handle second "Load More" click', async () => {
render(ItemList);
await clickLoadMore();
await clickLoadMore(); // This used to crash
expect(screen.getAllByRole('listitem').length).toBeGreaterThan(10);
});
Tool-specific reference and worked bug patterns, loaded only when needed:
Symptom fix: Fixes the immediate problem but doesn't prevent recurrence. Root-cause fix: Addresses the structural issue that allowed the bug to exist.
// Symptom fix: Add null check where the crash happens
const name = user?.profile?.name ?? 'Unknown';
// Root-cause fix: Ensure profile is always populated at creation
async function createUser(data: CreateUserRequest): Promise<User> {
return await db.users.create({
...data,
profile: { name: data.name } // Profile guaranteed at creation
});
}
When to ship a symptom fix: When the root cause is expensive to fix and the symptom fix is safe. But always log the root cause as a follow-up task.
When to insist on root-cause fix: When the bug pattern could recur in other places, when data integrity is at risk, or when the symptom fix introduces its own complexity.
Before:
During:
After:
tools
{{ 𝚫𝚫𝚫 }} Rebuild roadmap-system.zip, the distributable snapshot of the roadmap tooling (scripts, HTML template, conventions reference, and every roadmap-touching skill, including this one).
tools
--- name: "Suggest: Task" description: "{{ 𝚫𝚫𝚫 }} Suggest the next logical task — grounded in the roadmap's pre-vetted ready-set when one exists, codebase analysis otherwise" when_to_use: "When you don't know what to work on next and want a grounded recommendation rather than picking arbitrarily." model: haiku effort: low disable-model-invocation: true allowed-tools: ["Read", "Glob", "Grep", "Bash(python3:*)", "Bash(npm:*)", "Bash(bun:*)", "Bash(pnpm:*)", "Bash(deno:*)"] argument-hint: [named
development
{{ 𝛀𝛀𝛀 }} Convert an old simple-style roadmap (single Markdown, four statuses, <a name> anchors, roadmaps.json pointer registry) into the rich phase-array format (roadmaps.json source of truth + PHASE task list + prose overview).
data-ai
{{ ƔƔƔ }} Create a pull request to main — wordy or shiny (with screenshots), ready-for-review or draft