| name | cf-sys-debug |
| description | Systematic 4-phase debugging — root cause investigation, hypothesis testing, regression-guarded fix, mandatory bug doc. Auto-invoke when a bug is non-trivial or a previous fix didn't stick — signals: bug recurs ("still broken", "same error again", "came back"), flaky / intermittent / race / timing, "used to work, now broken", regression after update or dependency bump, hard-to-reproduce, works locally but fails in CI (or vice versa), multi-stage pipeline failure, stack trace into a library, user already tried fixes that didn't help, or user asks to "find the root cause", "investigate", "diagnose", "why is this happening", "trace this", "bisect", "deep dive". Also load BEFORE touching code whenever the cause is unclear and a hypothesis must be stated first. Prefer over cf-fix for hard, recurring, or investigation-heavy bugs (cf-fix may delegate here). Do NOT auto-invoke for trivial typos, one-line fixes, or config errors with an obvious cause.
|
| created | "2026-02-17T00:00:00.000Z" |
| updated | "2026-07-04T00:00:00.000Z" |
Systematic Debugging
CLI Requirement: OPTIONAL — Uses the memory MCP from coding-friend-cli for fast indexed search and storage. Without the CLI: falls back to grep over docs/memory/ and direct file writes. Full functionality preserved, slower memory recall. See CLI requirements.
Custom Guide
Custom guide — auto-loaded below (if the raw command shows instead of its output, run it yourself):
bash "${PLUGIN_ROOT}/lib/load-custom-guide.sh" cf-sys-debug
If output is not empty, integrate returned sections: ## Before → before first step, ## Rules → apply throughout, ## After → after final step.
Core Constraint
Do not touch code until you can state the root cause in one sentence:
"I believe the root cause is [X] because [evidence]."
Name a specific file, function, and line. "A state management issue" is not testable. "Stale cache in useUser at src/hooks/user.ts:42 because the dependency array is missing userId" is testable. If you cannot be that specific, you do not have a hypothesis yet.
Same symptom after a fix = hard stop. Both a recurrence and "let me just try this" mean the hypothesis is unfinished. Re-read the execution path from scratch before touching code again.
After 3 failed hypotheses, stop. Use the Handoff Format below to surface what was checked, ruled out, and unknown. Ask how to proceed.
Rationalization Watch
When these surface, stop and re-examine:
| Thought | What it means | Rule |
|---|
| "I'll just try this one thing" | No hypothesis, random-walking | Stop. Write the hypothesis first. |
| "I'm confident it's X" | Confidence is not evidence | Run an instrument that proves it. |
| "Probably the same issue as before" | Treating a new symptom as a known pattern | Re-read the execution path from scratch. |
| "It works on my machine" | Environment difference IS the bug | Enumerate every env difference before dismissing. |
| "One more restart should fix it" | Avoiding the error message | Read the last error verbatim. Never restart more than twice without new evidence. |
Progress Signals
When these appear, the diagnosis is moving in the right direction:
| Thought | What it means | Next step |
|---|
| "This log line matches the hypothesis" | Positive evidence found | Find one more independent piece of evidence to cross-validate |
| "I can predict what the next error will be" | Mental model is forming | Run the prediction; if it matches, the model is correct |
| "Root cause is in A but symptoms appear in B" | Propagation path understood | Trace the call chain from A to B and confirm each link |
| "I can write a test that would fail on the old code" | Hypothesis is specific and testable | Write the test before applying the fix |
Do not claim progress without observable evidence matching at least one of these signals.
4-Phase Process + Documentation
Phase 1: Root Cause Investigation
1a. Check existing bug docs (memory recall):
Before investigating, search for related past bugs. Extract 2-3 keywords from the bug description.
Primary — Memory MCP (if memory_search tool is available):
Call memory_search with: { "query": "<bug keywords>", "type": "episode", "limit": 3 }
Fallback — grep (if memory MCP unavailable):
Check {docsDir} from .coding-friend/config.json (default: docs).
- Grep
^description: lines across {docsDir}/memory/bugs/**/*.md — match against bug keywords
- If no match, grep
^tags: lines across {docsDir}/memory/bugs/**/*.md
If matches found, read the top 1-2 matched files — they may reveal known root causes or patterns that save investigation time.
1b. Investigate:
- Read the actual error. Do not guess. Read the full stack trace, error message, and logs.
- Reproduce the bug. Write a test or command that triggers the failure reliably.
- Trace backward. Follow the call chain from the error to its origin. The bug is usually NOT where the error appears.
Phase 2: Pattern Analysis
- When did it start? Check recent changes:
git log --oneline -20, git diff HEAD~5
- Is it consistent? Does it fail every time, or intermittently? Intermittent = timing/state issue.
- What's the minimal reproduction? Strip away everything unrelated until you have the smallest case.
- Pay attention to deflection. When someone says "that part doesn't matter," treat it as a signal. The area someone avoids examining is often where the problem lives.
Bisect Mode
Activate when the symptom is "used to work, now broken" or "broke after an update". Random-walking forward from the current state wastes context and produces random fixes.
Flow:
- Find
last-known-good using the most recent tag where the behavior was correct: git tag --sort=-version:refname | head -5. Do not use a date or raw SHA as the anchor.
- Define a pass/fail test command before starting. It must be runnable non-interactively and produce an unambiguous exit code. Write it once; reuse it at every step.
- Run
git bisect start, git bisect bad (current), git bisect good <tag>. Let bisect drive; do not jump ahead.
- Context conservation: do not re-read large files at each step. Read once, note the key function or line, reference from notes.
- When bisect names the culprit commit: read only that commit's diff, not surrounding history. Identify the specific line that introduced the regression.
Phase 3: Hypothesis Testing
- Form one hypothesis using the template: "The bug is caused by [X] because [evidence]." Name file and line.
- Add one targeted instrument: a log line, a failing assertion, or the smallest test that would fail if the hypothesis is correct. Run it.
- If the evidence contradicts the hypothesis, discard it completely. Re-orient with what was just learned. Do not preserve a hypothesis the evidence disproves.
- External tool failure: diagnose before switching. When an MCP tool or API fails, determine why first (server running? API key valid? config correct?) before trying an alternative.
- Stack trace points into a library? Walk back 3 frames into your own code. The bug is almost always there, not in the dependency.
Phase 4: Implementation
- Fix the root cause, not the symptom. If the fix touches more than 5 files, pause and confirm scope with the user.
- Regression Guard — for any bug that recurred or was previously "fixed", the fix is not done until:
- A regression test exists that fails on the unfixed code and passes on the fixed code
- The test lives in the project's test suite, not a temporary file
- The commit message states why the bug recurred and why this fix prevents it
- Write a regression test that would have caught this bug
- Run the full test suite — your fix must not break anything else
- Verify the original error is gone — reproduce the original failure and confirm it's fixed
Capturing out-of-scope side-effects
While investigating or fixing, if you uncover a problem unrelated to the root cause under investigation that is non-trivial (addressing it inline would derail this fix), do NOT fix it now. Record it for later, then continue:
bash "${PLUGIN_ROOT}/lib/capture-later.sh" \
--name "<short title>" --description "<what & where — enough to act on cold>" \
--source cf-sys-debug [--slug <bug-doc/task slug, if one exists>] [--problem "<the bug under investigation>"]
This writes <docsDir>/later/YYYY-MM-DD-<name>.md with frontmatter (slug, problem, conversation_id). This is an in-repo audit trail, independent of the spawn_task tool.
Phase 5: Document the Bug
cf-sys-debug is only invoked for hard bugs — always document the findings.
- Read
language config (local .coding-friend/config.json overrides global, default: en)
- Use
MAIN_REPO_ROOT from the SessionStart bootstrap context (injected via session-init.sh). If absent, fall back to running pwd for $CWD and use $CWD as MAIN_REPO_ROOT. Read config from CF_CONFIG_FILE (= $MAIN_REPO_ROOT/.coding-friend/config.json) for docsDir (default: docs) — do NOT search sub-folders. Use CF_DOCS_ROOT as the docs base dir.
- Construct a write spec and delegate to cf-writer agent by spawning the
cf-writer custom agent (use absolute file_path):
WRITE SPEC
----------
task: create
file_path: {CF_DOCS_ROOT}/memory/bugs/YYYY-MM-DD-{name}.md
language: {language from config}
content: |
---
title: "<Short bug title>"
description: "<One-line summary of the bug and fix, under 100 chars>"
tags: [tag1, tag2, tag3]
created: YYYY-MM-DD
updated: YYYY-MM-DD
type: episode
importance: 4
source: conversation
---
# <Bug Title>
## Overview
<What went wrong — symptom and why it was hard to diagnose>
## Investigation
<What was tried and ruled out — hypotheses that didn't pan out>
## Root Cause
<What was actually wrong — the real cause>
## Fix
<What was changed to fix it>
## Prevention
<How to avoid this bug in the future>
## Related Files
- `path/to/file1`
- `path/to/file2`
readme_update: false
auto_commit: false
existing_file_action: skip
Backward compat: Existing bug memory files without a date prefix are still readable — do not rename them.
Frontmatter rules:
description: factual summary for grep recall. Good: "Circular dependency in plugin loader causing silent init failure". Bad: "Hard bug fixed".
tags: include error type, affected module, root cause category (e.g., [circular-dependency, plugin-loader, initialization])
Index in CF Memory (MANDATORY)
This step is REQUIRED — do NOT skip it.
After the cf-writer saves the bug doc, you MUST call the memory_store MCP tool to index it in the database. This is a separate action from writing the file — the cf-writer agent does NOT do this.
Call memory_store with:
title: from the frontmatter title
description: from the frontmatter description
type: episode
tags: from the frontmatter tags
content: the full markdown content (including frontmatter)
importance: 4
source: "auto-capture"
index_only: true
If the MCP tool is unavailable, log a warning to the user but do NOT fail silently.
Show the user a 2-line summary:
- Markdown file:
{docsDir}/memory/bugs/...md (created or updated)
- Memory DB: indexed ✓ — or: MCP unavailable, file only
Gotchas
| What happened | Rule |
|---|
| Patched symptom file instead of origin | Trace the execution path backward before touching any file |
| MCP not loading, switched tools instead of diagnosing | Check server status, API key, config before switching |
| Orchestrator said RUNNING but a downstream stage was misconfigured | In multi-stage pipelines, test each stage in isolation |
| Race condition diagnosed as stale-state bug | For timing-sensitive issues, inspect event timestamps and ordering before state |
| Reproduced locally but failed in CI | Align the environment first (runtime version, env vars, timezone), then chase the code |
| Stack trace points deep into a library | Walk back 3 frames into your own code; the bug is almost always there |
Adding a try/catch to suppress the error | Hides the bug, doesn't fix it — find and fix the root cause |
| Multiple changes at once | Can't tell which one fixed it — one change at a time, test after each |
Debugging Tools
git bisect — Find the exact commit that introduced a bug
git stash — Isolate your changes to test clean state
- Print/log tracing — Add strategic logging at decision points
- Minimal reproduction — Smallest possible code that triggers the bug
Outcome
Success Format
Root cause: [what was wrong, file:line]
Fix: [what changed, file:line]
Confirmed: [evidence or test that proves the fix]
Tests: [pass/fail count, regression test location]
Regression guard: [test file:line] or [none, reason]
Status: resolved, resolved with caveats (state them), or blocked (state what is unknown).
Handoff Format (after 3 failed hypotheses)
Symptom:
[Original error description, one sentence]
Hypotheses Tested:
1. [Hypothesis 1] → [Test method] → [Result: ruled out because...]
2. [Hypothesis 2] → [Test method] → [Result: ruled out because...]
3. [Hypothesis 3] → [Test method] → [Result: ruled out because...]
Evidence Collected:
- [Log snippets / stack traces / file content]
- [Reproduction steps]
- [Environment info: versions, config, runtime]
Ruled Out:
- [Root causes that have been eliminated]
Unknowns:
- [What is still unclear]
- [What information is missing]
Suggested Next Steps:
1. [Next investigation direction]
2. [External tools or permissions that may be needed]
3. [Additional context the user should provide]
Status: blocked
Review Reminder
After the fix is verified, ask the user if they want to run $cf-review or $cf-commit. Do NOT auto-run — wait for their choice.