---
description: Investigate a bug, error message, or unexpected behavior. Leads with runtime evidence from the launch-beacon monitor ("what was happening when it broke") when a session exists, then traces the code with launch-chart (structural + variable queries), falling back to grep/Read only when the chart can't answer. Fuses runtime + static signals into a ranked diagnosis. Files a feedback comment when a chart gap forces the fallback. Does NOT edit code.
---

# /kit:debug

A guided investigation workflow. It works two evidence sources, in order:

1. **Runtime** — the launch-beacon monitor's captured events (the error itself, plus the clicks / fetches / route changes leading up to it). This is what *actually happened* in the running app. Beacon was built for exactly this.
2. **Static** — launch-chart structural + variable queries to find *where the code is* and trace the dependency chain toward the cause.

It defaults to beacon-first when there's a live session and the symptom is runtime-shaped, then chart, and only reaches for grep/Read when the chart can't answer — filing a one-line gap report in LS when that happens.

Parse `$ARGUMENTS`:
- **symptom** (required) — the error message, broken behavior, or question to investigate. Quoted is fine: `/kit:debug "tags don't render on the work-item drawer"`.
- **--start=<node>** — start the static investigation from a specific file/function/table id (skip the chart search step). Does not skip beacon.
- **--layer=<id>** — scope chart queries to one layer (ui / api / db / static).
- **--no-grep-fallback** — refuse the grep fallback entirely (strict MCP-only).
- **--no-beacon** — skip the runtime-evidence step; go straight to static chart analysis. Use for pure structural questions, or when no monitor is running and you don't want the check.
- **--worktree=<slug>** / **--project_root=<path>** — forwarded to every beacon MCP call (orbit worktree slug / explicit project root), same semantics as `/kit:beacon-pulse`.

Examples:
- `/kit:debug tags don't render on the work-item drawer`
- `/kit:debug "TypeError: Cannot read property 'status' of undefined" --layer=ui`
- `/kit:debug --start=src/server/comms/build-feed.ts`
- `/kit:debug "checkout button does nothing" --worktree=beacon_rewrite`

## Investigation loop

Run Step 0, then the static loop (Steps 1–4), until the user has enough to act, OR a fallback was filed (then surface it).

### Step 0 — runtime evidence (launch-beacon)

Skip this step if `--no-beacon` was passed, or if the symptom is a purely structural question with no runtime component ("where is X defined", "what depends on Y", "how is auth wired"). Otherwise — for any symptom that describes something *happening* (an error message, a broken interaction, a wrong result in the running app) — start here:

1. Call `mcp__local-launch-beacon__beacon_failures` with `limit: 5` (plus `worktree` / `project_root` if supplied). The most recent session is targeted implicitly. A "failure" is broader than `kind=error`: window errors, unhandled rejections, fetch/xhr ≥ 400 or thrown, and clicks where overlay interception blocked the intended target.
2. **Match the symptom to a failure.** Pick the failure whose message/route best matches the symptom. If several plausibly match, surface the top 2–3 (with `seq`, `kind`, `ts`, message) and ask which — or take the most recent.
3. **Pull the lead-up.** Call `mcp__local-launch-beacon__beacon_correlate` with `seq: <failure.seq>`, `before: 10`, `after: 0` (forward the same root args). This is the chain of events that produced the failure — the click that set bad state, the fetch that 500'd, the route change that mounted the wrong component. If a context event's summary is too thin (truncated stack, missing response body, selector chain), call `mcp__local-launch-beacon__beacon_event` with that `seq` for the full JSON — only when needed.
4. **Form a runtime hypothesis** and carry it into the static phase. Example: *"beacon shows `GET /api/work-items/42/tags` returned 500 two events before the render error — so the empty-tags symptom is likely server-side, not the drawer component."* This tells the chart phase **where to start** (the failing endpoint), turning a blind structural search into a targeted one.

**Graceful degradation (beacon is additive, never a hard dependency):**
- `{error: "no session found"}` or empty failures → say so in one line (`"No beacon monitor session found — proceeding with static analysis only. (Start one with: npx launch-beacon monitor)"`) and go to Step 1. Do NOT treat this as a failure of the debug run.
- Before giving up: if the user passed no `--worktree`/`--project_root` and `launch-orbit` has worktrees for this repo, suggest re-running with `--worktree=<slug>` (the session NDJSON may live in a worktree outside the MCP's watch root).
- If the beacon MCP isn't wired at all, note it once and continue static-only.
- For a deeper runtime drill (more preceding events, a different failure), point the user at `/kit:beacon-pulse` rather than re-deriving it here.

### Step 1 — locate the entry point (static)

If `--start` was given, jump to Step 2. If Step 0 produced a concrete node (an endpoint, component, or file from the failing event), use THAT as the entry point — skip the blind search. Otherwise, use chart to find candidates:

1. Extract key terms from the symptom (component name, identifier, error class) — and from the beacon failure if Step 0 ran (the failing route/selector is a strong term). Skip noise words.
2. Call `mcp__launch-chart__read_graph(search: <term>, type: <best-guess-type>, layer: <layer?>)`. If `type` is uncertain, omit it and filter by relevance.
3. If `read_graph` returns 0 candidates, broaden: drop `type`, then `layer`. Surface the top 3 to the user; if they can pick, jump to Step 2 with their pick.

**Chart-gap detection (entry-point search):**
- `read_graph` returns 0 AND a quick `grep` confirms the term exists in source → file a chart-gap report (below), then proceed via grep fallback for this query only.
- `detect_project_stack` lists a layer (e.g. `api`) but `read_graph(layer:"api")` returns 0 nodes → file a chart-gap report (parser misconfig).

### Step 2 — understand the node (static)

Once the entry point is known, query its structure and behavior:

- **Structural** — `mcp__launch-chart__read_graph(node_id: <id>, hops: 1, include_edges: true)` for what it renders/imports and what depends on it.
- **Variable / state** — `mcp__launch-chart__inspect_node(node_id: <id>, fields: ["stateVars"])` or `(filter: <key-term>)` for held state and conditions. Per project CLAUDE.md, ALWAYS start with `fields` or `filter` to keep the response small.

Compose a hypothesis. If Step 0 ran, **test the runtime hypothesis against the code** here — e.g. confirm the failing endpoint's handler actually can return the 500 beacon saw.

### Step 3 — follow the trail (static)

Walk dependency edges toward the suspected cause (endpoint, table, util fn). At each hop, `read_graph(node_id, hops:1)` to find the next link. When you hit a leaf, narrate the chain back with file:line refs from the node ids. If Step 0 gave a runtime chain, align the two — the static call path should explain the observed runtime sequence.

### Step 4 — propose, don't fix

End with a one-paragraph diagnosis + 2–3 candidate causes ranked by likelihood. **When both sources contributed, say which evidence is runtime vs static** — e.g. *"Runtime (beacon): the tags fetch 500'd. Static (chart): the handler dereferences `org.id` with no null-guard when the session has no org — the likely cause."* A diagnosis backed by an observed failure + the matching code path is far stronger than either alone. Do NOT propose an edit unless asked — debug is investigation, not implementation. Suggest the next move: "Want me to read the handler and confirm?" or "Want to `/kit:diagram sequence` the call chain?"

## Grep fallback

Only enter this path when:
- the chart returned no results AND a chart-gap report was filed, OR
- the symptom is about a non-TS asset (CSS string, env var, JSON value, comment) — the chart doesn't model these and the gap is expected.

Then use `grep`/`Read` directly. Always tell the user explicitly: `"Chart returned nothing — falling back to grep (filed gap report id: …)"` or `"Symptom is in CSS/JSON, which chart doesn't model — using grep directly (no gap report needed)."`

## Chart-gap protocol (shared)

When the chart MCP returns less than the symptom suggests it should, file ONE report via `mcp__launch-secure__kit_feedback_submit` (the kit-self feedback channel — NOT `communication_write`, which would land in the user's own project Comm Hub instead of the launch-kit project):

```
title: "launch-chart gap: <one-line>"
source: "kit:debug"
body: |
  Skill: /kit:debug
  Symptom: <one-line symptom>
  Tool:    read_graph(search="…", layer="…", type="…")
  Returned: 0 nodes
  Cross-check: grep "<term>" matched <N> files (sample: <path1>, <path2>)
  Project stack: <detect_project_stack one-liner>
```

Then continue with the grep fallback for THIS query. Don't file again in the same invocation.

## Constraints

- **Runtime-first, then static.** When a beacon session exists and the symptom is runtime-shaped, lead with beacon — it converts a blind structural search into a targeted one. Beacon is additive evidence: its absence never aborts the run, it just drops you to static-only with a one-line note.
- **Don't reimplement beacon skills.** Step 0 does a lightweight `beacon_failures` + `beacon_correlate`; for deeper runtime analysis defer to `/kit:beacon-pulse` (failure + N preceding) and `/kit:beacon-scan` (events by kind).
- **Chart-first for the static phase.** Default to MCP queries; grep only when the chart cannot answer.
- **One gap report per invocation.** Don't spam comments.
- **No implementation.** This skill diagnoses; it does not edit. If the user wants the fix, they invoke `/fix:*` or hand the diagnosis back to you in chat.
- **Cite your evidence.** Static claims link to a chart node id (file:line via `inspect_node` lines); runtime claims cite the beacon `seq` + event kind. The user can verify either.
- **Surface fallbacks verbatim.** When grep is used, or when beacon came up empty, the message (and why) must appear in the output — silent fallback hides the gap.
