Libra CodeHub

CedarCopilot/cedar-mail

Branch: staging

remove: unused task-group hard-pin feature (zero production usage)

merged#2846CedarCopilot

CedarCopilot wants to merge 1 commit into staging from remove/task-group-hard-pin

Live on prod, 0 of 3 surfaces working, 2 days leftTimeline and evidence
  1. Opened
    Sep 20, 2026, 1:05 PM
  2. Sep 20, 2026, 1:09 PM
  3. Merged
    Sep 20, 2026, 1:24 PM
  4. Live on staging
    Sep 20, 2026, 1:24 PM
  5. Live on prod
    Sep 20, 2026, 1:24 PM
  6. Observed 0 hours, 3 surfaces, 0 requests
    Sep 20, 2026, 1:24 PM
  7. Watching

    Live on prod, 0 of 3 surfaces working, 2 days left

    Sep 20, 2026, 1:24 PM
  8. Pipelines steady after this deploy
    Sep 20, 2026, 1:24 PM
  9. Pipelines steady after this deploy
    Sep 20, 2026, 1:24 PM

Behaviors Libra is checking

The taskGroups.pinConversation and taskGroups.unpinConversation tRPC mutations are no longer available to clients.Inconclusivehigh confidence

Since 2026-09-20T20:24:09Z in staging, OTEL returned 0 spans matching taskGroups, pinConversation, or unpinConversation across span names, routes, and attributes; therefore successful mutation spans, errors, volume, distinct users, and latency are all unobserved. CloudWatch.

staging, checked Sep 20, 2026, 9:07 PM
The cedar-cli groups command no longer accepts the pin or unpin verbs for task-group conversations.Inconclusivelow confidence

Verification reached its budget before a verdict.

staging, checked Sep 20, 2026, 10:04 PM
The deployment removes the unused task_group_conversations table so CRM conversations can no longer be persisted as hard-pinned task-group assignments.Inconclusivelow confidence

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

staging, checked Sep 20, 2026, 10:06 PM
The task groups page no longer displays controls for searching, adding, or removing pinned conversations.Inconclusivemedium confidence

Since the 2026-09-20T20:24:09Z deploy, staging CloudWatch shows 7 successful taskGroups.listGroups calls across the API service, with durations 242–2,512 ms, average 1,183.9 ms, and p95 2,512 ms. No taskGroups.pinConversation or taskGroups.unpinConversation calls were observed.

staging, checked Sep 20, 2026, 11:06 PM
Task creation no longer checks conversation hard-pins and routes tasks only by an explicit group, AI classification, or the virtual Misc fallback.Inconclusivelow confidence

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

staging, checked Sep 20, 2026, 11:08 PM
The taskGroups tRPC API no longer exposes pinConversation or unpinConversation mutations.Not checked
prod

Failures attributed to this change

No prod customers are affected while this is only in staging. If promoted, prod impact is unknown because Libra still needs a concrete exception, route, and failed user action before assigning severity. 0 hits · no retained affected-user count · no retained trace sample.Introducedmedium confidence

internal_only

staging, first seen Sep 20, 2026, 2:15 PM
No prod customers are affected while this is only in staging. If promoted, prod impact is unknown because Libra still needs a concrete exception, route, and failed user action before assigning severity. 0 hits · no retained affected-user count · no retained trace sample.Introducedmedium confidence

internal_only

staging, first seen Sep 20, 2026, 2:15 PM
No prod customers are affected while this is only in staging. If promoted, prod impact is unknown because Libra still needs a concrete exception, route, and failed user action before assigning severity. 2 hits · no retained affected-user count · no retained trace sample.Introducedmedium confidence

internal_only

staging, first seen Sep 20, 2026, 2:15 PM
No prod customers are affected while this is only in staging. If promoted, prod impact is unknown because Libra still needs a concrete exception, route, and failed user action before assigning severity. 1 hit · no retained affected-user count · no retained trace sample.Introducedmedium confidence

internal_only

staging, first seen Sep 20, 2026, 4:15 PM
No prod customers are affected while this is only in staging. If promoted, prod impact is unknown because Libra still needs a concrete exception, route, and failed user action before assigning severity. 1 hit · no retained affected-user count · no retained trace sample.Introducedmedium confidence

internal_only

staging, first seen Sep 20, 2026, 4:15 PM
No prod customers are affected while this is only in staging. If promoted, prod impact is unknown because Libra still needs a concrete exception, route, and failed user action before assigning severity. 0 hits · no retained affected-user count · no retained trace sample.Introducedmedium confidence

internal_only

staging, first seen Sep 20, 2026, 8:15 PM
No prod customers are affected while this is only in staging. If promoted, prod impact is unknown because Libra still needs a concrete exception, route, and failed user action before assigning severity. 0 hits · no retained affected-user count · no retained trace sample.Introducedmedium confidence

internal_only

staging, first seen Sep 20, 2026, 8:24 PM
No prod customers are affected while this is only in staging. If promoted, prod impact is unknown because Libra still needs a concrete exception, route, and failed user action before assigning severity. 0 hits · no retained affected-user count · no retained trace sample.Introducedmedium confidence

internal_only

staging, first seen Sep 20, 2026, 8:24 PM
No prod customers are affected while this is only in staging. If promoted, prod impact is unknown because Libra still needs a concrete exception, route, and failed user action before assigning severity. 0 hits · no retained affected-user count · no retained trace sample.Introducedmedium confidence

internal_only

staging, first seen Sep 20, 2026, 8:24 PM
No prod customers are affected while this is only in staging. If promoted, prod impact is unknown because Libra still needs a concrete exception, route, and failed user action before assigning severity. 0 hits · no retained affected-user count · no retained trace sample.Introducedmedium confidence

internal_only

staging, first seen Sep 20, 2026, 8:24 PM

Libra found 3 production surfaces on prod but could not judge any of them yet. 3 surfaces had no requests at all. Libra checks hourly for 3 days after each deploy.

Summary

task_group_conversations let a user hard-pin a specific CRM conversation to a task group, bypassing the AI router (routeTaskToGroup step "2. Hard-pin"). A direct production query ,

SELECT count(*) FROM task_group_conversations;

, returns 0, for any user, ever, despite this being a real, shipped, reachable UI feature (pin/unpin on /tasks/groups), not dead/unreachable code. Removing it outright: schema, tRPC procedures, UI, router step, CLI verbs, and the smoke-test case that exercised it.

Files touched

  • apps/mail/modules/userTasks/components/TaskGroupsPage.tsx , removed PinConversationPopover, the PinnedConversation type, readPins, the pin/unpin state and mutation hooks, the "Pinned conversations" row in GroupCard, and the stale "pinned conversations are unpinned" copy in the delete-confirm dialog.
  • apps/mail/tests/modules/userTasks/taskGroupsPage.test.tsx , dropped the pin/unpin mutation mocks, the crm.searchConversationsMinimal mock (no longer used), and the pin-popover assertion.
  • apps/server/src/trpc/routes/task-groups.ts , removed the pinConversation and unpinConversation procedures and the now-unused taskGroupConversations import.
  • apps/server/src/services/task-groups/routeTaskToGroup.ts , removed the hard-pin routing step; the router now goes straight from "1. Explicit lane" to "2. AI classify" to "3. Misc". Dropped 'pin' from the routedBy union (verified via a repo-wide routedBy grep , no downstream consumer had a stale 'pin' branch; every other reference was already 'explicit' | 'ai' | 'misc').
  • apps/server/src/cli/task-groups.ts , removed the pin/unpin CLI verbs and their usage docs.
  • apps/server/src/scripts/task-groups-route-smoke.ts , removed the hard-pin test case (step 4: "Hard-pin overrides AI"); the rest of the smoke test (AI classify, Misc fallback, explicit id) is untouched.
  • apps/server/src/db/aop-schema.ts , removed the taskGroupConversations Drizzle table, its relations, the pinnedConversations: many(...) relation on taskGroups, and the now-unused primaryKey import.
  • apps/server/src/db/schema.ts , dropped the taskGroupConversations/ taskGroupConversationsRelations re-export.
  • apps/server/src/db/migrations/drop_task_group_conversations.sql (new) , hand-authored DROP TABLE IF EXISTS task_group_conversations;, following this repo's existing hand-authored-migration convention (see task_groups.sql, drop_flow_rows.sql).
  • apps/server/docs/wiki/task-groups.md, apps/mail/modules/userTasks/TASK_GROUPS_DESIGN.md, apps/mail/modules/userTasks/TASK_AXES_DESIGN.md, apps/server/src/db/migrations/scripts/split-post-meeting-task-group.ts , updated design docs and a comment to reflect the removal so they don't describe dead code as live.

Migration must run through normal deploy , do not merge-and-forget

The currently-deployed production code still references task_group_conversations (the live pinConversation/unpinConversation endpoints) until this PR merges and its migration runs. The new drop_task_group_conversations.sql was not applied to any database by this session , it ships in the PR and runs through the normal deploy pipeline. Whoever merges this should confirm the migration step actually runs as part of that deploy, not skip it assuming the schema change is "just cleanup."

Test plan

  • pnpm --filter @zero/server run types , clean
  • pnpm --filter @zero/mail run types , clean
  • pnpm deps:check , no dependency violations
  • pnpm --filter @zero/server exec vitest run src/cli/__tests__/task-groups.test.ts src/services/task-groups/__tests__/manage-task-groups.test.ts , 26 passed
  • pnpm --filter @zero/server exec vitest run src/mastra/tools/task/__tests__/*.test.ts (createTaskTool + task-tags, which mock routeTaskToGroup's result) , 48 passed
  • apps/mail jest: `tests/modules/userTasks/taskGroupsPage.tes
Show production surfaces and changed-file mapping

Production surfaces

SurfaceRequestsErrorsp95UsersVerdict
drop_task_group_conversations.sql0 → 00 → 0 (0%)not measured0No traffic
No requests recorded since this deploy.
/tasks/groups0 → 00 → 0 (0%)not measured0No traffic
No requests recorded since this deploy.
/tasks/groups (TaskGroupsPage)0 → 00 → 0 (0%)not measured0No traffic
No requests recorded since this deploy.
drop_task_group_conversations.sql0 → 00 → 0 (0%)not measured0No traffic
No requests recorded since this deploy.
/tasks/groups0 → 00 → 0 (0%)not measured0No traffic
No requests recorded since this deploy.
/tasks/groups (TaskGroupsPage)0 → 00 → 0 (0%)not measured0No traffic
No requests recorded since this deploy.

Changed files → surfaces

  • apps/mail/modules/userTasks/TASK_AXES_DESIGN.mdno production surface mapped
  • apps/mail/modules/userTasks/TASK_GROUPS_DESIGN.mdno production surface mapped
  • apps/mail/modules/userTasks/components/TaskGroupsPage.tsxno production surface mapped
  • apps/mail/tests/modules/userTasks/taskGroupsPage.test.tsxno production surface mapped
  • apps/server/docs/wiki/task-groups.mdno production surface mapped
  • apps/server/src/cli/task-groups.tsno production surface mapped
  • apps/server/src/db/aop-schema.tsno production surface mapped
  • apps/server/src/db/migrations/drop_task_group_conversations.sqlno production surface mapped
  • apps/server/src/db/migrations/scripts/split-post-meeting-task-group.tsno production surface mapped
  • apps/server/src/db/schema.tsno production surface mapped
  • apps/server/src/scripts/task-groups-route-smoke.tsno production surface mapped
  • apps/server/src/services/task-groups/routeTaskToGroup.tsno production surface mapped
  • apps/server/src/trpc/routes/task-groups.tsno production surface mapped