Agent skill
investigate
Structured investigation of a bug or unexpected behavior before implementing a fix
Install this agent skill to your Project
npx add-skill https://github.com/gitkraken/vscode-gitlens/tree/main/.claude/skills/investigate
SKILL.md
/investigate - Bug Investigation
Perform structured root cause analysis before implementing any fix.
Usage
/investigate [symptom description or issue reference]
Instructions
1. Understand the Symptom
- Restate the symptom to confirm understanding
- Identify: extension host or webview? Node.js or browser? Which feature area?
- Ask clarifying questions if ambiguous
2. Trace the Code Path
- Find the entry point (command, event handler, IPC message)
- Read every function in the call chain — do NOT assume behavior from names
- For decorated methods, understand how the decorator alters behavior (see Decorator Reference below)
- Read decorator source in
src/system/decorators/if behavior is unclear
3. Form Hypotheses
- List at least 2 possible root causes
- For each, identify what evidence would confirm or refute it
- Gather evidence by reading code, checking error types, tracing data flow
4. Audit Impact
- Search for ALL call sites of functions you plan to modify
- Check both
src/env/node/andsrc/env/browser/paths - Check sub-providers:
src/env/node/git/sub-providers/andsrc/git/sub-providers/
5. Present Findings
## Investigation: [Symptom]
### Symptom
[What goes wrong]
### Code Path
[Entry point] -> [Function 1] -> [Function 2 (@gate)] -> [Function 3]
### Root Cause
[Cause with file:line evidence]
### Alternative Causes Considered
1. [Alternative] — ruled out because [evidence]
### Proposed Fix
[Minimal change to address root cause]
### Impact
- Files to modify: [list]
- Call sites checked: [count]
- Platform paths verified: Node.js [yes/no], Browser [yes/no]
6. Get Confirmation
Present findings and proposed fix. Wait for user confirmation before implementing.
Anti-Patterns
- Do NOT start implementing before completing the investigation
- Do NOT blame logging decorators for hangs — check
@gate()first - Do NOT propose disabling/removing a feature when asked to fix it
- Do NOT suppress errors — fix the root cause or propagate properly
Common Misdiagnosis Patterns to Avoid
- Blaming logging decorators for hangs: When a method hangs, the issue is almost never in
@info()/@debug()/@trace(). Check@gate()(promise never resolving) or the actual async operation first. - Confusing
@gate()and@sequentialize():@gate()returns the SAME promise to concurrent callers.@sequentialize()QUEUES calls. These solve different problems. - Wrong error type handling: Use the error's
.is()static method with reason discriminator:PushError.is(ex, 'noUpstream'), notinstanceof+ex.message.includes(...). - Platform-specific bugs: Something working in Node.js may fail in browser (and vice versa). Always check the
@env/abstraction layer. - Scope/context bugs:
getScopedLogger()returns stale scope afterawaitin browser. Capture the scope before the firstawait. - Suppressing errors instead of fixing them: Do NOT silence errors by catching and ignoring them. Fix the root cause or propagate them properly (e.g., use
errors: throwso catch blocks handle them).
Decorator Reference
Source files: src/system/decorators/
@info() / @debug() / @trace() — Logging (log.ts)
Same decorator at different log levels. Wraps methods to log entry/exit with timing and scope tracking.
Options (LogOptions):
args—falseto suppress, or function for custom formattingexit—trueto log return value, or function for custom exit stringonlyExit— Suppress entry log;{ after: N }only logs if duration > N mstiming—falseto disable;{ warnAfter: N }overrides slow threshold (default 500ms)when— Conditional: skip logging entirely if returns false
Key behavior:
- Wraps Promise results via
.then()— does NOT await them - Slow calls (> 500ms default) log at WARN level
- Creates
ScopedLoggerfor async context tracking
getScopedLogger() constraints:
- Must be called BEFORE any
awaitin method body (browser uses counter-based fallback unreliable afterawait) - Must be inside a method decorated with
@info()/@debug()/@trace() - ESLint rule
@gitlens/scoped-logger-usageenforces both - Node.js uses
AsyncLocalStoragefor reliable cross-async scope
@gate() — Concurrent Call Deduplication (gate.ts)
Returns the SAME promise to concurrent callers. Only one invocation runs at a time per instance (or per grouping key).
Options (GateOptions):
timeout— Default: 300000ms (5 minutes)rejectOnTimeout— Default:true. Whenfalse, retries instead of rejecting
Key behavior:
- Stores pending promise on instance via
$gate$methodNameproperty - Clears gate via
.finally()when promise settles - Deadlock detection: warns at 90s and 180s, sends telemetry (
op/gate/deadlock) - On timeout: rejects with
CancellationError(or retries ifrejectOnTimeout: false) - Optional
getGroupingKeycreates independent gates per key - Synchronous returns pass through unaffected
Common hang pattern: If a gated method hangs, check what the promise is waiting for. Is there a circular dependency? Is a nested gated call waiting on the outer gate?
@memoize() — Result Caching (memoize.ts)
Caches return value permanently on instance via Object.defineProperty (non-writable, non-configurable).
Options (MemoizeOptions):
resolver— Custom cache key generator from argumentsversion—'providers'for version-keyed invalidation
Key behavior:
- Cache stored as instance property:
$memoize$methodName - Version-keyed: cache key prefixed with version counter;
invalidateMemoized('providers')bumps counter causing cache miss - No TTL — cached for lifetime of instance
- Caches Promises — a rejected Promise stays cached permanently
- Checks
Object.hasOwn(this, prop)before computing
@sequentialize() — Sequential Execution Queue (sequentialize.ts)
Queues async calls to execute one at a time (unlike @gate() which deduplicates).
Options (SequentializeOptions):
getDedupingKey— Consecutive calls with same key share result while waitinggetQueueKey— Creates independent parallel queues per key
Key behavior:
- Chains calls via
.then()— each waits for previous to complete - Without
getQueueKey, all calls share single queue - With
getQueueKey, different keys execute in parallel
Decorator Stacking
Execution order is bottom-up (outermost runs first):
@debug() // Runs 1st: creates scope, logs entry/exit
@gate() // Runs 2nd: deduplicates concurrent calls
async method() { ... }
Debugging priority:
@gate()— hangs, timeouts, deadlocks@memoize()— stale data, cached rejections- Logging decorators — rarely the cause
Recommended Agent Skills
Expand your agent's capabilities with these related and highly-rated skills.
commit
Create well-formatted git commits following GitLens conventions
analyze
Deep design and implementation analysis with devil's advocate evaluation
add-command
Create new VS Code commands with all required boilerplate
add-icon
Add new icons to the GitLens GL Icons font
deep-planning
Use when formulating the best technical approach for a task — before writing implementation plans or code. Triggers on architecture decisions, complex features, refactors, or when the user asks how to approach something. Investigates current codebase, questions existing patterns, researches alternatives, and presents approaches with trade-offs.
review
Code review against GitLens standards with optional impact completeness audit
Didn't find tool you were looking for?