---
name: bug-fixer-diagnostic
description: Advanced diagnostic workflow for bug-fixer agent with mandatory Context7 MCP consultations, Chrome DevTools MCP integration, and systematic root cause analysis across all Clean Architecture layers.
---
# Bug Fixer Diagnostic Skill
**Purpose**: Provide systematic debugging workflow with mandatory Context7 consultations and MCP tool integration for diagnosing and fixing bugs in production code.
---
## 🎯 CORE DEBUGGING PRINCIPLES
### Debugging Philosophy
- **Research First**: ALWAYS consult Context7 before implementing fixes
- **Root Cause Over Symptoms**: Never apply band-aid fixes
- **Minimal Changes**: Surgical fixes only, no refactoring
- **Verification Required**: ALL tests must pass after fix
- **Tools Are Mandatory**: Use MCPs (Chrome DevTools, Supabase) for diagnosis
### When NOT to Use This Skill
- ❌ Adding new features → Use Architect → TDD flow
- ❌ Large refactorings → Discuss with user first
- ❌ Performance optimization → Use separate optimization workflow
- ❌ Code cleanup → Not a bug, suggest improvements only
---
## 📋 4-PHASE DEBUGGING WORKFLOW
### PHASE 0: Bug Classification & Triage
**Objective**: Understand the bug and classify it for appropriate debugging strategy.
#### Step 0.1: Extract Bug Information
**Questions to answer**:
1. **Symptoms**: What is the observable error?
- Exact error message/stack trace
- Expected vs actual behavior
- Frequency (always, intermittent, specific conditions)
2. **Context**: Where does it occur?
- Which layer? (UI, API, use case, service, database, E2E test)
- Which file/function?
- Which environment? (local, staging, production)
3. **Impact**: How severe?
- Blocking users completely?
- Data corruption risk?
- Intermittent/flaky?
- Visual only?
4. **Reproduction**: Can you reproduce it?
- Steps to reproduce
- Consistency (100%, 50%, rare)
- User role/permissions needed
#### Step 0.2: Classify Bug Type
**Bug categories** (determines debugging strategy):
**1. Validation Bug** (Zod schema issue)
- Symptoms: Validation errors, safeParse failures, type mismatches
- Tools: Context7 for Zod patterns
- Reference: `references/zod-validation.md`
**2. Database Bug** (Supabase/RLS issue)
- Symptoms: Data not appearing, RLS blocks, query errors
- Tools: Supabase MCP to query state, Context7 for RLS patterns
- Reference: `references/supabase-rls.md`
**3. UI Bug** (React/Next.js issue)
- Symptoms: Hydration errors, visual glitches, interaction failures
- Tools: Chrome DevTools MCP, Context7 for React/Next.js
- Reference: `references/nextjs-errors.md`, `references/react-debugging.md`
**4. E2E Bug** (Playwright test failure)
- Symptoms: Test failures, selector issues, timeouts, flaky tests
- Tools: Playwright debugger with --debug, Chrome DevTools MCP
- Reference: `references/playwright-debugging.md`
**5. Integration Bug** (Layer boundary issue)
- Symptoms: Type mismatches, data transformation errors
- Tools: Read relevant files, trace data flow
- Strategy: Check entity schemas, service interfaces, API contracts
**6. Performance Bug** (Slow queries, memory leaks)
- Symptoms: Timeouts, high memory usage, slow responses
- Tools: Supabase advisors, Chrome DevTools performance tab
- Note: May need separate performance optimization workflow
**7. Authorization Bug** (CASL/RLS permission issue)
- Symptoms: UI elements visible but API returns 403, elements hidden when user should have access, "Permission denied" errors
- Tools: Chrome DevTools console for ability inspection, Supabase MCP for RLS testing, Context7 for CASL patterns
- Strategy: Verify CASL + RLS alignment, check ability loading, test permission mapping
- Reference: See **CASL Authorization Debugging** section below
**Deliverable**: Bug classification and initial diagnosis notes
---
### PHASE 1: Deep Diagnosis (RESEARCH FIRST!)
**Objective**: Identify root cause through systematic investigation with Context7 guidance.
**⚠️ MANDATORY**: Never skip Context7 consultation for your bug type.
#### Step 1.1: Context7 Research (ALWAYS FIRST)
**Why Context7 is mandatory**:
- Training data may be outdated
- Breaking changes in library versions
- New best practices emerge
- Known issues and workarounds
**Bug-specific Context7 queries**:
```typescript
// For Validation Bugs (Zod)
await context7.get_library_docs({
context7CompatibleLibraryID: "/colinhacks/zod",
topic: "safeParse error handling issues flatten custom errors debugging",
tokens: 3000
})
// For Database Bugs (Supabase RLS)
await context7.get_library_docs({
context7CompatibleLibraryID: "/supabase/supabase",
topic: "RLS policies debugging circular errors authentication performance",
tokens: 3000
})
// For UI Bugs (Next.js)
await context7.get_library_docs({
context7CompatibleLibraryID: "/vercel/next.js",
topic: "server components hydration errors client boundaries debugging",
tokens: 2500
})
// For UI Bugs (React)
await context7.get_library_docs({
context7CompatibleLibraryID: "/facebook/react",
topic: "hooks dependencies useEffect memory leaks debugging",
tokens: 2500
})
// For Authorization Bugs (CASL)
await context7.get_library_docs({
context7CompatibleLibraryID: "/stalniy/casl",
topic: "ability can cannot conditions debugging rules checking",
tokens: 2500
})
// For E2E Bugs (Playwright)
await context7.get_library_docs({
context7CompatibleLibraryID: "/microsoft/playwright",
topic: "debugging test failures selectors wait for timeout race conditions",
tokens: 3000
})
// For Unit Test Bugs (Vitest)
await context7.get_library_docs({
context7CompatibleLibraryID: "/vitest-dev/vitest",
topic: "debugging test failures error handling mock issues async",
tokens: 3000
})
```
**Review Context7 output for**:
- ✅ Latest error handling patterns
- ✅ Known issues and workarounds
- ✅ Best practices for this specific case
- ✅ Breaking changes in recent versions
- ✅ Common mistakes to avoid
#### Step 1.2: Layer-Specific Diagnosis
**For Validation Bugs (Entities/Zod)**:
```typescript
// 1. Read the problematic schema
const entityFile = await Read('app/src/features/[feature]/entities.ts')
// 2. Check usage in use cases - look for .parse() vs .safeParse()
await Grep({
pattern: "EntitySchema\\.parse",
path: "app/src/features/[feature]/use-cases",
output_mode: "content",
"-n": true
})
// 3. Common issues to check:
// ❌ Using .parse() instead of .safeParse()? (throws instead of returning result)
// ❌ Error messages not clear?
// ❌ Refinement logic incorrect?
// ❌ Type mismatch between schema and usage?
```
**Best practices from Context7**:
- ✅ Always use `.safeParse()` for controllable error handling
- ✅ Use `.flatten()` on errors for better structure
- ✅ Create schemas inside components for translation access
- ✅ Check for circular refinements
**For Database Bugs (Services/Supabase)**:
```typescript
// 1. Query actual database state
const data = await supabase.execute_sql({
query: `SELECT * FROM [table] WHERE [condition]`
})
// 2. Check RLS policies
const policies = await supabase.execute_sql({
query: `SELECT * FROM pg_policies WHERE tablename = '[table]'`
})
// 3. Test RLS with specific user context
await supabase.execute_sql({
query: `
SET LOCAL ROLE authenticated;
SET LOCAL request.jwt.claims.sub = '[test-user-id]';
SELECT * FROM [table]; -- Should return expected data
`
})
// 4. Check for circular policy references (common issue)
// Look for policies that reference the same table they're applied to
// 5. Check logs for errors
const logs = await supabase.get_logs({
project_id: "project-id",
service: "postgres"
})
// 6. Run security advisors
const advisors = await supabase.get_advisors({
project_id: "project-id",
type: "security"
})
```
**Common RLS issues** (from Context7):
- ❌ Circular policies (policy references same table)
- ❌ Missing auth.uid() checks
- ❌ Not scoping to authenticated role (TO authenticated)
- ❌ Complex joins in RLS (performance killer)
**Best practice** (from Context7):
```sql
-- ✅ GOOD: No circular reference
CREATE POLICY "access_policy" ON tasks
FOR SELECT
TO authenticated
USING (
organization_id IN (
SELECT organization_id
FROM user_organizations
WHERE user_id = (SELECT auth.uid()) -- No join back to tasks!
)
);
-- ❌ BAD: Circular reference
CREATE POLICY "bad_policy" ON tasks
FOR SELECT
USING (
auth.uid() IN (
SELECT user_id FROM team_user
WHERE team_user.team_id = tasks.team_id -- CIRCULAR! References tasks
)
);
```
**For UI Bugs (Components/React)**:
```typescript
// 1. Use Chrome DevTools MCP to inspect live state
await mcp__chrome_devtools__new_page()
await mcp__chrome_devtools__navigate_page({
url: "http://localhost:3000/problematic-page"
})
// 2. Take snapshot for visual inspection
const snapshot = await mcp__chrome_devtools__take_snapshot()
// 3. Evaluate JavaScript to check state
const state = await mcp__chrome_devtools__evaluate_script({
script: `
return {
errors: document.querySelector('.error')?.textContent,
consoleErrors: [...console.memory || []],
hydrationErrors: window.__NEXT_DATA__?.props?.pageProps?.errors
}
`
})
// 4. Check for hydration mismatches (Next.js specific)
// Look for console warnings: "Hydration failed because..."
// Common cause: Server/client render different content
// 5. Consult Context7 for React patterns
await context7.get_library_docs({
context7CompatibleLibraryID: "/facebook/react",
topic: "hooks useEffect dependencies errors hydration debugging",
tokens: 2500
})
```
**Common React/Next.js issues** (from Context7):
- ❌ useEffect missing dependencies
- ❌ Server/client rendering mismatch (hydration error)
- ❌ Race conditions (component unmounts before async completes)
- ❌ Incorrect 'use client' directive placement
**Best practices from Context7**:
```typescript
// ✅ Cleanup on unmount to prevent race conditions
useEffect(() => {
let cancelled = false
fetchData().then(result => {
if (!cancelled) setData(result)
})
return () => { cancelled = true }
}, [])
// ❌ Race condition - no cleanup
useEffect(() => {
fetchData().then(setData) // Fails if component unmounts!
}, [])
```
**For E2E Test Failures (Playwright)**:
```bash
# 1. Run with Playwright Inspector for step-through debugging
npx playwright test failing-test.spec.ts:42 --debug
# 2. Run with verbose API logs
DEBUG=pw:api npx playwright test failing-test.spec.ts
# 3. Generate trace for analysis
npx playwright test failing-test.spec.ts --trace on
# 4. View trace after failure
npx playwright show-trace trace.zip
# 5. Run in UI mode for interactive debugging
npx playwright test --ui
```
```typescript
// Use Chrome DevTools MCP for live inspection
await mcp__chrome_devtools__new_page()
await mcp__chrome_devtools__navigate_page({
url: "http://localhost:3000/test-page"
})
// Wait for problematic element
await mcp__chrome_devtools__wait_for({
selector: ".expected-element",
timeout: 5000
})
// Take screenshot of current state
await mcp__chrome_devtools__take_screenshot()
```
**Common Playwright issues** (from Context7):
- ❌ Using brittle CSS selectors (`.button.primary.large`)
- ❌ Not using web-first assertions (`toBeVisible()`)
- ❌ Manual checks instead of automatic retries
- ❌ Race conditions with async content
**Best practices from Context7**:
```typescript
// ✅ GOOD: Web-first assertion (auto-retries)
await expect(page.getByText('welcome')).toBeVisible()
// ❌ BAD: Manual check (no retry)
expect(await page.getByText('welcome').isVisible()).toBe(true)
// ✅ GOOD: Semantic selector (robust)
await page.getByRole('button', { name: 'Submit' })
// ❌ BAD: Brittle CSS selector
await page.click('.btn-primary-submit-form-action')
```
#### Step 1.3: Root Cause Identification
**Document findings**:
```markdown
## Root Cause Analysis
**Bug Type**: [Validation/Database/UI/E2E/Integration/Performance]
**Layer Affected**: [Entities/Use Cases/Services/Components/E2E]
**Root Cause**:
[Clear explanation of WHY the bug occurs, not just what]
**Evidence**:
1. [Finding from Context7 documentation]
2. [Finding from Chrome DevTools inspection]
3. [Finding from Supabase query]
4. [Finding from Playwright debugger]
5. [Finding from code analysis]
**Why It Wasn't Caught**:
- Missing test coverage?
- Edge case not considered?
- Environment-specific issue?
- Timing/race condition?
**Impact Scope**:
- Which users affected?
- Which features broken?
- Data integrity risk?
```
**Deliverable**: Complete root cause analysis document
---
### PHASE 2: Research-Driven Fix Implementation
**Objective**: Apply minimal, targeted fix using latest patterns from Context7.
**⚠️ CRITICAL**: Only fix AFTER understanding root cause and verifying latest patterns.
#### Step 2.1: Verify Latest Fix Patterns (Context7)
```typescript
// NEVER use outdated patterns from training data
// ALWAYS verify latest approach with Context7
// Example: Fixing Zod validation
await context7.get_library_docs({
context7CompatibleLibraryID: "/colinhacks/zod",
topic: "safeParse error handling best practices latest version",
tokens: 2000
})
// Review to ensure fix follows:
// ✅ Latest API patterns
// ✅ Recommended best practices
// ✅ No deprecated methods
// ✅ Optimal performance approach
```
#### Step 2.2: Implement Minimal Fix
**Principle**: Make the SMALLEST change that fixes the root cause.
**Example Fix Patterns** (validated with Context7):
**Validation Bug Fix**:
```typescript
// ❌ BEFORE (using .parse - throws)
export async function createEntity(input: unknown) {
const validated = EntityCreateSchema.parse(input) // Throws!
return service.create(validated)
}
// ✅ AFTER (using .safeParse - returns result)
export async function createEntity(input: unknown) {
const result = EntityCreateSchema.safeParse(input)
if (!result.success) {
// Use flatten() for better error structure (Context7 pattern)
const errors = result.error.flatten()
throw new ValidationError('Invalid input', errors)
}
return service.create(result.data)
}
```
**RLS Policy Bug Fix**:
```sql
-- ❌ BEFORE (circular policy - slow!)
CREATE POLICY "bad_policy" ON tasks
FOR SELECT
USING (
auth.uid() IN (
SELECT user_id FROM team_user
WHERE team_user.team_id = tasks.team_id -- CIRCULAR!
)
);
-- ✅ AFTER (no circular reference - fast!)
CREATE POLICY "fixed_policy" ON tasks
FOR SELECT
TO authenticated
USING (
organization_id IN (
SELECT organization_id
FROM user_organizations
WHERE user_id = (SELECT auth.uid()) -- No join to tasks!
)
);
```
**UI Race Condition Fix**:
```typescript
// ❌ BEFORE (race condition)
export function Component() {
const [data, setData] = useState(null)
useEffect(() => {
fetchData().then(setData) // Race condition if component unmounts!
}, [])
return
{data?.name}
}
// ✅ AFTER (cleanup on unmount - Context7 pattern)
export function Component() {
const [data, setData] = useState(null)
useEffect(() => {
let cancelled = false
fetchData().then(result => {
if (!cancelled) setData(result)
})
return () => { cancelled = true }
}, [])
return {data?.name}
}
```
**E2E Test Selector Fix**:
```typescript
// ❌ BEFORE (brittle selector - Context7 anti-pattern)
await page.click('.button-primary') // Breaks if CSS changes
// ✅ AFTER (semantic selector - Context7 best practice)
await page.getByRole('button', { name: 'Submit' }) // Robust
// OR (data-testid - also recommended)
await page.click('[data-testid="submit-button"]') // Explicit test hook
```
#### Step 2.3: Add Defensive Error Handling
If bug was caused by missing error handling, add proper guards:
```typescript
// Add null checks
if (!user) {
throw new NotFoundError('User not found')
}
// Add type guards
if (typeof data.email !== 'string') {
throw new ValidationError('Email must be a string')
}
// Add try-catch for external calls
try {
await externalApi.call()
} catch (error) {
logger.error('External API failed', { error })
throw new IntegrationError('Failed to contact external service', { cause: error })
}
```
**Deliverable**: Implemented fix with defensive guards
---
### PHASE 3: Comprehensive Verification
**Objective**: Ensure fix works and no regressions introduced.
**⚠️ CRITICAL**: NEVER consider a bug fixed until ALL verification passes.
#### Step 3.1: Run Affected Tests
```bash
# 1. Run unit tests for affected layer
npm run test app/src/features/[feature]/
# 2. Run integration tests
npm run test app/src/app/api/[feature]/
# 3. Run ALL E2E tests (regression check)
npm run test:e2e
# 4. Run type checking
npm run typecheck
# 5. Run linting
npm run lint
```
**All tests must pass**. If any fail:
- Investigate if test revealed another bug
- Fix the newly discovered bug
- Re-run full test suite
#### Step 3.2: Manual Verification
**For UI bugs**:
```typescript
// 1. Use Chrome DevTools MCP to verify fix
await mcp__chrome_devtools__new_page()
await mcp__chrome_devtools__navigate_page({
url: "http://localhost:3000/fixed-page"
})
// 2. Reproduce original bug scenario
await mcp__chrome_devtools__click({ selector: "button" })
// 3. Verify fix worked
const state = await mcp__chrome_devtools__evaluate_script({
script: "return document.querySelector('.error') === null"
})
// 4. Take screenshot for comparison
await mcp__chrome_devtools__take_screenshot()
```
**For database bugs**:
```typescript
// 1. Verify RLS policy works correctly
await supabase.execute_sql({
query: `
SET LOCAL ROLE authenticated;
SET LOCAL request.jwt.claims.sub = '[test-user-id]';
SELECT * FROM tasks; -- Should only return user's tasks
`
})
// 2. Check security advisors again
await supabase.get_advisors({
project_id: "project-id",
type: "security"
})
```
**For E2E bugs**:
```bash
# 1. Run fixed test in debug mode
npx playwright test fixed-test.spec.ts --debug
# 2. Run in UI mode to visually verify
npx playwright test fixed-test.spec.ts --ui
# 3. Run multiple times to check for flakiness
for i in {1..10}; do npx playwright test fixed-test.spec.ts; done
```
#### Step 3.3: Regression Check
**Verify no new issues**:
1. **Run FULL Test Suite**:
```bash
npm run test
npm run test:e2e
```
2. **Check All Affected Flows**:
- If you fixed validation, test ALL CRUD operations
- If you fixed RLS, test with different user roles
- If you fixed UI, test on different browsers/devices
3. **Performance Check**:
- Did fix introduce performance regression?
- Use Chrome DevTools Performance tab
- Use Supabase EXPLAIN ANALYZE for queries
4. **Security Check**:
- Did fix introduce security vulnerability?
- Run Supabase security advisors
- Verify RLS still enforces isolation
**Deliverable**: All tests passing, no regressions detected
---
### PHASE 4: Documentation & Prevention
**Objective**: Document fix and recommend preventive measures.
#### Step 4.1: Document the Fix
```markdown
## Bug Fix Report
**Bug ID**: [Reference to issue/ticket if applicable]
**Summary**: [One sentence description]
**Root Cause**:
[Detailed explanation of why bug occurred]
**Files Changed**:
- `path/to/file1.ts` (Lines X-Y)
- `path/to/file2.ts` (Lines X-Y)
**Fix Applied**:
[Explanation of what was changed and why]
**Research Sources**:
- Context7: [Library and topic searched]
- Chrome DevTools: [Findings from inspection]
- Supabase: [Database queries/logs reviewed]
- Playwright: [Debugging commands used]
**Verification**:
- ✅ Unit tests passing
- ✅ Integration tests passing
- ✅ E2E tests passing
- ✅ Manual testing completed
- ✅ No regression detected
**Prevention**:
[How to prevent this bug in future]
- [ ] Add missing test coverage?
- [ ] Update validation schema?
- [ ] Improve error messages?
- [ ] Add documentation?
```
#### Step 4.2: Suggest Preventive Measures
**Recommendations to user**:
1. **Test Coverage Gap**:
"This bug wasn't caught because we lack test coverage for [scenario].
I recommend adding tests to prevent regression."
2. **Pattern Improvement**:
"According to latest Context7 docs, the recommended pattern is [X].
Consider refactoring similar code to follow this pattern."
3. **Monitoring**:
"Add logging/monitoring for this scenario to catch future issues early."
4. **Documentation**:
"This edge case should be documented in [location] to help future developers."
**Deliverable**: Complete bug fix report with prevention recommendations
---
## 🚨 COMMON ANTI-PATTERNS TO AVOID
### ❌ DON'T: Fix Without Research
```typescript
// ❌ WRONG: Fixing based on assumptions
// "I think this is the problem, let me try this fix"
export function createEntity(input: any) { // Using 'any' to bypass error
// ...
}
```
```typescript
// ✅ CORRECT: Research first, then fix
// 1. Consulted Context7 for Zod patterns
// 2. Identified .parse() vs .safeParse() issue
// 3. Applied recommended pattern
export function createEntity(input: unknown) {
const result = EntityCreateSchema.safeParse(input)
if (!result.success) {
throw new ValidationError('Invalid input', result.error.flatten())
}
return service.create(result.data)
}
```
### ❌ DON'T: Skip Verification
```bash
# ❌ WRONG: "Looks good, done!"
# Fix applied, no tests run
```
```bash
# ✅ CORRECT: Comprehensive verification
npm run test # All unit tests
npm run test:e2e # All E2E tests
npm run typecheck # Type safety
npx playwright test --ui # Visual verification
```
### ❌ DON'T: Ignore Chrome DevTools for UI Bugs
```typescript
// ❌ WRONG: Guessing what's wrong with UI
// "Maybe it's a CSS issue? Let me change some styles"
```
```typescript
// ✅ CORRECT: Use Chrome DevTools MCP to inspect
await mcp__chrome_devtools__new_page()
await mcp__chrome_devtools__navigate_page({ url: "..." })
const snapshot = await mcp__chrome_devtools__take_snapshot()
// Now I can SEE the actual problem!
```
### ❌ DON'T: Overengineer the Fix
```typescript
// ❌ WRONG: Complex refactoring for simple bug
// Changing entire architecture to fix a typo
```
```typescript
// ✅ CORRECT: Minimal fix
// Change 'organizaton_id' → 'organization_id' (typo fix)
```
### ❌ DON'T: Skip Root Cause Analysis
```typescript
// ❌ WRONG: Band-aid fix
try {
buggyFunction()
} catch {
// Ignore error (just hiding the problem!)
}
```
```typescript
// ✅ CORRECT: Fix root cause
// Identified that buggyFunction fails due to null input
// Added null check at source instead of catching error
if (input === null) {
throw new ValidationError('Input cannot be null')
}
validFunction(input)
```
---
## ✅ QUALITY CRITERIA
Your bug fix is complete when:
### Research Quality
- ✅ Context7 consulted for ALL library-related fixes
- ✅ Chrome DevTools MCP used for ALL UI/E2E bugs
- ✅ Supabase MCP used for ALL database bugs
- ✅ Playwright debugger used for ALL E2E test failures
- ✅ Root cause clearly identified and documented
### Fix Quality
- ✅ Minimal change to fix root cause
- ✅ No over-engineering or unnecessary refactoring
- ✅ Follows latest patterns from Context7
- ✅ Defensive error handling added
- ✅ Type-safe (no `any` types)
- ✅ Clear code comments explaining fix
### Verification Quality
- ✅ ALL unit tests pass
- ✅ ALL integration tests pass
- ✅ ALL E2E tests pass
- ✅ Manual verification completed
- ✅ No regression detected
- ✅ Performance not degraded
### Documentation Quality
- ✅ Root cause documented
- ✅ Fix approach explained
- ✅ Research sources cited
- ✅ Prevention recommendations provided
- ✅ Changed files clearly listed
---
## 🔐 CASL AUTHORIZATION DEBUGGING
**When to use**: Authorization bug (type 7) - UI elements visible but API returns 403, elements hidden when user should have access, "Permission denied" errors.
### Symptoms of CASL Bugs
1. **UI visible, API rejects**:
- User sees "Delete" button
- Click triggers API call
- API returns 403 Forbidden
- Root cause: CASL says YES, RLS says NO (RLS is stricter)
2. **UI hidden, should be visible**:
- User should have permission
- Button/element completely missing from DOM
- Root cause: CASL ability not loaded correctly, or wrong logic in `defineAbilitiesFor()`
3. **Inconsistent behavior**:
- Works for Owner, fails for normal users
- Works in one workspace, fails in another
- Root cause: Conditional permissions not working, workspace context incorrect
### Diagnostic Steps
#### Step 1: Inspect Ability in Browser
**Use Chrome DevTools console to inspect user's current ability**:
```javascript
// In browser console (when on the page with issue)
// Add temporary logging to component
console.log('Current ability:', ability);
console.log('Can delete board?', ability.can('delete', 'Board'));
console.log('All rules:', ability.rules);
```
**What to look for**:
- Is ability loaded? (not null/undefined)
- Does `ability.can('action', 'Subject')` return expected value?
- Are there any rules at all? (empty rules = no permissions loaded)
#### Step 2: Verify Ability Loading
**Check if `loadUserAbility()` is being called**:
```typescript
// Look for AbilityProvider in layout/page
// app/(main)/{feature}/layout.tsx
// Should have:
const ability = await loadUserAbility(userId, workspaceId);
// Common mistakes:
// ❌ Not awaiting loadUserAbility()
// ❌ Wrong userId or workspaceId
// ❌ loadUserAbility() not called at all
```
**Verify data sources**:
- User object correct? Check user.id
- Workspace object correct? Check workspace.id, workspace.owner_id
- Permissions loaded? Check permissions array length
#### Step 3: Compare CASL vs RLS
**Read both implementations**:
```typescript
// 1. Read CASL logic
Read('features/{feature}/abilities/defineAbility.ts')
// 2. Query RLS policies
supabase_mcp.execute_sql({
query: "SELECT * FROM pg_policies WHERE schemaname = 'public' AND tablename = '{table}'"
})
```
**Look for misalignment**:
- CASL allows Owner → RLS should too
- CASL allows permission → RLS should check same permission
- CASL has Super Admin restrictions → RLS should too
#### Step 4: Test Permission Mapping
**Check resource-to-subject mapping**:
```typescript
// In defineAbilitiesFor(), find mapResourceToSubject()
function mapResourceToSubject(resource: string): Subjects {
const mapping: Record = {
'boards': 'Board', // DB table → CASL subject
'cards': 'Card',
};
return mapping[resource] || 'all';
}
// Common mistakes:
// ❌ Wrong mapping ('boards' → 'Boards' - typo!)
// ❌ Missing mapping (permission exists but no mapping)
// ❌ Wrong subject case ('board' vs 'Board')
```
**Verify permission names**:
```sql
-- Query actual permissions in database
SELECT full_name FROM permissions WHERE user_id = '{userId}';
-- Compare to CASL mapping:
// 'boards.create' → can('create', 'Board')
// 'boards.delete' → can('delete', 'Board')
```
### Common CASL Bugs and Fixes
#### Bug 1: AbilityContext Not Provided
**Symptom**: `Error: useAppAbility must be used within AbilityProvider`
**Fix**:
```typescript
// ❌ BEFORE - Component outside provider
export default function Page() {
return ; // Uses useAppAbility() → ERROR
}
// ✅ AFTER - Wrap with provider in layout
export default async function Layout({ children }) {
const ability = await loadUserAbility(userId, workspaceId);
return (
{children}
);
}
```
#### Bug 2: Typo in Action/Subject Names
**Symptom**: User has permission but UI still hidden
**Diagnosis**:
```typescript
// Check CASL check:
ability.can('delete', 'Board') // FALSE
// But permission exists:
{ full_name: 'boards.delete' }
// Problem: mapResourceToSubject has typo
const mapping = {
'boards': 'Boards', // ❌ Should be 'Board' (singular!)
};
```
**Fix**:
```typescript
// ✅ CORRECT - Singular, PascalCase
const mapping = {
'boards': 'Board',
'cards': 'Card',
'comments': 'Comment',
};
```
#### Bug 3: Stale Ability (Permissions Changed)
**Symptom**: User was granted permission but UI still doesn't update
**Root cause**: Ability loaded once on page load, not reactive to permission changes
**Fix**:
```typescript
// ✅ Option 1: Reload ability when permissions change
const queryClient = useQueryClient();
await updatePermissions(userId, newPermissions);
queryClient.invalidateQueries(['ability']); // If ability in React Query
// ✅ Option 2: Navigate to refresh server component
router.refresh(); // For Next.js App Router
```
#### Bug 4: CASL + RLS Mismatch
**Symptom**: Button visible, API returns 403
**Diagnosis**:
```typescript
// CASL says YES:
if (user.id === workspace.owner_id) {
can('delete', 'Organization'); // ❌ Owner CAN delete
}
// But PRD says:
// "Super Admin restrictions: Cannot delete Organization"
// RLS correctly blocks:
CREATE POLICY "block_org_delete" ON organizations
FOR DELETE USING (false); // ✅ Nobody can delete (even Owner)
```
**Fix**:
```typescript
// ✅ Align CASL with PRD and RLS
if (user.id === workspace.owner_id) {
can('manage', 'all');
cannot('delete', 'Organization'); // Add restriction
}
```
### CASL Debugging Checklist
- [ ] ✅ Inspect ability in browser console (`ability.can()`, `ability.rules`)
- [ ] ✅ Verify AbilityProvider wraps components
- [ ] ✅ Check loadUserAbility() is called with correct user/workspace
- [ ] ✅ Verify permissions array has data
- [ ] ✅ Compare defineAbilitiesFor() logic to RLS policies
- [ ] ✅ Check mapResourceToSubject() mappings (singular, PascalCase)
- [ ] ✅ Verify action names match ('create' not 'add')
- [ ] ✅ Test Owner bypass logic
- [ ] ✅ Test Super Admin restrictions
- [ ] ✅ Test normal user permission mapping
- [ ] ✅ Verify no typos in subject names
### Coordination with Supabase Agent
If CASL and RLS are misaligned:
1. **Determine correct logic** from PRD
2. **Update CASL** if Implementer was wrong
3. **Update RLS** if Supabase Agent was wrong
4. **Ask Architect** if PRD is unclear
---
## 📚 REFERENCE DOCUMENTS
Load these on demand when needed:
1. **`references/playwright-debugging.md`** - Playwright debugging patterns
2. **`references/vitest-debugging.md`** - Vitest test debugging
3. **`references/zod-validation.md`** - Zod schema debugging
4. **`references/supabase-rls.md`** - RLS policy debugging
5. **`references/nextjs-errors.md`** - Next.js error patterns
6. **`references/react-debugging.md`** - React debugging patterns
---
## 🎯 REMEMBER
1. **Research is MANDATORY** - Never fix without consulting Context7
2. **Chrome DevTools for UI** - Use MCP to inspect live browser state
3. **Playwright for E2E** - Use debugger for test failures
4. **Minimal fixes only** - Don't refactor, just fix the bug
5. **Verify everything** - All tests must pass, no exceptions
6. **Document thoroughly** - Future developers need to understand
7. **Root cause first** - Never apply band-aid fixes
8. **Layer boundaries** - Respect Clean Architecture even in fixes
**Your success is measured by**:
- ✅ **Research**: Did you consult all available documentation?
- ✅ **Diagnosis**: Did you identify the true root cause?
- ✅ **Fix**: Is it minimal, targeted, and follows best practices?
- ✅ **Verification**: Do ALL tests pass with no regression?
---
**YOU ARE THE BUG DETECTIVE. YOUR FIXES ARE SURGICAL, RESEARCHED, AND VERIFIED.**