Libra CodeHub

CedarCopilot/cedar-mail

Branch: staging

fix(conversations,tasks): resolve Attio CRM link + task due-date edit bugs

merged#2817CedarCopilot

CedarCopilot wants to merge 4 commits into staging from fix/attio-link-and-task-due-date

Live on prod, 0 of 12 surfaces degradedTimeline and evidence
  1. Opened
    Sep 17, 2026, 10:13 PM
  2. Sep 18, 2026, 10:00 AM
  3. Merged
    Sep 18, 2026, 12:21 PM
  4. Live on prod
    Sep 18, 2026, 12:21 PM
  5. Observed 2 days, 12 surfaces, 467 requests
    Sep 18, 2026, 12:21 PM
  6. Pipelines steady after this deploy
    Sep 18, 2026, 12:21 PM
  7. Mixed signals

    Live on prod, 0 of 12 surfaces degraded

    Sep 21, 2026, 2:01 AM

Behaviors Libra is checking

Historical conversations with unresolved Attio or HubSpot CRM metadata receive usable external URLs during single-conversation and conversation-list hydration without requiring the deal to be relinked.Degradedlow confidence

Verification reached its budget before a verdict.

prod, checked Sep 21, 2026, 12:02 AM
CRM badges in conversation overviews and field renderers open the resolved deal or account URL when available, and render as non-clickable badges instead of navigating to a broken `#` link when no URL is available.Degradedlow confidence

Verification reached its 120s wall-clock budget before a tied verdict. Libra is keeping this intent verifying until the next check.

prod, checked Sep 21, 2026, 2:01 AM
Conversation hydration batches Attio workspace-slug and HubSpot hub-ID self-healing lookups once per provider and only for users with unresolved entries, avoiding per-conversation lookup amplification.Degradedmedium confidence

Since 2026-09-18T19:21:06Z, production has positive traffic on /api/conversations.list (208 spans, 6 traces, p95 117 ms) and /api/users.conversations (27 spans, 5 traces, p95 79 ms), with one /api/conversations.list OTEL error. No matching OTEL spans, attributes, or CloudWatch.

prod, checked Sep 21, 2026, 12:04 AM
When a CRM deal is linked without a pre-resolved workspace or portal value, the link operation resolves it from the user who performed the action, falling back to the conversation owner only when no acting user is available.Degradedmedium confidence

Post-deploy production logs show substantial linked-operation activity: 2,291 completion messages across worker/API streams versus 2,242 starts, including 2,185 HubSpot, 51 Attio, and 54 Salesforce completions. However, telemetry does not expose workspaceSlug, instanceUrl, or.

prod, checked Sep 21, 2026, 1:03 AM
Operators can inspect missing HubSpot hub IDs without writes and run the backfill command to resolve and persist hub IDs for all existing HubSpot connections.Degradedlow confidence

Verification reached its budget before a verdict.

prod, checked Sep 21, 2026, 1:05 AM
Users can change a non-null due date for tasks shown only on a conversation or deal surface without receiving a `Task not found` error.Inconclusivehigh confidence

Since the 2026-09-18T19:21:06Z deploy, production has 0 OTEL spans for `/api/trpc/userTasks.updateTask` and 0 userTasks-related route/span matches. CloudWatch has 0 entries for `userTasks.updateTask`, `optimisticUpdateTaskDueDate`, or `Task not found`. There are 279.

prod, checked Sep 19, 2026, 6:55 AM

Libra measured 3 of 12 production surfaces on prod. 6 surfaces had under 20 requests, so Libra has not judged them; 3 surfaces had no requests at all. Libra has 3 low-confidence degraded verdicts it is still confirming. Libra checks hourly for 3 days after each deploy.

Summary

Two bugs reported by Christopher Grittner (Hey Telo) in #cedar-heytelo, 2026-09-16, production:

  • Attio "view in CRM" link opened a blank tab instead of the Attio record. The workspace slug needed to build the link was never persisted anywhere Cedar could read it back , the connect-time fetch from Attio's /v2/self read the wrong response shape (nested data.workspace.slug instead of the actual flat workspace_slug field), so it silently failed for every Attio-connected account, not just this one. Fixed the response-shape bug, stamp the slug onto deals at write-time (getAllDeals/getDealsByIds/getAccountByEmail), and added a read-time self-heal (fetches + persists live via the existing token, no reconnect needed) so already-broken links fix themselves on next hydration. Frontend no longer renders a dead link when no URL resolves.
  • Editing a task's due date from a deal's Overview/Next Steps/Timeline surfaces threw "Task not found" and silently failed. The date-set path resolved the task from a flat in-memory store with no fallback to the conversation's own task list , a fallback the "clear date" branch already had, just never threaded through. Fixed by passing conversationId through so both branches share the same resolution.

Test plan

  • apps/server/src/services/integrations/crm/__tests__/attio.test.ts (new) , pins computeAttioUrls against real deal data, with/without a resolved slug
  • apps/mail/tests/modules/crm/crm-sync-href.test.ts (new) , proves the frontend never renders a dead # link
  • apps/mail/tests/modules/userTasks/optimisticUpdateTaskDueDateConversationOnly.test.tsx (new) , reproduces "Task not found" pre-fix, passes post-fix
  • Backend: 1666 tests passing (src/services/crm, src/trpc/routes, src/services/integrations/crm/__tests__, src/mastra/tools/conversation)
  • Frontend: crm, userTasks, conversations module suites passing
  • pnpm --filter @zero/server run types clean
  • Verified live against the real reporter's account/deal data (Attio workspace slug resolves correctly, task date write succeeds)

šŸ¤– Generated with Claude Code

RetriggerConfidence Score: 4/5

The PR is not yet safe to merge because the previous shared-conversation CRM attribution issue remains partly unresolved and the explicit no-cast requirement is still violated.

Findings

  1. P1Ā Wrong Attio workspace owner ā–¶
  2. P1Ā Hydration can stall indefinitely ā–¶
  3. P2Ā Backfill overstates resolved connections ā–¶
  4. P2Ā Unchecked assertions violate requirements ā–¶
Fix with agent prompt
### Issue 1
apps/server/src/services/crm
Show production surfaces and changed-file mapping

Production surfaces

SurfaceRequestsErrorsp95UsersVerdict
db.getConversationIdForThread234 → 3790 → 0 (0%)56 ms → 100 ms0Working
379 requests since the deploy with 0 errors (0 errors in the 234-request baseline before it). p95 100 ms, was 56 ms.
/api/trpc/crm.getConversation30 → 320 → 0 (0%)12120 ms → 9532 ms0Working
32 requests since the deploy with 0 errors (0 errors in the 30-request baseline before it). p95 9532 ms, was 12120 ms.
POST /api/trpc/crm.getConversation19 → 210 → 0 (0%)12120 ms → 9519 ms0Working
21 requests since the deploy with 0 errors (0 errors in the 19-request baseline before it). p95 9519 ms, was 12120 ms.
trpc.crm.getConversation9 → 100 → 0 (0%)11396 ms → 9438 ms3Insufficient traffic
10 requests, under the 20 Libra needs
crm.getConversation9 → 100 → 0 (0%)11316 ms → 9392 ms3Insufficient traffic
10 requests, under the 20 Libra needs
enrichment.enrichConversationContacts32 → 70 → 0 (0%)2443 ms → 571 ms7Insufficient traffic
7 requests, under the 20 Libra needs
execute_tool update-conversation-fields16 → 30 → 0 (0%)11244 ms → 5230 ms0Insufficient traffic
3 requests, under the 20 Libra needs
/api/trpc/crm.searchConversationsMinimal0 → 30 → 0 (0%)not measured → 9572 ms0Insufficient traffic
3 requests, under the 20 Libra needs
model_chunk Conversation Field Updater43 → 20 → 0 (0%)1 ms → 1 ms0Insufficient traffic
2 requests, under the 20 Libra needs
/api/trpc/crm.getConversations0 → 00 → 0 (0%)not measured0No traffic
No requests recorded since this deploy.
/api/trpc/crm.listConversationDocs0 → 00 → 0 (0%)not measured0No traffic
No requests recorded since this deploy.
/api/trpc/crm.getConversationFieldDefinitions0 → 00 → 0 (0%)not measured0No traffic
No requests recorded since this deploy.

Changed files → surfaces

  • apps/server/src/mastra/tools/conversation/conversationTool.tsdb.getConversationIdForThread/api/trpc/crm.getConversationPOST /api/trpc/crm.getConversationtrpc.crm.getConversationcrm.getConversationenrichment.enrichConversationContacts+6
  • apps/server/src/services/crm/conversations.ts/api/trpc/crm.searchConversationsMinimal/api/trpc/crm.getConversations
  • apps/mail/modules/conversations/components/ConversationOverviewCard.tsxno production surface mapped
  • apps/mail/modules/conversations/components/conversationFieldRenderers.tsxno production surface mapped
  • apps/mail/modules/crm/types/index.tsno production surface mapped
  • apps/mail/modules/userTasks/hooks/use-optimistic-task-actions.tsno production surface mapped
  • apps/mail/tests/modules/crm/crm-sync-href.test.tsno production surface mapped
  • apps/mail/tests/modules/userTasks/optimisticUpdateTaskDueDateConversationOnly.test.tsxno production surface mapped
  • apps/server/package.jsonno production surface mapped
  • apps/server/src/mastra/tools/conversation/__tests__/conversationTool.test.tsno production surface mapped
  • apps/server/src/mastra/tools/conversation/linkCrmDealTool.tsno production surface mapped
  • apps/server/src/scripts/backfill-hubspot-hub-id.tsno production surface mapped
  • apps/server/src/services/crm/__tests__/external-crm-events.test.tsno production surface mapped
  • apps/server/src/services/crm/external-crm-events.tsno production surface mapped
  • apps/server/src/services/integrations/crm/__tests__/attio.test.tsno production surface mapped
  • apps/server/src/services/integrations/crm/__tests__/hubspot-links.test.tsno production surface mapped
  • apps/server/src/services/integrations/crm/attio-links.tsno production surface mapped
  • apps/server/src/services/integrations/crm/attio.tsno production surface mapped
  • apps/server/src/services/integrations/crm/driver.tsno production surface mapped
  • apps/server/src/services/integrations/crm/hubspot-links.tsno production surface mapped
  • apps/server/src/services/integrations/crm/hubspot.tsno production surface mapped
  • apps/server/src/trpc/routes/crm.tsno production surface mapped
  • apps/server/src/trpc/routes/integrations.tsno production surface mapped