refactor(plugin)!: ship as a Claude Code plugin, one dir link per harness
Skills move to skills/<name>/SKILL.md and the repo gains both manifests, so it is now a marketplace hosting exactly one plugin whose root is the repo root (source: "."). Claude Code installs it from git; no symlinks involved. Why: install.sh wired the library into every consuming repo as an ABSOLUTE symlink to $CHECKOUT_DIR. The checkout moved three times, most recently when local-dev@1966d5a dropped the embedded .skills/ copy, and nothing migrated the links — 243 dangling links across 12 repos, hidden from git by the **/.claude/skills/ ignore rule. Worse, ~/dev spans two path roots (/Users/mathias on the iCloud-shared Macs, /home/mathias on koala), so an absolute link stored there is broken on one side by construction. Re-running the installer cannot fix that; only removing the mechanism can. install.sh keeps the harnesses with no plugin system and drops from 42 links per harness to one directory link, via the convergent ~/.agents/skills convention that Pi and OpenCode both read: ~/.agents/skills -> $CHECKOUT_DIR/skills ~/.config/crush/skills -> ~/.agents/skills ~/.config/opencode/skills -> ~/.agents/skills wire_claude_repo is deleted outright rather than gated behind a flag — on a mixed-root fleet it cannot be made correct. Antigravity and Vibe targets go too; both unused. CHECKOUT_DIR keeps its host-local default deliberately: pointing it into ~/dev would put a git working tree inside iCloud with two concurrent writers. Taskfile drops five near-identical wiring loops (they had already drifted) and delegates to install.sh. `task check` gains frontmatter validation, a stray-root-entry gate, and manifest/tag version parity — the loose secret-hygiene.md sat unloadable for months because nothing checked layout. Verified: claude plugin validate . passes clean; a probe skill loaded as skills:zzprobe through --plugin-dir; task check green at 42 skills. Refs: #23 Bump-Type: minor Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BeAp5LTscnz2W7Ubt4eJ6m
This commit is contained in:
@@ -0,0 +1,146 @@
|
||||
---
|
||||
name: debug
|
||||
description: Systematic hypothesis-first debugging. Generate 3-5 ranked hypotheses with verification steps before suggesting any fix. Use when encountering a bug, test failure, or unexpected behavior.
|
||||
---
|
||||
|
||||
# Debug
|
||||
|
||||
## Overview
|
||||
|
||||
Debugging is hypothesis generation, not fix generation. The first instinct — "try this and see if it works" — wastes time and teaches nothing. A disciplined debug session produces a small set of ranked, falsifiable hypotheses, each with a concrete verification command. The fix comes after one hypothesis is confirmed, not before.
|
||||
|
||||
**Core principle:** A hypothesis you cannot verify in one command is not a hypothesis — it is a guess.
|
||||
|
||||
## When to Use
|
||||
|
||||
- Any test failure whose cause is not immediately obvious
|
||||
- Any production error or unexpected behavior
|
||||
- Any bug report from a user or stakeholder
|
||||
- Before reaching for a debugger or adding print statements: form hypotheses first
|
||||
|
||||
**Do not use for:**
|
||||
- Compile errors with clear messages (just fix the typo)
|
||||
- Already-diagnosed bugs where you know the cause (go straight to TDD with a regression test)
|
||||
|
||||
## Iron Laws
|
||||
|
||||
1. **Never suggest "try X and see what happens."** Every hypothesis must have a specific expected outcome if correct.
|
||||
2. **Generate 3-5 hypotheses, ordered by likelihood (most likely first).** Fewer than 3 means you stopped thinking; more than 5 means you are not prioritizing.
|
||||
3. **Diagnose only — do not fix in this skill.** The fix happens in a separate TDD cycle (load `tdd` skill) once a hypothesis is confirmed.
|
||||
|
||||
## Process
|
||||
|
||||
### Step 1: Read the failure
|
||||
|
||||
Before forming hypotheses:
|
||||
- Read the full error message and stack trace, not just the headline
|
||||
- Read the file where the failure originated, around the failing line
|
||||
- If the failure is from a test, read the test and the code under test
|
||||
- Identify the **failure mode** — what actually went wrong (e.g. "nil pointer dereference in goroutine spawned by handler") not just what the error says ("runtime error")
|
||||
|
||||
### Step 2: Generate hypotheses
|
||||
|
||||
For each hypothesis, capture three things:
|
||||
|
||||
- **Mechanism:** what specific code path or state would produce this exact failure
|
||||
- **Verification:** the single command or file inspection that confirms or denies it
|
||||
- **Expected outcome if correct:** the specific output you would see
|
||||
|
||||
Order by likelihood. The most likely cause is hypothesis 1.
|
||||
|
||||
### Step 3: Output
|
||||
|
||||
Use this format:
|
||||
|
||||
```
|
||||
HYPOTHESIS 1 (likelihood: high): <mechanism in one sentence>
|
||||
VERIFY: <exact command or file:line to inspect>
|
||||
EXPECTED IF CORRECT: <specific output, value, or condition>
|
||||
|
||||
HYPOTHESIS 2 (likelihood: medium): <mechanism>
|
||||
VERIFY: <exact command>
|
||||
EXPECTED IF CORRECT: <specific output>
|
||||
|
||||
[... up to 5 ...]
|
||||
|
||||
RECOMMENDED NEXT STEP: Run VERIFY for hypothesis 1 first.
|
||||
```
|
||||
|
||||
End with the recommendation, not a fix.
|
||||
|
||||
## Worked Example
|
||||
|
||||
**Failure:**
|
||||
```
|
||||
--- FAIL: TestInvoiceParser_HandlesEmptyPDF (0.00s)
|
||||
parser_test.go:47: panic: runtime error: index out of range [0] with length 0
|
||||
```
|
||||
|
||||
**Output:**
|
||||
|
||||
```
|
||||
HYPOTHESIS 1 (likelihood: high): parser indexes into pages[0] before checking len(pages) > 0; empty PDFs produce a zero-page document
|
||||
VERIFY: rg -n 'pages\[0\]' internal/parser/
|
||||
EXPECTED IF CORRECT: at least one site reads pages[0] without a preceding length check
|
||||
|
||||
HYPOTHESIS 2 (likelihood: medium): the test fixture is a zero-byte file rather than a valid empty PDF; pdf library returns nil pages slice instead of empty
|
||||
VERIFY: ls -la testdata/empty.pdf && file testdata/empty.pdf
|
||||
EXPECTED IF CORRECT: file size 0 bytes or "data" rather than "PDF document"
|
||||
|
||||
HYPOTHESIS 3 (likelihood: low): a recently changed dependency reordered the page-extraction API; pages[0] now refers to metadata, not content
|
||||
VERIFY: git log --oneline -10 -- go.sum | grep -i pdf
|
||||
EXPECTED IF CORRECT: a pdf-library bump in the last few commits
|
||||
|
||||
RECOMMENDED NEXT STEP: Run VERIFY for hypothesis 1 first.
|
||||
```
|
||||
|
||||
## Anti-Patterns
|
||||
|
||||
| Anti-Pattern | Why It Fails |
|
||||
|---|---|
|
||||
| "Maybe try restarting the service" | Not a mechanism. Not falsifiable. Teaches nothing if it works. |
|
||||
| "Could be a race condition" | Mechanism without specifics. Which two operations race? On what state? |
|
||||
| "Let me add some print statements and see" | Skips hypothesis generation. Generates noise, not understanding. |
|
||||
| Single hypothesis presented as fact | If you are sure, write the regression test, do not run a debug skill. |
|
||||
| Mixing hypotheses with fix suggestions | The skill is diagnose-only. The fix is a TDD task on its hypothesis. |
|
||||
|
||||
## Brain MCP Integration
|
||||
|
||||
The brain holds prior debug sessions across the project. Use it to skip rediscovering known failure modes.
|
||||
|
||||
**At debug start:**
|
||||
- Run `brain_query` with the error message snippet + the package name. Past sessions may have logged identical or similar failures with their resolved hypotheses.
|
||||
|
||||
**After a hypothesis is confirmed:**
|
||||
- Run `brain_write` with the failure signature → confirmed mechanism. Future debug sessions on the same area get the answer immediately.
|
||||
|
||||
**Never:**
|
||||
- Run `brain_write` for an unconfirmed hypothesis. Speculation in the brain pollutes future queries.
|
||||
|
||||
### Logging
|
||||
|
||||
Call `session_log` once at the end of every phase to record the outcome.
|
||||
Pass-rate is computed downstream by the `/pass-rate` HTTP endpoint, which
|
||||
treats `pass` as success, `fail` as failure, `skip` as neither.
|
||||
|
||||
**At end of each phase:**
|
||||
- `session_log` with `{skill: "debug", phase: "<phase-name>", final_status: "pass" | "fail" | "skip", message: "<one-line summary>", duration_ms: <wall-clock>, project_root: "<absolute path>"}`
|
||||
|
||||
**Phases for this skill:** read-failure, generate-hypotheses, output
|
||||
|
||||
**Status semantics:**
|
||||
- `pass` — the phase's intended outcome was reached.
|
||||
- `fail` — the phase's intended outcome was NOT reached.
|
||||
- `skip` — phase was skipped intentionally.
|
||||
|
||||
**Why this matters:** the routing pod (Plan 6) reads pass-rate to decide whether to route a future call to a local model. If your skill never logs, the routing pod sees no data.
|
||||
|
||||
## Mode 2 Routing Note
|
||||
|
||||
This skill produces high-volume mechanical output (hypothesis enumeration) and is a candidate for Mode 2 routing to a local model in the future. Until Plan 6 ships the routing pod, treat as Mode 1 only. The hypothesis format and discipline are identical regardless of which model generates them.
|
||||
|
||||
## Cross-References
|
||||
|
||||
- After a hypothesis is confirmed, load `tdd` skill — write a failing regression test that proves the bug exists, then fix it.
|
||||
- For test failures specifically caused by mock-vs-real divergence, also load `tdd/references/testing-anti-patterns.md`.
|
||||
- Load `code-review` skill if the diagnosis surfaces a structural issue (god object, shotgun surgery) rather than a pointwise bug.
|
||||
Reference in New Issue
Block a user