# Triage bugs and suggestions

Triage the bugs and suggestions customers report, and tell them when the work ships.

A signal is one bug or one suggestion. Each person who reported it is a
reporter on that signal. Everyone in the organization can file, triage, edit
and delete signals. Connecting Linear or GitHub needs an admin or owner, see
[Send signals to Linear or GitHub](/articles/send-signals-to-linear-or-github).

## Where signals come from

| Source | How it files |
| --- | --- |
| The agent | Files from a conversation when **Track bugs and suggestions** is on under [Agent → Behavior](/articles/set-up-your-agent). The switch is on by default. At most 3 signals per conversation. |
| The [feedback panel](https://docs.median.sh/components/feedback) | AI reads each note and files it as a bug or a suggestion, or drops it as spam. Needs the same switch. |
| [Crash reports](https://docs.median.sh/components/crash-reports) | `reportError` files a bug at **High** priority. Needs the same switch. |
| Notes from elsewhere | `median feedback submit`, `POST /v1/feedback`, or `median.feedback.submit` over MCP. Read the same way as a feedback panel note. |
| Discord channels | Median reads the channels you pick for bugs and suggestions. See [Set up Discord support](/articles/set-up-discord-support#feedback). |
| The feedback board | Posts on your public site's board. These file even with **Track bugs and suggestions** off. See [Set up your public site](/articles/set-up-your-public-site#feedback). |
| Filed directly | **New signal** on this page, `median signals file`, `POST /v1/signals`, `median.signals.file` over MCP, or the [assistant](/articles/ask-the-assistant). |

With **Track bugs and suggestions** off, the agent, the feedback panel, crash
reports and notes from elsewhere file nothing. Filing directly still works.

Duplicate checks and feedback reading run on AI and use credits, see
[Plans](/articles/manage-your-plan-and-credits#plans).

### Duplicates

| Check | Rule |
| --- | --- |
| Same title | A new report whose title matches a signal of the same kind joins it. Matching ignores case and punctuation, and includes the titles of merged duplicates. It looks at the 60 most recently updated signals of the same kind, Declined ones included. |
| AI read | A new signal from the agent, a note or a crash report is compared with the 40 most recently updated signals of the same kind, Declined ones included. A match merges it into the older one. It is skipped once a teammate has changed the new signal. |
| Same person | One report per person per signal. A repeat updates their quote and adds the new conversation to the report. |

A report about a Declined signal joins it, and the signal stays Declined. A report about a Done
signal joins it, and the agent tells the customer it was fixed and asks them to
try again.

## The list

**Signal → All** shows the 60 most recently updated signals, bugs and
suggestions together. A red **Bug** or blue **Suggestion** tag marks each one.
The **All** row in the sidebar counts open signals, up to 100. The **Signal** tab shows a dot while any are open.

| Part | What it holds |
| --- | --- |
| **Approvals** | Open signals, each as a card with **Accept**, **Decline** and **Go to thread** |
| **In review**, **In progress**, **Planned** | Expanded when the page loads |
| **Done**, **Declined** | Collapsed when the page loads. Click the heading to expand. |

With nothing filed, the page reads "No bugs or suggestions reported yet."

### A row

| Part | Does |
| --- | --- |
| Priority glyph | Opens a menu to change the priority |
| Status glyph | Opens a menu to change the status |
| Title | Opens the signal's page |
| Commit glyph and count | Commits that may have fixed it, waiting for a decision |
| Linear identifier or GitHub `#12` | Opens the issue |
| People glyph and count | How many people reported it |
| Time | Since the last change |
| Chat glyph | **Go to thread**, the newest conversation it was reported from |
| **More actions** menu | The row menu below |

| Row menu item | Shown when |
| --- | --- |
| **Edit** | Always. Changes the title and details. |
| **Go to thread** | It was reported from a conversation |
| **Mark done** | It is not Done |
| **Decline** | It is past Open and not Declined |
| **Reopen** | It is Done or Declined. Sets it back to Open, into Approvals. |
| **Open LIN-123**, **Open #12** | It has an issue in Linear or GitHub |
| **Delete** | Always |

## Accept and decline

| Action | Status | Trackers | Reporters |
| --- | --- | --- | --- |
| **Accept** | Planned | Opens an issue in every connected tracker that has none for it yet | No message |
| **Decline** | Declined | Closes any linked issue as canceled or not planned | No message |

The card leaves Approvals as soon as you press either button. If a tracker
refuses the issue, the signal still moves to Planned and the tracker's error
shows under Approvals, or in the sidebar on the signal's page. Accept never opens a second issue in a tracker that
already has one for the signal.

### Several at once

Tick the checkbox on a row, or on a group heading to take the whole group. A bar
appears at the bottom with the count.

| Button | Does |
| --- | --- |
| **Status** | Sets one status on every selected signal |
| **Priority** | Sets one priority on every selected signal |
| **Accept** | Accepts each one. Reports "Accepted N.", "Skipped N already accepted." and "N could not reach a tracker." |
| **Decline** | Declines each one. Reports "Declined N." |
| **Delete** | Asks first, then deletes them |
| **Clear selection** | Unticks everything |

A signal counts as already accepted when it is past Open and has an issue in
every connected tracker. Bulk **Accept** on a Planned signal that is missing an
issue opens the missing one.

## Status and priority

| Status | Means | Linked issue | Reporters |
| --- | --- | --- | --- |
| Open | Waiting in Approvals | Reopened or moved back to not started | No message |
| Planned | Accepted | Reopened or moved back to not started | No message |
| In progress | Being worked on | Moved to started | No message |
| In review | Being checked | Moved to started | No message |
| Done | Shipped | Completed or closed | Each gets one message |
| Declined | Not happening | Canceled or closed as not planned | No message |

Status also flows back from Linear and GitHub. The full mapping is under
[Send signals to Linear or GitHub](/articles/send-signals-to-linear-or-github).

| Priority | The agent picks it when |
| --- | --- |
| Urgent | Nobody can use the product |
| High | Something important is unusable and there is no way around it |
| Medium | It gets in the way and there is a workaround |
| Low | It is a papercut |

The agent uses Medium when it does not pick one. A Linear issue carries the
same priority.

## A signal's page

Click a title to open the signal.

| Area | Contents |
| --- | --- |
| Header | The kind tag, the title, **Edit**, when it was filed, how many people reported it, and "Updated by" with a name and time |
| **Status**, **Priority** | Menus to change either |
| **Actions** | **Go to thread**. **Accept** and **Decline** while Open. **Mark done** until Done or Declined. **Reopen** once Done or Declined. A link to each tracker issue. Past Open with no issue, "No linked tracker issue." **Delete**. |
| **On the board** | Each [feedback board](/articles/set-up-your-public-site#feedback) post filed under it, most voted first, with its votes or **Waiting**. Opens the post. Shown when there is one |
| Body | The write-up, rendered as Markdown |
| Also reported as | Titles of duplicates merged into it |
| **Commits** | Commits that may have fixed it, see [Send signals to Linear or GitHub](/articles/send-signals-to-linear-or-github) |
| **Reporters** | One row per person, newest first, up to 50 |

A signal that was deleted or merged away shows "This signal was deleted or
merged into another."

## File a signal by hand

Press **New signal** at the top of the list.

| Field | Rules |
| --- | --- |
| **Type** | **Bug** or **Suggestion**. Default Bug. |
| **Title** | Required. Up to 120 characters. |
| **Details (optional)** | Markdown. Up to 4,000 characters. |
| **Priority** | Urgent, High, Medium or Low. Default Medium. |

**Create** adds it to Approvals with no reporters. A signal filed by hand skips
the AI duplicate read and never opens a tracker issue on its own.

| Error | Cause |
| --- | --- |
| `Enter a title.` | The title is empty |
| `Already on the list as "<title>".` | A signal of the same kind has that title or alias. Only the 60 most recently updated signals are checked. |
| `This was declined before as "<title>". Reopen that one instead.` | A Declined signal of the same kind has that title. Reopen it instead |

**Edit** on a row or on the signal's page changes the title and details. Edits
update any linked issue.

## Reports

The **Reporters** section on a signal's page has one row per person, newest
first, up to 50.

| Part | Contents |
| --- | --- |
| Name | Their name, their email, or "Customer #N" |
| Quote | What they said, up to 400 characters |
| Image glyph and count | Screenshots on the report |
| Thread ids | Each conversation they reported it from, for example `W-12` |
| Page address | For a feedback panel note or a crash report, the page it was sent from, shown instead of thread ids. Reads "Feedback panel" or "Crash report" when no page was sent. |
| Time | When they reported it |

Click a row to open the report.

| Section | Contents |
| --- | --- |
| Header | "Reported" with the date, and " · Notified of completion" once they were told |
| Quote | Their words |
| **From the feedback panel** | Shown for panel notes |
| **From a crash report** | Shown for reports sent with `reportError` |
| Screenshots | Up to 6. Click one to open it full size. |
| Threads | Each conversation by id and subject |
| Facts | **Page**, **Viewport**, values from your app's state, and values you attached with [context](https://docs.median.sh/components/context) that are a single link. A link already shown as a screenshot is left out. |
| **Browser logs** | Error, Unhandled rejection, Console and Request entries |
| Attached values | Every other attached value, one block each under its label |
| **Server check: Passed** or **Server check: Failed** | What your [diagnostics endpoint](https://docs.median.sh/agent/median-config#server-diagnostics) returned |

**Copy report** copies the whole report as plain text.

### Screenshots

| Source | What lands on the report |
| --- | --- |
| A conversation | Images the customer sent in the thread. Only theirs, from the last 60 messages. |
| The agent | Image links the customer gave it |
| The [feedback panel](https://docs.median.sh/components/feedback) | Images picked, dragged or pasted in with the note |
| Your code | An image address passed to `attach`, or image links sent with `POST /v1/feedback` |

Each report keeps up to 6. Issues opened in Linear or GitHub show them inline.

## Telling reporters

When a signal moves to Done, each reporter gets one message in the
conversation they last reported from. It is posted under the agent's name.

| Kind | Message |
| --- | --- |
| Bug | This bug has been fixed. Thanks for reporting it. |
| Suggestion | Your suggestion has been implemented. Thanks for sending it. |

- Every move to Done sends it. That includes the list, the signal's page, the
  bulk bar, **Confirm fix**, a commit on a repository with **Close signals
  automatically**, and the issue closing in Linear or GitHub.
- A reporter is messaged once. Reopening and marking Done again does not
  message them a second time.
- Feedback panel notes have no conversation, so they get no message. Neither
  does a report whose conversation was deleted.
- A report heard in a Discord channel gets a reply from the bot on the
  original message, while **Reply when it's done** is on.
- Declining and deleting send nothing.
- Reporters merged into a signal that is already Done get the message.
- Each message fires the `message.created` [webhook](https://docs.median.sh/api/webhooks).

## Merge duplicates

Merging has no button in the dashboard. Use the CLI, the API, MCP, or ask the
[assistant](/articles/ask-the-assistant).

| Surface | Call |
| --- | --- |
| CLI | `median signals merge <keep-id> --duplicate <remove-id>` |
| API | `POST /v1/signals/{id}/merge` with `{ "duplicateId": "..." }` |
| MCP | `median.signals.merge({ signalId, duplicateId })` |

- The signal you name first survives. The duplicate is deleted.
- Reports move to the survivor. A person on both keeps one report.
- The duplicate's title becomes an alias under "Also reported as". A signal
  keeps up to 6.
- The survivor keeps the higher priority of the two.
- The survivor takes the duplicate's Linear or GitHub issue when it has none of
  its own.
- Commits claimed for the duplicate move to the survivor.

See the [CLI reference](https://docs.median.sh/api/cli-reference) and the
[management API](https://docs.median.sh/reference/manage) for the full options.

## Delete a signal

**Delete** asks for confirmation, then removes the signal with its reports and
commit claims. Nobody is messaged. Issues in Linear or GitHub stay as they are.

New signals fire `bug.created` or `suggestion.created`, and changes fire
`signal.updated`. See [Webhooks](https://docs.median.sh/api/webhooks).
