Skip to content

Unified inbox (work items)

Contributor and operator reference. For the full product vision, phased rollout, and edge-case decisions, read The inbox is the product.

The unified inbox is a work queue backed by the workItems table. Email- and upload-sourced orders appear as rows; users review in full-page flows, with optional team dashboard metrics, snooze, and keyboard-driven navigation. When the legacy path is off, the app reads work items instead of scanning emailEvents metadata alone.

Flag Role
unified_inbox_v2 Org-scoped Flagsmith flag toggling work-item UI, redirects, and APIs that assume work items.
email_enabled Shows inbox / email-based entry points (used together in staging setups).
inline_inbox_label_rules_v1 Org-scoped. Lets the sweep apply customer auto-archive rules (inbox_routing_rules). Off means rules are stored but never fire.
inbox_archive_erp_edi_duplicates_v1 Org-scoped, default off. Lets the same sweep archive items the ERP dedupe lookup already tagged with an EDI- job number. Off leaves the tag advisory.
inbox_conversation_view_v1 Org-scoped, default off. Replaces the cockpit’s Email and Thread tabs with one Conversation tab: the thread’s messages, archive and trash activity, and internal notes with @mentions. Off makes the conversation and notes routes return 404.
inbox_sla_engine_v1 Org-scoped, default off. SLA engine: deadlines computed for new items, open-before-terminal then slaDueAt sort, and the row’s SLA pill with its priority-override card. Off hides the pill and 404s the SLA policy and priority routes.

Script to enable the first two in a Flagsmith environment: infrastructure/flagsmith/scripts/enable_unified_inbox_for_env.py. Flag definitions ship in infrastructure/flagsmith/configs/flags.json. Local fallback defaults live in apps/webapp/src/middleware/feature-flags.ts.

  • Schema: packages/db/src/schema/work-items.ts
  • Internal notes: packages/db/src/schema/inbox-notes.ts. A note belongs to the work item it was written on and, when that item came from threaded mail, to its email thread, so it shows on every item in the thread.
  • Dual-write: Work items are created in the same transactional paths as inbound email (/api/webhook/inbound-email) and PDF upload handlers so legacy and v2 stay consistent during rollout.
  • State & Lifecycle Model (RFC Option A.5): Under RFC β€” work_items State Model, archiving is a lifecycle-only timestamp operation (archived_at), with status = 'archived' retained only for legacy rows under the work_items_archived_status_consistent CHECK constraint (status != 'archived' OR archived_at IS NOT NULL). User-facing uxState is derived server-side via uxStateSql (apps/webapp/src/lib/inbox/work-item-ux-state-sql.ts and apps/webapp/src/lib/inbox/work-item-inbox-query.ts).

Webapp (Astro + React) β€” Three-Pane Cockpit Architecture

Section titled β€œWebapp (Astro + React) β€” Three-Pane Cockpit Architecture”

Both operator and supervisor inbox surfaces render inside the dark cockpit frame (ShellFrame.astro + AppRail.astro / RailNav.tsx):

  • Operator Three-Pane Cockpit (/inbox β†’ apps/webapp/src/pages/inbox.astro):
    • Wrapped in CockpitShell.astro (<ShellFrame scroll="hidden"> + Astro ClientRouter soft transitions) and mounted via InboxCockpit.tsx (apps/webapp/src/components/inbox/InboxCockpit.tsx):
      1. Pane 1 β€” Left Rail (56px): AppRail.astro / RailNav.tsx (navigation, notifications, help, org/account menu).
      2. Pane 2 β€” Queue List (CockpitRow.tsx): Flat virtualized/paginated work-item list with queue tabs (Mine, Available, Branch), lifecycle selector (active | archived | deleted), status filters (needs_review, needs_fix, processing, ready, failed), search, bulk actions, and Superhuman-style keyboard navigation (j/k, e, Enter, /).
      3. Pane 3 β€” Detail & Inline Preview (CockpitDetail.tsx + CockpitItemPreview.tsx): Inline PDF/spreadsheet/email preview, PO/sender metadata, AuroraRoutingCard, and state-aware SubmitButton / β€œFull review” action (/orders/pdf/[documentId]/review).
  • Supervisor Team Surface (/team β†’ apps/webapp/src/pages/team.astro):
    • Gated by inbox.view.team (hasElevatedInboxAccess); renders SupervisorInboxRoot.tsx (ConservativeInbox table + SupervisorKanban board + SupervisorDetailDrawer).
  • Queries / helpers: apps/webapp/src/lib/inbox/*
  • Review URLs: apps/webapp/src/lib/inbox/work-item-review-url.ts, inbox-return-to.ts
  • Representative APIs: apps/webapp/src/pages/api/work-items/* (dashboard, bulk actions, snooze, check-updates, test order, admin backfill trigger, conversation, notes)
  • Single-item visibility: apps/webapp/src/lib/inbox/visible-work-item.ts, the check the detail, thread, conversation, and notes routes share.
  • Temporal client helpers: apps/webapp/src/lib/temporal.ts (startWorkItemBackfillWorkflow, PDF extraction unchanged on pdf-extraction queue)

Package: apps/ts-temporal-worker
Task queue: erp-unlocked-ts

Workflow Purpose
WorkItemBackfillWorkflow Migrate existing email events into workItems (idempotent; fixed workflow id work-item-backfill).
WorkItemUnsnoozeWorkflow Periodic unsnooze of rows past snoozedUntil (intended as a schedule; see workflow file for CLI hints).

Requires TEMPORAL_ADDRESS (and related TLS vars) where the webapp starts backfill or PDF workflows.

  • Deferred product work: debt/backlog/projects/unified-inbox/TODOS.md
  • Email pipeline (high level): The inbox is the product (diagrams and dual-write notes)
  • User operations guide: Inbox RBAC and SLA (RBAC capabilities, SLA policy controls, supervisor/admin dashboard actions)
  • Auto-archive rules and the ERP duplicate sweep: debt/projects/inbox-label-taxonomy/plan.md (why rules are keyed on customer name, the excludeSenders narrowing, and the EDI-/J- split)