CedarCopilot wants to merge 0 commits into staging from feat/slack-style-reactions
Live on prod, no production signal yet
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.
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.
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.
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.
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.
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.
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.
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:
reactToMessage wrote nothing locally on the grounds that
the next read came from the provider. It now patches the mirror and returns the aggregate.id (a Cedar
uuid there) while the write path addresses messages by providerMessageId.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.
Both additive and idempotent, already applied to the shared DB:
whatsapp_message_identity.sql , message_provider_urn + partial indexlinkedin_message_reactions.sql, whatsapp_message_surface_columns.sql,
container_participant_attendee_id.sqlpnpm dlx oxlint@latest --deny-warnings , cleanpnpm deps:check , no dependency violations (1431 modules, 7309 dependencies)pnpm --filter @zero/mail run paraglide:compile , cleanpnpm --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.whatsapp/, channels/, crm/,
linkedin/, context-items/. The full unscoped vitest run src/ was left to CILibra has not measured any production surfaces for this change yet.