root-cause-method
Skill0 starsChanged 3 months agoApache-2.0View on GitHub
The evidence discipline for any debugging session, bug investigation, incident, or triage. Use this skill whenever diagnosing why something is broken: an exception or stack trace, a failing or flaky test, a wrong value on screen, a 500, a slow endpoint, a memory leak, a race condition, a "worked yesterday" regression, or a scanner/monitor alert. Trigger on 'debug', 'why is this broken', 'this doesn't work', 'fix this bug', 'root cause', 'stack trace', 'error', 'crash', 'regression', 'flaky', 'reproduce', 'works on my machine', 'intermittent'. Defines the reproduce-before-theorize rule, the confidence ladder, the finding format, and the anti-guessing rules that every debugmaxxing command depends on.
In this repository3 other files it gives its agents.
- backend-debuggingSkill
- debuggable-codeSkill
- frontend-debuggingSkill
The file
Show its rules and text
We split this skill into the rules it gives an agent
14 rules. About 0 are common, 11 are distinctive, and 3 are very specific to this project.
≈0
Common · estimate
Many files like this have a rule of this kind.
≈11
Distinctive · estimate
This author's own choices. Worth a look.
3
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- Distinctive: line 29
If you cannot reproduce it, you are not debugging yet — you are guessing, and you must say so.
- Distinctive: line 35
If you can name neither, you have a hypothesis.
- Distinctive: line 55
Instrument, then wait: If it still won't come, add the log line, metric, or assertion that would *distinguish between your top hypotheses*, …
- Distinctive: line 73
Reproduce before you theorize: Do not read code looking for something wrong.
- Very specific: line 76
Read the whole error: All of it — the message, every stack frame including the ones in node_modules, the "caused by" chain, the lines of …
- Distinctive: line 87
Verify the code you're editing is the code that's running: Before deep analysis, when behavior makes no sense: stale build artifact, cached …
- Distinctive: line 106
Zero bugs found is a valid result: If the behavior is actually correct and the expectation was wrong, say so.
- Distinctive: line 120
Never present a Hypothesis in the voice of a Confirmed.
- Distinctive: line 125
Every investigation runs this loop.
- Distinctive: line 150
Prefer the cheap, decisive move over the clever one.
- Very specific: line 209
Fix the cause at the right layer: If a component crashes on undefined, the question is why it's undefined.
- Distinctive: line 212
Fix the class, not the instance,: when the same mistake appears in many places — but only after one instance is fully understood.
- Distinctive: line 229
Never run destructive commands against a real database or environment to test a theory.
- Very specific: line 235
Every command in this toolkit closes with one line — → Recommended next: … — so the user is never left holding a diagnosis with no path …
≈ “Common” is an estimate: at least 1 in 5 of the 350,495 skill files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.
Files
Root Cause Method
A fix you cannot explain is not a fix. It is a change that made the symptom stop showing up in front of you, and it will come back — in production, on someone else's machine, at a worse time. This skill defines how to debug so that the fix is explainable.
The one rule
No fix without a reproduction and a mechanism.
You must be able to name both:
- Reproduction — a specific, repeatable way to make the bug happen. A command, a request, a click path, a test, a payload. State how reliably it fires (10/10, 3/10, only under load). If you cannot reproduce it, you are not debugging yet — you are guessing, and you must say so.
- Mechanism — the causal chain from a known-good state to the wrong outcome, with the exact
line where reality diverged from intent. "The
userIdisundefinedatauth.ts:44because the middleware that sets it runs after this route, so the query atorders.ts:91filters onundefinedand Postgres returns every row."
If you can name neither, you have a hypothesis. Label it a hypothesis and say exactly what observation would confirm or kill it.
"It's probably X" is not a mechanism. "This looks suspicious" is not a mechanism. A diff that makes the test pass is not a mechanism.
When you can't reproduce it
Plenty of real bugs are production-only, one-customer-only, or once-a-week. The rule doesn't bend — you still don't get to call a guess a fix — but "cannot reproduce" is a state with a defined exit, not a dead end. Work it in this order:
- Narrow it with the data you already have. Slice every known occurrence by release, instance/host, tenant, account, input shape, client version, region, and time of day. A bug that happens on one instance, or for one tenant, or only since one release, has already told you most of the answer — and none of that required reproducing it.
- Find the boundary condition. "Sometimes" is nearly always "when X." Apply pressure deliberately rather than waiting: concurrency, slow responses, an empty collection, an enormous one, a null field, the second call rather than the first, a cache just past expiry, a month or DST boundary, a fresh session versus a stale one.
- Instrument, then wait. If it still won't come, add the log line, metric, or assertion that would distinguish between your top hypotheses, ship it, and let the bug reproduce itself. This is a legitimate move, not a failure — but say that it's what you're doing, and say which hypotheses the instrumentation will separate.
- Only then reason from the code — and everything it produces is a Hypothesis, labelled as one, carrying the observation that would settle it.
The trap is step 4 wearing step 1's clothes: read the code, find something that looks wrong, fix it, declare the intermittent bug closed. You haven't fixed it. You've changed something and given up your ability to tell whether it mattered — and an intermittent bug that "stopped happening" after an unverified change is indistinguishable from one that is simply between occurrences.
Anti-guessing rules
These exist because the default failure mode of AI debugging is confident, plausible, symptom-silencing change.
- Reproduce before you theorize. Do not read code looking for something wrong. Make the bug happen, watch it happen, and let the observation direct the reading. Reading first produces a list of things that could be wrong, none of which are the bug.
- Read the whole error. All of it — the message, every stack frame including the ones in
node_modules, the "caused by" chain, the lines of log before the error. The answer is in there far more often than people expect, and skipping to the code is how it gets missed. - Change one thing at a time. Shotgun debugging — three edits, re-run, it works — leaves you not knowing which change mattered, and usually two changes you didn't need. Revert what didn't help.
- Never fix a symptom you can't explain. Adding a
try/catch, a null check, an?., asetTimeout, a retry, akeyprop, anawait, or a cache bust can be the correct fix — but only when you can say why the value was null, why the timing was wrong, why the identity changed. Applied without that, they are silencers: the bad state still exists and now it's invisible. - Verify the code you're editing is the code that's running. Before deep analysis, when behavior makes no sense: stale build artifact, cached bundle, service worker, wrong container, wrong branch deployed, hot reload that didn't apply, an installed copy shadowing the source, an env var set in the shell but not in the process. Prove it — add a deliberate crash or a unique log line and confirm you see it.
- The bug is in your code until proven otherwise. Framework, compiler, and standard-library bugs exist, but they're rare and they've usually been reported by someone else. Before blaming one, search for the known issue and construct a minimal case that isolates it from your code. If you find a genuine one, cite the issue.
- Correlation is a lead, not a cause. "It started after we upgraded X" tells you where to look. It does not tell you what broke. Find the line.
- Don't trust the bug report's framing. "The save button is broken" may be a serialization bug, an auth expiry, a CORS preflight, or a 200 with an error body. Confirm the symptom yourself before adopting the reporter's theory of it.
- Preserve the evidence. Copy the failing output, the log excerpt, the request/response, the value you observed — into the report, before you start changing things. You will need to compare against it, and rebuilding a lost repro costs more than saving it did.
- If you fixed it and don't know why it works, you haven't fixed it. Say that plainly rather than declaring victory. An unexplained fix is an open bug with a longer fuse.
- Zero bugs found is a valid result. If the behavior is actually correct and the expectation was wrong, say so. Do not manufacture a defect to have something to report.
Confidence ladder
Label every claim. This is the single highest-value thing you can do for the reader.
| Label | Bar |
| Confirmed | You reproduced it, observed the mechanism directly (a logged value, a debugger stop, a failing assertion at the divergence), and the fix makes the repro pass. |
| Likely | The mechanism explains every observed symptom and you found no contradicting evidence, but you did not observe the divergence directly. |
| Hypothesis | Consistent with the symptoms; not yet tested. Must be accompanied by the experiment that would settle it. |
| Ruled out | Tested and eliminated. Report these — they're how the reader knows the search was real. |
Never present a Hypothesis in the voice of a Confirmed. Most bad debugging output is correct information delivered at the wrong confidence.
The loop
Every investigation runs this loop. Say which step you're on.
- Observe — what actually happens, precisely. Exact error text, exact wrong value, exact timing. "Doesn't work" is not an observation.
- Reproduce — get it to happen on demand, as small and as fast as possible.
- Localize — cut the search space in half, repeatedly. Bisect over commits, over inputs, over layers (is the data wrong leaving the DB? leaving the API? entering the component? after render?). Binary search beats reading.
- Hypothesize — one falsifiable statement about the mechanism.
- Predict — "if this is the cause, then X will be true." Something you can check that distinguishes this hypothesis from the others.
- Test the prediction — a log line, a breakpoint, a one-line experiment. If the prediction fails, the hypothesis is dead. Kill it and go back to 4. Do not patch the hypothesis to survive.
- Fix the cause — at the level where the invariant should have held, not at the place where the symptom appeared.
- Verify — the repro now passes, and the fix is why. Re-break it deliberately if the causal link is at all in doubt.
- Prevent — the regression test, assertion, type, or invariant that would have caught it.
Steps 5 and 6 are the ones people skip, and skipping them is exactly what makes debugging feel like guesswork.
Localization tactics, in order of preference
Prefer the cheap, decisive move over the clever one.
- Bisect the timeline —
git bisect runwith a script that exits non-zero on the bug. Answers "what changed" in log₂(n) steps and requires no understanding at all. - Bisect the input — halve the payload, the dataset, the config, the CSS, the component tree. The smallest input that still fails is close to a proof by itself.
- Bisect the stack — check the value at each layer boundary (DB → ORM → service → handler →
wire → client store → props → DOM). The boundary where it first goes wrong is the boundary
that owns the bug. The cheapest first cut is the client/server split: reissue the request with
curl, outside the browser. If it reproduces there the whole frontend is eliminated in one step; if it doesn't, the backend is. Take that cut before any other. - Delete until it works — in a scratch copy, remove everything not needed to reproduce. What's left is the bug.
- Diff a working case against a broken one — two users, two environments, two requests, two inputs. The difference set is your suspect list.
- Read the code — last, and now with a specific question rather than a general suspicion.
Finding format
Report every diagnosis in this shape. Most severe or most blocking first.
[Confirmed | Likely | Hypothesis] Short, specific title
In plain English: one sentence a non-specialist could follow, before any of the detail —
"The date is saved as text, so the report can't add it up and shows zero."
Symptom: what the user or the test actually sees
Repro: the exact command/steps, and reliability (e.g. 10/10, ~3/10 under concurrency)
Root cause: path/to/file.ext:120-135 (plus contributing sites)
Mechanism: the causal chain, step by step, with the divergence pinpointed —
expected X, actual Y, because Z
Evidence: what you observed that proves it — the logged value, the query, the failing
assertion, the network response. Quote it.
Fix: the specific change, as code
Verify: the command that fails before the fix and passes after
Prevention: the test/type/assertion/invariant that would have caught this earlier
Blast radius: what else this bug affects that nobody has reported yet — other call sites,
other tenants, corrupted data already writtenLead with the plain-English line, always. You do not know who is reading — the same report
goes to the person who wrote the code and the person who merely owns it. The structured detail
below it loses nothing by being preceded by one clear sentence, and a reader who can't parse
Mechanism: still deserves to know what broke.
The same applies to the terms inside the report. Translate jargon the first time you use it,
in the same breath, and then use it freely: "an N+1 (the code runs one query per row instead of
one query for all of them)", "a stale closure (the function is still using a value from an
earlier render)", "a stacking context (an invisible layer that traps z-index inside it)". One
clause. This costs an expert nothing and is the difference between a useful report and an
intimidating one for everyone else. Never use an acronym on first mention without expanding it.
Close every investigation with:
- Ruled out — hypotheses tested and eliminated, and how. This is coverage evidence.
- Still unexplained — anything the mechanism does not account for. Never round this to zero. A leftover unexplained symptom usually means a second bug.
Fix quality bar
- Fix the cause at the right layer. If a component crashes on
undefined, the question is why it's undefined. Guarding the component is right when absent is a legal state; it's wrong when the API contract says the field is required and the API broke it. - Fix the class, not the instance, when the same mistake appears in many places — but only after one instance is fully understood. Generalizing from a guess multiplies the guess.
- Match the codebase. Existing framework, error handling, naming, style. A foreign-looking patch gets reverted.
- Remove your instrumentation. Debug logs, breakpoints, scratch files, temporary
console.logs — clean them up, or list precisely what you left and why. - Say what the fix does not cover. Data already written wrong. Clients already caching the bad response. Other code paths reaching the same sink.
- Flag behavior changes. A correctness fix that changes an output someone depends on needs to be called out, not discovered in production.
Scope discipline
Investigation is read-only apart from temporary instrumentation and scratch files. Do not refactor while debugging — a cleanup mixed into a bug fix hides which change mattered and makes the revert impossible. Note the cleanups you spotted and move on.
Never run destructive commands against a real database or environment to test a theory. Never
paste real credentials, tokens, customer PII, or full production payloads into the transcript —
redact them (user_id=8812, token=eyJ…4Kq) and refer to them by location.
Always end with the next step
Every command in this toolkit closes with one line — → Recommended next: … — so the user is
never left holding a diagnosis with no path forward. The through-line is reproduce → localize
→ prove → fix → prevent:
- Bug reproduced but not localized →
/debugmaxxing:bisect, or the layer command that owns it. - Mechanism confirmed →
/debugmaxxing:fixto patch the cause with a regression test. fixfinished → re-run the repro and the command that found it, to confirm it comes back clean.- Cause found but not reproducible on demand →
/debugmaxxing:reprobefore patching anything. - Nothing found and behavior is correct → say so and point at what to check outside the code.
Tailor it to what you actually found. Never end a report without it.
Does it work?
Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.
Reviews can't be read right now.
Your trail
Files you open will collect here, so you can find them again.
Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.
Now do this for your own.
Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.

