| name | root-cause-first |
| description | Root-cause-first debugging and repair workflow. Use when asked to fix a bug, runtime error, failing test, broken tool/agent behavior, unexpected logs, regression, flaky behavior, architecture issue, or any request that says "fix", "debug", "solve from the root", "no band-aids", "no fallback", or "understand what is happening before changing code". Do not use for greenfield feature work, mechanical edits, or cleanup without an observed failure. |
Root Cause First
Use this skill to prevent premature patches. The goal is to make the smallest correct change only after the failure mechanism is understood.
Red Flags โ Stop If You Catch Yourself Thinking
These thoughts mean you are about to ship a band-aid. Stop and find the mechanism first.
| Thought | Reality |
|---|
"I'll add a ?. or null-check so it stops crashing" | You are hiding the signal. Ask why it is null and fix that. |
| "Wrapping it in try/catch makes the error go away" | Catch-and-continue turns a loud bug into a silent one. |
| "The test is flaky, let me add a retry or a longer timeout" | Flaky means a real race or order dependency. Retry hides it. |
| "I'll widen the parser to accept the weird input too" | The weird input is the clue. Find who produced it. |
| "I'll return a default on error so the flow continues" | Synthetic success masks the failure from everyone downstream. |
| "It works now, I am not sure why" | Then it is not fixed. You moved the bug. |
Relationship To SSOT Enforcer
If the root cause is duplicated constants, scattered policy, parallel calculations, copied parsing, or multiple modules answering the same question, switch to ssot-enforcer for the consolidation step. After consolidation, return here to verify the fix against the original failure.
When Not To Use
Do not use this skill for:
- New features or design work where there is no failure to explain.
- Mechanical edits such as renames, formatting, dependency bumps, or documentation cleanup.
- Broad architecture cleanup unless it is tied to a concrete symptom, failing test, bad runtime behavior, or user-reported regression.
Required Workflow
-
State the observed failure in concrete terms.
- Quote the exact error, log line, failed assertion, bad behavior, or user-visible symptom.
- Identify where in the execution path the failure happens.
-
Reconstruct the intended contract.
- Determine what the component is supposed to guarantee.
- Identify upstream inputs, downstream consumers, and invariants that must hold.
-
Trace the real path before editing.
- Read the code that produces the failure and the code that consumes its output.
- Prefer logs, tests, traces, raw API responses, local reproduction, and call sites over assumptions.
- Distinguish provider/tool behavior from adapter/parser/orchestrator behavior.
-
Name the root cause.
- Explain the mechanism that makes the observed failure inevitable or likely.
- If there are multiple plausible causes, list what evidence confirms or eliminates each one.
- Do not implement until there is one primary cause or a clearly bounded uncertainty.
-
Reject band-aids explicitly.
- Do not add fallback, retry, catch-and-continue, synthetic success, timeout inflation, broad parsing, or error suppression unless the root cause proves that behavior is the correct product contract.
- If such handling is truly needed, state why it is not masking the failure and what invariant it preserves.
-
Implement the root fix.
- Change the source of the bad state or the incorrect contract boundary.
- Keep the patch scoped to the root cause.
- If the source of the bad state is duplicated or scattered truth, consolidate it through
ssot-enforcer instead of adding a local workaround.
- Add diagnostics when future failures would otherwise be ambiguous.
-
Verify against the original failure.
- Add or update a regression test that fails on the old behavior.
- Run the narrow test first, then broader typecheck/test coverage as risk requires.
- If the fix cannot be fully verified in-environment (visual/UI/device-only behavior, or unavailable services), say so explicitly and name exactly what a human must check. Do not claim success you could not observe.
- In the final answer, report root cause, fix, and verification.
Worked Example
Symptom. TypeError: cannot read 'email' of undefined thrown in renderProfile() for ~2% of users.
Band-aid (what to avoid).
const email = user.profile?.email ?? "";
The optional chain suppresses the only signal that something upstream is wrong.
Root-cause trace. Follow where user is built. loadUser() returns the record before profile is hydrated for accounts created before the profile backfill. The invariant "every user has a profile" is violated at the data layer, not in the view.
Root fix. Enforce the invariant at the source so every consumer can trust it:
function loadUser(id) {
const row = db.fetchUser(id);
return { ...row, profile: row.profile ?? defaultProfile(row.id) };
}
Now renderProfile() can trust its input and the ?. is unnecessary. (The invariant has exactly one owner โ see ssot-enforcer.)
Verification. Add a regression test: loadUser() for a pre-backfill account returns a non-null profile; renderProfile() renders without optional chaining and the old input reproduces the original crash on the unpatched code.
Stop Conditions
Stop and ask or report a blocker when:
- The failure cannot be reproduced or located after reading the relevant path.
- Required credentials, services, logs, or artifacts are unavailable.
- Fixing the real cause requires a product decision rather than an engineering correction.
Final Answer Shape
Keep the close-out short:
Root cause: one concrete mechanism.
Fix: what changed and where.
Verification: commands/tests run and any remaining risk.