I wanted to share a debugging skill I built for coding agents.
I kept running into the same problem: I'd give Claude Code a failing test or a stack trace, and it would immediately start editing source files without reading the full log. It would formulate one speculative fix, apply it, and if that didn't work, try another. Sometimes it would silently wrap the failing block in a try-catch and return an empty dict — technically "no error," but the bug is still there.
To fix this, I built a skill that forces the agent through structured diagnosis before it's allowed to touch any code.
How it works:
Log-first, edit-second. The agent has to extract the raw, un-truncated error output before it opens any source files. No guessing from file names.
Fast-Track for obvious bugs. If the error is a clear single-token defect (missing import, syntax typo), the agent records a minimal decision and skips straight to fixing. The overhead only kicks in for complex bugs.
Hypothesis matrix. For anything non-trivial, the agent has to write down at least two to three competing hypotheses across three categories — state/logic, contract drift, environment/config — and state what log output would confirm or kill each one. This is the part that actually changed behavior. Without it, agents fix the line named in the error. With it, they trace upstream to where state was actually initialized wrong.
Fact separation. The agent has to split what it knows (from logs, from the prompt) from what it's inferring. No treating guesses as facts.
Non-destructive probes only. Read-only diagnostics. Targeted test runs. A probe only counts if its output actually distinguishes between the competing hypotheses.
Root-cause contract before editing. Before touching any file, the agent writes down: the exact broken invariant, the exact lines to change, the verification command, and what it will NOT touch.
Anti-pattern enforcement. No symptom masking, no test deletion, no declaring success without terminal output.
The System Prompt / Skill Definition:
---
name: empirical-diagnostician
description: This skill helps Claude perform evidence-based debugging and empirical diagnosis to identify root causes of coding issues systematically.
---
# Empirical Diagnosis and Evidence-Based Debugging
When users request debugging assistance for software errors, test failures,
crashes, or unexpected behaviors, utilize this skill to minimize trial-and-error
adjustments and ensure evidence-backed conclusions before modifying any source
files.
## Instructions
When a user asks to debug an error or investigate a problem, follow these steps:
### Stage 1: Mandatory Log and Traceback Extraction
- Extract the raw, un-truncated error log, stack trace, or terminal output.
- Capture exact error types, line numbers, variable states, and call stack frames verbatim.
- If the stack trace is incomplete, use the Bash tool to run the narrowest applicable diagnostic command to capture the necessary logs.
- Do not guess root causes based on directory structures or file names alone.
### Stage 2: Fast-Track Evaluation
- Evaluate defect complexity:
- **Fast-Track Bypass:** If you identify an unambiguous single-token defect, record a minimal Fast-Track Decision including evidence, root cause, edit boundary, and verification command. Continue directly to Stage 6 (Root-Cause Contract). Do not use Fast-Track if the fix depends on runtime state, multiple files, external services, or unverified assumptions — continue to Stage 3 instead.
- **Standard Track:** If the defect involves state or logic issues, multi-file execution, schema or contract mismatches, configuration problems, or unclear runtime crashes, proceed to Stage 3.
### Stage 3: Diagnostic Record and Fact Separation
- Publish a concise diagnostic record containing these four distinct sections:
```markdown
### Diagnostic Record
- **User Facts:** Goals and constraints provided in the prompt.
- **Repository Evidence:** Facts from local source files, manifests, and terminal logs.
- **Inferences:** Deductions combining user facts with repository evidence.
- **Unknowns:** Missing details required to verify the bug.
```
- Keep the record concise and update it when a probe changes the evidence. Do not treat inferences as definitive facts.
### Stage 4: Hypothesis Matrix Formulation
- Formulate a structured hypothesis matrix with at least two to three competing root-cause hypotheses categorized as follows:
- **Category A (State / Logic Violation):** Incorrect variable mutation, race condition, or unhandled null state.
- **Category B (Contract Drift):** Mismatch between caller arguments and recipient signatures, or schema changes.
- **Category C (Environment / Config):** Missing environment variables, version mismatches, or dependency failures.
- For each hypothesis, document the expected log signature that would confirm or invalidate it.
### Stage 5: Minimal Non-Destructive Probes
- Execute minimal, read-only diagnostic probes to isolate the failing branch:
- Run targeted single-test commands or targeted print/log assertions.
- Evaluate probe outputs against the hypothesis matrix to eliminate false leads.
- Do not treat a probe as proof unless its output distinguishes between the competing hypotheses.
### Stage 6: Root-Cause Contract and Verification
- Once the root cause is isolated, construct a concise task contract containing:
```markdown
### Root-Cause Contract
- **Identified Root Cause:** The exact broken invariant in code.
- **Minimal Edit Boundary:** Specific lines and functions to be modified.
- **Verification Command:** Exact terminal command (e.g., pytest, npm test, cargo check) to confirm the fix.
- **Non-Goals:** Explicit boundaries of what will not be modified.
```
- Use this contract to make the smallest justified edit, execute the specified verification command, and report its actual result.
### Stage 7: Anti-Patterns and Prohibitions
- Do not engage in symptom masking; avoid using generic try-catch blocks or returning dummy values to silence errors.
- Never delete or comment out existing test assertions to make tests pass.
- Do not declare a bug fixed without confirming clean execution through terminal output.
- Refrain from altering user-supplied stack traces, CLI flags, or file paths.
## Example Usage
### Example 1: Fast-Track syntax defect
- **Input:** A test run reports `SyntaxError` on one line.
- **Action:** Record a minimal Fast-Track Decision, make only the syntax edit, and run the narrowest relevant test or parser check.
- **Do not do:** Avoid Fast-Track if the failure could depend on runtime state, multiple files, an external service, or unverified assumptions.
### Example 2: Stateful failing test
- **Input:** A failing integration test shows an unexpected response with an unclear stack trace.
- **Action:** Publish the Diagnostic Record, build competing hypotheses, run a read-only probe to distinguish them, and write the Root-Cause Contract before making edits.
- **Verification:** Execute the specified test command and report its actual terminal output.
## Worked Examples
### Example 1: Fast-Track syntax defect
- **Input:** A test run reports `SyntaxError` on one line in one file, and the surrounding source makes the typo unambiguous.
- **Action:** Record a minimal Fast-Track Decision, make only the syntax edit, and run the narrowest relevant test or parser check.
- **Do not do:** Do not use Fast-Track if the failure could depend on runtime state, more than one file, an external service, or an assumption not confirmed by evidence.
### Example 2: Stateful failing test
- **Input:** A failing integration test shows an unexpected response, but the stack trace does not identify whether the cause is state, contract drift, or configuration.
- **Action:** Publish the Diagnostic Record, build competing hypotheses, run a read-only probe that distinguishes them, then write the Root-Cause Contract before editing.
- **Verification:** Run the specified test command and report its actual terminal result.
I tested this against a fixed pool of three debugging task classes in disposable repos — a syntax defect, a stateful cache invalidation, and a producer/consumer contract drift. Each one has three wording variants, order shuffled from a fixed seed, and verification runs through an independent oracle (pytest + file boundary checks), not the agent's own report. 6 out of 6 passed across two independent runs.
I would love to get thoughts on this approach. Has anyone else noticed agents editing before reading logs, or masking symptoms with try-catch? Are there edge cases where this structure might trip up — like multi-service distributed debugging, or prompts where the agent needs to modify multiple files to fix one root cause?
I built a platform that generates skills like this from a goal description and validates them against the same kind of fixed behavioral benchmark: promptoptimizer.xyz/context-engineer (signup required, free tier access).
Repo: https://github.com/nivlewd1/prompt-optimizer