CedarCopilot wants to merge 2 commits into staging from fix/slack-supersede-inflight-turn
Libra exercised 2 changed behaviors; none regressed.
Libra ran this behavior against the change and confirmed the check detects when it breaks.
Changed code: slack-bot-chat.ts.
Libra ran this behavior against the change and confirmed the check detects when it breaks.
Changed code: slack-bot-chat.ts.
Live on prod, watching, 2 days left
Libra has verdicts on 0 of 8 tracked behaviors on prod; 8 are still being checked. Libra checks hourly for 3 days after each deploy.
A Pax user (Trey) sent two quick Slack messages in one thread: "Yes, try to schedule for early next week", then "run it by me before sending". Each was its own Slack event and its own agent run. Run 2 started before run 1 had persisted anything (chat rows were only written when a run finished), so it saw none of run 1's work, re-derived the task with a different calendar call, and its draft-email overwrote run 1's Gmail draft (same draftId). Cedar showed Trey the first run's 3 slots, then saved and previewed a different 4-slot draft.
Evidence: chat_messages for thread slack:T0789EW2ZM3:D0BPM9PPL0P:1790269797.716889. Two user rows with Slack ts 1790269827 and 1790269841 (14s apart) and two draft-email tool results on draftId r163057067623954677, with different bodies. Turn 1 rows were written at 17:11:15 UTC, after turn 2 had already started.
metadata.authorUserId).AbortSignal passed to the agent. A final check() runs before anything is persisted, so a run that finishes as a newer message lands still loses. The aborted run persists and posts nothing and deletes its "thinking" placeholder.persistCompletedChatMessages now merges metadata on user rows so the arrival-time author is not wiped on completion. Other rows keep replace semantics.Polling reads the DB rather than process memory because the two events can land on different API instances.
Unit: pnpm --filter @zero/server exec vitest run src/mastra/utils src/mastra/routeHandlers/slack. Manual: in a Slack thread with Cedar, send "draft a reply to X" and, within a few seconds, "show me before sending". Expect one reply covering both, and only one Gmail draft.
cedar-prod, CloudWatch /aws/ecs/aws-prod-api/api-service)['cedar-prod']
| where message == '[slack-turn-supersession] Superseded , aborting run'
| project _time, user_id, thread_id, user_message_id
Each row is a run that was cancelled. The matching thread should show exactly one assistant reply after it:
['cedar-prod']
| where message == '[persistCompletedChatMessages] Saved' and thread_id startswith 'slack:'
| where thread_id == '<thread_id from above>'
Failure signal: [slack-turn-supersession] Supersession check failed warns (DB poll errors), or a superseded thread with two assistant replies.
autofix:local stops on one unrelated frontend test (apps/mail/tests/modules/chat-store/chatHistoryList.test.ts, a date-bucket test). Lint, deps:check, server typecheck and the server vitest suites for this area pass.
š¤ Generated with Claude Code
Libra has not measured any production surfaces for this change yet.