CCPM Debugging
Systematic debugging with Linear integration and structured troubleshooting workflow.
When to Use
This skill auto-activates when:
- •User mentions: "error", "failing", "broken", "debug", "bug", "issue"
- •Running
/ccpm:verifycommand - •Tests failing during
/ccpm:verify - •Build errors during implementation
- •Runtime errors or exceptions
- •Unexpected behavior
Core Debugging Philosophy
Defense-in-Depth Approach
Multiple layers of investigation:
- •Symptoms - What's observable?
- •Immediate cause - What directly caused the error?
- •Root cause - Why did it happen?
- •Systemic issues - Could this happen elsewhere?
- •Prevention - How to prevent recurrence?
Systematic Process
Observe → Hypothesize → Test → Confirm → Fix → Verify → Document
Never skip steps. Each step builds on the previous.
Integration with CCPM
Use with /ccpm:verify
When verification fails, this skill provides structured debugging:
User: "/ccpm:verify AUTH-123" Claude: [ccpm-debugging activates] Starting systematic debugging for AUTH-123... Step 1: GATHER SYMPTOMS Running /ccpm:verify to collect error information... Found failures: - 3 failing tests - 1 TypeScript compilation error - 0 linting errors Step 2: ANALYZE FAILURES Test failures: 1. test/auth/login.test.ts:45 ❌ "should handle invalid credentials" Expected: 401 Unauthorized Actual: 500 Internal Server Error 2. test/auth/jwt.test.ts:78 ❌ "should refresh expired tokens" Error: Cannot read property 'id' of undefined 3. test/api/protected.test.ts:23 ❌ "should reject missing tokens" Timeout: Request exceeded 5000ms TypeScript error: src/auth/jwt.ts:45:12 - error TS2345: Argument of type 'string | undefined' is not assignable to parameter of type 'string' Step 3: FORM HYPOTHESES Hypothesis 1: JWT token handling has type safety issue (TS error) Hypothesis 2: Error handling missing (test 1 shows 500 instead of 401) Hypothesis 3: Token refresh logic has null reference bug (test 2) Hypothesis 4: Protected route timeout suggests authentication middleware hanging (test 3) Step 4: TEST HYPOTHESES [Proceeds with systematic testing...] Step 5: UPDATE LINEAR Adding debugging findings to AUTH-123...
Linear Integration
Automatic Linear updates during debugging:
// After each debugging step
Linear.addComment(taskId, {
title: "Debugging Progress",
content: `
## Symptoms
- 3 failing tests (login, jwt, protected routes)
- TypeScript type error in jwt.ts:45
## Root Cause
Missing null check before accessing user.id in token refresh
## Fix Applied
Added null guard and proper error handling
## Verification
Re-running tests...
`
});
Blocker Logging
When encountering blockers:
Claude: [ccpm-debugging detects blocker] Found BLOCKER: Redis connection failing in staging Adding blocker to Linear AUTH-123: --- 🚫 BLOCKER: Redis Connection Issue Environment: Staging Error: ECONNREFUSED 127.0.0.1:6379 Impact: Cannot test session management Investigation: - Redis service not running in staging - Docker compose missing Redis container - Connection string using localhost instead of service name Temporary workaround: - Skip Redis-dependent tests in staging - Test locally with Docker Resolution needed: - Update staging docker-compose.yml - Fix Redis service configuration --- Blocker logged. Continuing with fixable issues...
Debugging Strategies
Strategy 1: Error Message Analysis
Read error messages carefully:
Error: Cannot read property 'id' of undefined Analysis: 1. What's undefined? → Some object 2. What property? → 'id' 3. Where? → Line number in stack trace 4. When? → During what operation? Investigation: - Check stack trace for call site - Identify which object is undefined - Trace back to why it's undefined - Find where it should be defined
Strategy 2: Reproduce Minimally
Create minimal reproduction:
Full test failing: - 100 lines of setup - Multiple database calls - Complex state management Minimal reproduction: - 10 lines - Mock database - Isolated function call Result: Easier to identify exact issue
Strategy 3: Binary Search
For intermittent or complex issues:
Problem: Feature works sometimes, fails other times Binary search approach: 1. Identify working commit (git bisect start) 2. Test middle commit 3. Mark as good or bad 4. Repeat until culprit commit found Result: Exact commit that introduced bug
Strategy 4: Rubber Duck Debugging
Explain the problem out loud:
User: "I don't understand why this is failing" Claude: [ccpm-debugging activates] Let's walk through this step by step: 1. What are you trying to do? → "Log in a user with JWT" 2. What do you expect to happen? → "Return 200 with token" 3. What actually happens? → "Returns 500 error" 4. What's different between expectation and reality? → "Error handling is missing" 5. Why would error handling be missing? → "Forgot to wrap async call in try-catch" Often explaining the problem reveals the solution!
Root-Cause Tracing
The 5 Whys Technique
Keep asking "why?" until you find root cause:
Problem: Tests failing in CI but passing locally Why? → Database connection timeout in CI Why? → Database takes longer to start in CI Why? → No health check waiting for database Why? → Docker Compose doesn't have healthcheck configured Why? → Template missing this configuration Root cause: Missing healthcheck in docker-compose.yml template Fix: Add healthcheck to template, not just local override
Trace Backwards
Start from symptom, trace backwards:
Symptom: User sees "Internal Server Error"
↓
Application log: TypeError: Cannot read property 'email' of null
↓
Code: const email = user.email
↓
user comes from: await db.findUser(id)
↓
findUser returned: null (user not found)
↓
Why null? User ID was: undefined
↓
ID came from: req.params.userId
↓
Route defined as: /api/users/:id (not :userId)
↓
Root cause: Route parameter mismatch
Common Debugging Patterns
Pattern 1: Null/Undefined Issues
// ❌ Crash waiting to happen
function getEmail(user) {
return user.email; // Crashes if user is null
}
// ✅ Defensive
function getEmail(user) {
if (!user) {
throw new Error('User is required');
}
return user.email;
}
// ✅ Even better with TypeScript
function getEmail(user: User | null): string {
if (!user) {
throw new Error('User is required');
}
return user.email;
}
Pattern 2: Async/Await Errors
// ❌ Unhandled promise rejection
async function login(email, password) {
const user = await db.findUser(email); // Could throw
return generateToken(user);
}
// ✅ Proper error handling
async function login(email, password) {
try {
const user = await db.findUser(email);
if (!user) {
throw new UnauthorizedError('Invalid credentials');
}
return generateToken(user);
} catch (error) {
if (error instanceof DatabaseError) {
logger.error('Database error during login', error);
throw new ServiceUnavailableError();
}
throw error;
}
}
Pattern 3: Race Conditions
// ❌ Race condition
let counter = 0;
async function increment() {
const current = counter;
await delay(10);
counter = current + 1; // Lost updates!
}
// ✅ Atomic operation
let counter = 0;
const lock = new Mutex();
async function increment() {
await lock.acquire();
try {
counter++;
} finally {
lock.release();
}
}
Debugging Workflows
Workflow 1: Test Failure
1. Read test failure message carefully 2. Identify what's expected vs actual 3. Find the code being tested 4. Add console.log or debugger 5. Re-run test in isolation 6. Step through with debugger 7. Identify exact line causing failure 8. Fix the issue 9. Verify test passes 10. Update Linear with fix
Workflow 2: Runtime Error
1. Capture full error message + stack trace 2. Identify error location from stack trace 3. Reproduce error consistently 4. Add error handling/logging at error site 5. Trace backwards to root cause 6. Fix root cause (not just symptom) 7. Add test to prevent regression 8. Update Linear with findings
Workflow 3: Performance Issue
1. Measure baseline performance 2. Profile to find bottleneck 3. Hypothesize cause 4. Test hypothesis (enable/disable features) 5. Confirm bottleneck 6. Optimize bottleneck 7. Measure improvement 8. Document in Linear
Integration with Other CCPM Skills
Works with ccpm-code-review
Debugging fixes issue
↓
ccpm-code-review verifies fix
↓
- Tests now passing?
- Build successful?
- Issue documented?
↓
Only then mark as complete
Works with sequential-thinking
Complex debugging scenario
↓
sequential-thinking structures investigation
↓
Thought 1: Gather symptoms
Thought 2: Form hypotheses
Thought 3: Test hypothesis A
Thought 4: Root cause identified
Thought 5: Fix applied
Thought 6: Verification complete
Works with pm-workflow-guide
Debugging started
↓
pm-workflow-guide suggests /ccpm:verify
↓
ccpm-debugging provides systematic approach
↓
After fix: pm-workflow-guide suggests /ccpm:verify
Examples
Example 1: Failing Tests
User: "Tests are failing, help me debug"
Claude: [ccpm-debugging activates]
Starting systematic debugging...
Step 1: GATHER SYMPTOMS
Running tests to collect failure information...
Found 2 failures:
1. test/api/users.test.ts:34
"GET /api/users should return user list"
Expected status 200, got 404
2. test/api/users.test.ts:45
"POST /api/users should create user"
Expected status 201, got 404
Step 2: PATTERN RECOGNITION
Both tests hitting /api/users route → routing issue likely
Step 3: HYPOTHESIS
Route might not be registered or path is wrong
Step 4: INVESTIGATION
Checking route registration...
Found in src/app.ts:
```typescript
app.use('/api/user', userRouter); // ← Missing 's'
Should be:
app.use('/api/users', userRouter);
Step 5: ROOT CAUSE Typo in route registration path
Step 6: FIX Updated route path to '/api/users'
Step 7: VERIFICATION Re-running tests... ✅ All tests passing (2/2)
Step 8: LINEAR UPDATE Adding fix summary to Linear task...
Issue: Route registration typo Fix: Corrected /api/user → /api/users Tests: Now passing Commit: abc123f
Debugging complete! ✅
### Example 2: Memory Leak
User: "Application memory usage keeps growing"
Claude: [ccpm-debugging activates]
Investigating memory issue...
Step 1: MEASURE BASELINE Starting memory: 150MB After 1 hour: 450MB After 2 hours: 750MB Pattern: Linear growth → memory leak
Step 2: PROFILE Using Node.js --inspect and Chrome DevTools...
Heap snapshot shows:
- •EventEmitter listeners growing
- •WebSocket connections not being cleaned up
Step 3: HYPOTHESIS Event listeners not being removed after WebSocket disconnect
Step 4: CODE INVESTIGATION Found in src/websocket/handler.ts:
wss.on('connection', (ws) => {
ws.on('message', handleMessage);
ws.on('close', () => {
console.log('Connection closed');
// ❌ Not removing listeners!
});
});
Step 5: FIX
wss.on('connection', (ws) => {
const messageHandler = handleMessage.bind(null, ws);
ws.on('message', messageHandler);
ws.on('close', () => {
ws.removeListener('message', messageHandler);
ws.removeAllListeners();
console.log('Connection closed and cleaned up');
});
});
Step 6: VERIFICATION Running memory test for 2 hours... Memory stable at ~160MB ✅
Step 7: LINEAR UPDATE Blocker resolved: Memory leak in WebSocket handler Fix: Proper cleanup of event listeners Testing: 2-hour stability test passed
Debugging complete! ✅
## Tips for Effective Debugging ### Do's - ✅ Read error messages completely - ✅ Check stack traces for exact line numbers - ✅ Reproduce issue consistently - ✅ Create minimal reproduction - ✅ Test one hypothesis at a time - ✅ Document findings in Linear - ✅ Add regression tests - ✅ Fix root cause, not symptoms ### Don'ts - ❌ Make random changes hoping to fix it - ❌ Skip error messages - ❌ Change multiple things at once - ❌ Ignore warnings - ❌ Forget to verify the fix - ❌ Leave debugging code in production - ❌ Skip documentation ## Summary This skill provides: - ✅ Systematic debugging approach - ✅ Root-cause tracing - ✅ Linear integration for tracking - ✅ Blocker logging - ✅ Defense-in-depth investigation - ✅ Integration with CCPM verification workflow **Philosophy**: Systematic over random, root-cause over symptoms, document for future. --- **Source**: Adapted from [claudekit-skills/debugging](https://github.com/mrgoonie/claudekit-skills) **License**: MIT **CCPM Integration**: `/ccpm:verify`, `/ccpm:sync`, Linear blocker tracking