sharp-edges

Living catalogue of confirmed hazard patterns in agent-studio. Each entry documents a real bug we've shipped. Invoke this skill during debugging and code review to check against known failure modes.

Safety Notice

This listing is imported from skills.sh public index metadata. Review upstream SKILL.md and repository scripts before running.

Copy this and send it to your AI assistant to learn

Install skill "sharp-edges" with this command: npx skills add oimiragieo/agent-studio/oimiragieo-agent-studio-sharp-edges

Sharp Edges

Living catalogue of confirmed hazard patterns in agent-studio. Each entry documents a real bug we've shipped. Invoke this skill during debugging and code review to check against known failure modes.

SE-01: Windows Backslash Paths

Symptom: Glob patterns match correctly in CI (Linux) but silently fail on developer machines (Windows).

Root cause: path.relative() returns backslash-separated paths on Windows (node_modules\foo ), but glob patterns use forward slashes. [^/]* in regex won't block
.

Fix:

// ALWAYS normalize before regex matching const rel = path.relative(root, filePath).replace(/\/g, '/');

Test assertion:

assert(normalizePath('a\b\c') === 'a/b/c'); assert(!normalizePath('a\b').includes('\'));

Files affected: .claude/lib/utils/path-constants.cjs , any glob-based exclusion logic.

SE-02: Prototype Pollution via JSON.parse

Symptom: Object.prototype gains unexpected properties after parsing malformed JSON from hook input or agent memory.

Root cause: JSON.parse('{"proto":{"isAdmin":true}}') silently mutates Object.prototype in older Node.js versions or when objects are created via Object.create(null) checks are missed.

Fix:

// Use safeParseJSON — strips proto, constructor, prototype keys const { safeParseJSON } = require('.claude/lib/utils/safe-json.cjs'); const { success, data } = safeParseJSON(rawInput, {});

Test assertion:

const before = Object.getPrototypeOf({}); safeParseJSON('{"proto":{"evil":true}}', {}); assert(!{}.evil);

Files affected: Any hook that calls JSON.parse() on stdin input.

SE-03: Hook Exit Code Protocol

Symptom: Hook appears to "block" when it should allow, or silently "allows" when it should block.

Root cause: Hooks use exit codes 0 (allow) and 2 (block). Using process.exit(1) or process.exit(true) does not block — it produces unexpected behavior.

Fix:

// CORRECT if (shouldBlock) process.exit(2); // Block tool execution process.exit(0); // Allow tool execution

// WRONG if (shouldBlock) process.exit(1); // Not a valid block code process.exit(false); // Not valid

Test assertion:

// Mock hook execution and verify exit code const result = execHook(input); assert(result.exitCode === 0 || result.exitCode === 2);

Files affected: All hooks in .claude/hooks/ .

SE-04: Async Exception Swallowing in Promise.all

Symptom: One failing async operation silently causes all results to be lost; agent believes all tasks succeeded.

Root cause: Promise.all([...]) rejects on the first failure and discards all other results. When wrapped in try/catch that returns [] , the caller sees empty results with no error.

Fix:

// Use Promise.allSettled to get partial results const results = await Promise.allSettled(tasks); const succeeded = results.filter(r => r.status === 'fulfilled').map(r => r.value); const failed = results.filter(r => r.status === 'rejected').map(r => r.reason); return { succeeded, failed, partial: failed.length > 0 };

Test assertion:

const tasks = [resolvesWith('ok'), rejectsWith('error'), resolvesWith('ok2')]; const result = await safeAllSettled(tasks); assert(result.succeeded.length === 2); assert(result.failed.length === 1);

SE-05: ReDoS in Glob-to-Regex

Symptom: Glob pattern matching hangs or takes exponential time on long path strings.

Root cause: Naive glob-to-regex conversion produces patterns with nested quantifiers like (.) or (.+)+ which backtrack exponentially on non-matching strings.

Fix:

// Use anchored, non-backtracking patterns // BAD: (.) -> exponential backtracking // GOOD: [^/]* -> linear, no backtracking function globToRegex(glob) { const escaped = glob.replace(/[.+^${}()|[]\]/g, '\$&'); return escaped .replace(/**/g, 'DOUBLE') .replace(/*/g, '[^/]') // Single * -> no path separators .replace(/DOUBLE/g, '.'); // ** -> any path }

Test assertion:

const regex = globToRegex('**/*.js'); const longString = 'a'.repeat(10000) + '.ts'; const start = Date.now(); regex.test(longString); assert(Date.now() - start < 100); // Must complete in <100ms

SE-06: DST Arithmetic Bugs

Symptom: Date calculations are off by 1 hour for events that cross daylight saving time boundaries.

Root cause: Adding 24 60 60 * 1000ms to a timestamp does not always equal "tomorrow" — DST transitions can make a day 23 or 25 hours long.

Fix:

// WRONG: assumes 24h = 1 day const tomorrow = new Date(date.getTime() + 86400000);

// CORRECT: use date arithmetic, not ms arithmetic const tomorrow = new Date(date); tomorrow.setDate(tomorrow.getDate() + 1);

Test assertion:

// Test across a known DST boundary (e.g., March DST change) const dst = new Date('2026-03-08T01:00:00'); // Day before US spring forward const nextDay = addDays(dst, 1); assert(nextDay.getDate() === 9); assert(nextDay.getHours() === 1); // Same time, next day

SE-07: Array Mutation During forEach Iteration

Symptom: Items are skipped or processed twice; behavior is non-deterministic.

Root cause: Mutating an array (push, splice, shift) while iterating with forEach or for...of causes the iterator to skip or re-visit elements.

Fix:

// WRONG: mutates during iteration arr.forEach(item => { if (condition) arr.push(newItem); // Skips items });

// CORRECT: collect mutations, apply after const toAdd = []; arr.forEach(item => { if (condition) toAdd.push(newItem); }); arr.push(...toAdd);

// OR: work on a copy [...arr].forEach(item => { ... });

Test assertion:

const arr = [1, 2, 3]; const result = safeForEach(arr, item => item * 2); assert(result.length === 3); // Original not mutated, all items processed

Usage

Invoke at the START of any debugging session:

Skill({ skill: 'sharp-edges' });

Then match your symptom against SE-01 through SE-07. If your bug is new and reproducible, add it to this catalogue.

Adding New Entries

Pattern for new entries:

SE-0N: [Short Title]

Symptom: [What you observe] Root cause: [Why it happens] Fix: [Code fix with before/after] Test assertion: [Minimal test that would catch it] Files affected: [Where in agent-studio this applies]

Memory Protocol

After invoking: if you find a new sharp edge, append it here via technical-writer agent.

Memory Protocol (MANDATORY)

Before starting: Read .claude/context/memory/learnings.md

After completing:

  • New pattern -> .claude/context/memory/learnings.md

  • Issue found -> .claude/context/memory/issues.md

  • Decision made -> .claude/context/memory/decisions.md

ASSUME INTERRUPTION: If it's not in memory, it didn't happen.

Source Transparency

This detail page is rendered from real SKILL.md content. Trust labels are metadata-based hints, not a safety guarantee.

Related Skills

Related by shared tags or category signals.

Coding

pyqt6-ui-development-rules

No summary provided by upstream source.

Repository SourceNeeds Review
Coding

code-analyzer

No summary provided by upstream source.

Repository SourceNeeds Review
Coding

gcloud-cli

No summary provided by upstream source.

Repository SourceNeeds Review