Libra CodeHub

CedarCopilot/cedar-mail

Branch: staging

fix(channels): a resolved DM keeps its kind when a later writer only guessed

merged#2578CedarCopilot

CedarCopilot wants to merge 4 commits into staging from fix/dm-container-kind

Live on prod, no production signal yetTimeline and evidence
  1. Opened
    Aug 25, 2026, 10:05 AM
  2. Aug 25, 2026, 2:48 PM
  3. Merged
    Aug 25, 2026, 2:56 PM
  4. Live on prod
    Aug 25, 2026, 2:56 PM
  5. Observed 33 hours
    Aug 25, 2026, 2:56 PM
  6. Pipelines steady after this deploy
    Aug 25, 2026, 2:56 PM
  7. Unobserved

    Live on prod, no production signal yet

    Aug 27, 2026, 12:13 AM

Behaviors Libra is checking

Data model behavior from db/integration-schemas.ts, migrations/drive_unlinked_file.sql keeps working in prod.Inconclusivelow confidence

Verification cited only 15 matching post-deploy events, below the 20-event floor for calling a change verified. Nothing is failing; there is not yet enough traffic to confirm it.

prod, checked Aug 26, 2026, 11:14 PM
Email workflow behavior from components/OverviewDocTab.tsx, files/AttachedSection.tsx, files/ConversationFileTree.tsx keeps working in prod.Inconclusivelow confidence

Strict CloudWatch fallback found 3 recent prod failure log lines for mirror.upsertEmailThread failed (write CONNECTION_CLOSED aws-1-us-east-2.pooler.supabase.com:6543), but the same failure had 8 log lines in the comparable pre-deploy baseline. Libra is not blaming this PR.

prod, checked Aug 27, 2026, 12:13 AM

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.

What

ingestSlackMessages derives a container's kind from the id prefix , startsWith('D') ? 'dm' : 'channel' , and Slack now issues C… ids for group DMs, so a C-shaped mpim reaches ensureContainer claiming to be a channel. COALESCE(EXCLUDED.kind, …) took that guess over a stored 'group', because the guess is not NULL.

Measured on the first real DM backfill against Invoicebutler: two mpims admitted as 'group' came back out of ingest as 'channel', carrying Slack's own mpdm-mihir--kristina141--quinnlitherland11-1 in place of the deliberate 'directmessage'.

Why the asymmetry is right

'dm'/'group' is only ever set by a caller that RESOLVED the conversation , the admission rung asked conversations.members. 'channel' is what every prefix guess falls back to. The specific value is evidence, the general one is a default, and a default must not overwrite evidence.

display_name inverts the usual rule on a DM for a sharper reason: a channel's name legitimately changes and the newest sync is right, but a DM's name is the deliberate constant 'directmessage' (design: dm-auto-sync.md §4 Phase 1), chosen because rung 5 tokenises a container name against split_part(company_domain, '.', 1). Letting Slack's mpdm name land there feeds people's names to a rung built for cedar-acme-shaped ones and invites a link to a company that merely shares a token.

A NULL is still filled, so nothing is left nameless, and a real channel still renames.

Verification

  • Test confirmed to FAIL against the previous COALESCE before being kept.
  • vitest run src/services/channels src/services/integrations/slack , 246 passed, 26 skipped.
  • pnpm --filter @zero/server run types , exit 0.
  • pnpm deps:check , no violations.

🤖 Generated with Claude Code

Greptile Summary

The PR changes channel-container conflict updates so resolved Slack DM/group metadata survives later prefix-based guesses while ordinary channels remain renameable.

  • Preserves stored dm and group kinds when a later upsert supplies a generic channel classification.
  • Preserves non-NULL DM/group display names while continuing to fill missing names.
  • Adds regression coverage for group-DM preservation, channel renames, and filling a missing DM name.

Confidence Score: 5/5

The PR appears safe to merge, with the intended conflict-update behavior covered by focused regression tests.

The changed upsert preserves resolved DM/group metadata while retaining the existing update behavior for ordinary channels and missing names, and no concrete reachable regression remains.

Important Files Changed

FilenameOverview
apps/server/src/services/channels/containers.tsUpdates conflict resolution to protect resolved DM/group classification and naming without preventing normal channel renames.
apps/server/src/services/channels/test/link-store.test.tsAdds focused regression tests for the protected metadata and the intended channel/NULL-name exceptions.

Sequence Diagram

rendering diagram…

Reviews (1): Last reviewed commit: "fix(channels): a resolved DM keeps its k..." | Re-trigger Greptile

Show production surfaces and changed-file mapping

Production surfaces

Libra has not measured any production surfaces for this change yet.

Changed files → surfaces

  • apps/mail/modules/conversations/components/OverviewDocTab.tsxno production surface mapped
  • apps/mail/modules/conversations/components/files/AttachedSection.tsxno production surface mapped
  • apps/mail/modules/conversations/components/files/ConversationFileTree.tsxno production surface mapped
  • apps/mail/modules/conversations/components/files/DriveSection.tsxno production surface mapped
  • apps/mail/modules/conversations/components/files/FilesTab.tsxno production surface mapped
  • apps/mail/modules/documents/attachment-file-view.tsxno production surface mapped
  • apps/mail/tests/modules/conversations/filesTabAttached.test.tsxno production surface mapped
  • apps/mail/tests/modules/conversations/filesTabDrive.test.tsxno production surface mapped
  • apps/mail/tests/modules/conversations/filesTabHarness.tsxno production surface mapped
  • apps/server/docs/google-drive-integration.mdno production surface mapped
  • apps/server/src/db/integration-schemas.tsno production surface mapped
  • apps/server/src/db/migrations/drive_unlinked_file.sqlno production surface mapped
  • apps/server/src/scripts/backfill-email-attachments.tsno production surface mapped
  • apps/server/src/services/channels/__test__/link-store.test.tsno production surface mapped
  • apps/server/src/services/channels/containers.tsno production surface mapped
  • apps/server/src/services/file-system/__tests__/drive-unlink.test.tsno production surface mapped
  • apps/server/src/services/file-system/drive-pin.tsno production surface mapped
  • apps/server/src/services/file-system/drive-unlink.tsno production surface mapped
  • apps/server/src/services/file-system/uploads/extraction.tsno production surface mapped
  • apps/server/src/services/file-system/uploads/index.tsno production surface mapped
  • apps/server/src/services/file-system/uploads/types.tsno production surface mapped
  • apps/server/src/services/mail/attachments/__tests__/load-email-attachment.test.tsno production surface mapped
  • apps/server/src/services/mail/attachments/load-email-attachment.tsno production surface mapped
  • apps/server/src/trpc/routes/drive.tsno production surface mapped
  • apps/server/src/trpc/routes/files.tsno production surface mapped