CedarCopilot wants to merge 0 commits into staging from fix/crm-active-deal-status-and-task-channel
Live on prod, no production signal yet
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 84 recent prod failure log lines for [UpdateExternalCrmWorkflow] Driver update failed (Step updateOpportunityField failed: HTTP 400: [{\), but the same failure had 79 log lines in the comparable pre-deploy baseline. Libra is not blaming this PR.
Libra has verdicts on 0 of 2 tracked behaviors on prod; 2 are still being checked. Libra checks hourly for 3 days after each deploy.
On a conversation carrying several linked CRM deals (a company with a live deal plus old closed ones), several code paths ignored the "conversation follows its open deal" rule and let a terminal sibling win. Surfaced auditing Peter (Pirros): his open HGA - 5 deal (stage Scoping, correctly active:true in metadata) rendered under a Lost card because a closed sibling on the same conversation had stamped status = Lost - Company. Same class as the Cordogan Clark meeting-notes skip (gating read the wrong deal).
The per-event CRM sync wrote crm_conversations.status from whichever deal triggered the event, not the conversation's active deal. A closed sibling's sync (or the agent field-update path) therefore clobbered the open deal's stage , last-writer-wins across the collapsed deals. The active-deal marking in integration_metadata was correct; only the status column (which the pipeline board groups by) diverged. Meanwhile the agent saw N indistinguishable <linked_crm_deal> blocks with no signal of which was authoritative.
status follows the active deal , new shared guard dropStatusWriteFromNonActiveDeal drops the status write when the incoming deal isn't the conversation's active one (pickActiveExternalCrmDeal). Applied at both sync entry points:
applyFullProgrammaticCrmMappings (handleExecuteExternalCrm + conversation-sync / initial-sync / auto-link / pull-refresh)applyCrmFieldSync (agent field-update path) , also keeps the stage changeKey reported handled so the agent won't re-apply the closed deal's stage.formatIntegrationMetadataForAgent now marks the active deal and each deal's open/closed state when a conversation has 2+ linked deals, so subagents (gating, crm-updater, notes) reason off the right deal.VirtualizedTableRow derives isCrmSynced from the active deal (pickActiveDeal) so the synced badge and the shown deal come from one source.crm_conversation_updates / agent_tool_calls should show [crm-field-sync] Skipped status write ... is not the active deal ... when a closed sibling of a multi-deal conversation syncs. Repro tests cover both sync paths (open + closed sibling on one conversation, closed deal syncs ā status stays on the active deal's stage).
vitest crm-field-sync-full (5/5), active-deal + summary (28/28)pnpm --filter @zero/server run types ā cleanpnpm deps:check ā no violationsThe related Haworth Tompkins task-creation failure is not in this PR , it was the multi-action task_channel being rejected by the constraint that migration 0059_retire_multi_action_channel.sql (PR #2526, already on staging) tightened. Prod code behind that deploy still wrote multi-action; it resolves on deploy. Nothing to add here.
š¤ Generated with Claude Code
This PR makes CRM conversation status, agent context, and the frontend synced indicator follow the selected active deal rather than an arbitrary linked sibling.
The cross-provider identity ambiguity should be fixed before merging because it can allow a non-active terminal deal to overwrite conversation status.
The new guard compares provider-scoped deal IDs without retaining the event provider, while the repository's active-deal reconciliation explicitly treats pro
Libra has not measured any production surfaces for this change yet.