Skill v1.0.1
currentAutomated scan100/100+1 new
version: "1.0.1" name: debug description: Use to diagnose a corgi stack OR gather runtime data while investigating a bug. Entry modes — (1) broken stack — a service won't start, crashed, is stuck/unhealthy, or hangs on boot ("debug the stack", "why is X not starting", "the api is down"); (2) bug ticket needing data — pull logs, request traces, analytics, or backend perf, local or deployed ("pull the staging logs", "check the 500s", "why is this endpoint slow"); (3) CI red on a PR — a pushed branch's checks failed ("why is CI red", "the build broke"); (4) env/version skew — same code, different behavior across environments or app versions ("works on staging not prod", "old build works but latest is broken", "fine locally but 500 in prod"). Local-first (corgi ps/status/doctor/logs); escalates to the repo's logs/analytics provider (auto-detected) on demand and after asking. Client-side/UI perf is the app framework's job, not this. NOT for authoring corgi-compose.yml (corgi skill), starting a stack (run skill), or reviewing PRs (review skill).
Corgi debug
Four entry modes:
- Broken stack — a service won't start, crashed, is unhealthy, or hangs on
boot. Steps 0–3 (local); Step 4 only if a deployed env is implicated.
- Bug investigation — on a ticket, hit _"need more data"_: runtime logs, a
request trace, analytics, or backend perf ("why is order fetching slow", a slow endpoint/query) — local or deployed. Stack may be fine; often jump straight to Step 4 (provider data / APM trace), Steps 0–2 only if it reproduces locally. Client-side / UI perf (render jank, slow animation, first-load, bundle size) is app-profiling — not corgi's stack data → the app framework's perf tooling, not here.
- CI red on a PR — a pushed branch's checks failed ("why is CI red", "the build
broke on my PR"). corgi has no CI command — the failing job log comes from the forge CLI, then reproduce locally → Step 5.
- Env / version skew — _same code, different behavior across environment or app
version_: "works on staging not prod", "an old app build works, the new one is broken", "fine locally, 500 in prod on the same request". Usually not a stack fault and not in the logs — a deploy-state + contract problem. Skip the local ladder → Step 6.
Local-first. Exhaust the fast local probes before anything external. The provider is read from the repo (README/CLAUDE.md), never hard-coded, queried on demand. Work in order; stop the moment the cause is explained — don't run the whole ladder for a one-liner.
Guardrails (non-negotiable)
- Never foreground a `corgi run` from a Bash call —
beforeStartruns inline
_before_ run-state is written and corgi run streams forever, so a sync call hangs (10-min timeout) and never reaches the next step. Always --detach (or run_in_background: true). See ../corgi/references/long-running.md.
- Bound every read. Logs via idle-limited
corgi logs(--idle <Ns>); raw files
via Read(limit: ~100). Never an unbounded read of a .log/.state.json (logs cap at 50MB).
- Local before external (broken-stack mode). Stack won't start → exhaust
ps→
status → doctor → logs before any analytics MCP (most "won't start" bugs are ports/docker/env, found locally). Bug-investigation mode, symptom is deployed/remote (a staging/prod 500 that may not reproduce locally) → go straight to Step 4; local probes only if it reproduces.
- Analytics only when the investigation needs it AND the user OKs it. Detect the
provider, ask first, read-only, scope to service+env+window, never echo secrets.
- Run safe fixes; ask before destructive/remote (
--force, dropping a DB,
killing a port the user may want, anything hitting a deployed env).
- **Cross-env / cross-version bug → build the repro matrix and pin the deployed
version BEFORE reading code.** "Works in A not B" is its own bug class (Step 6). Don't diff against main HEAD or theorize about data until you know which commit/version each env actually runs — the report's own version string (a screenshot build number, "broke in the latest release") is evidence; use it, don't substitute main.
- ~2 honest tries per service, then report
needs attention+ what you saw.
Step 0 — Snapshot (broken-stack mode)
corgi ps --json # name/kind/port/status/url (+ startedAt); reconciles corgi_services/.state.jsoncorgi status --json # live TCP/HTTP probe — the ONLY liveness truth# uptime = startedAt from `corgi ps --json`; an older corgi omits it → cat corgi_services/.state.json
Trust `corgi status` (live probe) for liveness, not `corgi ps` status. ps status only means the PID/container _exists_ — db_services only go running/stopped, never crashed, and a container-backed service (docker runner, pid 0) isn't refreshed by a liveness check, so a crash won't flip it to crashed. Judge those by corgi status + docker ps.
Classify on real signals (JSON keys lowercase):
| State | Tell | Next | |
|---|---|---|---|
| healthy | corgi status healthy:true | not the problem | |
| crashed | ps status:"crashed" OR a newest *.crashed.log | Step 2 | |
| up but probe red | ps status:"running" BUT corgi status healthy:false | Step 2 + check the healthCheck: path | |
| never spawned / mid-beforeStart | entry absent from .state.json | Step 1 |
Uptime = now − startedAt; call out "X up 4m, Y never came up". No exit code in `.state.json` — read it from the log body (Step 2), not the file or the .crashed suffix.
Seen this before? If .corgi/memory/ exists, scan incidents/ (corgi memory list --type incident --json) for a past failure matching the symptom and reuse the recorded fix before re-deriving it (see the memory skill). Absent → skip.
"Stuck" splits (corgi has no booting-vs-ready flag):
- Hung in `beforeStart` →
corgi run --detachitself never returned; service
absent from .state.json. Launched in a Bash call → still blocking; check that run's output / --logs; --omit beforeStart to isolate.
- `start:` running, port never opens → tail logs: advancing = slow boot, a stack
trace = real failure.
- Slow first boot vs hung — a first run that clones repos / seeds a DB / pulls
images legitimately takes minutes. Tell apart ONLY by whether the log keeps advancing: corgi status --watch (or a 2nd corgi status --json after ~30s, compare the healthy count). Cross-ref ../corgi/references/debugging.md.
Step 1 — corgi doctor when something won't start
corgi doctor --json # tools installed, Docker up, ports free — and ONLY those
First thing on "doesn't work". Fixes (offer, then run the safe one): port busy → corgi run --services <x> --kill-port --detach (ask if it's a port the user wants); Docker down → start it, re-doctor; missing tool → install (required: names it).
Doctor does NOT cover — route elsewhere: clone not done → service dir empty / "failed to clone" in run output (corgi init, fix branch/URL/token); seed/dump failure → only on corgi run -s; check seedFrom*; env unset/wrong → corgi env <x>; wrong `healthCheck:` path → service binds the port but corgi status gets 404/5xx; bad compose → corgi validate.
Step 2 — Local logs
Needs a prior corgi run (capture is on unless --logs=false). Default = bounded, single service:
corgi logs --service <x> --json --idle 3s # the stuck/crashed one, bounded
Cross-service correlation only (reads each newest file in full, no idle — pricier): corgi logs --all --json. Find the first error / stacktrace / crash marker (*.crashed.log) + the exit code from the log body. No logs captured → re-run just that service with --logs (Step 3).
Step 3 — Targeted fix / retry
Smallest change that could work, then re-gate. Never foreground a `corgi run`.
Minimal code comments. Don't narrate the change in code comments — it should read from the diff and the PR/commit message. Add a comment only for a genuinely non-obvious invariant that would otherwise be lost, and only where the file already comments. No "// added/changed X" notes.
- Service with dependencies →
corgi run --services <x> --with-deps --detach --logs.
Without --with-deps an isolated retry against a down dependency just re-crashes.
- A consumer can't reach a healthy producer (404 / connection-refused to another
service, not a crash) → the producer is fine; check the consumer's resolved env: corgi env <consumer> for a stale or wrong dependency URL — a prior --host/remote run rewrote it, the producer was excluded from the run set so no depends_on_services alias got generated, or the .env was hand-edited. Re-run without the override / with --with-deps so corgi regenerates the wiring.
- Reproduce a suspect service on its branch / worktree →
corgi run --services <x> --with-deps --detach --logs --service-branch <x>=<branch> (corgi makes a non-destructive worktree off the pushed branch; main checkout untouched), or --service-dir <x>=<worktree-dir> for live/uncommitted code. Guard: corgi run --help | grep service-branch (absent → git checkout <branch> && corgi run --services <x>).
- DB is the problem (most common) → `corgi run --dbServices <db> --services none
--detach, then corgi status --json` / its logs.
- Full-stack flake →
corgi restart. - A `beforeStart` step wrongly skipped →
--no-cache(re-runs _every_
beforeStart step, ignoring cacheKey — not an image/deps rebuild). Truly stale generated artifacts → corgi clean -i corgi_services / corgi run --fromScratch.
- Env wrong →
corgi env <x>. Compose wrong →corgi validate.
Re-gate: corgi status --ready --service <x> --timeout 120s --json. Green → report. Red after ~2 tries → needs attention + the error, move on.
Step 4 — Logs / analytics from the provider (on demand, ask first)
Use when the ticket needs runtime/deployed data (a staging/prod request, a 500 you can't reproduce locally, or a slow backend path's trace/timing — APM / slow-query log) or local logs don't explain a local bug. For a bug investigation this is first-class, not a last resort.
Before the ask-gate: (a) if the symptom carries a ticket key whose tracker MCP is connected, read the ticket first (mcp__linear-server__get_issue / mcp__atlassian__getJiraIssue) — it usually already holds the endpoint / request-id / timestamp / env to scope the query; (b) verify the provider's MCP is actually connected — absent → jump to step 4 (connect-this-MCP message + docs pointer); don't collect scoping you can't use.
- Detect the provider — one bounded pass over the docs (corgi is
provider-agnostic; extend the pattern to whatever the repo names): `` grep -liE 'coralogix|cloudwatch|ecs|datadog|sentry|new ?relic|grafana|loki|kibana|elastic|splunk|honeycomb' README.md CLAUDE.md Claude.md claude.md 2>/dev/null ` (filename casing varies — Claude.md vs CLAUDE.md.) Map the hit to its access path — a connected **MCP** for that provider if one exists (e.g. Coralogix → DataPrime; CloudWatch/ECS → the AWS MCP; Datadog/Sentry/Grafana → their MCP), else the provider's CLI/API. Multiple providers named (e.g. APM + logs) → pick the one the repo ties to **logs** for the affected service, not the APM/metrics one. Then read that provider's own section in the repo docs for the exact fields to scope the query (application/subsystem, environment, request id / endpoint / error class). None found → infer from the deploy target and say so: "no logs provider named in the docs — this stack deploys on <infer>`; connect its MCP/CLI or paste logs," and stop.
- Ask before querying — provider + which env + the time window + the
service/request.
- Query read-only, scoped to service + env + a tight window. Summarize the
relevant lines; never dump raw payloads or secrets.
- MCP not connected → tell the user exactly what to connect, and point at the
repo's logs/observability README section.
Step 5 — CI red on a PR (forge logs, then reproduce locally)
A pushed branch's checks failed. corgi has no CI command — pull the failing job's log from the forge CLI, then reproduce with the service's own gate.
- Failing log (read-only): GitHub —
gh pr checks <n>(which failed) →
gh run view <run-id> --log-failed. GitLab — glab ci status -R <repo> → glab ci trace -R <repo> (the failing job).
- Read the first real error — stacktrace / failing test / lint / build; skip the
green jobs.
- Reproduce locally with the gate, don't guess:
corgi test --service <x>(or
corgi exec <x> --ensure-deps -- <lint/build cmd>). Green locally but red in CI → an env gap (a CI-only var, a tool/node version, stale cache) — name it.
- Fix → push, re-check
gh pr checks/glab ci status. ~2 tries, then report
needs attention + the failing job.
Step 6 — Env / version skew (works in one env or version, not another)
Same code, different behavior across environment or app version. Not a crash, not in the stack logs — a deploy-state + contract problem. Don't read main HEAD; read what's actually deployed.
- Build the repro matrix first. version(s) ✕ environment ✕ last-known-good. The
bug report is evidence — a screenshot build string (app version + build number), the env, "used to work in the previous version" — use it; don't substitute main. Missing a cell → ask ("which version/env reproduces, when did it last work") before digging.
- Pin the deployed artifact per env — the crux, and the thing you usually can't
see from the repo. Find the real commit/version each env runs: a /version//health endpoint, a build-embedded SHA, the container image tag/label, the forge's deployments/releases, CI's last-deploy. Can't find it → ask; never speculate on data you can't see. Watch for a frozen prod or a hotfix/* branch that diverges from main.
- Diff the contract against the DEPLOYED peer, not `main`. Client↔server skew:
does the client call a field / route / param the _deployed_ server actually has? For GraphQL an unknown field is a validation error that rejects the WHOLE operation (no partial data, errorPolicy can't save it) → one new field blanks the entire query/screen. Decisive 2-command check: git grep <field> <deployed-sha> vs the client query. Same shape for REST (new required param, dropped response field), enums, or a missing DB column.
- Weight in-repo precedent. Hit this class before?
grepCLAUDE.md + recent
commits + hotfix/* branches for prior deploy-lag / dropped-or-renamed-field incidents, and .corgi/memory/incidents/. A documented precedent is a strong prior — check it early, not after theorizing.
- Cheapest decisive check first. The actual error string from the failing
env/client (the GraphQL Cannot query field …, the 4xx body, the stack line) settles it in one read — pull it before building data / edge-case theories.
Root cause is usually deploy order: a client shipped a contract change before the peer reached that env. Fix = deploy the lagging peer (often API-first) and/or a client-side tolerance net; always flag the order so it doesn't recur.
Report
What was wrong, what you ran, what fixed it (or what's still needs attention and why). Stopped at the analytics ask-gate → name the provider and the query you'd run.
Posting to the ticket (only if asked): human and concise — symptom → why, framed in the repro matrix (which env/version breaks and why the others don't) → fix → one prevention line. Link SHAs / PRs, don't dump them. Preview before posting; don't post-then-revise.
Called from another skill
The stories skill (or any investigation) can invoke debug mid-flight when it needs runtime data — jump to Step 4 (provider data) or Steps 0–2 (reproduce locally). Return the findings; don't take over the parent flow.