# Debugging Architect

> Use when something is broken, failing, flaky, or slow and the cause is not yet known. Reproduce, narrow, then one falsifiable hypothesis at a time under a three-hypothesis ceiling; regression test before the fix; a guard so it cannot recur silently. Produces a fixed-shape Diagnosis. Production incidents restore service first.

- Skill: `matis-dev/debugging-architect` (Agent Skill, multi-file: 4 files)
- Install (CLI): `npx skillmds@latest add matis-dev/debugging-architect`
- Raw SKILL.md: https://api.skillmd.com/api/skills/matis-dev/debugging-architect/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Coding & Dev Tools
- Author: matis-dev (https://skillmd.com/u/matis-dev)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/matis-dev/debugging-architect

---


# Skill: Debugging Architect — Diagnosis Without the Spiral

> **Apply Guidelines Skill** — load the `guidelines-meta` skill before proceeding.
> **Profile section owned:** §Guardrails → Known blind spots (Guidelines §5). Every root cause that a green pipeline failed to catch gets written there, so the next change is checked against it.


**Why this skill exists:** debugging is the one activity where an agent reliably makes things worse. The failure mode is not being wrong — it's being wrong *repeatedly and confidently*, changing five things at once, and leaving a codebase that's harder to reason about than before it started. Every rule below exists to stop that.

---

## Operational Constraints (Strict)

1. **Reproduce before you theorise.** A bug you cannot reproduce is a bug you cannot verify you fixed. If it can't be reproduced, that is the finding — say so and pivot to §Unreproducible.
2. **Change one thing at a time.** Each pass alters exactly one variable and predicts the result *before* running. Two simultaneous changes make a passing result uninterpretable.
3. **Hard pass ceiling (Guidelines §16).** Three hypotheses tested and disproved → **stop**. Do not start a fourth. State what's been ruled out, name the assumption most likely wrong, and ask one diagnostic question. This is §17's debug-spiral rule made binding: "still broken" three turns running means the *frame* is wrong, not that the next guess needs more effort.
4. **Never fix by weakening.** Deleting an assertion, loosening a tolerance, adding a skip, widening a mock, or wrapping the symptom in a `try/catch` is not a fix — it is the bug plus concealment (Testing Architect constraint 3).
5. **No speculative fixes.** "This might help" is not a fix. If you can't state *why* the change makes the symptom impossible, you haven't found the cause. Changes that "seem to help" without an explanation are the beginning of the spiral, not the end of it.
6. **Revert your own probes.** Debug logging, temporary instrumentation, and narrowing scaffolds come out before you're done — or are called out explicitly if deliberately kept.
7. **Production first, diagnosis second.** If users are affected, `deployment-architect`'s Rollback Mode runs *first*. Restore service, then debug the artifact at leisure. Production is not a debugging environment.
8. **Git and golden-file guards are enforced by the plugin's PreToolUse hook** (Guidelines §9, §10): every git write, `gh` publish, `--no-verify`, and snapshot update is denied by the runtime; read-only inspection stays open. `git bisect` is a branch-moving operation and is denied like the rest — surface the command for the user, or use a read-only equivalent.

---

## What to Read, and When

The six phases below are the spine and apply to every run. These are read on demand:

| Read | When |
|---|---|
| `${CLAUDE_SKILL_DIR}/references/flaky.md` | The failure is intermittent. Different discipline: you are proving a *rate*, not a state. |
| `${CLAUDE_SKILL_DIR}/references/unreproducible.md` | It cannot be reproduced. Ship instrumentation, not a guess. |
| `${CLAUDE_SKILL_DIR}/references/diagnosis-format.md` | Writing up the result. |
| `module-gate-battery` | Running the gates after the fix — and §3 when a test "passes but the feature is broken". |
| `module-propagation` | The fix touches shared shape, a public API, or an origin. |
| `module-findings` | Stating confidence in a root cause. |

---

## Phase 1 — Establish the Symptom

Answer these before touching code. Guessing here costs whole sessions.

| Question | Why it matters |
|---|---|
| **What exactly happens, and what was expected?** | "It doesn't work" is not a symptom. Get the actual output, error, or wrong value. |
| **What's the smallest input that triggers it?** | Reduction *is* diagnosis — a shrinking repro usually names the cause on its own. |
| **When did it last work?** | A known-good point converts an open search into a bounded one. |
| **What changed since?** | Code, dependency, data, config, environment, clock, or a service you don't own. |
| **Deterministic or intermittent?** | Intermittent means state, ordering, timing, or concurrency — see §Flaky. |
| **Where does it *not* happen?** | Passing locally but failing in CI is itself a diagnosis: environment, not logic. |

**Get the real error text.** Not a paraphrase — the stack trace, the failing assertion, the actual value. A paraphrased error has already lost the detail that identifies the cause.

---

## Phase 2 — Narrow Before Hypothesising

Resist the first plausible theory. Cut the search space first; a bisected search beats an inspired guess, and it works when you have no intuition at all.

**Bisect along whichever axis is cheapest here:**

- **Time** — which commit introduced it? `git bisect` is the tool; **surface the commands, don't run them** (§8). A `git log` over the suspect paths is often enough.
- **Code path** — does the failure survive when a layer is removed? Call the unit directly, bypass the framework, stub the collaborator.
- **Data** — does it fail for all inputs or one shape? Empty, boundary, unicode, very large, null, pre-migration rows.
- **Environment** — local vs. CI vs. deployed. Runtime version, env vars, timezone, locale, filesystem case-sensitivity, network egress.
- **Config** — does it fail with defaults? A wrong flag is a common and invisible cause.

**The layer question, asked early:** is this *your code*, *your usage of a dependency*, *the dependency itself*, or *the environment*? Each has a different fix and a different owner, and mixing them up wastes the most time.

---

## Phase 3 — One Hypothesis at a Time

For each pass, write these three lines **before** running anything:

```
Hypothesis: <the specific mechanism you believe causes this>
Prediction:  if true, <this exact observable> will happen
Test:        <the single change or probe that discriminates>
```

Rules:
- **The prediction must be falsifiable.** "It'll probably work better" tests nothing. "The value will be `undefined` at line 42" is a real test.
- **A disproved hypothesis is progress** — record it. The list of ruled-out causes is the most valuable artifact of a hard debug session, and it's what makes handing over possible.
- **Confidence gate** (`module-findings` §2). Before declaring a root cause, self-score: is this *proven* or merely *consistent with the evidence*? Below ~80, say so plainly and keep it labelled as a theory. A confidently-stated wrong diagnosis is worse than an honest "not certain yet" — the user acts on it.
- **Count the passes.** At three disproved hypotheses, stop (§3). Not a suggestion.

**Check the known blind spots first** — the profile's §Guardrails list, plus `implementing-architect`'s protocols. A startling number of "impossible" bugs are: a stale template binding that type-check can't see, a test double missing a method, a config declared in one environment but not another, or a cache serving a previous build.

---

## Phase 4 — Prove the Cause

Before fixing, close the loop both ways:

1. **Explain the whole symptom.** A cause that explains most of it is usually adjacent to the real one. Unexplained residue means you're not done.
2. **Make it appear and disappear on demand.** Toggle the cause; the symptom must follow. That's proof, not correlation.
3. **Explain the "why now"** — if this code is old, what changed to expose it? Skipping this is how the same bug returns in a different shape.

---

## Phase 5 — Fix It Once

1. **Write the failing test first**, per the `testing-architect` skill. It must fail for the *right reason* — run it before the fix and read the failure. A regression test that would pass without the fix is decoration.
2. **Fix the cause, not the symptom.** If the honest fix is large, say so and let the user choose between a scoped mitigation (labelled as such, with the real fix as a follow-up) and doing it properly now. Never disguise a mitigation as a fix.
3. **Smallest change that removes the cause** (Guidelines §2, §3). Do not refactor the surrounding area because you were in there.
4. **Check propagation.** If the fix touches shared shape, a public API, or an external origin, load the `module-propagation` skill and run the matching protocol — bug fixes hit the same mirror sites features do, with less scrutiny.
5. **Run the gates** per the `module-gate-battery` skill, once as a batch.
6. **Remove your probes** (§6).

---

## Phase 6 — Make It Impossible to Recur Silently

The step that separates debugging from firefighting. A fix nobody can accidentally undo is worth more than the fix itself.

- **The regression test stays.** Named for the bug's behaviour, not its ticket.
- **If a gate should have caught this and didn't**, that is a finding about the *gates*: write it into the profile's §Guardrails → Known blind spots, in one line, phrased as what to check next time.
- **If the class can recur elsewhere**, add it to §Recurring Propagation Sites so future changes get swept.
- **If it was a production incident**, hand the guard to `deployment-architect` — a pre-flight check is cheaper than a second outage.
- **If the root cause was a wrong assumption in a plan**, say so; that's feedback for `planning-architect`, not shame.

---

## Quality Checklist Before Claiming "Fixed"

- [ ] Reproduced before diagnosing — or explicitly reported as unreproduced.
- [ ] Narrowed before hypothesising; the search was cut, not guessed at.
- [ ] Each pass changed one variable with a written, falsifiable prediction.
- [ ] Stopped and reassessed at three disproved hypotheses rather than continuing.
- [ ] Root cause explains the **whole** symptom, and toggling it toggles the symptom.
- [ ] Failing test written first, and it failed for the right reason.
- [ ] Fix addresses the cause; any mitigation is labelled a mitigation.
- [ ] Nothing was weakened — no deleted assertion, loosened tolerance, added skip, or swallowed exception.
- [ ] Propagation protocol run if the fix touched shared shape, a public API, or an origin.
- [ ] Debug probes removed.
- [ ] Blind spot written to the profile if a gate should have caught this.
- [ ] Confidence stated honestly; an unproven cause is labelled unproven.
- [ ] **No `git add`, `commit`, `push`, or `bisect`** — commands surfaced for the user.

---

_v1.0 — version history in CHANGELOG.md_

