console-debugging/SKILL.md
Console.log debugging patterns for React/TypeScript. Use when tracking down bugs involving data transformations, conditional logic, state management, or when you need to trace data flow through pipelines.
npx skillsauth add sanurb/skills console-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.
Effective console.log debugging patterns for React/TypeScript applications. Use when tracking down bugs involving data transformations, conditional logic, or state management.
Log everything → Too noisy, can't see signal
Filter to test case → Now logs are relevant
Count in vs out → Quantify the bug
Trace through branches → See exactly which logic is wrong
Root cause → Fix with confidence
Problem: Logging every occurrence floods the console with 50+ irrelevant entries.
Solution: Filter all logs to ONE specific test case you can reproduce.
// BAD: Logs for every item
console.log("[GraphEdge] rendered:", item.id);
// GOOD: Filter to the specific case you're debugging
const isDebugCase = item.id.startsWith("pwny");
if (isDebugCase) {
console.log("[GraphEdge] rendered:", item.id);
}
// BETTER: Create a debug set once, check membership
const debugIds = new Set(["pwny", "komv", "ymzk"]);
const isDebug = debugIds.has(item.id.slice(0, 4));
Problem: You know something's wrong but not where data is being lost.
Solution: Log counts at pipeline boundaries.
// Before transformation
console.log("[transform] input count:", items.length); // 5
// After transformation
console.log("[transform] output count:", result.length); // 1 ← BUG: 4 items lost!
// In loops/filters, count removals
let skipped = 0;
for (const item of items) {
if (shouldSkip(item)) {
skipped++;
continue;
}
// process...
}
console.log("[transform] skipped:", skipped, "processed:", result.length);
Problem: Items are being filtered/transformed wrong, but you can't see which code path they take.
Solution: Log at EVERY branch exit with a descriptive label.
for (const item of items) {
const isDebug = debugIds.has(item.id);
if (conditionA) {
if (isDebug) console.log("[item] PATH A (conditionA true):", item.id);
// handle A
} else if (conditionB) {
if (isDebug) console.log("[item] PATH B (conditionB true):", item.id);
// handle B
} else if (shouldSkip) {
if (isDebug) console.log("[item] SKIPPED (shouldSkip):", item.id);
continue;
} else {
if (isDebug) console.log("[item] PATH DEFAULT:", item.id);
// default handling
}
if (isDebug) console.log("[item] ADDED:", item.id);
result.push(item);
}
Output reveals the bug:
[item] ADDED: "2c778760"
[item] PATH A (conditionA true): "88dafdf7" ← Why is this going to A??
[item] SKIPPED (shouldSkip): "4f6c4330"
Problem: You see which branch fired but not WHY.
Solution: Log the values that determined the branch.
// BAD: Just says it was skipped
if (isDebug) console.log("[item] SKIPPED");
// GOOD: Shows WHY it was skipped
if (isDebug) console.log("[item] SKIPPED:", {
reason: "source is hidden",
sourceId: item.sourceId,
isInHiddenSet: hiddenSet.has(item.sourceId),
isInExpandedStack: expandedStack.has(item.sourceId), // ← This might override!
});
For React state bugs, log state at key lifecycle points:
// In the state setter
function toggleExpanded(id: string) {
console.log("[toggleExpanded] called with:", id);
setExpanded(prev => {
const next = new Set(prev);
next.add(id);
console.log("[toggleExpanded] new state:", [...next]);
return next;
});
}
// In the consuming memo/effect
const result = useMemo(() => {
console.log("[useMemo] expandedSet:", [...expanded]);
// ... transformation
console.log("[useMemo] result count:", result.length);
return result;
}, [expanded, otherDeps]);
console.log(`[${componentOrFunction}] ${ACTION}: ${identifier}`, { details });
[ComponentName] or [functionName] in bracketsExamples:
console.log("[EdgeFilter] SKIPPED: edge-123", { reason: "hidden", sourceId: "abc" });
console.log("[useMemo] RECOMPUTED:", { inputCount: 5, outputCount: 1, deps: [...deps] });
console.log("[GraphEdge] RENDERED: pwny", { isCollapsed: true, stackId: "xyz" });
These bugs occur when multiple conditions COULD match but fire in wrong order.
Symptom: An item matches condition A but SHOULD have matched condition B first.
Detection pattern:
const matchesA = conditionA(item);
const matchesB = conditionB(item);
if (isDebug && matchesA && matchesB) {
console.log("[item] MATCHES BOTH A and B:", item.id, {
willTakePath: "A", // Current behavior
shouldTakePath: "B?" // Question for yourself
});
}
if (matchesA) {
// This fires, but should B have priority?
}
Fix pattern: Check the higher-priority condition FIRST:
// BEFORE: A checked first
if (matchesA) { ... }
else if (matchesB) { ... }
// AFTER: B has priority
if (matchesB) { ... } // ← Check this first now
else if (matchesA) { ... }
// BAD: Causes confusion about ID mismatches
console.log("id:", item.id.slice(0, 8));
// GOOD: Log full IDs
console.log("id:", item.id);
For "wrong data displayed" bugs, log the data transformation, not the display:
// Less useful: logging in JSX
return <div>{items.map(i => { console.log(i); return <Item {...i} /> })}</div>
// More useful: logging in the useMemo/transformation
const processedItems = useMemo(() => {
console.log("[processItems] input:", items.length);
const result = items.filter(...).map(...);
console.log("[processItems] output:", result.length);
return result;
}, [items]);
// BAD: Assume hover state is broken, refactor it
// (Wastes time if the bug is elsewhere)
// GOOD: Add logs first, confirm hypothesis, then fix
console.log("[hover] state:", hoverState);
console.log("[hover] expected:", expectedState);
// NOW you know if hover is the problem
When debugging with console.log:
development
Sets up an `## Agent skills` block in AGENTS.md/CLAUDE.md and `docs/agents/` so the engineering skills know this repo's issue tracker (GitHub, GitLab, fp, or local markdown), triage label vocabulary, and domain doc layout. Run before first use of `fp-plan`, `fp-implement`, `fp-review`, `to-issues`, `to-prd`, `triage`, `diagnose`, `tdd`, `improve-codebase-architecture`, or `zoom-out` — or if those skills appear to be missing context about the issue tracker, triage labels, or domain docs.
development
Build a throwaway prototype to flush out a design before committing to it. Routes between two branches — a runnable terminal app for state/business-logic questions, or several radically different UI variations toggleable from one route. Use when the user wants to prototype, sanity-check a data model or state machine, mock up a UI, explore design options, or says "prototype this", "let me play with it", "try a few designs".
tools
Control herdr (a terminal-native agent multiplexer) from inside it. Manage workspaces and tabs, split panes, spawn sibling agents, read pane output, and wait for state changes — all via CLI commands that talk to the running herdr instance over a local unix socket. Use when running inside herdr (HERDR_ENV=1). Do not use outside herdr.
documentation
Compact the current conversation into a handoff document for another agent to pick up.