Libra CodeHub

CedarCopilot/cedar-mail

Branch: staging

feat(inbox): reactions on every channel, and one id namespace for WhatsApp messages

merged#2513CedarCopilot

CedarCopilot wants to merge 0 commits into staging from feat/slack-style-reactions

Live on prod, no production signal yetTimeline and evidence
  1. Opened
    Aug 16, 2026, 5:59 PM
  2. Merged
    Aug 16, 2026, 7:02 PM
  3. Live on prod
    Aug 16, 2026, 7:02 PM
  4. Observed 3 days
    Aug 16, 2026, 7:02 PM
  5. Pipelines steady after this deploy
    Aug 16, 2026, 7:02 PM
  6. Unobserved

    Live on prod, no production signal yet

    Aug 19, 2026, 6:59 PM

Behaviors Libra is checking

Chat workflow behavior from conversation-canvas/CreateConversationDialog.tsx, hooks/use-chat-reactions.ts, chat/handleChat.ts keeps working in prod.Degradedlow confidence

Strict CloudWatch fallback found 2 prod failure log lines for [SUBSCRIPTION] Error setting up Gmail watch (Failed to setup Gmail watch: {\n \), but this domain-wide failure family is not tied to this intent.

prod, checked Aug 19, 2026, 6:59 PM
Email workflow behavior from SunlitBackground/SunlitBackground.tsx, SunlitBackground/sunlit.css, ui/day-divider.tsx keeps working in prod.Degradedlow confidence

Strict CloudWatch fallback found 2 prod failure log lines for [SUBSCRIPTION] Error setting up Gmail watch (Failed to setup Gmail watch: {\n \), but this domain-wide failure family is not tied to this intent.

prod, checked Aug 19, 2026, 6:59 PM
Calendar sync behavior from components/EventConversationBadge.tsx, store/calendarSlice.ts keeps working in prod.Inconclusivelow confidence

Strict CloudWatch fallback found 1 prod failure log lines for [MEETING_NOTES_WEBHOOK] Error processing webhook (AskElephant webhook payload must contain at least one attendee with a valid email address), but this domain-wide failure family is not tied to this intent.

prod, checked Aug 19, 2026, 3:49 AM
CRM integration behavior from components/ChangeCompanyEnrichmentDialog.tsx, components/CompanyMergeDropdown.tsx, crm/conversations.ts keeps working in prod.Inconclusivelow confidence

Strict CloudWatch fallback found 47 prod failure log lines for [UpdateExternalCrmWorkflow] Driver update failed (Value \), up from 1 in the comparable baseline, but this domain-wide failure family is not tied to this intent.

prod, checked Aug 19, 2026, 4:49 AM
Data model behavior from migrations/container_participant_attendee_id.sql, migrations/linkedin_message_reactions.sql, migrations/whatsapp_message_identity.sql keeps working in prod.Inconclusivelow confidence

OTEL fallback saw 190,868 prod spans for the repo/env since deploy, but 5,678 were errors and the intent has no concrete surface to attribute them to.

prod, checked Aug 19, 2026, 5:50 AM
Slack integration behavior from components/SlackThreadPanel.tsx, crm/channel-reactions.ts, crm/slack-events.ts keeps working in prod.Inconclusivelow confidence

Strict CloudWatch fallback found 7 prod failure log lines for [backfillSlackChannelHistory] Backfill failed (Failed query: insert into \), but this domain-wide failure family is not tied to this intent.

prod, checked Aug 19, 2026, 1:32 PM

Libra has verdicts on 0 of 6 tracked behaviors on prod; 6 are still being checked. Libra has 2 low-confidence degraded verdicts it is still confirming. Libra checks hourly for 3 days after each deploy.

Summary

Slack-style reactions across all three chat channels, plus the WhatsApp mirror-first read and the identity fix that turned out to be underneath it.

Reactions everywhere. Slack reactions were only ever written by the live reaction_added webhook, so a reaction placed before Cedar stored the message , or while the worker was down, or on a channel connected after the fact , was invisible forever; users saw their own and nobody else's. conversations.history carries the full reactor list on every message, so a sync already had the answer and was dropping it. LinkedIn and WhatsApp get reactions for the first time, through one ReactionChip and one aggregate shape, so a chip cannot look different per screen.

One id namespace for WhatsApp messages. crm_whatsapp_messages.message_provider_id was filled from two namespaces , Unipile's message_id over the webhook, m.provider_id ?? m.id over the REST sync , so one physical message arriving by both routes became two rows. This is the identical defect 53e1afa01 fixed for LinkedIn in Phase 5; it was never carried across. Measured before the flip: 203 of 216 rows keyed by WhatsApp's native id, 13 duplicate groups already formed, 2 chats carrying both namespaces at once.

Unipile's id wins for the reason it won on LinkedIn: message_received carries no WhatsApp-native id at all, so keying on the native id rejects every webhook message. An id present on both paths beats a better id present on one. The native id survives as message_provider_urn.

Worth knowing before anyone writes a sweep: length does not separate the namespaces. 3EB054F21AE14AF46B8B32 is native and 22 characters, exactly as long as Unipile's Np5fFtqPU_qPoX-ydwdLRA. LinkedIn could pre-filter on 2-%; there is no equivalent here, so the re-key classifies nothing and maps via the REST object carrying both ids.

The re-key already ran , it is a precondition, not cleanup, because the corrected sync meeting a natively-keyed row inserts a second row under the new key. Live: 190 re-keyed, 13 merged into their twin, 0 unmapped, 0 unreachable. After: 203 rows, one namespace, 203 distinct keys, 0 orphaned events. A second run is a no-op.

Three defects the mirror-first read (70e6518ee) introduced by landing on top of the reactions commit (373e16636), which had assumed the WhatsApp read still went straight to Unipile:

  • Reactions never survived a refetch , reactToMessage wrote nothing locally on the grounds that the next read came from the provider. It now patches the mirror and returns the aggregate.
  • The optimistic cache write never matched a mirrored LinkedIn message: it keyed on id (a Cedar uuid there) while the write path addresses messages by providerMessageId.
  • Both mirrored reads returned the OLDEST N messages (orderBy(asc).limit()), so a chat with any history opened on its beginning and never reached today.

Also in here: thread↔conversation link management from the badge (all three stores moving together), the company-link conflict surfaced instead of a 500, channel context for the chat agent, and a mark-done resurrection detector.

Migrations

Both additive and idempotent, already applied to the shared DB:

  • whatsapp_message_identity.sql , message_provider_urn + partial index
  • linkedin_message_reactions.sql, whatsapp_message_surface_columns.sql, container_participant_attendee_id.sql

Test plan

  • pnpm dlx oxlint@latest --deny-warnings , clean
  • pnpm deps:check , no dependency violations (1431 modules, 7309 dependencies)
  • pnpm --filter @zero/mail run paraglide:compile , clean
  • pnpm --filter @zero/mail exec jest , 2034 passed. Two suites timed out on a loaded local machine (load avg ~90) and pass in isolation: 45/45 in 30s vs a 195s estimate.
  • Server vitest scoped to the change surface , 908 passing across whatsapp/, channels/, crm/, linkedin/, context-items/. The full unscoped vitest run src/ was left to CI
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/(full-width)/SunlitBackground/SunlitBackground.tsxno production surface mapped
  • apps/mail/app/(full-width)/SunlitBackground/sunlit.cssno production surface mapped
  • apps/mail/components/ui/day-divider.tsxno production surface mapped
  • apps/mail/components/ui/emoji-picker.tsxno production surface mapped
  • apps/mail/components/ui/split-button.tsxno production surface mapped
  • apps/mail/modules/calendar/components/EventConversationBadge.tsxno production surface mapped
  • apps/mail/modules/calendar/store/calendarSlice.tsno production surface mapped
  • apps/mail/modules/cedar-os/src/cedar-os-components/chatComponents/contextKinds.tsno production surface mapped
  • apps/mail/modules/cedar-os/src/cedar-os-components/chatInput/ContextBadgeRow.tsxno production surface mapped
  • apps/mail/modules/cedar-os/src/store/agentConnection/agentConnectionSlice.tsno production surface mapped
  • apps/mail/modules/cedar-os/src/store/agentContext/agentContextSlice.tsno production surface mapped
  • apps/mail/modules/cedar-os/src/store/messages/MessageTypes.tsno production surface mapped
  • apps/mail/modules/conversations/components/timeline/MessageReactions.tsxno production surface mapped
  • apps/mail/modules/conversations/components/timeline/ReactionChip.tsxno production surface mapped
  • apps/mail/modules/conversations/components/timeline/emoji-shortcodes.tsno production surface mapped
  • apps/mail/modules/crm/components/ChangeCompanyEnrichmentDialog.tsxno production surface mapped
  • apps/mail/modules/crm/components/CompanyMergeDropdown.tsxno production surface mapped
  • apps/mail/modules/crm/components/conversation-canvas/CreateConversationDialog.tsxno production surface mapped
  • apps/mail/modules/home/components/AgentHomeHero.tsxno production surface mapped
  • apps/mail/modules/home/components/agentHomeActions.tsno production surface mapped
  • apps/mail/modules/inbox/components/ChannelMessageList.tsxno production surface mapped
  • apps/mail/modules/inbox/components/ChannelThreadView.tsxno production surface mapped
  • apps/mail/modules/inbox/components/SlackThreadPanel.tsxno production surface mapped
  • apps/mail/modules/inbox/hooks/use-chat-reactions.tsno production surface mapped
  • apps/mail/modules/threads/rendering/done-thread-resurrection.tsno production surface mapped
  • apps/mail/modules/threads/rendering/use-optimistic-actions.tsno production surface mapped
  • apps/mail/modules/threads/thread/components/ConversationBadge.tsxno production surface mapped
  • apps/mail/modules/threads/thread/components/thread-display.tsxno production surface mapped
  • apps/mail/modules/ux/layout/resolveContext.tsno production surface mapped
  • apps/mail/providers/query-provider.tsxno production surface mapped
  • apps/mail/tests/lib/gmailSimulator.tsno production surface mapped
  • apps/mail/tests/lib/labelFlowHarness.tsxno production surface mapped
  • apps/mail/tests/modules/chat-store/channelContext.test.tsxno production surface mapped
  • apps/mail/tests/modules/conversations/emojiShortcodes.test.tsno production surface mapped
  • apps/mail/tests/modules/inbox/channelAttachments.test.tsxno production surface mapped
  • apps/mail/tests/modules/inbox/channelReactions.test.tsxno production surface mapped
  • apps/mail/tests/modules/inbox/chatMessageOrdering.test.tsno production surface mapped
  • apps/mail/tests/modules/inbox/chatReactions.test.tsno production surface mapped
  • apps/mail/tests/modules/threads/mark-done-flow.test.tsxno production surface mapped
  • apps/server/docs/design/chat-stream-listener.mdno production surface mapped
  • apps/server/docs/design/whatsapp-architecture-parity.mdno production surface mapped
  • apps/server/docs/slack-images.mdno production surface mapped
  • apps/server/package.jsonno production surface mapped
  • apps/server/src/db/migrations/container_participant_attendee_id.sqlno production surface mapped
  • apps/server/src/db/migrations/linkedin_message_reactions.sqlno production surface mapped
  • apps/server/src/db/migrations/whatsapp_message_identity.sqlno production surface mapped
  • apps/server/src/db/migrations/whatsapp_message_surface_columns.sqlno production surface mapped
  • apps/server/src/db/outbound-ddl.sqlno production surface mapped
  • apps/server/src/db/outbound-schema.tsno production surface mapped
  • apps/server/src/docs/bug-mark-done-threads-reappear.mdno production surface mapped
  • apps/server/src/mastra/routeHandlers/chat/handleChat.tsno production surface mapped
  • apps/server/src/mastra/types/chat-context.tsno production surface mapped
  • apps/server/src/mastra/utils/context-items/hydrate-channels.test.tsno production surface mapped
  • apps/server/src/mastra/utils/context-items/hydrate.tsno production surface mapped
  • apps/server/src/mastra/workflows/chat/chat-workflow.tsno production surface mapped
  • apps/server/src/scripts/rekey-whatsapp-message-ids.tsno production surface mapped
  • apps/server/src/services/channels/__test__/octx-awaited.test.tsno production surface mapped
  • apps/server/src/services/crm/__tests__/channel-reactions.test.tsno production surface mapped
  • apps/server/src/services/crm/__tests__/slack-reactions.test.tsno production surface mapped
  • apps/server/src/services/crm/channel-reactions.tsno production surface mapped
  • apps/server/src/services/crm/conversations.tsno production surface mapped
  • apps/server/src/services/crm/slack-events.tsno production surface mapped
  • apps/server/src/services/crm/slack-reactions.tsno production surface mapped
  • apps/server/src/services/crm/thread-conversation-link.tsno production surface mapped
  • apps/server/src/services/inbox/slack-messages.tsno production surface mapped
  • apps/server/src/services/inbox/slack-thread.tsno production surface mapped
  • apps/server/src/services/integrations/linkedin/messaging.tsno production surface mapped
  • apps/server/src/services/integrations/linkedin/unipile-client.tsno production surface mapped
  • apps/server/src/services/integrations/slack/__tests__/slack-attachment-urls.test.tsno production surface mapped
  • apps/server/src/services/integrations/slack/slack-attachment-urls.tsno production surface mapped
  • apps/server/src/services/integrations/slack/slack.tsno production surface mapped
  • apps/server/src/services/integrations/whatsapp/__tests__/message-identity.test.tsno production surface mapped
  • apps/server/src/services/integrations/whatsapp/__tests__/message-surface-fields.test.tsno production surface mapped
  • apps/server/src/services/integrations/whatsapp/__tests__/participant-refs.test.tsno production surface mapped
  • apps/server/src/services/integrations/whatsapp/index.tsno production surface mapped
  • apps/server/src/services/mail/list/route-list-threads.tsno production surface mapped
  • apps/server/src/services/mail/sync/threads.tsno production surface mapped
  • apps/server/src/trpc/routes/crm.tsno production surface mapped
  • apps/server/src/trpc/routes/inbox.tsno production surface mapped
  • apps/server/src/trpc/routes/linkedin.tsno production surface mapped
  • apps/server/src/trpc/routes/outbound.tsno production surface mapped