---
title: Bug Report Intake
---

# Bug Report Intake

## What it is

**One-line:** Bug reports flow into Omniscio two ways: testers email `[BUG: <slug>]` to your intake address (your Omniscio agent address, the AgentMail inbox, or your connected Gmail account; auto-spawns a Claude session in the matching project), or Omniscio polls a Sentry organization every 5 minutes and spawns a session per new unresolved issue. Both share one audit log, one retention sweep, and one universal **Bug Intake** sidebar virtual project.

**One exception, and only one:** a bug report or a feature idea sent from the **feedback dialog** by
someone other than the operator no longer starts an investigation at all. It is answered by a person
through its own Help Desk conversation instead (see [Report conversations](report-conversations.md)) —
one acknowledgement to the sender, a reply already drafted for support to approve, and no session. The
operator's own reports, `feedback` items and questions are unchanged, and everything below still
describes them. The decision is `shouldHandInAppReportToHelpDesk`
(`src/main/services/bug/in-app-report-helpdesk-route.ts`), and the email copy of such a report stands
down rather than racing the saved report for the spawn.


### What it is (user-visible)

#### Opt-in toggle (2026-05-23)

Bug Intake is **off by default for new installs**. Flip **Settings → Features → Enable Bug Intake** (`bugIntakeEnabled`) to turn it on. Existing installs that already have any row in `bug_intake_processed` or any configured `project_intake_sources` row get migrated ON automatically on first launch via `src/main/services/opt-in-toggle-migration.ts` (its `userHasBugIntake()` heuristic checks those two tables only).

When the toggle is off:

- The **Bug Intake sidebar row is hidden** (driven through `project-visibility.ts`).
- The **5-minute Sentry poll scheduler is a no-op** (no HTTP fetches, no candidate creation, no auto-spawns, no audit rows).
- The **email-intake fast-path** in the Gmail/IMAP inbound pipeline is bypassed — `[BUG: <slug>]` and `[FR: <slug>]` emails fall through to the regular inbox unrouted.
- Every Bug Intake IPC handler (sources CRUD, pending list, dismiss / spawn-now, audit list, manual run) rejects with `{success: false, error: "Bug Intake is disabled..."}`.
- The Edit-Project **"Auto-triage incoming bug reports"** toggle UI still renders, but writes to it are ignored while the feature toggle is off.

Settings search keywords "bug intake", "bug reports", "sentry intake", "auto-spawn bugs" all land on the feature toggle.

#### Sidebar entry

A sidebar entry — **Bug Intake** (Bug icon, in the Omniscio built-ins group) — owns the entire bug-report inbox. Four always-on tabs, plus a fifth gated one:

- **Sources** — per-project intake source configuration (CRUD) for **Sentry** and **GitHub**. One row per `(project, source)`; UNIQUE-constrained, so a project can have one Sentry source and one GitHub source. A Sentry row shows the org/project pair and the linked Sentry credential; a GitHub row shows the owner/repo (plus an optional label filter) and needs no credential — it uses the `gh` CLI login. Both show last poll / last success / last error timestamps and toggles for **Enabled** and **Auto-spawn**.
- **Review** — held + unrouted candidates awaiting a decision: issues the triage gate held rather than auto-spawning, plus `autoSpawn = false` matches and **first-connect-cap of 5** deferrals (the 5 most-recently-seen spawn, the rest land here so a new source doesn't carpet-bomb your inbox). Each row carries a primary **Spawn** and a single **Ignore ▾** menu that folds the three suppress actions — **Ignore this issue**, **Ignore all like this** (the error class), and **Dismiss (one-off)**. Decisions render as plain-English labels ("Held for review", "Dismissed", "Routed to fix"), never the raw DB value.
- **Ignored** — active ignore rules (one per "Ignore this" / "Ignore class"), each with an **Un-ignore** button to resume spawning future occurrences.
- **Audit** — read-only listing of the last 500 decisions across every intake source (email + Sentry + GitHub). Decision values: `routed`, `unrouted`, `cooldown`, `cap`, `invalid`, `failed`, `manually_dismissed`. (A Sentry first-connect-cap deferral is recorded as `unrouted` with `reason='pending review'`, not a distinct decision value.) Any row whose decision spawned a session carries an **Open session** link that jumps straight to it — the fix/intake session lives under its _owning_ project, so this routes through the shared `navigateToSession` helper (switches project + selects it, lazy-loads an archived/out-of-project session, and shows an honest "Session not found" toast for a stale id).
- **Sessions** (gated, in-development) — the fix sessions Bug Intake spawned, gathered across their owning repo projects. See "Sessions tab" below.
- **Tracker** (gated, in-development) — the durable backlog: every report as an item with a status you move (New → Triaged → Building → Done / Won't do). See "Tracker tab" below.

The header carries a global **Pause all polling** toggle bound to the `intakeGloballyPaused` setting — flipping it stops every source from starting a session, instantly, without losing per-source enabled state. **Enable Bug Intake** switched off does the same. What each source does meanwhile (`src/main/services/intake/intake-switches.ts`, one rule for all of them):

- **Sentry and GitHub** skip their polls.
- **In-app reports** wait in the relay's queue and start as soon as intake resumes.
- **Gmail bug reports** are left unread by intake, and are picked up once intake resumes if they are still inside the poll's search window.
- **Bug-report emails to your agent address** get the ordinary email handling — the same thing that happens when the daily cap is reached, because an email cannot be held for later.

The email leg's own 30-second per-slug cooldown and per-project daily cap count **email reports only**; in-app reports have their own budget and never use them up.

**Entry-aware layout.** The same panel renders two ways. In BOTH modes the settings chrome — the triage/dedup **policy toggles** plus the "Appended investigation instructions" card — starts **collapsed** behind a compact **Settings** disclosure so the operational tabs lead; only the **Pause all polling** switch stays in the header. Opened from the **sidebar** (or settings-search / a tour) it defaults to the **Sources** tab; opened from the inbox's **"+N more" overflow card** (the held-issue roll-up) it lands on **Review** (the items). One click on **Settings** reveals the policy panel inline in either mode. (A class/single held-issue card opens the per-issue **detail pane** instead — see the next section — and never opens this panel.) A one-shot intent set right before that overflow bounce (`src/renderer/src/features/bug-intake/intake-view-entry.ts`) decides which — and ONLY that path triggers the focused view, so every other entry stays full. Invariants: `bug-intake-inbox-view-contract.md`.

The original email-based intake (`[BUG: <slug>]` subject routing — covered below) is still active and writes into the same audit table.

#### Sessions tab (gated, in-development)

The fifth tab, **Sessions**, lists the fix sessions Bug Intake has spawned — gathered **across their owning repo projects** by `source` label (`sentry_intake`, `github_intake`, `in_app_amc_intake`, `in_app_amc_assigned_intake` — a report owner routing assigned to you — and `bug_intake`; see `src/shared/bug-intake-session-source.ts`), not by project. A fix session's workdir is derived from the project it was routed into, so it can never live inside the `__bug_intake__` virtual project the way an SMS or Calendar session lives inside its own — this makes the tab the shared session-host seam's first **cross-project** host (`src/renderer/src/features/bug-intake/bug-intake-session-host.ts`).

The **email concierge** (`in_app_amc_liaison`) is deliberately **not** listed here, even though it is spawned through the same intake path: it emails the reporter back rather than fixing anything, and it is linked to the fix session it accompanies, so showing both would double-count one report as two agents on the bug.

- Opening a session shows its chat **beside** the list rather than replacing the Bug Intake panel: a resizable rail (`src/renderer/src/features/bug-intake/BugIntakeSessionsRail.tsx`) takes over the sessions-sidebar slot for whichever repo project owns that session, with a **"← Bug Intake"** button that clears the chat and returns you to the full panel.
- Rows behave like any other session row: right-click for the shared management menu (pause / archive / rename / pin / snooze), keyboard nav, and the X to close.
- There is **no "+ New session" affordance** on this tab — the intake engine spawns these sessions itself from triaged bugs, so a user cannot spawn a paid session from here.
- The tab label carries **no running/needs-you count badge**, unlike the other Sessions tabs. That badge counts sessions _inside_ one project, and these sessions live in their owning repos — so it could only ever read zero. It is left off rather than shown as decoration.
- The tab (and the split-view rail) is gated behind the in-development `bug-intake-sessions` flag (setting `bugIntakeSessionsEnabled`) — hidden until shipped, same mechanism as every other Lab feature; see Omniscio’s unreleased-feature (“Lab”) gate.
- **Archived fix sessions show too**, in the list's **Archived** section — so archiving one no longer makes it disappear from Bug Intake. This needs its own machinery: archived sessions are moved out of the live session list, and every other "load the archived ones" path is scoped to a single project — useless when your fix sessions are scattered across every repo. Omniscio therefore asks the database directly for _archived sessions with an intake source, across all projects_ (`session:list-archived-by-source`). Filtering happens in the database rather than in the app on purpose: a busy workspace can have hundreds of unrelated archived sessions, and fetching "the most recent N archived" would let them crowd out every fix session, silently showing you none.

#### Tracker tab (gated, in-development)

The **Tracker** tab is the durable backlog. Everything else in this panel answers "what
arrived and how was it routed"; the Tracker answers **"what is on my plate and where does
each thing stand"**.

Every incoming report — from all four doors — becomes one item with a **status you move**:
**New → Triaged → Building → Done**, plus **Won't do**, so declining a feature request is
not recorded as having finished it. Each row shows the report's type (Bug / Feature request
/ Feedback / Question), its source as a readable label (In-app / Email / Sentry / GitHub —
never the raw stored value), the report's real title, its age, the status control, and an
**Open session** link when a real fix session exists.

Why it needed building: `bug_intake_processed` was an append-only **idempotency ledger** —
its own migration comment says so. A report arrived, a session spawned, and the row was a
receipt. Nothing was ever open or closed, nothing had a priority or an owner, and an
un-worked row was deleted after 90 days. The tracker adds the missing half without a second
table: every door already writes exactly one row through one chokepoint, so a parallel list
would duplicate that wiring and create a second answer to "what reports exist".

- **Two things happen on their own.** A report that spawns a **real** fix session advances
  to **Building** by itself. And an item that is not finished is **never** deleted by the
  retention sweep — `done` / `wont_do` items and machine noise still age out as before, so
  the change only ever retains rows and cannot weaken the paid-re-spawn dedup guarantee.
- **`assigned:` is not a session.** It is an owner-routing sentinel, so a merely-assigned report
  stays **New** and shows no session link — otherwise every hand-off would falsely read as being
  worked on. (`forwarded:` was the v1 email-forward sentinel; that path is retired, but the row
  value is still read so reports handed off before it was removed keep reading correctly.) Once
  the owner's app has taken an in-app report, the
  operator's card for it closes to **Done** (the owner's own board carries the work from there:
  a teammate's pulled report is claimed onto THEIR Tracker exactly like the operator's).
- **A report that failed to start keeps its card.** Recording the failure never wipes the card;
  it stays **New** until the next app start, when an emailed report's claim is cleared so the
  mail can be fetched and tried again.
- **Every Help Desk ticket is on the Help Desk tab** — including one past its sender's daily limit, one
  whose escalation failed and one whose investigation could not start. A [BUG:]/[FR:] email the
  router declined becomes that ticket's card (one card, not two), and a Help Desk card is never
  cleared at app start.
- **Every Get Help question waiting on you is on the Help Desk tab too.** When a user escalates a
  Get Help conversation to the developer, it gets one card (source **Get Help**, titled by the
  question they asked). The card closes when the conversation is answered or resolved — from this
  computer or any other admin seat — and reopens if the person writes back. A card you mark Done
  yourself stays Done until they do: restarting the app or signing back in never brings it back.
  A card you moved to Triaged or Building yourself stays where you put it. A conversation that is really a Help Desk
  email ticket keeps that ticket's one card instead of getting a second, however old the ticket is.
- **Machine noise never reaches the board.** Rows the system ignored, consolidated, or that
  were dismissed are excluded, and they are not purge-exempt.
- **Existing reports appear immediately.** The claim is `ON CONFLICT DO NOTHING`, so an
  existing row can never gain a status from a later insert — the migration backfills the
  newest 5,000 real reports (Building where a real session is attached, else New) or the
  board would open empty on every existing install.
- **Terminal items are hidden by default** behind a **Show finished** toggle, so the tab
  reads as a backlog rather than a log. That toggle is a server-side filter: flipping it
  re-fetches.
- **Status changes work from the command line too** — `POST /intake/tracker/:source/:externalId/status`,
  sharing one schema and one write path with the button. Both channels are blocked from the
  mobile/web bridge.
- Gated behind the in-development `report-tracker` flag (setting `reportTrackerEnabled`) —
  hidden until shipped; see Omniscio’s unreleased-feature (“Lab”) gate.

Invariants T1–T20: `report-tracker-contract.md`.

#### Held issues also surface as read-only inbox cards (2026-06-03)

Every held Sentry issue (a triage hold or first-connect deferral, no session yet) ALSO appears in the unified **Inbox** under a **BUG INTAKE** header (red bug icon), so you can triage without opening the Bug Intake panel. The cards are **read-only** — a severity dot (red = fatal, amber otherwise), the human-readable error title, and a `×N` when several issues of one class are grouped. **Tapping a card opens a per-issue detail pane.** It leads with a pre-digested headline (severity + "seen N times" + "first seen …") so you read _how bad / how often / worth it_ at a glance, then the fields (project, culprit, first/last seen) in the shared `DetailsBlock` card and a **View original issue** link. For the errors Omniscio can honestly explain — its own internal **"Child process gone"** crash captures — a short plain-language **"What happened"** note sits ABOVE all of that, translating the raw title (e.g. _"Utility (killed)"_) into which background helper stopped, what the reason means, and whether it needs action; every other error shows the fields alone (no invented explanation). The note is derived by `src/renderer/src/features/inbox/detail/describe-child-process-crash.ts` and rendered as prose OUTSIDE the card (per `approval-standard-ui-contract.md` I3). The actions live here — **Spawn** (the standard "new session" icon button, leftmost), **Ignore this**, and **Always ignore** — and a plain-English line states that spawning **starts a paid Claude session**. Spawn is billable and fires immediately (a click OR the `N` hotkey — the app-wide New-session key) — there is no confirmation dialog; an in-flight guard still collapses a fast double-press to one spawn, and the backend is idempotent by issue id, so a duplicate can't start a second billable session. **Dismiss** is the pane's top-right **Archive** button (clearing every issue in the group; it confirms first above 5), not a bottom-row button. **Always ignore**, which permanently silences a whole error class, still confirms. Each button's keyboard shortcut shows on hover (its tooltip includes the key); there is no always-on hotkey legend. Keeping the inbox cards read-only means a stray tap on a card only opens the detail pane — it never spawns. A `+N more` overflow card past 25 held cards bounces to the Bug Intake panel instead. A bug whose Sentry source maps to an Omniscio project now appears under that **repo's** group in the Inbox and in its **Needs You** section + attention badge; an unmapped bug stays under the **BUG INTAKE** header.

**Titles self-heal.** A held row captured without a title (a first-connect deferral, or a legacy pre-triage row) shows a labeled **"Sentry issue #&lt;id&gt;"** rather than a bare number; the 5-minute Sentry poll backfills the real error title onto it (immediately if you hit **Poll now**), reusing the issues it already fetched — no extra API calls. An issue you've since resolved in Sentry keeps the labeled id (it's dropped off Sentry's unresolved feed). Implementation: the read-only card is rendered by `src/renderer/src/features/dashboard/UnifiedInboxRow.tsx` (it dispatches to the intake-triage layout on the `InboxIntakeTriageCardMeta` discriminator built in `src/renderer/src/stores/intake-inbox-items.ts`), with the per-issue detail pane in `src/renderer/src/features/inbox/detail/BugIntakeIssueDetailPane.tsx`; the title backfill (`backfillHeldRowTitles`) in `src/main/services/intake/intake-sentry-service.ts`. The `group-by-class-projection-with-a-readable-title` through
`friendly-group-header-and-self-healing-titles` invariants in `sentry-triage-contract.md`.

#### Less noise: dev-only HMR errors and already-investigated classes (2026-06-05)

Three refinements keep the queue from filling with non-actionable noise:

- **Dev-only Vite HMR errors never reach intake.** A stale-module-graph error like `The requested module '/@fs/…' does not provide an export named 'X'` (thrown only by the `npm run dev` server when a hot-reloaded export goes stale — it cannot occur in a packaged build) is dropped at the Sentry source, so it never spawns an investigation or raises a held card. This is a 4th class added to `isTransientDevNoise` (`src/main/services/sentry-init.ts`) alongside the existing Fast-Refresh / dynamic-import / preload-channel classes — non-packaged runs only; production capture is untouched. History: `Fast-Refresh dev-noise postmortem`.
- **An already-investigated error-class won't re-nag.** A borderline / low-severity issue whose class was already spawned for investigation within the last 24h — **even if that session has since ended** — is quietly recorded `consolidated` instead of adding another held inbox card. It never suppresses a real, above-bar spawn; only the redundant attention nag. Detail: `recentlySpawnedForClass` + `an-already-investigated-class-consolidates-instead-of-holding`
  and `recently-spawned-for-class-spans-any-session-status` in `sentry-triage-contract.md`.
- **Omniscio's own telemetry-upload permission failures never reach intake (2026-06-18).** When Omniscio's anonymized fleet telemetry (or the bug-report / share-mirror Firestore transports) can't upload because of a permissions / credentials denial (`PERMISSION_DENIED` / `UNAUTHENTICATED`), that benign plumbing failure is dropped at the Sentry source instead of spawning an investigation of Omniscio's **own** telemetry. The write is already swallowed (fire-and-forget) and the app is unaffected; a genuine code bug in those transports still reports normally (it carries no permission token). Dropped in **every** build (the primary `npm run dev` install reports too). This is `isInternalTelemetryPermissionError` (`src/main/services/sentry-init.ts`), alongside the dev-noise + transient-network drops. Detail: invariant I18 in `crash-reporting-contract.md`. Origin: Sentry issue 7559601870 — a `[FleetTelemetry] write crash failed: 7 PERMISSION_DENIED` that auto-spawned a paid Opus investigation of itself.

## Where to find it

Bug Intake lives in your projects sidebar as its own row named **Bug Intake** — a built-in row rather than one of your real repos, and nothing filed inside it belongs to a project of yours. Opening it fills the main panel with the bug-report inbox.

That panel carries four always-on tabs plus gated ones: **Sources**, where each tracked project's Sentry or GitHub feed is set up; **Review**, the reports waiting on your decision; **Ignored**, the rules that keep an error class quiet; and **Audit**, the last 500 routing decisions. The header holds a **Pause all polling** switch and a **Dedup pre-check** toggle, and a compact **Settings** disclosure opens the triage policy. The same held reports also surface in the unified **Inbox** under a **BUG INTAKE** header, so you can triage without opening the panel at all.

The feature itself is switched on in **Settings → Features**, under **"Enable Bug Intake"**.

## How it behaves

### Email intake — how a tester sends a bug report

A small team of trusted testers emails the operator's intake address: the Omniscio agent address, the AgentMail inbox, or the connected Gmail account (see "Gmail intake" below). Their subject line contains `[BUG: <slug>]` (for bugs) or `[FR: <slug>]` (for feature requests). Omniscio matches the slug to a project that has bug intake enabled and spawns a new Claude Code session in that project, pre-seeded with the email envelope and body inside an "untrusted external data" fence. Unmatched emails land in the unified inbox tagged "Unrouted bug report".

Everything the report needs is handled on the route it arrived on — its attachments are read from that mailbox, the "we got it" note and the findings reply go back out through it. **Who may send a report depends on that route:**

- **Omniscio agent address** — anyone on the Email Inbound approved list, plus you and your Agent Email approved senders. Setting your agent address to accept mail from "Anyone" lets strangers _talk_ to your agent, but never lets them start an investigation inside your code: their `[BUG:]` mail is handled like any other email.
- **AgentMail inbox** — the Email Inbound approved list, or anyone once an Inbox Secret Token is set (every AgentMail message must then carry it).
- **Gmail** — the Email Inbound approved list only; the secret token is never checked there, so it admits nobody.
- **Public support address** — never. Mail sent there always becomes a Help Desk ticket that a person answers, even when its subject carries `[BUG: <slug>]`, because a routed report emails its findings back automatically.

```
[BUG: amc] Login crashes on save
[FR: amc] Add dark-mode toggle to settings
```

The app's own feedback form writes the same shape for its other two report types, so the email copy of an in-app report routes too: `[FEEDBACK: amc] …` and `[QUESTION: amc] …`. These tags are never translated — a translated tag is one the router cannot read (from 2026-07-31 to 2026-09-25 feedback and questions were sent as "[Omniscio Feedback]", or a translation of it, and their email copies went unrouted). The older `[AMC Feedback]` / `[Omniscio Feedback]` forms are still accepted from installs that send them; a copy an install sent in another language during that window cannot be recognised and stays unrouted (its Firestore report is unaffected).

- Slug must be `[a-z0-9-]{1,64}` — anything outside is rejected at parse time
- Match is case-insensitive (`[bug: Omniscio]` works the same as `[BUG: amc]`)
- Subject must NOT start with `Re:` or `Fwd:` — the strict `^\[` anchor in the parser rejects them, so replies thread back to the spawned session instead of spawning a second one
- Body is sanitized to plain text (HTML stripped), truncated to 256 KB with head 200 KB + tail 50 KB preserved (error-stack tails win the tie)

#### Enabling email intake on a project

1. Open the project's gear menu → **Edit Project**
2. Scroll to **"Auto-triage incoming bug reports"** and flip the toggle ON
3. The slug input appears with a default derived from the slugified project name; edit if desired
4. **Save** — the slug is frozen on the project row and does NOT auto-update if you rename the project later

External AIs (using the `omniscio-control` skill) can enable, change, or disable email bug intake via the approval-gated `PATCH /project/:id/bug-intake` CLI route. Each PATCH lands as a `pending` row in the Omniscio inbox and requires the operator's approval before the slug change applies.

### Email intake rules — route a specific sender + slug to a specific repo

By default the **slug alone** picks the project (`[BUG: amc]` → the project whose `bug_intake_slug` is `amc`), and the sender only matters for the optional global approved-senders allow-list. **Email intake rules** add **sender-aware routing**: you bind a specific **from-address + slug** to a specific **project**, and an email matching that pair routes there.

A rule is managed in the **Bug Intake → Sources** tab, in the **Email rules** section beneath the Sentry sources. Each rule is `From address + Slug → Project` with an enable toggle. Add one with **+ Add email rule** (pick the from-address, the slug, and the target project from your real projects).

How a rule changes intake, two ways:

- **Routing.** When an email arrives, Omniscio first looks for an enabled rule matching the normalized from-address **and** the subject slug. If one matches, the session spawns in **that rule's project** — overriding the default `bug_intake_slug` lookup. If no rule matches, routing falls back to today's slug→project behavior (so nothing you already rely on breaks).
- **Admission.** A sender that has any enabled rule is **auto-admitted** even if it is not in the global approved-senders allow-list — so you don't have to add the address in two places. This is purely additive: if your allow-list is empty (the "accept everyone who passes the spam prescreen" mode), adding a rule does **not** flip it into allow-list-only mode and silently block everyone else.

Important behaviors:

- **The `[BUG: <slug>]` / `[FR: <slug>]` subject is still required.** A rule decides _where_ a detected slug goes and _who_ is allowed; it does not make a tagless email route. An email from a rule's sender with no subject tag is not intake-routed.
- **One target per (sender, slug).** A given from-address + slug pair maps to exactly one project (a UNIQUE constraint); the same sender can route different slugs to different projects, and different senders can share a slug to different projects.
- **From-address is matched case-insensitively** and normalized (a `Bugs <bugs@acme.com>` header matches a rule stored as `bugs@acme.com`).
- **A rule pointing at a removed (soft-deleted) project goes inert** — routing falls back to the slug lookup, and the rule shows "(project removed)" in the list.
- **From headers are spoofable** — rules are a routing convenience, not an authentication boundary. The spam/prompt-injection **prescreen still runs on every inbound**, and the optional secret-token gate still applies; those are the security layers.

## Related

- Email inbound architecture — the prescreen + AgentMail polling pipeline the email path hooks into
- [Automation credentials](automation-credentials.md) — where the Sentry token lives (kind `sentry`, OS-keyring encrypted)
- Feature events — the analytics table this writes to
- [RepoGuard](repoguard.md) — sibling plugin that uses the same Sentry HTTP client for its security/runtime scanner
- [Report conversations](report-conversations.md) — every in-app report also opens a Help Desk conversation, so support can reply inside the app and the reporter reads it under "Your reports"

This page is split across three further parts. [Part 2](bug-report-intake-part-2.md) covers the three channels Omniscio watches for you — Gmail, Sentry and GitHub — and how each source is set up and kept healthy. [Part 3](bug-report-intake-part-3.md) covers what an auto-spawned investigation receives, and how repeat reports of one issue collapse into a single investigation. [Part 4](bug-report-intake-part-4.md) covers the decision table you see when a report does not route, the analytics, what is deliberately out of scope, and the code map. For the alert cards a stalled intake raises, see [inbox alerts](inbox-alerts.md); for the mail plumbing behind the email channel, see [Agent email](agent-email.md).
