Omniscio documentation
Browse all documentation
  1. Getting Started13
  2. Sessions & Agents115
  3. Inbox & Notifications59
  4. Projects & Tasks95
  5. Automation & Scheduling75
  6. Knowledge & Memory26
  7. AI Features60
  8. Integrations100
  9. Plugins & Marketplace33
  10. Cloud & Teams56
  11. Settings & Customization58
  12. Account & Billing28
  13. Troubleshooting84
  14. CLI & API Reference22
  15. Legal & Policies4
  16. Uncategorised22

Bug Report Intake (part 4)

Part 4 of the Bug Report Intake page: the table of decisions you can see when a report does not route, the analytics events intake records, what is deliberately out of scope for the first version, and a map of every file and route behind the feature for readers who have the repository open.

What it is

This is part 4 of the Bug Report Intake page. It is the reference half: the table of decisions you can see when a report does not route, the analytics events intake records, what is deliberately out of scope for the first version, and a map of every file and route behind the feature for readers who have the repository open.

Where to find it

Nothing in this part is a place you visit — it is the reference behind the panel described on the earlier parts. Keep part 1 open for the Bug Intake sidebar row, part 2 for the Sources tab, and part 3 for what a spawned session receives.

How it behaves

Failure modes — what you'll see

Decision When it fires Tag / row state Session spawn?
routed Email: match + safe + under cap + not in cooldown Routed to <Project Name> yes
unrouted Email: subject parsed but no project has that slug Unrouted bug report no
cooldown Email: same slug fired in last 30s Bug intake — cooldown for <slug> (30s) no
cap Email: project hit bugIntakeDailyCap for the day Bug intake — daily cap reached no
invalid Email: body sanitized to empty Bug intake — empty body no
failed Spawn (DB insert or CLI launch) threw Bug intake failed: <reason> attempted; soft-deleted on launch failure
consolidated (email) Email: the copy of an in-app report whose in-app twin already claimed it Audit — Merged into open issue no — the in-app report's session has it
screen_held In-app, teammate, GitHub or Sentry: the safety screen flagged the report or could not check it Review tab + its own inbox card — Held by safety screen, the reason, Spawn anyway / Dismiss no — only you can start it
unrouted (pending review) Sentry: source's first poll exceeded 5 unresolved issues Review tab — Spawn now / Dismiss no
manually_dismissed Sentry: user clicked Dismiss in Review Audit only no
routed (Sentry) Sentry: autoSpawn on, new issue, dedupe row claimed Audit; session linked yes
(no row) Email: subject didn't match the regex; or Sentry: issue already had a bug_intake_processed row none no

Analytics

Successful routes emit a bug_intake_routed feature event with { decision, reportType } metadata for usage tracking. Duplicate detection adds two more: bug_intake_dedup_hint_emitted (Layer 1 prepended a hint) and bug_intake_self_dedup (Layer 2 archived a confirmed duplicate) — source + ids only, never report text. No PII (sender, body, Sentry payload) is recorded in the analytics table. The full audit trail lives in bug_intake_processed with project FK, slug, decision, reason, source kind (email | sentry | github), external_id, external_url, severity, the dedup signature, and the spawned session_id for forensics.

What's NOT in v1 (Sentry vertical, this PR)

  • GitHub manual-spawn from Review + first-connect-deferral audit rows: GitHub auto-spawn polling now ships (see "GitHub intake" above), but the Review-tab Spawn now button stays Sentry-only (a report the safety screen held is the exception: its Spawn anyway works for every source — see the Bug intake safety screen page), and GitHub's first-connect cap silently defers the overflow (no Review row) instead of recording deferrals the way Sentry does. Label-based report-type inference, issue-comment reply-back, multi-repo single sources, and per-source rate-limit backoff are also out of scope for the GitHub v1.
  • Per-project Sentry filtering (level/environment/release) — every unresolved issue is a candidate
  • Per-source prompt override — you can append your own instructions globally and per project (see "Appended investigation instructions" above), but not per individual Sentry source; the built-in investigation framing/preamble itself is fixed across email + Sentry
  • Domain-wildcard email rules (e.g. *@acme.com) — email intake rules match an exact (sender, slug) pair only; a global approved-senders allow-list + per-(sender, slug) rules (see "Email intake rules" above) already exist, but wildcards do not
  • Web form intake — email + Sentry only
  • Auto-reply to unrouted senders — they appear in the inbox tagged unrouted
  • Multi-org Sentry under one credential — one (project, source) row per project today

For agents

Where this lives in code

  • Sidebar virtual project: BUG_INTAKE_PROJECT_ID = '__bug_intake__' in src/shared/virtual-project-ids.ts
  • View / form: src/renderer/src/features/bug-intake/BugIntakeView.tsx + src/renderer/src/features/bug-intake/BugIntakeSourceForm.tsx
  • Shared types + Zod schemas: src/main/services/intake/intake-types.ts — sentryConfigSchema, githubConfigSchema, every IPC schema, and the renderer-facing DTOs (ProjectIntakeSourceDto, IntakeAuditRow)
  • Sentry HTTP client (shared with RepoGuard plugin): src/main/services/intake/intake-sentry-client.ts — fetchUnresolvedSentryIssues, testSentryConnection
  • Sentry service (poll + dedupe + spawn + manual spawn): src/main/services/intake/intake-sentry-service.ts — pollSentrySource, manuallySpawnSentryIntake
  • Event flattener: src/main/services/intake/intake-sentry-flatten.ts — flattenSentryEventForPrompt, buildSentrySessionName
  • Poll scheduler: src/main/services/intake/intake-poll-scheduler.ts — IntakePollScheduler class, initIntakePollScheduler, refreshIntakePollScheduler, 5-min cadence + jitter, per-source mutex, 24h escalation
  • Durable broken-source alert: src/main/services/intake/intake-source-alert.ts (maybeRaiseIntakeSourceAlert / clearIntakeSourceAlert / reconcileIntakeSourceAlerts, INTAKE_SOURCE_ALERT_THRESHOLD_MS) + shared dedupKey src/shared/alert-features/intake-source-alert.ts + listActiveByDedupKeyPrefix in queries-inbox-alerts.ts; wired into the scheduler tick (raise on fail / clear on success / reconcile on start). Contract: intake-source-alert-contract.md
  • Shared spawn entrypoint (email + Sentry): src/main/services/intake/intake-spawn.ts — spawnIntakeSession (accepts images?: StdinImageInput[], forwarded to processManager.launch)
  • In-app feedback screenshots → investigator: writer embeds Vision images inline + budget-gated via selectScreenshotsForFirestore / MAX_DOC_SCREENSHOT_BASE64_BYTES in firestore-bug-report-client.ts; the operator-side listener maps doc.screenshots → StdinImageInput[] and threads them through spawnIntakeSession (fixing session only, never the email liaison) in firestore-bug-report-listener.ts. writer-embeds-inline-budget-gated-vision-only → spawn-threads-images-to-the-cli in intake-dedup-contract.md
  • Operator-poll stuck alarm: bug-report-operator-alert.ts (maybeRaiseOperatorPollAlert / clearOperatorPollAlert / operatorPollFailingMs, OPERATOR_POLL_ALERT_THRESHOLD_MS, fixed key bug-report-operator-poll-broken); wired into firestore-bug-report-listener.ts pollOnce / startPolling / stopPolling / init (raise on a failed poll, clear on success / operator-off). Invariant operator-poll-broken-alert in operator-console-relay-contract.md; tests bug-report-operator-alert.test.ts
  • Email router (legacy): src/main/services/bug-report-router.ts — parseBugIntakeSubject, sanitizeAndTruncateBody, onIncomingEmailPostPrescreen
  • Gmail intake (poller + reply-back): src/main/services/email/gmail-bug-intake-poller.ts and src/main/services/email/gmail-bug-intake-completion.ts; the email_inbound_tracking.source channel column comes from the dated ledger migration 20260610063703-add-source-column-to-email-inbound-tracking.ts
  • Retention sweep: src/main/services/intake/intake-retention-sweeper.ts — 90-day periodic task, pausable via service registry
  • IPC handlers (10 channels, all Zod-validated): src/main/ipc/intake-handlers.ts
  • IPC channels: INTAKE_SOURCE_LIST / CREATE / UPDATE / DELETE, INTAKE_TEST_CONNECTION, INTAKE_POLL_NOW, INTAKE_AUDIT_LIST, INTAKE_PENDING_LIST, INTAKE_MANUAL_SPAWN, INTAKE_DISMISS plus push channels INTAKE_SOURCE_UPDATED, INTAKE_SOURCE_ERROR_ESCALATION in src/shared/ipc-channels/index.ts (defined in the diagnostics.ts domain map)
  • Schema: migrations v112 (bug_intake_processed) and v222 (project_intake_sources + extra columns source, external_id, external_url, severity, prior_event_id on bug_intake_processed) in src/main/db/database.ts; queries in src/main/db/queries-bug-intake.ts and src/main/db/queries-intake-sources.ts
  • Email-only: PROJECT_UPDATE_BUG_INTAKE_SLUG IPC + matching CLI route, settings UI panel in src/renderer/src/features/settings/BugIntakeSettings.tsx mounted inside the project edit dialog
  • Email intake rules (sender + slug → repo): table via ledger migration 20260530151130-email-intake-rules.ts; queries queries-email-intake-rules.ts (findProjectByEmailRule for routing, listEnabledRuleSenders for admission, CRUD); rule lookup ahead of the slug lookup in bug-report-router.ts; additive admission via the ruleSenders param of filterInboundMessages in email-inbound-service.ts; IPC email-intake-rule-handlers.ts (EMAIL_INTAKE_RULE_LIST / CREATE / UPDATE / DELETE + push EMAIL_INTAKE_RULES_UPDATED); UI EmailIntakeRulesSection.tsx in the Sources tab; contract .claude/memory/contracts/email-intake-rules-contract.md (14 test-locked invariants)
  • Built-in investigation steps (shared across all four surfaces): BUG_INTAKE_INVESTIGATION_STEPS in src/main/services/bug/bug-intake-prompt.ts — the "What to do" block (first analyze and report back a plain-English, markdown-formatted explanation of the item + recommended next steps; then a type-aware stop — a bug report carries the owner's standing go-ahead and continues, a feature request / question / feedback waits for it; drive the work with /dev-pipeline when installed — and when it is NOT, keep the plan checkpoint (investigate, report findings and a plan, wait before touching code), because the pipeline's PLAN gate is the only thing that judges an intake plan and the skill is default-OFF; report in plain English with short summaries; an error that is not ours closes with a one-line report rather than a question), spread into email buildBugIntakePrompt, Sentry flattenSentryEventForPrompt, GitHub flattenGithubIssueForPrompt, and in-app buildAgentPrompt. Each surface's intro framing stays per-source; only these steps are shared (so they can't drift). Locked by the shared-constant test + per-surface /dev-pipeline assertions in bug-intake-prompt.test.ts / intake-sentry-flatten.test.ts / intake-github-flatten.test.ts / firestore-bug-report-listener.test.ts
  • The intake defect's PLAN gate is answered without a person: the dev pipeline's GATE_1 prose park is decided by the autofix rubric the help desk uses — helpdesk-plan-autofix.ts beginIntakePlanAutofix (read readIntakePlanRow; eligibility via isProseGatePlanRow), probed through pipelineAutoAdvance.planGateAwaiting and approved through approvePlanGate in pipeline-auto-advance-runner.ts, wired at the turn-complete seam in auto-approve-or-auto-run.ts. One owner per gate: GATE_1 belongs to this arm for ANY eligible defect row — a help-desk ticket's included, because a ticket investigation runs the same pipeline prompt and the ticket arm only ever answers plan_approval — and plan_approval stays the ticket arm's, so a session parked at one gate can never be judged by both. Eligibility requires the recorded kind to be bug, and excludes GitHub, whose every claim is written with report_type: 'bug' whatever the issue says, so on that public tracker the recorded kind is a constant. The arm also DECLINES when the blanket per-gate Plan toggle is on, since pipelineGateReconciler would advance the gate ~4 minutes later and make a hold message false. A held plan still lands at needs_you with the deciding rule named. Governed by its own helpdeskPlanAutopilot switch, which can never reach a reporter. Locked by tests/unit/services/helpdesk/intake-plan-autofix.test.ts and tests/unit/lint/helpdesk-plan-switch-isolation.test.ts
  • Appended instructions (global + per-project): composer buildIntakeAppendInstructionsBlock in src/main/services/bug-intake-prompt.ts, spliced above the fence in all three builders (email buildBugIntakePrompt, Sentry flattenSentryEventForPrompt, in-app buildAgentPrompt in firestore-bug-report-listener.ts). Global value = bugIntakeAppendPrompt AppSettings field (textarea in BugIntakeView.tsx); per-project value = projects.bug_intake_append_prompt column (added by a dated ledger migration under src/main/db/migrations/), written via PROJECT_UPDATE_BUG_INTAKE_PROMPT → handleProjectUpdateBugIntakeAppendPrompt in project-bug-intake-service.ts, textarea in BugIntakeSettings.tsx. Shared length cap BUG_INTAKE_APPEND_PROMPT_MAX in src/shared/bug-intake-constants.ts. Per-project audit event project_bug_intake_prompt_updated (promptProvided boolean only, never the text)
  • Lint test: tests/unit/lint/intake-source-validation.test.ts — pins the 10-channel Zod contract + the renderer / server regex parity
  • Analytics registration: src/shared/feature-registry/intake-and-ai-manager.ts — bug_intake_routed, bug_intake_dedup_hint_emitted, bug_intake_self_dedup

Duplicate detection (similarity dedup)

  • Signatures + 24h window: src/main/services/intake/intake-dedup.ts — computeBugSignature, computeSentrySignature, normalizeComponent, INTAKE_DEDUP_WINDOW_HOURS = 24, SIGNATURE_MAX_LEN = 256 (pure functions, no DB)
  • Layer-1 hint lookup: findRecentSimilarIntake in src/main/db/queries-bug-intake.ts — same source + signature + project, within 24h, decision='routed' + live non-archived session only
  • Layer-1 wiring (kept parallel): email computes/looks-up/emits in bug-report-router.ts (gate read in email-inbound-service.ts); Sentry mirrors it in intake-sentry-service.ts (gate read in intake-poll-scheduler.ts)
  • Hint threading into the spawn prompt: src/main/services/bug-intake-prompt.ts
  • Layer-2 decision service: src/main/services/intake/intake-mark-duplicate.ts — markIntakeAsDuplicate, all guards + the note-then-archive ordering
  • Agent HTTP route: POST /agent/intake/duplicate-of via registerIntakeAgentRoutes in src/main/services/cli/cli-server-intake-routes.ts (registered unconditionally; behavior gated upstream by the toggle)
  • Toggle: intakeDedupEnabled (default true) — header switch data-setting-id="intake-dedup-enabled" in BugIntakeView.tsx
  • Schema: migration v257 adds bug_intake_processed.signature + idx_bug_intake_processed_signature_created(signature, created_at) in src/main/db/database.ts
  • Contract (agents with repo access): .claude/memory/contracts/intake-dedup-contract.md — 19 test-locked invariants + safe-change checklist

Related

The rest of the page is on part 1 (overview, sidebar panel and email), part 2 (Gmail, Sentry and GitHub sources) and part 3 (what a session receives, duplicate collapsing and safety). Related features: inbox alerts for the durable cards intake raises, Agent email for the mail channel, and Repoguard, which shares the same Sentry reading code for its own scanner.

Last verified 2026-10-04