---
name: project-systematic-debugging
description: Use when investigating a PRD Plugin bug, failing validation, unexpected behavior, compatibility issue, or reproduced request report before proposing fixes.
---

# Project Systematic Debugging

## Deterministic Entry

Plan and run `engineering.debug` when enabled. It owns git/state fact
collection and the hash-bound diagnosis request. Use this skill to evaluate the
evidence and form the diagnosis; do not substitute an unbounded model scan.

Adapted from Superpowers by Jesse Vincent, MIT licensed:
https://github.com/obra/superpowers

Use this skill to find root cause before fixing bugs while preserving PRD
Plugin request, health, evidence, and traceability state.

## Iron Law

```text
NO FIXES WITHOUT ROOT CAUSE INVESTIGATION FIRST
THREE CONSECUTIVE FINDS MEANS STOP FIXING AND RE-GROUND
```

PRD Plugin IDs are non-negotiable. Debugging must keep the bug tied to its
`REQ-*`, `MSG-*`, `HLT-*`, `EV-*`, or affected artifact IDs.

## The Debug Loop

Debugging is a **loop**, not a checklist you walk once. Each pass is four
phases, and the loop is bounded by the circuit breaker below — that bound is
the part that matters, because an unbounded loop is whack-a-mole.

### Phase 1: Root Cause Investigation

- Read the full error, report, request thread, and affected files.
- Reproduce the behavior or record why it cannot be reproduced.
- Check recent commits, version metadata, installed-version markers, and
  downstream/upstream package state when relevant.
- For state, evidence, session, registry, or timestamp bugs, run
  `python scripts/state_consistency_check.py --repo-root .` when available.
- For request/mailbox bugs, run `python scripts/message_check.py --repo-root .`
  and `python scripts/request_report.py --config .prd_plugin/config.json` when
  available.

### Phase 2: Pattern Analysis

- Find the nearest working PRD Plugin script, skill, template, or state record.
- Compare broken and working flows before changing code.
- Identify which PRD Plugin surface owns the fix: script, skill, template,
  docs, request state, evidence, health, traceability, or release metadata.

### Phase 3: Hypothesis And Test

- State one root-cause hypothesis.
- Add or identify the smallest failing test or validator output that proves the
  bug.
- Do not bundle multiple fixes or unrelated refactors.

### Phase 4: Implementation

- Use `project-test-driven-implementation`.
- Fix the root cause.
- Verify focused and broader checks.
- Write a systematic debugging report into the relevant `MSG-*`, `EV-*`,
  `HLT-*`, or closeout summary with: root cause, reproduction, fix, validation,
  remaining risk, and affected IDs.

Then **count the find** and either exit the loop (nothing else is broken) or
run another pass — subject to the breaker.

## The Circuit Breaker: Three Consecutive Finds

**Trigger.** Three consecutive bug finds in one debugging episode, where
**each fix worked** and each one revealed, or was immediately followed by,
another bug in the same area.

This is deliberately *not* a count of failed attempts. Failed attempts are
already covered by "no fixes without root cause". The dangerous pattern is the
opposite and it feels like progress: every fix is correct, every test goes
green, and the area keeps producing bugs.

**Diagnosis.** Three correct fixes in a row that keep uncovering more is
evidence that your **model of the system is wrong** — not that you are unlucky
and not that you are fixing badly. Further patching cannot repair a wrong
model, so continuing to patch is the one move guaranteed not to work.

**On the third find, stop fixing.** Do these four, in order, before writing any
more code:

1. **Re-ground.** Stop reading your own diff. Re-read the actual subsystem end
   to end: the data shape, the contract, and *both sides* of every boundary it
   crosses. Then write down, explicitly, what you assumed versus what the code
   does. The gap between those two lists is usually the real bug.
2. **Research — local and web. Both are required.**
   - *Local:* the wiki, `DEC-*` decisions, prior `REQ-*`/`MSG-*` threads on this
     subsystem, git history and blame for the area, and the existing tests. A
     recurring bug has usually been described before, sometimes with the fix.
   - *Web:* the failure *class*, not your symptom — the named error, the
     library's documented semantics, the protocol contract, the platform
     behaviour. Skipping this half is how a solved problem gets re-derived
     badly.
3. **Re-plan.** Write a plan for the root condition, not for the next symptom.
   Name the *class* of bug and the single change that removes the class. If the
   plan is just "fix the next one too", you have not re-grounded.
4. **Implement the plan.** As a plan, with tests, in one deliberate change —
   not as another patch on the pile.

**The counter resets** when the re-planned change is implemented and verified,
or when investigation confirms the next bug is genuinely unrelated to the
previous three. It does not reset because a fix passed.

**Grounded in this repo.** The cross-repo request transport (`REQ-125` and the
`REQ-130`–`REQ-134` chain) was repaired in four rounds of half-fixes: a detector
that watched only the producer, a mailbox that matched the wrong id, transport
residue that was never pruned, and a directional visibility error that delivered
nine replies with their bodies stripped. Every one of those fixes was correct.
Every one shipped. The area kept producing bugs because the model of the
transport was wrong — it was treated as one pipe when it is two independent
directions with different visibility rules. One re-grounding pass would have
found that faster than four correct patches did.

## Unrelated Finds: Park, Do Not Chase

Debugging turns up bugs that have nothing to do with the one you are on. Every
one of them is a chance to lose the thread. **Park it and stay on task.**

**First decide which kind of find it is**, because the two go to different
places and this decision is what makes the breaker work:

- **Related** — the same subsystem, boundary, or data path as the bug you are
  chasing, or something that could plausibly share a root cause with it. This
  **counts toward the three** in the circuit breaker.
- **Unrelated** — a different subsystem with no plausible shared root cause;
  you found it only because you happened to be looking nearby. This gets
  parked, and does **not** count toward the breaker.

If you genuinely cannot tell, **treat it as related**. That fails toward
stopping and re-grounding, which is the safe direction, and re-grounding is
exactly what reveals whether the two share a cause.

**How to park.** File it as a `REQ-*` with `prd_file_request`, **linked to the
episode** you are in, and say in one line what you saw and where. Then go back
to what you were doing. Do not fix it, do not "just quickly" look into it, and
do not widen the current change to cover it.

A record, not a note to yourself: a scratchpad line dies with the session,
while a `REQ-*` survives it, shows up in `prd_status` and the plugin UI as
open work, and can be picked up by whoever gets there first. That is what makes
"come back to it" real rather than an intention.

**Before you close** the debugging episode — before the completion claim, the
evidence record, or the report — list what you parked during it and confirm
each one is filed and visible. Anything genuinely trivial and inside the
current scope can be done now; everything else stays a `REQ-*`. What must not
happen is a parked finding quietly disappearing because the episode ended.

**Parking is not a way to avoid the breaker.** Labelling a related bug
"unrelated" to keep patching is the failure this whole section exists to
prevent — three related finds trip the breaker no matter how they were filed.

This is the same rule the **Bounded** dimension of CRAFTESB states for any
change: adjacent problems get filed as `REQ-*`, not built into the work in
hand. Here it is applied to the specific moment when it is hardest to follow —
mid-debug, with the fix apparently one edit away.

## Request And Health Hooks

- Clear low/medium-risk bug `REQ-*` records with bounded fixes should not stop
  at `in_review`.
- Persistent bugs append a new `MSG-*` to the existing request thread rather
  than creating duplicate `REQ-*` records.
- If root cause is broader than the reported bug, create or update `HLT-*`.
- If the fix changes behavior, update `CHG-*` and release metadata when the
  plugin package is affected.
- Completion claims require `EV-*` or equivalent fresh verification output.
- When the breaker fires, record what the re-grounding changed in your
  understanding — that belongs in the wiki (`project-llm-wiki`), because the
  next agent debugging this area needs the corrected model, not the patches.

## Staleness Coverage

Apply the shared policy in `.prd_plugin/method/staleness-rules.md`.


Before fixing, check whether the relevant `REQ-*`, `MSG-*`, `HLT-*`, `TRK-*`,
affected version, or reproduction evidence is stale. Refresh the report with a
new reproduction, mark it `needs_info`, supersede it, or carry the stale risk
forward before deciding the root cause is still current.

## Red Flags

- Guessing from symptoms.
- Proposing a fix before a root-cause hypothesis.
- Trying several fixes without a new hypothesis.
- **Fixing the third bug in a row without stopping to re-ground.**
- **Thinking "one more small fix and it'll be right"** — that thought is the
  breaker's trigger, said out loud.
- Treating green tests after each patch as evidence the model is right; they
  only prove that patch.
- Re-planning by listing the next symptom instead of naming the bug class.
- Chasing an unrelated bug found mid-debug instead of parking it.
- Labelling a related bug "unrelated" so the breaker never trips.
- Ending an episode with parked findings that were never filed.
- Calling local request state an upstream report.
- Marking a bug implemented without `graduated_to` links and verification.
