lobe-cli 0.1.4

Lobe — local-first HTTP performance profiling CLI for developers. Spins up a capture proxy, records DNS/TCP/TLS/TTFB/download phases per request, and flags anomalies against grounded network baselines.
---
name: lobe-diagnose
description: Diagnose HTTP performance issues from a lobe capture session. Use when the user says their API is slow, mentions lobe, or points at a `/tmp/lobe-capture-session-*.json` file. Returns a ranked, phase-by-phase triage report against grounded baseline thresholds.
---

# lobe-diagnose

You are analyzing a captured HTTP session produced by the `lobe` tool — a local performance proxy that measures every request's DNS, TCP, TLS, TTFB, and download phases. Your job is to systematically check utilization hotspots, saturation signals, and errors — in that priority order — then explain what's slow and what to fix first.

## Step 1 — Locate the session file

Priority order:

1. If the user provided a path, use it.
2. Otherwise, find the most recent capture:
   ```bash
   ls -t /tmp/lobe-capture-session-*.json 2>/dev/null | head -1
   ```
3. If no capture exists, tell the user to run:
   ```bash
   lobe capture <upstream>
   # hit the API, then press `e` in the TUI to export
   ```
   Then re-invoke this skill.

## Step 2 — Understand the file shape

The file is JSON. You care about the `events` array:

```json
{
  "version": 1,
  "listen_addr": "127.0.0.1:7878",
  "upstream": "http://localhost:3000",
  "exported_at_ms": 1704000000000,
  "event_count": 42,
  "events": [
    {
      "request_host": "localhost",
      "request_method": "GET",
      "request_path": "/api/users",
      "response_status_code": 200,
      "response_bytes": 1234,
      "report": {
        "dns_ms": 0,
        "tcp_ms": 1,
        "tls_ms": 0,
        "ttfb_ms": 45,
        "download_ms": 3,
        "total_ms": 49
      },
      "error_message": null,
      "created_at_ms": 1704000000000
    }
  ]
}
```

Read only what you need; do not print raw JSON back at the user.

## Step 3 — Apply the (Utilization Saturation Errors) method (this priority order)

### 3a. Utilization (do this first)

Sum `total_ms` per `(method, path)`. Rank routes by **cumulative wall-clock time**, not by individual latency. The routes at the top of this list are burning the most session time in aggregate — they're where optimization has the most leverage.

### 3b. Saturation

For each unique `(request_method, request_path)` group:

- Compute **P50, P90, P99** of `report.total_ms`.
- **Long tail**: if `P99 > 5 × P50`, the tail is unusually long — a signal that something occasionally goes wrong for this route.
- **TTFB dominance**: if median `ttfb_ms > 60% of total_ms`, the upstream is doing slow work before it starts writing the response. This is the most common finding in real captures.
- **Download dominance**: if median `download_ms > 60% of total_ms`, the response body is large relative to bandwidth (or the upstream is streaming slowly).
- **Bimodal distribution**: sort the total_ms values. If they cluster into two clearly separated bands (e.g., most around 4ms, another cluster around 47ms), flag it as bimodal — this is classic cache hit vs. miss, or a fast-path/slow-path branch.

### 3c. Errors

Count events where `response_status_code` is `null` OR outside `200..=299`. Group by `(status_code, method + path)`. Report the top patterns with counts and share of total requests.

If zero errors: write "No errors observed."

## Step 4 — Cross-check against baseline thresholds

Classify each request's host and compare to the rule-of-thumb baselines:

| Host category | Which hosts | TTFB baseline | Total baseline |
|---|---|---|---|
| **loopback** | `localhost`, `127.x.x.x`, `::1` | ≤ 50ms | ≤ 80ms |
| **LAN** | 10.x, 192.168.x, 172.16-31.x, `*.local` | ≤ 100ms | ≤ 200ms |
| **remote** | anything else (public internet) | ≤ 500ms | ≤ 1.5s |

Anything meaningfully over these is worth calling out. A localhost API returning in 340ms is 4× baseline — a big deal. A remote API returning in 340ms is fine.

## Phase interpretation cheatsheet

When a phase is anomalously high, use this to explain *why*:

- **DNS excess** → resolver issue, cold DNS lookup, custom `/etc/hosts` entries
- **TCP excess** → saturated network path or upstream at capacity
- **TLS excess** → old TLS version, deep certificate chain, slow ALPN negotiation
- **TTFB excess** → almost always slow DB queries, missing indexes, N+1 queries, or CPU-bound work before writing the response. This is 80% of real findings.
- **Download excess** → large response body relative to bandwidth, streaming slowly, or missing compression

## Step 5 — Output format

Return a markdown report with **these exact sections in this order**:

### Summary

2–3 sentences. Lead with the number that matters most. Example: *"412 requests captured over 3 minutes. Zero errors. GET /api/users P99 is 340ms — 4× the loopback baseline. TTFB dominates."*

### Utilization

Which endpoints burn the most cumulative time. Be specific: `GET /api/users burned 4.2s of the session (34% of total)`.

### Saturation

Tail behavior, TTFB dominance, bimodal patterns. Cite specific numbers, not generalities.

### Errors

Top error patterns with counts. If none, one line: "No errors observed."

### Recommendations

Exactly **3–5** specific, actionable items, ranked by expected impact. Each item must be concrete enough that a developer could act on it without asking follow-up questions.

**Good**: *"Add an index on `users.email`. GET /api/users P99=340ms with TTFB dominating (280ms of 340ms); the endpoint likely does a full-table scan by email."*

**Bad**: *"Look at your database performance."*

## Rules

- **Be concrete**. Cite specific endpoints, specific numbers, specific phase names.
- **Do not fabricate causes**. If the phase breakdown doesn't uniquely point at a root cause, say "insufficient evidence — recommend running `lobe explain` again with a larger capture" rather than guessing.
- **Prioritize wall-clock impact over ratio**. A phase that's 5× over baseline but only adds 3ms matters less than one that's 2× over but adds 200ms.
- **Do not print the raw session JSON back at the user**. They already have it.