ce-debug
Diagnosis loop for bugs and failing behavior. Use when asked to debug or fix failing or slow behavior.
By everyinc · 2,993 installs
npx skills add everyinc/compound-engineering-plugin --skill ce-debug
Source repository · Upstream listing
Debug and Fix
Find the root cause of a failure, then — when the user chooses to — fix it with test first discipline.
Done when: the causal chain from trigger to symptom is stated with no gaps and file:line evidence, and either a verified fix has been handed off (PR, commit, or the user's chosen stop) or a diagnosis only summary has been delivered. Escalate rather than persist: 2 3 hypotheses exhausted without confirmation, or 3 failed fix attempts, means diagnose why instead of trying again — that is the smart escalation references/investigate.md describes. One hypothesis, one change at a time; changing several to see what helps is shotgun debugging.
<bug description is whatever this skill was invoked with — a failure description, a mode: token, or an issue reference ( 123 , org/repo 123 , an issue URL) — from the user or from a calling skill ( ce babysit pr / lfg in mode:pipeline pass the failing jobs and log tails; lfg 's defect route passes mode:return to caller and the user's own reference). Blank if nothing was provided.
Mode
Default is interactive : investigate, run the Phase 2 fix choice gate, then the Phase 4 handoff.
mode:pipeline (set by an orchestrator such as ce babysit pr or lfg ): run fully non interactively and never call the blocking question tool. Strip the token from <bug description , then read references/pipeline mode.md and follow it — it overrides every "ask the user" point with a conservative default, replaces the Phase 2 fix gate with "fix convergent bugs, defer divergent ones", and replaces the Phase 4 handoff with a structured return whose status is exactly one of fixed and pushed fixed not pushed diagnosed no fix flaky infra needs human . The caller branches on those exact spellings, so never rename, abbreviate, or add to them.
mode:return to caller (set by lfg on its defect route): run non interactively, but the caller owns everything after the fix. Strip the token, then read references/return to caller.md and follow it — it keeps Phases 0 3 and the convergent or defer fix boundary, applies the Phase 3 branch rule so the fix lands on a feature branch, commits the fix owned files without pushing, skips the post fix polish and review steps, and returns a structured result whose status is exactly one of fixed diagnosed no fix needs human blocked . The same spelling rule applies.
Blocking questions
Wherever this skill asks the user something, use the host's blocking question tool already in the current tool list (match by capability, not by a host specific name). Presence in the current tool list is proof the tool exists; never call a user facing question tool to discover whether it exists. If a matching tool is listed but unloaded, use the host's tool discovery primitive to load that capability — do not search for another host's tool name. Fall back to numbered options on the host's chat surface only when no such tool is in the list or a real question call errors. Never silently skip the question, and never end a phase without a response.
Secrets in evidence
Debugging surfaces raw output constantly — command results, captured payloads, log excerpts — and the harness may render a command's output the moment it runs, so check secrets when you construct the command, not afterward. Keep credentials in env vars rather than on the command line; when a command's output may carry a secret (verbose HTTP traces, dumped headers, config or environment prints), capture it to a file and surface only sanitized excerpts, writing <REDACTED in place of each secret. No secret (credential, token, auth header, connection string) appears in anything shown, written, or committed. If sanitizing removes what the diagnosis needs, say so and ask the user rather than un redacting.
Artifact Root
Resolve <root only when you first compose a <root / path — a run that composes none skips this entirely.
<! ce docs root:start
Resolve the CE artifact root <root before composing any artifact path.
Read docs root from <repo root /.compound engineering/config.yaml only ( <repo root = git rev parse show toplevel ). Do not read it from config.local.yaml . Unset <root is docs , exactly as before.
Validate a set value: a repo relative directory whose real, symlink resolved path stays inside the repo and is neither the repo root nor under .git/ . Otherwise stop with an error naming docs root and the value never fall back to docs .
Use <root as the sole artifact location: create it if absent, compose each path as <root /<subdir with this skill's own subdirectory, and never also read docs .
<! ce docs root:end
Execution Flow
Five phases in order: 0 Triage 1 Investigate 2 Root Cause 3 Fix 4 Handoff. Beyond Phase 0's trivial bug fast path there is no skipping and no complexity tiers. A hard bug spends longer in each phase; it does not enter fewer.
Read references/investigate.md now and follow it for Phases 0 2 — issue fetching, reproduction, environment sanity and the dirty tree stash experiment, backward tracing, the tracker/PR history search, hypothesis grounding, and the escalation table. Only the gates below are stated here.
The issue of record. If the user handed you a ticket or issue, that is where this bug already lives, whichever system it is in; a Sentry issue counts as much as a Linear ticket. Carry its identifier and URL through to Phase 4. If the input is only a stack trace, test path, or description, this run has no issue of record . That is an ordinary state, not a gap to fill: ship the fix without one, never open a ticket to manufacture a record, and never ask the user whether to. Phase 1's tracker search reads prior work and never establishes a new home for the bug . An existing ticket for this bug is one to link in Phase 4, never one to create.
The trivial bug fast path (cause readable from the input, one line fix, no deep tracing) still runs Phase 2's fix choice gate before editing: it saves investigation ceremony, not the user's choice over whether to apply a fix.
Choosing the regression test. The regression test for a confirmed defect belongs wherever existing coverage already owns that behavior: start from the tests that exist rather than from a new file. Before recommending a test in Phase 2, inspect the existing tests and follow the project's testing rules; references/fix.md holds the test homes and the naming rule for Phase 3. A test that fails because the change deliberately reverses the behavior it asserts does not have a wrong expectation — that is the divergent case below, deferred rather than updated.
Phase 2 gate: present, then ask
Causal chain gate: do not proceed to Phase 3 until you can explain the full chain — trigger through every step to the observed symptom — with no gaps. "Somehow X leads to Y" is a gap. Only the user can authorize proceeding on a best available hypothesis when investigation is stuck.
Repair selection: after confirming the cause and before recommending a fix, read references/bakeoff.md to decide whether the repair needs competing approaches.
Once the root cause is confirmed, write the findings as a user visible block: the causal chain with file:line references; the proposed fix and the files it changes; which tests to use, add, modify, or strengthen, and whether existing tests should have caught this; and any related ticket or PR and how it shapes the recommendation — if an open PR already fixes this, lead with that link instead of a fresh fix.
Same turn presentation before the gate: do not open the fix choice question until that findings block has been written in full — in this turn or the immediately preceding assistant message. The blocking question tool renders only its own stem on modal harnesses, so a question fired on "root cause confirmed" alone leaves the user choosing with none of the causal chain in front of them. Naming the options is not presenting the findings, and a promise to explain after the choice is too late.
When the request has not already authorized the next action, ask (per Blocking questions ) which path to take. An explicit fix request is Phase 3. An explicit diagnosis only request skips to Phase 4. mode:pipeline and mode:return to caller never ask. The test recommendations are part of the diagnosis either way. Offer only the options the findings support, and recommend one:
1. Fix it now — proceed to Phase 3
2. Diagnosis only — I'll take it from here — skip the fix, write Phase 4's summary, end the skill
3. Rethink the design ( ce brainstorm ) — only when the bug cannot be fixed within the current design: the root cause is a wrong responsibility or interface rather than wrong logic, the requirements themselves are wrong, or every candidate fix is a workaround around an assumption that no longer holds. Size alone is not a design problem. Recommend it over a fix when no fix within the current design corrects the mechanism that produced the bug, only this occurrence of it.
When the findings leave nothing for this run to fix (the behavior is correct, the bug is already fixed, or an open PR already does the work), offer the next step they support in place of a fix, such as replying on the issue of record or finishing that PR. Replying on the issue, closing it, or changing an existing PR waits for the user's choice.
mode:pipeline and mode:return to caller : do not ask. Proceed to Phase 3 and apply a convergent fix; a divergent fix — one that would reverse a deliberate contract/behavior/product decision, including a "failing" test that asserts intended behavior — is deferred, not applied, per references/pipeline mode.md or references/return to caller.md . Never route to ce brainstorm here; a design problem becomes a needs human residual.
Phase 3: Fix
If the user chose "Diagnosis only," skip to Phase 4's summary. If they chose "Rethink the design," control has transferred to ce brainstorm and this skill ends.
Read references/fix.md before editing any file — the test first sequence, the failed fix rule, the check of other places the fix affects, and the post mortem trigger. Two rules decide whether the fix may start at all, so they stay here:
Branch. Check git status ; if the user has unstaged work in files that need modification, confirm before editing. If the current branch is the default branch, create a feature branch without asking — derive a name from the bug, git checkout b <name , and say which branch you moved to. Detect the default by comparing against main , master , or git rev parse abbrev ref origin/HEAD with its origin/ prefix stripped — the raw output is origin/<name , so an unstripped comparison never matches.
Record the pre fix scope: current HEAD , whether git status short is clean, and any pre existing changed files. Then keep a list of fix owned files (the tests and implementation changed for this bug) as you work. Phase 4 answers both of its questions from this record and cannot reconstruct it afterwards.
Phase 4: Handoff
mode:pipeline — skip this entire interactive handoff. No polish or review steps, no residual questions, no preview, no learning capture offer. Commit and push the convergent fix per references/pipeline mode.md , then emit that reference's structured return . Divergent / needs human items are deferred there (open thread or the caller's run report comment — never a PR body section). mode:return to caller — skip it too: commit the fix owned files on the feature branch, push nothing, and emit the structured return references/return to caller.md defines. In both modes the return is the last thing this skill writes. It ends this skill, not the turn. The caller runs in this same session, and its next step follows the return; do not announce a handoff to it. The rest of this section is the interactive path only.
Structure