CedarCopilot wants to merge 3 commits into main from staging
Not deployed
Libra has no production signal for this change yet because it has not deployed. Libra checks hourly for 3 days after each deploy.
This PR adds JSON custom-field support across CRM storage, agent updates, synchronization, filtering, and table editing, while also extracting Slack channel matching into a reusable service and exposing an account-setup command. It additionally updates analysis skills and adds generated audit artifacts.
The PR should not merge until JSON cell edits stop overwriting concurrent key updates and preserve null values.
The new JSON editor submits stale whole-object snapshots through a merge-bypassing route and changes nulls to empty strings on blur, creating two independent paths to incorrect persisted custom-field data; Slack state concurrency is an additional non-blocking concern.
Files Needing Attention: apps/mail/modules/crm/components/crm-cell.tsx, apps/server/src/trpc/routes/crm.ts, apps/server/src/services/integrations/slack/link-matching-channels.ts
| Filename | Overview |
|---|---|
| apps/mail/modules/crm/components/crm-cell.tsx | Adds the JSON key/value editor, but whole-snapshot commits can overwrite concurrent changes and null values are converted to empty strings on blur. |
| apps/server/src/trpc/routes/crm.ts | Adds the UI JSON write path and deliberately treats client payloads as authoritative whole objects, exposing stale-snapshot data loss. |
| apps/server/src/services/crm/json-field-merge.ts | Implements partial deep merging and rejection handling, while explicitly retaining last-writer-wins behavior and bypassing validation for jsonWhole values. |
| apps/server/src/services/crm/conversations.ts | Integrates resolved JSON values into the common conversation persistence path with extensive focused tests. |
| apps/server/src/services/crm/crm-field-sync.ts | Treats external CRM JSON values as authoritative whole values and forwards final persisted values through synchronization. |
| apps/server/src/services/integrations/slack/link-matching-channels.ts | Extracts existing channel-matching behavior, but whole-map channelState persistence can lose idempotency updates when the new entrypoints overlap. |
| apps/server/src/trpc/routes/channels.ts | Adds an authenticated on-demand Slack linking mutation for account setup. |
| apps/server/src/mastra/tools/conversation/updateConversationFieldsTool.ts | Resolves JSON partial patches before CRM push and persists the resulting authoritative whole value. |
### Issue 1
apps/server/src/trpc/routes/crm.ts:4093-4101
**Whole-object writes lose JSON keys**
If another writer updates a different key after the table loads, `JsonFieldCell` submits its stale full snapshot and this route marks it `jsonWhole`, bypassing the partial merge and silently overwriting the concurrent key update.
### Issue 2
apps/mail/modules/crm/components/crm-cell.tsx:559-562
**Null values become empty strings**
When a top
Libra has not measured any production surfaces for this change yet.