# Root Cause First

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

- Skill: `cohen-liel/root-cause-first` (Agent Skill)
- Install (CLI): `npx skillmds@latest add cohen-liel/root-cause-first`
- Raw SKILL.md: https://api.skillmd.com/api/skills/cohen-liel/root-cause-first/raw
- Safety review: pending (external: skill-scanner PASS, skillspector PASS)
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: AI & ML
- Author: cohen-liel (https://skillmd.com/u/cohen-liel)
- Updated: 2026-09-22
- Page: https://skillmd.com/skills/cohen-liel/root-cause-first

---


# 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

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

2. Reconstruct the intended contract.
   - Determine what the component is supposed to guarantee.
   - Identify upstream inputs, downstream consumers, and invariants that must hold.

3. 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.

4. 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.

5. 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.

6. 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.

7. 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).**

```js
// Crash disappears, but those users now silently render a blank email.
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:

```js
// loadUser() now owns the invariant: it always returns a non-null profile.
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.

