skills/fullstack-debugger/SKILL.md
Expert debugger for Next.js + Cloudflare Workers + Supabase stacks. Systematic troubleshooting for auth, caching, workers, RLS, CORS, and build issues. Activate on: 'debug', 'not working', 'error', 'broken', '500', '401', '403', 'cache issue', 'RLS', 'CORS'. NOT for: feature development (use language skills), architecture design (use system-architect).
npx skillsauth add curiositech/windags-skills fullstack-debuggerInstall 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.
Expert debugger for Next.js + Cloudflare Workers + Supabase stacks. Evidence-based troubleshooting with systematic layer isolation.
Is there an error message visible?
├── YES: Browser console error
│ ├── "Failed to fetch" → CORS/Network issue → Test endpoint with curl
│ ├── "Hydration failed" → SSR/Client mismatch → Check for browser APIs
│ ├── "Cannot read properties" → Data loading race → Add optional chaining
│ └── TypeScript error → Type mismatch → Check interface definitions
│
├── YES: Network tab shows red
│ ├── 401/403 status → Auth issue → Check JWT + RLS policies
│ ├── 404 status → Wrong endpoint → Verify worker routes
│ ├── 500 status → Server error → Check worker logs
│ └── CORS preflight fail → Missing headers → Add OPTIONS handler
│
├── YES: Build/Deploy error
│ ├── "Module not found" → Import path wrong → Check relative paths
│ ├── "Type error" → TypeScript strict → Fix type definitions
│ ├── Memory exceeded → Bundle too large → Check webpack config
│ └── Deploy timeout → Worker size → Optimize dependencies
│
└── NO: Silent failure/unexpected behavior
├── Empty data returned → RLS blocking → Test with direct SQL
├── Stale data shown → Cache not invalidating → Check KV cache keys
├── Auth state lost → Session issue → Verify localStorage persistence
└── Worker not updating → Deploy failed → Check wrangler status
Which layer contains the bug?
│
├── Client (Browser/React)
│ ├── Console errors present → Fix JavaScript/React issues
│ ├── Network requests failing → Check endpoint accessibility
│ └── State management broken → Debug React Query/Context
│
├── Next.js Application
│ ├── Build fails → Fix TypeScript/import issues
│ ├── Pages not rendering → Check routing/components
│ └── SSR/SSG issues → Verify static generation setup
│
├── Cloudflare Worker
│ ├── Worker logs show errors → Fix worker code
│ ├── CORS headers missing → Add proper headers
│ └── KV cache issues → Check cache keys/expiration
│
├── Supabase Database
│ ├── Auth failing → Check user session/JWT
│ ├── Queries empty → Test RLS policies
│ └── Realtime broken → Verify subscriptions
│
└── External APIs
├── Rate limited → Check headers/implement backoff
├── Changed response format → Update parsing logic
└── Service unavailable → Add error handling/fallbacks
After applying fix, how to verify?
│
├── Local testing
│ ├── Run `npm run build` → Ensure no build errors
│ ├── Test in browser → Verify UI works correctly
│ ├── Check console → No new errors introduced
│ └── Test edge cases → Boundary conditions work
│
├── Worker testing
│ ├── Deploy to staging → `wrangler deploy --env staging`
│ ├── Test endpoints → Curl all affected routes
│ ├── Check logs → `wrangler tail` shows no errors
│ └── Monitor for 10min → No immediate regressions
│
└── Database testing
├── Test as anon user → RLS policies work correctly
├── Test as auth user → Permissions appropriate
├── Check query performance → No new slow queries
└── Verify data integrity → No data corruption
Symptoms: Applying common fixes without understanding root cause Detection: If you're changing multiple things at once without testing each Fix: Stop. Reproduce issue first, then test ONE hypothesis at a time
Symptoms: Debugging client code when issue is in worker, or vice versa Detection: If you've been debugging for 30+ minutes in wrong layer Fix: Use decision tree above to isolate which layer actually has the bug
Symptoms: "Fixed" code still showing old behavior due to cached responses Detection: If fix looks correct but behavior unchanged Fix: Clear ALL caches - browser, React Query, KV, TypeScript, CDN
Symptoms: Assuming all empty queries are RLS issues Detection: If you immediately jump to RLS without checking other causes Fix: First verify query syntax, then check network, then RLS policies
Symptoms: Making multiple changes rapidly when live site is broken Detection: If you're editing production code without local reproduction Fix: Reproduce locally first, or rollback immediately and debug systematically
Initial symptom: User reports getting logged out randomly, 401 errors in network tab
Step 1: Layer isolation
Step 2: Evidence gathering
# Check current JWT
node -e "
const jwt = localStorage.getItem('sb-project-auth-token');
console.log('JWT payload:', JSON.parse(atob(jwt.split('.')[1])));
console.log('Expires:', new Date(JSON.parse(atob(jwt.split('.')[1])).exp * 1000));
"
Step 3: Root cause analysis JWT shows expiry 1 hour ago. Token refresh failing because:
Step 4: Fix implementation
-- Create policy that allows token refresh
CREATE POLICY "Allow token refresh" ON profiles
FOR SELECT USING (
auth.jwt() IS NOT NULL
OR current_setting('request.jwt.claims', true)::json->>'exp' > extract(epoch from now())::text
);
Step 5: Verification
Lesson: Token expiry can create cascading auth failures when RLS policies block refresh attempts
Initial symptom: "Access to fetch blocked by CORS policy" when calling worker API
Step 1: Reproduction
# Direct curl works
curl https://my-worker.workers.dev/api/meetings
# Returns data successfully
# Browser fetch fails with CORS error
fetch('https://my-worker.workers.dev/api/meetings').catch(console.error)
# CORS error
Step 2: Diagnosis
# Check what headers are returned
curl -i -H "Origin: https://my-site.com" https://my-worker.workers.dev/api/meetings
# Missing Access-Control-Allow-Origin header
Step 3: Trade-off analysis Option A: Allow all origins (*) - Simple but less secure Option B: Whitelist specific domains - Secure but requires maintenance Option C: Dynamic origin checking - Flexible but more complex
Step 4: Fix with security consideration
// worker.js - Option B chosen for security
const ALLOWED_ORIGINS = [
'https://my-site.com',
'https://my-site-staging.pages.dev',
'http://localhost:3000' // dev only
];
function corsHeaders(origin) {
return {
'Access-Control-Allow-Origin': ALLOWED_ORIGINS.includes(origin) ? origin : 'null',
'Access-Control-Allow-Methods': 'GET, POST, OPTIONS',
'Access-Control-Allow-Headers': 'Content-Type, Authorization',
};
}
// Handle preflight
if (request.method === 'OPTIONS') {
return new Response(null, {
headers: corsHeaders(request.headers.get('Origin'))
});
}
Step 5: Verification
# Test from allowed origin
curl -H "Origin: https://my-site.com" -i https://my-worker.workers.dev/api/meetings
# Should include Access-Control-Allow-Origin: https://my-site.com
# Test from disallowed origin
curl -H "Origin: https://evil-site.com" -i https://my-worker.workers.dev/api/meetings
# Should include Access-Control-Allow-Origin: null
Lesson: CORS failures often indicate missing preflight handling; security requires thoughtful origin whitelisting
Initial symptom: Meeting search returns empty array, but Supabase dashboard shows data exists
Step 1: Evidence collection
// Test current query
const { data, error, count } = await supabase
.from('meetings')
.select('*', { count: 'exact' })
.limit(5);
console.log({ data, error, count }); // data: [], error: null, count: 0
Step 2: Isolate RLS vs query issue
-- In Supabase SQL Editor, test as anon user
SET ROLE anon;
SELECT COUNT(*) FROM meetings; -- Returns 0
RESET ROLE;
-- Test as admin
SELECT COUNT(*) FROM meetings; -- Returns 1000+
Step 3: Policy analysis
-- Check existing policies
SELECT policyname, permissive, roles, cmd, qual
FROM pg_policies
WHERE tablename = 'meetings';
-- Shows: "authenticated_read" policy with USING (auth.uid() IS NOT NULL)
Step 4: Root cause Anonymous users need read access to public meetings, but policy requires authentication. Query silently fails instead of erroring.
Step 5: Fix with proper scoping
-- Remove overly restrictive policy
DROP POLICY "authenticated_read" ON meetings;
-- Add public read for published meetings
CREATE POLICY "public_read_published" ON meetings
FOR SELECT USING (status = 'published');
-- Add authenticated read for all meetings
CREATE POLICY "authenticated_read_all" ON meetings
FOR SELECT TO authenticated USING (true);
Step 6: Verification
-- Test as anon - should see published only
SET ROLE anon;
SELECT COUNT(*), COUNT(*) FILTER (WHERE status = 'published')
FROM meetings; -- Should show same count for both
-- Test as authenticated - should see all
SET ROLE authenticated;
SELECT COUNT(*), COUNT(*) FILTER (WHERE status = 'published')
FROM meetings; -- Total count > published count
Lesson: RLS policies fail silently; always test with actual user roles, not just admin dashboard
Before marking debug complete, verify:
This skill should NOT be used for:
system-architect for design decisionsperformance-engineer for speed/efficiencysecurity-analyst for vulnerability assessmentdata-engineer for schema/query optimizationfrontend-expert for design/usability problemsDelegate when you see:
system-architectperformance-engineersecurity-analystdata-engineerfrontend-expertdata-ai
license: Apache-2.0 NOT for unrelated tasks outside this domain.
development
Use when designing caching strategies (cache-aside, write-through, write-behind), implementing distributed locks, building rate limiters, leaderboards, real-time streams (XADD/consumer groups), pub/sub, or tuning eviction policies. Triggers: thundering-herd on cache miss, dogpile on key expiry, Redlock vs SET-NX-PX choice, sliding-window rate limiter, hot-key on a single cluster slot, big-key blowup, MULTI/EXEC across slots, KEYS in production. NOT for Redis Cluster operations/admin (different domain), embedded KV (SQLite, leveldb), in-process LRU caches, or Memcached.
tools
Drawing the `'use client'` boundary correctly in React Server Components apps (Next.js App Router, RSC frameworks) — leaf-pushing, slot composition, serialization rules, and environment poisoning prevention. Grounded in react.dev and Next.js 16 docs.
development
Use when designing rate limiting for an API, choosing between token bucket / sliding window / leaky bucket / fixed window, implementing it in Redis, deciding edge (Cloudflare/Upstash) vs origin enforcement, sizing per-user vs per-IP vs per-endpoint quotas, returning the right 429 response with Retry-After, or fixing the boundary-burst bug in fixed-window limiters. Triggers: 429 too many requests, INCR + EXPIRE, ZADD + ZREMRANGEBYSCORE + ZCARD, X-RateLimit-Remaining header, Cloudflare WAF rate limiting rules, Upstash @upstash/ratelimit, leaky bucket shaping vs policing, distributed rate limiter consistency. NOT for DDoS mitigation specifically (different scale), CAPTCHA / bot management, full WAF design, or per-user quota billing.