agentleFS
kenjichristopherv-delkenjichristopherv-del/debugmaxxing/skills/root-cause-method/SKILL.md

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.

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
  1. Distinctive:

    If you cannot reproduce it, you are not debugging yet — you are guessing, and you must say so.

    line 29
  2. Distinctive:

    If you can name neither, you have a hypothesis.

    line 35
  3. Distinctive:

    Instrument, then wait: If it still won't come, add the log line, metric, or assertion that would *distinguish between your top hypotheses*, …

    line 55
  4. Distinctive:

    Reproduce before you theorize: Do not read code looking for something wrong.

    line 73
  5. Very specific:

    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 …

    line 76
  6. Distinctive:

    Verify the code you're editing is the code that's running: Before deep analysis, when behavior makes no sense: stale build artifact, cached …

    line 87
  7. Distinctive:

    Zero bugs found is a valid result: If the behavior is actually correct and the expectation was wrong, say so.

    line 106
  8. Distinctive:

    Never present a Hypothesis in the voice of a Confirmed.

    line 120
  9. Distinctive:

    Every investigation runs this loop.

    line 125
  10. Distinctive:

    Prefer the cheap, decisive move over the clever one.

    line 150
  11. Very specific:

    Fix the cause at the right layer: If a component crashes on undefined, the question is why it's undefined.

    line 209
  12. Distinctive:

    Fix the class, not the instance,: when the same mistake appears in many places — but only after one instance is fully understood.

    line 212
  13. Distinctive:

    Never run destructive commands against a real database or environment to test a theory.

    line 229
  14. Very specific:

    Every command in this toolkit closes with one line — → Recommended next: … — so the user is never left holding a diagnosis with no path …

    line 235

≈ “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.

Keep what fits and drop the rest. Your version, ready for your agents.
Files
SKILL.md

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 userId is undefined at auth.ts:44 because the middleware that sets it runs after this route, so the query at orders.ts:91 filters on undefined and 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:

  1. 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.
  2. 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.
  3. 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.
  4. 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.

  1. 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.
  2. 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.
  3. 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.
  4. Never fix a symptom you can't explain. Adding a try/catch, a null check, an ?., a setTimeout, a retry, a key prop, an await, 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.
  5. 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.
  6. 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.
  7. 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.
  8. 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.
  9. 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.
  10. 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.
  11. 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.

LabelBar
ConfirmedYou 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.
LikelyThe mechanism explains every observed symptom and you found no contradicting evidence, but you did not observe the divergence directly.
HypothesisConsistent with the symptoms; not yet tested. Must be accompanied by the experiment that would settle it.
Ruled outTested 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.

  1. Observe — what actually happens, precisely. Exact error text, exact wrong value, exact timing. "Doesn't work" is not an observation.
  2. Reproduce — get it to happen on demand, as small and as fast as possible.
  3. 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.
  4. Hypothesize — one falsifiable statement about the mechanism.
  5. Predict — "if this is the cause, then X will be true." Something you can check that distinguishes this hypothesis from the others.
  6. 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.
  7. Fix the cause — at the level where the invariant should have held, not at the place where the symptom appeared.
  8. Verify — the repro now passes, and the fix is why. Re-break it deliberately if the causal link is at all in doubt.
  9. 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 run with 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 written

Lead 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:fix to patch the cause with a regression test.
  • fix finished → re-run the repro and the command that found it, to confirm it comes back clean.
  • Cause found but not reproducible on demand → /debugmaxxing:repro before 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.

Download now (ZIP)

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.