CedarCopilot wants to merge 2 commits into staging from feat/task-board-type-filter
Live on prod, no production signal yet
No production telemetry since 2026-09-17T21:34:54Z positively identifies the changed task-view behavior. Queries found 0 spans for userTasks/listUserTasks, filterTypes, TaskListView, or TaskKanbanBoard and 0 matching CloudWatch log lines. Generic task telemetry was unrelated (1.
Verification reached its 130s outer budget before the verifier returned. Libra is keeping this intent verifying until the next check.
Verification reached its 120s wall-clock budget before a tied verdict. Libra is keeping this intent verifying until the next check.
Unable to complete the verification within the available tool loop.
Unable to verify the behavior from the available telemetry: no matching OTEL spans or CloudWatch log lines were found for hydrateTaskConversations, agentOperatingProcedures, or the user-tasks surface after the deploy, and source inspection was constrained by the read budget.
Unable to complete verification: telemetry returned no matching OTEL spans, and the route source could not be read due to the source budget limit.
Libra has verdicts on 0 of 6 tracked behaviors on prod; 6 are still being checked. Libra checks hourly for 3 days after each deploy.
TaskFilterMenu), alongside the existing Task group / Channel / Deal stage / CRM sync facets, filtering tasks by which playbook (AOP) their underlying deal belongs to.conversation object (aopId), rather than a hardcoded static option list.useAOPs() , the same hook CRMFilterBar.tsx's own "Type" (AOP) multi-select already uses, so there is only ever one AOP-list query in the app rather than a second, competing fetch.A customer is about to split their pipeline into two playbooks by deal origin. They need to filter their task board by playbook independently of the existing pipeline filter, and this should work for any user with more than one AOP.
apps/server/src/trpc/routes/user-tasks.ts
TaskConversationHeader gains aopId: string | null and aopName: string | null.hydrateTaskConversations() selects crmConversations.aopId, then resolves the distinct AOP ids in the batch to their names via a small parallel query (agentOperatingProcedures), matching the file's existing Promise.all([priorityOptions, openTasks]) idiom.conversationHeaders needed no changes , it shares hydrateTaskConversations and inherits the new fields automatically.apps/mail/modules/userTasks/utils/task-filters.ts
FilterableTask.conversation gains aopId?: string | null.NO_TYPE_KEY constant ('__no_type__'), mirroring NO_STATUS_KEY.TaskFilters.filterTypes: string[], a new 'types' member on TaskFacet/TASK_FACETS, and a types entry in FACET_DEFS (keyOf: (t) => t.conversation?.aopId ?? NO_TYPE_KEY).makeTaskFilter / taskFacetCounts needed no changes , confirmed they're fully generic over TASK_FACETS.apps/mail/modules/userTasks/components/TaskFilterMenu.tsx
FacetSubmenu, sourced from useAOPs() (not a hardcoded array like STAGE_OPTIONS/CRM_OPTIONS), with a trailing "No type" option using NO_TYPE_KEY.apps/mail/modules/userTasks/hooks/use-task-list-view-options.ts
filterTypes added as a URL-persisted param (filterTypes), following the exact pattern of filterCrm/filterStatuses.TaskKanbanBoard.tsx / TaskListView.tsx / use-task-group-buckets.ts
filterTypes through the three existing makeTaskFilter({...}) call sites, which the compiler caught as newly required once TaskFilters.filterTypes became non-optional.pnpm --filter @zero/server run types , clean.pnpm --filter @zero/mail run types , clean for every file touched by this diff (remaining failures are pre-existing generated-file errors , @/paraglide/*, ./+types/* , unrelated to this change, present in a fresh worktree before any codegen step runs).filterTypes=...) and survives a refresh.š¤ Generated with Claude Code
https://claude.ai/code/session_019jTe7TuDHKYaFL6Ny2Z5h6
This PR hydrates task conversations with their playbook identity and adds a URL-persisted Type facet across the task l
Libra has not measured any production surfaces for this change yet.