Libra CodeHub

CedarCopilot/cedar-mail

Branch: staging

Compose the unified inbox in the client, and add a setup flow that previews it

merged#2641CedarCopilot

CedarCopilot wants to merge 3 commits into staging from feat/inbox-client-composed-feed

Live on prod, 1 of 11 behaviors degradedTimeline and evidence
  1. Opened
    Aug 31, 2026, 9:57 PM
  2. Aug 31, 2026, 11:17 PM
  3. Merged
    Sep 1, 2026, 12:01 AM
  4. Live on prod
    Sep 1, 2026, 12:01 AM
  5. Observed 3 days
    Sep 1, 2026, 12:01 AM
  6. Pipelines steady after this deploy
    Sep 1, 2026, 12:01 AM
  7. Mixed signals

    Live on prod, 1 of 11 behaviors degraded

    Sep 3, 2026, 11:44 PM

Behaviors Libra is checking

The unified all-channel feed requests only linked channel items while dedicated Slack and WhatsApp channel views can still include unlinked containers through linkedOnly=false.Degradedmedium confidence

Production telemetry shows 288 concrete inbox.listChannelItems executions since 2026-09-01T07:01:02Z: 284 successes and 4 tied errors (1.4%). Successful calls averaged 1056 ms; failed calls averaged 1692.5 ms. All failures were `Unexpected end of JSON input`, so the route is.

prod, checked Sep 3, 2026, 11:43 PM
The login page explains known OAuth callback failures with actionable provider-specific guidance, shows any provider error description, and displays the machine-readable error code for support.Inconclusivelow confidence

Verification reached its 120s wall-clock budget before a tied verdict. Libra is keeping this intent verifying until the next check.

prod, checked Sep 3, 2026, 10:43 PM
The required_scopes_missing callback continues to render only its dedicated translated error panel instead of also showing the generic OAuth error alert.Inconclusivehigh confidence

No positive production runtime evidence was found since 2026-09-01T07:01:02Z (~71h): 0 OTEL spans for GET /login or any login-named route/span, 0 spans containing required_scopes_missing, 0 server spans for deployed commit 7e84a6d739bd7a6f9af82297725fa8527539dbb3, and 0.

prod, checked Sep 3, 2026, 10:45 PM
The unified inbox is assembled in the client from independently fetched and cached email, Slack, LinkedIn, and WhatsApp feeds rather than from one server-merged page.Inconclusivelow confidence

Verification reached its budget before a verdict.

prod, checked Sep 3, 2026, 11:44 PM
Creating a draft waits for surfaceDraftInInbox to finish and writes the email label join with latest_message_at in the insert so the first save is immediately listable.Not checked
prod
Email rows in the unified feed use the same mail.listThreads read path as the Email surface, including its kill switch, fallback, mirror probe, reconcile, and draft-thread handling.Not checked
prod

Libra has verdicts on 1 of 11 tracked behaviors on prod; 10 are still being checked. Libra checks hourly for 3 days after each deploy.

Implements Phase 8 and Phase 0 of apps/mail/docs/inbox-triage.md.

What changes

The unibox is now assembled in the browser from one query per channel instead of one server-merged page. inbox.listItems and assembleInboxFeed are deleted; feed.ts drops 875 → 500 lines and no longer reads mail at all.

Email is read through mail.listThreads , the router, with its reconcile, drafts union and Gmail fallback. Previously the unibox read the mirror directly through listThreadsFromDb, which is how the two lists came to disagree about which threads exist. Measured on a live inbox: a 20-row inbox.listItems page carried 5 email rows and omitted 9 threads newer than chat rows it did include.

Three rules that used to be copied are now shared, each in one place:

rulelives incallers
the cross-channel merge + watermarkfeed-merge.ts (browser-safe, @zero/server/inbox/merge)the client store, the headless driver
which channels participatefeed-scope.tsinbox.listChannelItems, inbox.getFeedScope
which inbox a route is onroute-inbox.tsuseThreads, the unified feed

conversationFilter never crosses the wire , inbox.getFeedScope has a .strict() output schema that cannot carry it, and each route re-resolves the rule from inboxId itself.

Bugs found and fixed along the way

  • mergeAndPage truncated a single-source feed at page two. Sources resume at <= cursorSortedAt, so every page re-read its boundary row and spent the limit + 1 that proves "more remains". Now takes a required sourcesSaturated , required so a new caller cannot silently reintroduce it.
  • The sources ordered by timestamp alone while the pager tiebreaks by id, so a LIMIT landing mid-tie-band took an arbitrary subset and the skipped rows sorted above the minted cursor , excluded forever. All six source queries now order (last_message_at DESC, <itemId> COLLATE "C" DESC). The collation is load-bearing: the default one folds case and ignores punctuation, and would disagree with compareItemsDesc. Three rows vanished from a paged walk of a real feed before this.
  • The headChanged latch was per-connection, so once the unibox became a second caller of mail.listThreads whichever surface read first consumed the signal. Now keyed per surface, and a divergence fans out to every surface , the reconcile dedup key carries none, so the second reader routinely joins the first's in-flight run.
  • Both lists now resolve the active inbox from the URL. The persisted settings.activeInboxId fallback is gone; it let the two lists ask different questions about the same route.
  • drafts.create awaits surfaceDraftInInbox, and the label join row is born with its ordering key rather than backfilled by a second statement.

Verification

Headless, as the operator, against real data:

CEDAR_API_URL=http://localhost:8790 pnpm --filter @zero/server exec tsx \
  src/mail-admin/cli.ts compose-feed --for <email> [--inbox "<name>"] [--rounds 3]

compose-feed runs the browser's own composition , same routes, same mergeChannelFeeds , and checks four invariants over real paging rounds: ordered, unique, stable (each rendered list is a literal prefix of the next), watermark. Each was probed against a deliberately broken history. Green on the plain unibox, a mail-only split, and a CRM-filtered inbox.

  • server vitest: 714 passed
  • mail jest: 2986 passed (321 suites)
  • tsc -b --force clean in both packages
  • the watermark invariant is proved by randomized simulation, and mutation-tested , removing the watermark fails exactly the stability assertion

Also here: a setup flow that shows you the product while you configure it

Adds /onboarding-setup , a sidebar-driven flow over the four surfaces a new user actually has to configure (Inbox, sub-inboxes, Tasks, Pipeline), each step paired with a **live pre

Show production surfaces and changed-file mapping

Production surfaces

Libra has not measured any production surfaces for this change yet.

Changed files → surfaces

  • apps/mail/app/(auth)/login/login-client.tsxno production surface mapped
  • apps/mail/app/(full-width)/onboarding-setup/page.tsxno production surface mapped
  • apps/mail/app/routes.tsno production surface mapped
  • apps/mail/docs/inbox-triage.mdno production surface mapped
  • apps/mail/docs/onboarding-setup-flow.mdno production surface mapped
  • apps/mail/docs/wiki/unified-feed-composition.mdno production surface mapped
  • apps/mail/modules/conversations/components/LeftSidebarContent.tsxno production surface mapped
  • apps/mail/modules/conversations/components/WhatsAppChannelLinkDialog.tsxno production surface mapped
  • apps/mail/modules/debugger/components/InboxDebuggerTab.tsxno production surface mapped
  • apps/mail/modules/debugger/components/MailDebuggerPanel.tsxno production surface mapped
  • apps/mail/modules/drafting/components/email-composer.tsxno production surface mapped
  • apps/mail/modules/inbox/components/ChannelThreadView.tsxno production surface mapped
  • apps/mail/modules/inbox/components/InboxList.tsxno production surface mapped
  • apps/mail/modules/inbox/hooks/use-channel-artifact-item.tsno production surface mapped
  • apps/mail/modules/inbox/hooks/use-inbox-item-actions.tsno production surface mapped
  • apps/mail/modules/inbox/hooks/use-inbox-items.tsno production surface mapped
  • apps/mail/modules/inbox/hooks/use-open-channel-item.tsno production surface mapped
  • apps/mail/modules/inbox/hooks/use-slack-sync-on-load.tsno production surface mapped
  • apps/mail/modules/inbox/store/inboxSlice.tsno production surface mapped
  • apps/mail/modules/inbox/types.tsno production surface mapped
  • apps/mail/modules/onboarding/setup/SetupFlow.tsxno production surface mapped
  • apps/mail/modules/onboarding/setup/components/SettingRow.tsxno production surface mapped
  • apps/mail/modules/onboarding/setup/components/SetupPreviewContext.tsxno production surface mapped
  • apps/mail/modules/onboarding/setup/components/SetupPreviewFrame.tsxno production surface mapped
  • apps/mail/modules/onboarding/setup/components/SetupRail.tsxno production surface mapped
  • apps/mail/modules/onboarding/setup/components/SplitTemplateGallery.tsxno production surface mapped
  • apps/mail/modules/onboarding/setup/constants.tsno production surface mapped
  • apps/mail/modules/onboarding/setup/hooks/use-setup-pipeline.tsno production surface mapped
  • apps/mail/modules/onboarding/setup/hooks/use-setup-settings.tsno production surface mapped
  • apps/mail/modules/onboarding/setup/hooks/use-setup-splits.tsno production surface mapped
  • apps/mail/modules/onboarding/setup/hooks/use-setup-task-groups.tsno production surface mapped
  • apps/mail/modules/onboarding/setup/index.tsno production surface mapped
  • apps/mail/modules/onboarding/setup/preview/LiveInboxPreview.tsxno production surface mapped
  • apps/mail/modules/onboarding/setup/preview/PreviewInbox.tsxno production surface mapped
  • apps/mail/modules/onboarding/setup/preview/mock-threads.tsno production surface mapped
  • apps/mail/modules/onboarding/setup/steps/InboxDisplayStep.tsxno production surface mapped
  • apps/mail/modules/onboarding/setup/steps/LiveInboxStep.tsxno production surface mapped
  • apps/mail/modules/onboarding/setup/steps/PipelineStep.tsxno production surface mapped
  • apps/mail/modules/onboarding/setup/steps/SubInboxStep.tsxno production surface mapped
  • apps/mail/modules/onboarding/setup/steps/TasksStep.tsxno production surface mapped
  • apps/mail/modules/onboarding/setup/steps/WelcomeStep.tsxno production surface mapped
  • apps/mail/modules/onboarding/setup/steps/index.tsno production surface mapped
  • apps/mail/modules/onboarding/setup/types.tsno production surface mapped
  • apps/mail/modules/threads/components/SplitInboxTabs.tsxno production surface mapped
  • apps/mail/modules/threads/hooks/use-inbox-compiled-queries.tsno production surface mapped
  • apps/mail/modules/threads/hooks/use-inboxes.tsno production surface mapped
  • apps/mail/modules/threads/hooks/use-route-inbox.tsno production surface mapped
  • apps/mail/modules/threads/lib/route-inbox.tsno production surface mapped
  • apps/mail/modules/threads/lib/unread-filter.tsno production surface mapped
  • apps/mail/modules/threads/mail.tsxno production surface mapped
  • apps/mail/modules/threads/threadList/hooks/use-threads.tsno production surface mapped
  • apps/mail/modules/threads/threadList/store/threadSlice.tsno production surface mapped
  • apps/mail/modules/threads/threadList/utils/mail-list-hotkeys.tsxno production surface mapped
  • apps/mail/modules/userTasks/TASK_REORDERING_DESIGN.mdno production surface mapped
  • apps/mail/modules/userTasks/components/OpenTaskExecutionCard.tsxno production surface mapped
  • apps/mail/modules/userTasks/components/TaskExecutionList.tsxno production surface mapped
  • apps/mail/modules/userTasks/components/TaskKanbanBoard.tsxno production surface mapped
  • apps/mail/modules/userTasks/components/TaskKanbanCard.tsxno production surface mapped
  • apps/mail/modules/userTasks/components/TaskKanbanColumn.tsxno production surface mapped
  • apps/mail/modules/whatsapp/components/AddToConversationPopover.tsxno production surface mapped
  • apps/mail/tests/lib/inboxFeed.tsno production surface mapped
  • apps/mail/tests/modules/inbox/channelKeyCollision.test.tsno production surface mapped
  • apps/mail/tests/modules/inbox/channelThreadRemind.test.tsxno production surface mapped
  • apps/mail/tests/modules/inbox/feedScopeGate.test.tsxno production surface mapped
  • apps/mail/tests/modules/inbox/mergeWatermark.test.tsno production surface mapped
  • apps/mail/tests/modules/inbox/openChannelItem.test.tsxno production surface mapped
  • apps/mail/tests/modules/inbox/slackChannelHint.test.tsno production surface mapped
  • apps/mail/tests/modules/inbox/unifiedInboxOrder.test.tsno production surface mapped
  • apps/mail/tests/modules/onboarding/setupRail.test.tsxno production surface mapped
  • apps/mail/tests/modules/onboarding/setupSteps.test.tsno production surface mapped
  • apps/mail/tests/modules/thread/unibox-navigation-parity.test.tsno production surface mapped
  • apps/mail/tests/modules/threads/inboxCreateRace.test.tsxno production surface mapped
  • apps/mail/tests/modules/threads/routeInboxParity.test.tsxno production surface mapped
  • apps/mail/tests/modules/threads/unread-filter.test.tsno production surface mapped
  • apps/mail/tests/modules/userTasks/taskKanbanCardExecute.test.tsxno production surface mapped
  • apps/mail/tests/modules/userTasks/taskKanbanSorting.test.tsno production surface mapped
  • apps/mail/tests/modules/ux/activeViewNeverBlank.test.tsxno production surface mapped
  • apps/server/package.jsonno production surface mapped
  • apps/server/src/cli/__tests__/no-heavy-imports.test.tsno production surface mapped
  • apps/server/src/http/app.tsno production surface mapped
  • apps/server/src/lib/__tests__/auth-callback-logging.test.tsno production surface mapped
  • apps/server/src/lib/auth-callback-logging.tsno production surface mapped
  • apps/server/src/mail-admin/cli.tsno production surface mapped
  • apps/server/src/mail-admin/compose-feed.tsno production surface mapped
  • apps/server/src/services/crm/__tests__/email-threads.test.tsno production surface mapped
  • apps/server/src/services/crm/email-threads.tsno production surface mapped
  • apps/server/src/services/inbox/__test__/feed-core.test.tsno production surface mapped
  • apps/server/src/services/inbox/__test__/feed-db.test.tsno production surface mapped
  • apps/server/src/services/inbox/__test__/feed-merge.test.tsno production surface mapped
  • apps/server/src/services/inbox/__test__/feed-unread-db.test.tsno production surface mapped
  • apps/server/src/services/inbox/__tests__/feed-inbox-scope.test.tsno production surface mapped
  • apps/server/src/services/inbox/__tests__/feed-unread-filter.test.tsno production surface mapped
  • apps/server/src/services/inbox/channel-query.tsno production surface mapped
  • apps/server/src/services/inbox/feed-containers.tsno production surface mapped
  • apps/server/src/services/inbox/feed-core.tsno production surface mapped
  • apps/server/src/services/inbox/feed-merge.tsno production surface mapped
  • apps/server/src/services/inbox/feed-scope.tsno production surface mapped
  • apps/server/src/services/inbox/feed.tsno production surface mapped
  • apps/server/src/services/inbox/labels.tsno production surface mapped
  • apps/server/src/services/inbox/types.tsno production surface mapped
  • apps/server/src/services/mail/inboxes/inbox-definitions.tsno production surface mapped
  • apps/server/src/services/mail/list/__tests__/route-list-threads-async-reconcile.test.tsno production surface mapped
  • apps/server/src/services/mail/list/reconcile-dispatch.tsno production surface mapped
  • apps/server/src/services/mail/list/route-list-threads.tsno production surface mapped
  • apps/server/src/trpc/routes/__tests__/drafts-create-surfaces-in-inbox.test.tsno production surface mapped
  • apps/server/src/trpc/routes/__tests__/inbox-channel-routes.test.tsno production surface mapped
  • apps/server/src/trpc/routes/drafts.tsno production surface mapped
  • apps/server/src/trpc/routes/inbox.tsno production surface mapped
  • apps/server/src/trpc/routes/mail.tsno production surface mapped