Sentry Triage Gate
A decision step between Omniscio noticing a new Sentry issue and Omniscio spawning an investigation session for it. The triage gate ignores what you muted, consolidates onto an investigation already running, and grades the rest by severity — and by default it does not HOLD: an issue it would otherwise park starts a session instead, so one underlying bug split into many issue IDs still cannot spawn a pile of near-duplicate paid sessions while nothing waits in an inbox for a click.
What it is
What it is. A decision step that runs between "Omniscio noticed a new Sentry issue"
and "Omniscio spawns a Claude investigation session for it." Before this feature, every
new Sentry issue with auto-spawn on became a full session immediately — and because
Sentry splits one underlying bug into many distinct issue IDs, you'd get a pile of
near-duplicate sessions burning tokens (e.g. four sessions for the same
Cannot read properties of null error in one minute). The triage gate decides
whether each issue is worth a session at all, and holds the rest in a review
queue.
This page is self-contained: it describes the behavior in prose before pointing at code, so an outside AI given just this page can answer questions about it.
Where to find it
Where you see it
The Bug Intake sidebar virtual project (the same place Sentry sources are configured). Its header carries the triage controls, and its tabs changed:
- Sources — your Sentry org/project connections (unchanged).
- Review (was "Pending") — issues the gate is holding: it didn't auto-spawn them, and it's asking you to decide. Each row shows the real error title, level, and event count, plus four buttons: Spawn (start a session now), Ignore this (never spawn for this exact Sentry issue again), Ignore class (never spawn for this kind of error again), and Dismiss (a one-off, not a permanent rule). If you click Spawn on an issue whose error class already has a live investigation running, Omniscio opens that existing session instead of starting a second one — and drops a short inbox note saying it consolidated — so a manual Spawn can't accidentally double up on a class you (or the gate) already started. This tab is empty while Auto-spawn held issues is on (the default) — there is nothing held to review, not a list that was hidden.
- Ignored (new) — your active ignore rules, each with an Un-ignore button.
- Audit — the last 500 decisions, now including the new outcomes
(
ignored/consolidated/triage).
How it behaves
Held issues also reach the unified inbox (2026-06-03)
Only while Auto-spawn held issues is OFF — with the default on there are no held issues, so no cards. When it is off, you don't have to open Bug Intake to act on a held issue: held Sentry issues ALSO appear in the unified inbox as cards, grouped by error type, so they reach you on any view (including mobile) the moment the gate holds them. Each card carries the same actions as the Review tab:
- A class card (N issues of one error type) → Spawn (one investigation for the class), Always ignore (permanently mute the whole class), Dismiss all (one-off dismiss of every issue in the class; confirms first above 5).
- A single card (a one-off issue with no class key) → Spawn, Ignore this, Dismiss.
- An overflow card ("+N more awaiting review") appears past 25 cards and deep-links to the Review tab.
Opening a card shows its detail pane, where the actions also have one-key shortcuts while the pane is open — N Spawn · T Always ignore · G Ignore this (only the ones that apply to that card). The keys show on hover (each button's tooltip includes its key), not as an always-on legend. Spawn is the standard New session icon button and its key is N — the same key that starts a New session everywhere else in the app — because spawning an investigation is just opening a new session for that error. N is the only one that starts a paid session, so it's guarded against an accidental double-press (and Ctrl+Enter is deliberately NOT wired to it). Dismiss is the pane's top-right Archive button (it clears every issue in the group and confirms first above 5), so there is no separate bottom-row Dismiss button or D key.
The cards self-clear: every refresh re-queries the held list, so a card vanishes
the instant its issue is spawned, dismissed, ignored, or auto-promoted — there is no
separate "clear" event, and nothing new is stored (the inbox is just a second window
onto the same held rows). It only shows while Bug Intake is enabled. Contract +
invariants: sentry-triage-contract.md
("Inbox Review surface" – the-inbox-card-self-clears-by-requery through
registered-like-any-inbox-source).
Issues held by the safety screen
Once the gate decides an issue deserves a session, one more check runs before the agent starts: the bug intake safety screen, a very cheap AI that reads what the agent would be handed and looks for text trying to give the agent orders. An issue it flags, or cannot check, shows in Review and as an inbox card of its own with a Held by safety screen badge and the reason. Unlike an ordinary held issue it is never auto-promoted or judged by the AI pass: only you can start it (Spawn anyway) or dismiss it. Spawn anyway still opens an investigation already running for the same error instead of starting a second one. The Bug intake safety screen page has the details.
How the gate decides (cheapest check first, first match wins)
First, an unconditional safety drop (before any rule below, and even when the master
switch is off). Omniscio's own background telemetry has three helpers that mirror their
failures to Sentry. When one reports a Firestore permission error (PERMISSION_DENIED /
UNAUTHENTICATED), that's harmless plumbing config — not an app bug — so Omniscio records it
as ignored and never spawns an investigation. Without this, a machine running an older
copy of Omniscio could flood the shared error tracker with that noise, trip the "50+ events =
investigate" rule below, and spawn a paid session to investigate Omniscio's own telemetry
(a self-referential loop). A genuine code bug in those helpers (no permission keyword)
still gets investigated, and so does a permission error from anywhere else. See the
contract invariant internal-telemetry-noise-is-dropped-pre-gate.
For each new Sentry issue, Omniscio runs these rules in order and stops at the first one that matches:
Ignore list. If you've ignored this exact issue, or its class (the kind of error — same code location + title), it's skipped. (free)
Consolidate. If a session investigating this same error class is already open, the new issue is held instead of spawning a duplicate. This alone fixes the "four identical sessions at once" problem — the first one spawns, the rest consolidate. (free)
Severity bar — plus evidence it's real. Sentry assigns each issue a level (
fatal/error/warning/info/debug). Issues clearly below your bar (defaulterror) are held for review. At or above the bar, Omniscio also wants some evidence the problem is real before spending on an investigation: it spawns if the issue is a regression, or if it has fired enough times — twice for an issue above the bar, 50+ for one right at it. Otherwise it's held. (free)Why "above the bar" still needs a second sighting. Every native crash reaches Sentry marked
fatal, which is above the default bar — so before this rule, a single crash on ONE person's machine, on any version, started a paid investigation immediately. In one two-week sample, 10 of 15 fatal issues were single-event. A crash that happens once and never again is almost always an environmental blip (the machine was out of memory, the GPU driver hiccuped), which is the same conclusion Omniscio had already reached for crashes reported the other way. Nothing is lost: a held crash still shows up in Review and the inbox, still gets the cheap AI second look, and auto-promotes to a full session the moment it happens again.fatalstill spawns far sooner thanerror— 2 events versus 50 — so severity still means something.Hold for the AI queue. Gray-zone issues right at the bar aren't judged on the spot — they're held, and a separate AI pass looks at them every ~15 minutes (see below). The 5-minute poll spends nothing on them. (free)
Auto-spawn held issues — on by default. Anything rules 3 and 4 decided to hold starts a session anyway, right then, instead of waiting in Review for you to click Spawn. Rules 1 and 2 still win over this one, so a muted issue still never spawns and a duplicate still folds into the investigation already running. Turn the switch off and holding comes back exactly as it was.
Anything the gate doesn't spawn lands in the Review tab — where the AI queue picks it up. With Auto-spawn held issues on (the default) there is nothing to land there: the tab is empty because nothing is held, never because it was hidden.
Why rule 5 is the default. The owner's Sentry source auto-spawns, and yet issues kept arriving in his inbox as "awaiting review" cards he had to open and click. The audit trail showed 33 of them cleared by hand. Holding is still the right shape for someone watching spend closely — which is why the switch exists — but out of the box a crash should become an investigation, not a task.
The AI queue: gray-zone issues judged every ~15 minutes by Haiku
Rather than calling AI on every 5-minute poll, Omniscio batches it. Roughly every 15 minutes it takes the gray-zone (borderline) issues it's been holding and asks Haiku (a fast, cheap model) about each kind of error once — spawn / hold / ignore. A "spawn" recommendation starts one session for that error class (any other held issues of the same class fold into it, so you don't get duplicates); "hold" leaves it in Review; "ignore" leaves it in Review with a suggestion that you mute the class (it never mutes anything on its own — you confirm).
Auto-promote (held issues that get worse)
A held issue isn't forgotten. Every poll, Omniscio re-checks the issues it's holding against fresh Sentry data and promotes it to a full session automatically when either of two things happens:
- its event count crosses your auto-promote threshold (default 100 events); or
- it now clears the spawn bar — which includes a held above-bar crash that has simply happened a second time (rule 3 above).
Setting the threshold to 0 turns off the count half only. The second half keeps
working, so a crash that recurs is always picked up — that is what stops the
"second sighting" rule from ever parking a real, repeating crash forever.
The settings (Bug Intake header, when the gate is on)
| Control | Setting key | Default | What it does |
|---|---|---|---|
| Triage gate | sentryTriageEnabled |
on | Master switch. Off = every issue auto-spawns (pre-triage behavior). |
| Auto-spawn held issues | sentryTriageAutoSpawnHeld |
on | Rule 5. On = an issue the gate would hold starts a session instead, so nothing waits in Review. Off = the rest apply. |
| Consolidate | sentryTriageConsolidateByClass |
on | Hold issues whose error class already has a live session. |
| Spawn bar | sentryTriageAutoSpawnMinLevel |
error |
Severity at/above which issues auto-spawn. |
| (AI on/off) | sentryTriageAiSecondLookEnabled |
on | The ~15-min Haiku queue that judges gray-zone issues. |
| (AI cap) | sentryTriageAiDailyCapUsd |
$0.25 |
Per-day spend ceiling for the AI queue; at the cap, gray-zone issues just hold. |
| Auto-promote at | sentryTriageAutoPromoteEventCount |
100 |
Event count that auto-promotes a held issue (0 = off). |
All seven are read fresh on every poll, so toggling them takes effect without a restart. With the master switch off, the gate is a pure passthrough — behavior is exactly as it was before this feature, except the unconditional telemetry-noise safety drop above, which always applies (it runs before the gate, not inside it).
While Auto-spawn held issues is on, the panel hides the Spawn bar, Auto-promote and AI second-look rows. With nothing ever held there is no held band for them to act on, and a control that visibly does nothing is worse than no control. Turn Auto- spawn held issues off and they come straight back with their saved values.
What the AI queue costs you, and how it's bounded
With Auto-spawn held issues on (the default) the AI queue has no gray-zone band to judge, so it spends nothing. Everything below describes the behavior with the review queue ON.
- It only looks at the gray-zone (borderline) issues — not every issue.
- It runs about once every 15 minutes, not on every poll, and is deduplicated to one call per error class per run — so cost scales with how many distinct kinds of error you have, not how many issues, and not how often Omniscio polls.
- The model is Haiku (via OpenRouter), with a second model (Qwen) as an automatic backup only if Haiku errors.
- Spend is recorded to Omniscio's cost log under the source
sentry-triage-aiand capped per day. Once the cap is hit, gray-zone issues simply hold (no spend, no surprise spawns). - It requires an API-key account (the login/OAuth path can't make these calls). With no API-key account, the queue is off and gray-zone issues hold.
- It never spawns on its own uncertainty: any failure (cap hit, no account, bad response, network error) falls back to holding the issue for you.
Ignore: "this one" vs "everything like this"
- Ignore this keys on the exact Sentry issue ID. A brand-new clone of the same bug (a different ID) would still come through.
- Ignore class keys on the error's class — its normalized code location + title — so future occurrences of the same kind of error are all suppressed. This is the one that stops recurring noise. It's disabled for native crashes that have no usable class key (use "Ignore this" there).
- Ignoring is reversible: the Ignored tab's Un-ignore restores future spawning. Ignoring an issue also immediately clears any matching rows already sitting in Review.
What the spawned investigation receives
When the gate spawns a session, the investigator's prompt now includes the issue's actual stack trace and recent breadcrumbs — fetched from Sentry's latest event at spawn time, not just the title, level, and a Sentry link. Secrets and user paths are scrubbed out first. The fetch is fail-safe: if Sentry can't be reached, the session still starts with the metadata it always had (it's never blocked).
For Omniscio's own crashes (the jls-trading-co / mission-control Sentry project),
the investigator is additionally pointed at the on-disk crash logs in
<userData>/logs (main.log, heartbeat-final.log, diagnostic-report.json) —
the same forensic evidence Omniscio's crash email carries. That pointer appears only for
Omniscio's own crash source, since an investigator can't read another project's machine
logs. This is what lets an Omniscio crash captured by Sentry become a full investigation
with the real stack trace and the local logs, instead of just a one-line title.
Relationship to the existing duplicate detection
This is a new, earlier layer. The triage gate decides whether to spawn; the older 24h duplicate detection ("Dedup pre-check") runs after, on issues that do spawn, and hints the agent that a session might be a duplicate. They're independent and both on by default. See bug-report-intake.md for the duplicate-detection side.
For agents
Code pointers (for agents with repo access)
- Pure decision engine:
decideTriage/classifyBySeverity/computeSentryClassKeyinsrc/main/services/intake/intake-triage.ts— rule 5 (deps.autoSpawnHeld) is the last branch ofdecideTriage, andTriageDecision.autoSpawnedflags the spawns it caused so the poll summary can count them separately. - Gate wiring + auto-promote pass + the 15-min AI drain (Phase D):
pollSentrySourceinsrc/main/services/intake/intake-sentry-service.ts; the drain cadence (aiSweepAt/AI_QUEUE_DRAIN_INTERVAL_MS) is inintake-poll-scheduler.ts, which also readssentryTriageAutoSpawnHeldfresh on every tick. The per-poll summary line carriesauto-spawned-held=, the count of sessions rule 5 started. - Unconditional pre-gate telemetry-noise drop + its shared prefix/token source of truth
(also consumed by sentry-init's
beforeSendemit-side drop, so the two never drift):textMatchesInternalTelemetryPermissionFailureinsrc/shared/sentry-internal-telemetry-noise.ts, applied inpollSentrySourcebefore the triage gate. - AI queue evaluator:
evaluateSentryTriageAi+makeTriageQueueAiRunner(Haiku primary) insrc/main/services/intake/intake-triage-evaluator.ts. - Ignore rules:
intake_ignore_rulestable +src/main/db/queries-intake-ignore.ts. - UI:
src/renderer/src/features/bug-intake/BugIntakeView.tsx. - Schema: dated ledger migration (
triage_class_key+triage_metacolumns,intake_ignore_rulestable). - Invariants + safe-change rules:
.claude/memory/contracts/sentry-triage-contract.md.
Related
bug-report-intake.md covers the older duplicate detection that runs after the gate, on issues that do spawn, and hints the investigating agent that a session may be a duplicate. To find anything else this library holds, start from INDEX.md.
Last verified 2026-09-30