---
name: bworlds-triage
description: "Trace one grouped client error on a Build down to the code path that produces it, using the recorded Sessions that hit it as evidence, then record it as a Finding only after showing the exact Finding and asking. Every command it runs is free. Use when the user says 'what is this error', 'triage the errors on [slug]', 'why are users hitting this', 'investigate this error group', or 'something is failing in production'. For applying and verifying a fix, use bworlds-fix."
---

# Error investigation

One grouped error, the Sessions that hit it, the code path behind it. That is the whole job.

Everything here runs with your own credential through the `bworlds` CLI. Load `bworlds-cli` for the command contract, the exit codes, the safety rules and the Findings protocol. Reading is free, and every command in this skill is free: telemetry reads cost no Workspace credits and a Finding write costs none. No command here debits the Workspace. `bworlds-fix` describes fixing a Finding and verifying its evidence. Rechecks can cost Workspace credits; read `https://docs.bworlds.co/docs/reference/pricing` before running one.

This skill runs no repository command. It reads the code in your own checkout, changes no file there, and commits nothing.

## 1. Read the heartbeat before the lists

```bash
bworlds telemetry errors <slug> --window 7
```

The heartbeat line opens that output, and it decides whether the rest of it means anything. `bworlds-cli` states what its three values mean.

- `alive`: the SDK reported within the last ten minutes. The error list is a measurement. Continue.
- `no_signal`: the SDK reported before and not since. A quiet app and a removed SDK read the same here. Run `bworlds telemetry sessions <slug>` and look at the newest Session's date. Older than the window: report the measurement as stale, trace the groups that exist, and say that nothing newer was measured. Recent Sessions exist: the SDK is reporting and the heartbeat is lagging, so continue.
- `no_data`: the SDK never reported. **Stop.** Nothing was measured, so there is no grouped error to trace and an empty list carries no verdict about the app. Report the Build as unmonitored, point at `https://docs.bworlds.co/docs/guides/sdk`, and say plainly that today nobody hears about a failure on this Build. Never call it healthy or error-free.

`bworlds telemetry uptime <slug>` is BWorlds' own probe of the Build's URL and detected backend. It answers whatever the SDK does, so it never establishes that production is watched.

## 2. Choose one error group

Read the list with the heartbeat `alive` or `no_signal`. An empty list on `alive` is a real measurement: no grouped error in that window. Report it, offer a wider `--window`, and stop rather than inventing an error to investigate. For each group, note the message, the route, how many times it fired, when it was first and last seen, and how many Sessions it touched.

Choose by impact, not by count:

- An error on a route that carries money or sign-up beats a noisier one on a settings page.
- Filter out localhost and preview noise. `--url` keeps only groups whose route or message contains a fragment.
- One group per investigation. Two groups on the same route are often one cause, but prove it from Sessions rather than assuming it.

Say which group you picked and why before going further. When the user names a group, use theirs.

## 3. Read the Sessions that hit it

```bash
bworlds telemetry sessions <slug> --has-errors --json
bworlds telemetry sessions <slug> <session-id> --failed-only --json
```

When `--has-errors` returns no Session for this group, the group has no recorded path behind it. The error can fire where the SDK records no Session. Say so, widen `--window` once, and then continue on the message and the code alone. Mark the diagnosis lower confidence in the report, and do not call one occurrence a pattern. A `not_found` exit (code `5`) on a Session id means it is outside this Build; take ids from this Build's list and do not probe others.

A session detail returns a summary first: identity, pages, captured requests, signals, recorded errors and console lines. Read that. Use `--depth full` only when the summary lacks what the investigation needs; it returns the whole server record, including every recorded interaction and the signed replay media links.

A signed replay link is a credential in a URL: anyone holding it can watch that person's recorded session without signing in. Keep every one of them inside this investigation. Never paste one into a Finding, a commit, a pull request or a message.

What the Sessions are for:

- **The path in.** Which pages the person visited, in what order, before the error fired.
- **The request that failed.** Status, route, and whether it failed for everyone or for one account shape.
- **The repeat.** Three Sessions on the same page is a reproducible path. One Session is an anecdote, and worth saying so.
- **The signals.** Rage clicks around the failure say the person noticed and retried.

Telemetry withholds the email address of the Builder's end users and reports `"userEmailWithheld": true` in its place. Leave it withheld. Do not pass `--include-personal-data` to pull that address into your own context, and never ask for it to make the investigation read better. Ask only when the user genuinely needs to contact that person, and keep the address out of Findings, commits and messages whatever the answer.

## 4. Reach the code path

Read the code in your own checkout. You were invited into this Workspace because you work on this app, so you hold it already.

Confirm it is the right repository first. `bworlds build info <slug>` prints `GitHub: connected to <owner>/<repo>`. Compare that with your checkout's `origin`. A Builder can connect a repository their app does not ship from, and a mismatch means the Sessions and the code you are joining belong to two different apps.

Now join the evidence to the code:

- Match the failing route from the Sessions to the handler, page or function that serves it.
- Match the error message to where it is thrown or where the unchecked value comes from.
- Read the data access on that path: a missing null check, an absent row-level policy, a query that assumes a row exists.
- Check `git log` on those files. A recent change on the path, right before the first-seen date, is usually the cause.

With no checkout of this app, stop at the Sessions evidence. Report the route, the message and the path in, and name the missing code as the reason the cause stays open. `GitHub: not connected` on `build info` is worth reporting too: BWorlds holds no repository for this Build, so no Control can recheck a code fix on it later.

You are done when you can state the cause as one sentence a developer could act on, with `file:line` evidence. If you cannot, say so. An undiagnosed error stays undiagnosed; do not record a Finding that guesses.

## 5. Decide what happens to the diagnosis

Four outcomes:

- **Already tracked.** `bworlds findings list <slug> --json` shows an `open`, `needs_user` or `in_progress` Finding on the same risk. Match on the `--source-ref` risk slug, or on title plus area. Add what you learned with `bworlds findings edit <slug> <finding-id> --rationale "..." --confirm` instead of creating a second one. Then stop.
- **Not worth a Finding.** Localhost noise, a browser extension, a one-off from a since-removed page. Say so and close the investigation in the report.
- **Not diagnosed.** The code path stayed open. Report what the Sessions showed and what is missing, record nothing, and tell the user which piece of evidence would settle it.
- **Worth a Finding.** Go to step 6.

If the `findings list` read itself fails, say so and go on to step 6. Tell the user the duplicate check did not run.

## 6. Show the Finding, then ask

Never run `findings create` before the user has seen the exact Finding it would write. Print all of it:

| Field | Value |
|---|---|
| Title | the one line the Builder will read on their Findings list |
| Status | `open`, or `needs_user` when it needs context only the Builder has |
| Severity | `critical`, `high`, `medium` or `low` |
| Area | `security`, `operations`, `code-quality` or `legal-compliance` |
| Service line | `run` |
| Source ref | `bworlds-triage:{YYYY-MM-DD}:{risk-slug}` |
| Rationale | why it matters, framed against the Builder's own workflow |
| Steps | the fix, one step per line |
| Evidence | the `file:line` and the Session count behind it |

Then offer three choices and wait:

- **Record it**, as shown.
- **Adjust first**: change the wording, status or severity, then show the amended Finding again.
- **Record nothing**: print the summary line and stop.

Only on "record it":

```bash
bworlds findings create <slug> \
  --title "..." --rationale "..." --steps "..." \
  --severity high --area operations \
  --service-line run \
  --source-ref bworlds-triage:2026-10-01:checkout-null-cart \
  --status open --confirm --json
```

Details the fields hold to:

- **Service line is `run`.** A production error is reliability work. Use `launch` only when the error blocks safe sharing or selling outright.
- **One Finding per cause.** The Sessions and the file references are evidence inside it, never separate Findings.
- **Write for the Builder.** Plain language, 2000 characters or fewer, no Control ids, no raw table names. Never put a secret value in a title, a description or metadata: name the location and the key type only.
- **The end user stays anonymous.** Session counts and routes, never an email address and never a signed replay link.

A Finding this skill records carries the Workspace's own authority. The Builder's list shows its reporter as `Your team`, never as BWorlds review, and no flag changes that. Say so when you tell the user where the Finding will appear.

When a write fails, `bworlds-cli` says to print a warning and go on. Here that also means printing the whole Finding again, so the diagnosis survives in the conversation and the user can record it by hand.

Close with the one line the Findings protocol requires: `Findings written: N created, N edited, N updated, N skipped, N failed`. Print it even when the answer was "record nothing", because that line is how anyone knows this step ran.

## 7. Report

```
## [Slug] Triage — [Date]

**Heartbeat**: [alive / no_signal / no_data]
**Error**: [message] on [route] · [N] times · [N] Sessions · first seen [date]
**Path in**: [what the Sessions show the person doing]
**Cause**: [one sentence] · [file:line]
**Outcome**: [Finding recorded / existing Finding edited / nothing recorded, reason]
**Next**: [bworlds-fix on that Finding / what the Builder must decide]
```

Hand a diagnosed cause to `bworlds-fix`. It takes the Finding from its prompt, or from its remediation steps when the Finding records no prompt, to a change your own coding agent makes. A Finding this skill records has `detection: manual`, and no Control raised it, so BWorlds cannot recheck it. `bworlds-fix` closes it with a reason instead, and the proof that it worked is the error group's count falling after the next publish.
