diff --git a/DESIGN.md b/DESIGN.md index 026e1f6b633..cff31c9a04c 100644 --- a/DESIGN.md +++ b/DESIGN.md @@ -407,9 +407,12 @@ Project, Parent Task, Tags, Worktree, Last updated**. The reusable width tokens and configurable column order. Review the complete composition in **Design proposals → Components → Task list → All columns**. -- Created by is the recorded creator (human or agent); Assignee is the current - assignee. Both show names without avatars. Do not infer either from tree depth - or task status. +- Created by is the recorded creator (human or agent) and shows a name. Assignee + shows the current agent's avatar or human's profile photo beside their name, + with the shared avatar fallback when an image is unavailable. Human photos load + only from Paperclip asset URLs; external profile URLs use initials. The avatar stays + visible while a long name reveals on hover or focus. Do not infer either + identity from tree depth or task status. - Task combines the status icon and title under one header and one Columns-menu choice. Sorting Task uses the title. Status sorting is unavailable for now. - Parent Task refers to the structural parent; it can show its ID and title. diff --git a/ui/src/components/TaskList.assignees.test.tsx b/ui/src/components/TaskList.assignees.test.tsx new file mode 100644 index 00000000000..e418cdca5e9 --- /dev/null +++ b/ui/src/components/TaskList.assignees.test.tsx @@ -0,0 +1,122 @@ +// @vitest-environment jsdom +import { act, type ComponentProps } from "react"; +import { createRoot, type Root } from "react-dom/client"; +import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; +import { appearanceForPalette } from "@paperclipai/shared"; +import { createIssue } from "../../storybook/fixtures/paperclipData"; +import { TaskList, type TaskListItem } from "./TaskList"; +import { TooltipProvider } from "./ui/tooltip"; + +vi.mock("@/lib/router", () => ({ + Link: ({ children, to, state: _state, disableIssueQuicklook: _quicklook, issuePrefetch: _prefetch, ...props }: + ComponentProps<"a"> & { to?: string; state?: unknown; disableIssueQuicklook?: boolean; issuePrefetch?: unknown }) => + {children}, +})); + +Object.assign(globalThis, { IS_REACT_ACT_ENVIRONMENT: true }); + +const agentItem: TaskListItem = { + issue: createIssue({ id: "agent-task", title: "Agent task", assigneeAgentId: "agent-1", assigneeUserId: null }), + assignee: "Research Agent", + assigneeAgent: { + id: "agent-1", + name: "Research Agent", + appearance: { ...appearanceForPalette("deep-tide"), customAvatarAssetId: "22222222-2222-4222-8222-222222222222" }, + }, +}; +const humanItem: TaskListItem = { + issue: createIssue({ id: "human-task", title: "Human task", assigneeAgentId: null, assigneeUserId: "user-1" }), + assignee: "Alex Smith", + assigneeUserAvatarUrl: "/api/assets/33333333-3333-4333-8333-333333333333/content", +}; +const brokenHumanAvatar = "/api/assets/44444444-4444-4444-8444-444444444444/content"; +const imageRequests = vi.fn<(src: string) => void>(); + +describe("TaskList assignee identities", () => { + let host: HTMLDivElement; + let root: Root; + + beforeEach(() => { + imageRequests.mockClear(); + host = document.createElement("div"); + document.body.append(host); + root = createRoot(host); + // Radix preloads profile pictures through Image. jsdom does not load them. + vi.stubGlobal("Image", class extends EventTarget { + complete = false; + naturalWidth = 0; + set src(value: string) { + imageRequests(value); + queueMicrotask(() => { + this.complete = true; + this.naturalWidth = value === brokenHumanAvatar ? 0 : 100; + this.dispatchEvent(new Event(this.naturalWidth ? "load" : "error")); + }); + } + }); + }); + + afterEach(async () => { + await act(async () => root.unmount()); + host.remove(); + vi.unstubAllGlobals(); + }); + + async function render(items: TaskListItem[]) { + await act(async () => root.render( + , + )); + } + + const cell = (title: string) => host.querySelector(`[role="rowgroup"][aria-label="${title} task group"] [role="cell"][data-column="assignee"]`)!; + + it("shows agent appearance and human profile pictures beside their names", async () => { + await render([agentItem, humanItem]); + expect(cell("Agent task").textContent).toBe("Research Agent"); + expect(cell("Agent task").querySelector("img")?.getAttribute("src")).toBe("/api/assets/22222222-2222-4222-8222-222222222222/content"); + expect(cell("Human task").textContent).toBe("Alex Smith"); + expect(cell("Human task").querySelector("img")?.getAttribute("src")).toBe(humanItem.assigneeUserAvatarUrl); + }); + + it.each([ + "https://example.com/track.png", + "http://example.com/track.png", + "//example.com/track.png", + "/profile/alex.png", + ])("uses initials without loading an untrusted human avatar: %s", async (source) => { + await render([{ ...humanItem, assigneeUserAvatarUrl: source }]); + expect(cell("Human task").querySelector('[data-slot="avatar-fallback"]')?.textContent).toBe("AS"); + expect(cell("Human task").querySelector("img")).toBeNull(); + expect(imageRequests).not.toHaveBeenCalled(); + }); + + it("uses initials for missing or failed pictures and leaves unassigned rows without an avatar", async () => { + const unassigned: TaskListItem = { + issue: createIssue({ id: "unassigned-task", title: "Unassigned task", assigneeAgentId: null, assigneeUserId: null }), + }; + await render([agentItem, { ...humanItem, assigneeUserAvatarUrl: null }, unassigned]); + expect(cell("Human task").querySelector('[data-slot="avatar-fallback"]')?.textContent).toBe("AS"); + expect(cell("Unassigned task").textContent).toBe("Unassigned"); + expect(cell("Unassigned task").querySelector("img, [data-slot=avatar], [data-slot=agent-avatar]")).toBeNull(); + + await act(async () => cell("Agent task").querySelector("img")!.dispatchEvent(new Event("error"))); + expect(cell("Agent task").querySelector('[data-slot="agent-avatar"]')?.textContent).toBe("RA"); + await render([{ ...humanItem, assigneeUserAvatarUrl: brokenHumanAvatar }]); + expect(cell("Human task").querySelector('[data-slot="avatar-fallback"]')?.textContent).toBe("AS"); + expect(cell("Human task").querySelector("img")).toBeNull(); + }); + + it("removes and restores assignee avatars through the Columns control", async () => { + await render([agentItem, humanItem]); + const columns = host.querySelector('button[aria-label="Columns"]')!; + await act(async () => columns.dispatchEvent(new KeyboardEvent("keydown", { key: "Enter", bubbles: true }))); + const assignee = [...document.querySelectorAll('[role="menuitemcheckbox"]')].find(item => item.textContent === "Assignee")!; + expect(assignee).toBeDefined(); + await act(async () => assignee.click()); + expect(host.querySelector('[role="cell"][data-column="assignee"]')).toBeNull(); + expect(host.querySelector("img")).toBeNull(); + await act(async () => assignee.click()); + expect(cell("Agent task").querySelector("img")?.getAttribute("src")).toBe("/api/assets/22222222-2222-4222-8222-222222222222/content"); + expect(cell("Human task").querySelector("img")?.getAttribute("src")).toBe(humanItem.assigneeUserAvatarUrl); + }); +}); diff --git a/ui/src/components/TaskList.tsx b/ui/src/components/TaskList.tsx index b653de661b9..791219d0f37 100644 --- a/ui/src/components/TaskList.tsx +++ b/ui/src/components/TaskList.tsx @@ -3,6 +3,7 @@ import type { Issue } from "@paperclipai/shared"; import { ChevronDown, ChevronRight, GitBranch } from "lucide-react"; import { buildIssueTree } from "../lib/issue-tree"; import { issueFilterLabel } from "../lib/issue-filters"; +import { paperclipHumanAvatarUrl } from "../lib/issue-chat-human-author"; import { defaultTaskListColumns, defaultTaskListSort, isTaskListColumnSort, taskListOrderToIssueSort, type TaskListColumnId, type TaskListSort, type TaskListOrder } from "../lib/task-list"; import { sortIssues } from "../lib/issue-sort"; import { timeAgo } from "../lib/timeAgo"; @@ -13,6 +14,9 @@ import { TaskRowGroup } from "./TaskRowGroup"; import { Badge } from "./ui/badge"; import { Button } from "./ui/button"; import { useTaskRowBudget } from "../hooks/useTaskRowBudget"; +import { AgentAvatar, type AvatarAgent } from "./AgentAvatar"; +import { deriveInitials } from "./Identity"; +import { Avatar, AvatarFallback, AvatarImage } from "./ui/avatar"; import { useTaskListColumnPreferences } from "../hooks/useTaskListColumnPreferences"; import type { TaskCollectionPreferenceLocation } from "../lib/task-collection-preferences"; @@ -21,6 +25,8 @@ export type TaskListItem = { unread?: boolean; createdBy?: string | null; assignee?: string | null; + assigneeAgent?: AvatarAgent | null; + assigneeUserAvatarUrl?: string | null; }; export type TaskListGrouping = "none" | "status" | "project"; @@ -94,6 +100,8 @@ export function TaskList({ items, allItems = items, initialColumns = defaultTask const expanded = !collapsed.has(issue.id); const parent = issue.parentId ? byId.get(issue.parentId)?.issue : null; const branch = issue.currentExecutionWorkspace?.branchName; + const assigneeName = item.assignee ?? (issue.assigneeAgentId ? "Unknown agent" : issue.assigneeUserId ? "User" : "Unassigned"); + const assigneeUserAvatarUrl = paperclipHumanAvatarUrl(item.assigneeUserAvatarUrl); return ( + ) : issue.assigneeUserId ? ( + + ) : null, project: issue.project?.name, parentTask: parent ? {parent.identifier} {parent.title} : undefined, tags: issue.labels?.length ? {issue.labels.map(label => {label.name})} : undefined, diff --git a/ui/src/components/TaskListColumns.tsx b/ui/src/components/TaskListColumns.tsx index d1dd128f528..2815818cf7f 100644 --- a/ui/src/components/TaskListColumns.tsx +++ b/ui/src/components/TaskListColumns.tsx @@ -56,10 +56,11 @@ export function TaskListViewport({ children, visibleColumns = defaultTaskListCol const detailColumns = taskListColumns.filter(column => column.id !== "task"); type DetailColumn = Exclude<(typeof detailColumns)[number]["id"], "taskId">; -/** Values are resolved by the collection; identity columns intentionally accept names only. */ +/** Values are resolved by the collection; names remain separate from avatar presentation. */ export type TaskRowColumnValues = Partial, ReactNode>> & { createdBy?: string | null; assignee?: string | null; + assigneeAvatar?: ReactNode; }; function SortableColumnHeader({ id, label, start, sort, onSortChange, onColumnOrderChange, helpId }: { @@ -184,7 +185,12 @@ export function TaskRowColumnCells({ identifier, values }: { identifier: string; {columns.filter(id => id !== "task").map(id => ( - {id === "taskId" ? identifier : values[id] ?? "—"} + {id === "assignee" && values.assigneeAvatar ? ( + + {values.assigneeAvatar} + {values.assignee ?? "—"} + + ) : {id === "taskId" ? identifier : values[id] ?? "—"}} ))} diff --git a/ui/src/pages/CoordinatorTasks.test.tsx b/ui/src/pages/CoordinatorTasks.test.tsx index 1ad5885cde5..3a61befe4ec 100644 --- a/ui/src/pages/CoordinatorTasks.test.tsx +++ b/ui/src/pages/CoordinatorTasks.test.tsx @@ -45,8 +45,8 @@ vi.mock("@/components/TaskListToolbar", () => ({ TaskListToolbar: ({ filters, vi })); vi.mock("@/components/TaskList", async importOriginal => ({ ...await importOriginal(), - TaskList: ({ items, emptyState, onSortChange }: { onSortChange?: (sort: TaskListSort) => void; items: { issue: { id: string; title: string } }[]; emptyState: React.ReactNode }) => -
{useLocation().search}{items.length ? items.map(item =>

{item.issue.title}

) : emptyState}
, + TaskList: ({ items, emptyState, onSortChange }: { onSortChange?: (sort: TaskListSort) => void; items: { issue: { id: string; title: string }; assignee?: string | null }[]; emptyState: React.ReactNode }) => +
{useLocation().search}{items.length ? items.map(item =>

{item.issue.title}

) : emptyState}
, })); describe("Coordinator tasks using the proposed table", () => { @@ -152,6 +152,15 @@ describe("Coordinator tasks using the proposed table", () => { expect(mocks.get).not.toHaveBeenCalled(); }); + it.each([ + ["local-board", "Board"], + ["missing-user", "missi"], + ])("keeps the shared human identity label when %s is absent from the directory", async (assigneeUserId, label) => { + mocks.list.mockResolvedValue([createIssue({ id: "human-task", companyId: "company", parentId: null, assigneeAgentId: null, assigneeUserId })]); + await mount("/issues"); + expect(host.querySelector('[data-task-id="human-task"]')?.getAttribute("data-assignee")).toBe(label); + }); + it("opens routine task links, filters before paging, and lets the checkbox change the collection", async () => { const routine = createIssue({ id: "routine-task", companyId: "company", parentId: null, originKind: "routine_execution", title: "Routine report" }); const manual = createIssue({ id: "manual-task", companyId: "company", parentId: null, originKind: "manual", title: "Manual report" }); diff --git a/ui/src/pages/CoordinatorTasks.tsx b/ui/src/pages/CoordinatorTasks.tsx index 2e38c33b197..151668e54d5 100644 --- a/ui/src/pages/CoordinatorTasks.tsx +++ b/ui/src/pages/CoordinatorTasks.tsx @@ -24,7 +24,8 @@ import { useIssueRowDetails } from "@/hooks/useIssueRowDetails"; import { useCoordinatorWorkspaceEnabled } from "@/hooks/useCoordinatorWorkspaceEnabled"; import { usePublishSharedQueryData, useSharedPollingQuery } from "@/hooks/useSharedPolling"; import { collectLiveIssueIds } from "@/lib/liveIssueIds"; -import { buildCompanyUserLabelMap } from "@/lib/company-members"; +import { formatUserLabel } from "@/lib/assignees"; +import { buildCompanyUserProfileMap } from "@/lib/company-members"; import { coordinatorAttentionTasksQueryKey } from "@/lib/coordinator-attention"; import { createIssueDetailLocationState } from "@/lib/issueDetailBreadcrumb"; import { defaultIssueFilterState, type IssueFilterState } from "@/lib/issue-filters"; @@ -155,8 +156,8 @@ export function CoordinatorTasks({ viewControl }: { viewControl?: ReactNode } = const sharedLiveRuns = useSharedPollingQuery({ companyId, resourceKey: "live-runs", queryKey: queryKeys.liveRuns(companyId!), enabled: !!companyId, refetchInterval: false, leaderOnly: true }); const liveRuns = useQuery({ queryKey: queryKeys.liveRuns(companyId!), queryFn: () => heartbeatsApi.liveRunsForCompany(companyId!), enabled: sharedLiveRuns.enabled, refetchInterval: sharedLiveRuns.refetchInterval }); usePublishSharedQueryData(sharedLiveRuns, liveRuns.data, liveRuns.dataUpdatedAt); - const users = buildCompanyUserLabelMap(directory.data?.users); - const agentNames = new Map(agents.data?.map(agent => [agent.id, agent.name])); + const users = buildCompanyUserProfileMap(directory.data?.users); + const agentById = new Map(agents.data?.map(agent => [agent.id, agent])); const projectById = new Map(projects.data?.map(project => [project.id, project])); const records = (needsAttention ? attentionTasks.flatMap(task => task.data ?? []) : tasks.data?.pages.flatMap(page => page.items) ?? []) as Issue[]; const unreadIds = new Set(view === "unread" ? records.map(issue => issue.id) : []); @@ -190,8 +191,10 @@ export function CoordinatorTasks({ viewControl }: { viewControl?: ReactNode } = const items: TaskListItem[] = [...new Map(scopedRecords.map(issue => [issue.id, issue])).values()].map(issue => ({ issue: { ...issue, project: issue.project ?? projectById.get(issue.projectId ?? "") }, unread: unreadIds.has(issue.id) || issue.isUnreadForMe === true, - createdBy: issue.createdByAgentId ? agentNames.get(issue.createdByAgentId) ?? "Unknown agent" : users.get(issue.createdByUserId ?? "") ?? "Board", - assignee: issue.assigneeAgentId ? agentNames.get(issue.assigneeAgentId) ?? "Unknown agent" : users.get(issue.assigneeUserId ?? "") ?? null, + createdBy: issue.createdByAgentId ? agentById.get(issue.createdByAgentId)?.name ?? "Unknown agent" : users.get(issue.createdByUserId ?? "")?.label ?? "Board", + assignee: issue.assigneeAgentId ? agentById.get(issue.assigneeAgentId)?.name ?? "Unknown agent" : users.get(issue.assigneeUserId ?? "")?.label ?? formatUserLabel(issue.assigneeUserId), + assigneeAgent: agentById.get(issue.assigneeAgentId ?? ""), + assigneeUserAvatarUrl: users.get(issue.assigneeUserId ?? "")?.image, })); const collection = useTaskListCollection({ items, companyId, userId, query, onQueryChange: setQuery, view: needsAttention ? "all" : view, layout, onLayoutChange: setLayout, diff --git a/ui/src/pages/DesignSandboxTasks.tsx b/ui/src/pages/DesignSandboxTasks.tsx index ca6ae1fc9c2..3c56c479cf4 100644 --- a/ui/src/pages/DesignSandboxTasks.tsx +++ b/ui/src/pages/DesignSandboxTasks.tsx @@ -16,7 +16,8 @@ import { useBreadcrumbs } from "@/context/BreadcrumbContext"; import { useCompany } from "@/context/CompanyContext"; import { useDialogActions } from "@/context/DialogContext"; import { useTaskListCollection } from "@/hooks/useTaskListCollection"; -import { buildCompanyUserLabelMap } from "@/lib/company-members"; +import { formatUserLabel } from "@/lib/assignees"; +import { buildCompanyUserProfileMap } from "@/lib/company-members"; import { queryKeys } from "@/lib/queryKeys"; import { taskListViewStorageKey } from "@/lib/task-list-views"; import type { Issue } from "@paperclipai/shared"; @@ -66,14 +67,16 @@ export function DesignSandboxTasks() { }, enabled: !!companyId && !!userId, }); - const users = buildCompanyUserLabelMap(directory.data?.users); - const agentNames = new Map(agents.data?.map(agent => [agent.id, agent.name])); + const users = buildCompanyUserProfileMap(directory.data?.users); + const agentById = new Map(agents.data?.map(agent => [agent.id, agent])); const projectById = new Map(projects.data?.map(project => [project.id, project])); const items: TaskListItem[] = [...new Map((tasks.data?.pages.flat() ?? []).map(issue => [issue.id, issue])).values()].map(issue => ({ issue: { ...issue, project: issue.project ?? projectById.get(issue.projectId ?? "") }, unread: unread.data?.has(issue.id) ?? false, - createdBy: issue.createdByAgentId ? agentNames.get(issue.createdByAgentId) ?? "Unknown agent" : users.get(issue.createdByUserId ?? "") ?? (issue.createdByUserId === "local-board" ? "Board" : "Unknown user"), - assignee: issue.assigneeAgentId ? agentNames.get(issue.assigneeAgentId) ?? "Unknown agent" : users.get(issue.assigneeUserId ?? "") ?? (issue.assigneeUserId === "local-board" ? "Board" : null), + createdBy: issue.createdByAgentId ? agentById.get(issue.createdByAgentId)?.name ?? "Unknown agent" : users.get(issue.createdByUserId ?? "")?.label ?? (issue.createdByUserId === "local-board" ? "Board" : "Unknown user"), + assignee: issue.assigneeAgentId ? agentById.get(issue.assigneeAgentId)?.name ?? "Unknown agent" : users.get(issue.assigneeUserId ?? "")?.label ?? formatUserLabel(issue.assigneeUserId), + assigneeAgent: agentById.get(issue.assigneeAgentId ?? ""), + assigneeUserAvatarUrl: users.get(issue.assigneeUserId ?? "")?.image, })); const collection = useTaskListCollection({ items, companyId, userId, viewStorageKey: taskListViewStorageKey("design-integration-sandbox", userId ?? "local-board"), diff --git a/ui/storybook/public/api/assets/33333333-3333-4333-8333-333333333333/content b/ui/storybook/public/api/assets/33333333-3333-4333-8333-333333333333/content new file mode 100644 index 00000000000..7e63dcc804a Binary files /dev/null and b/ui/storybook/public/api/assets/33333333-3333-4333-8333-333333333333/content differ diff --git a/ui/storybook/stories/design-proposals/task-row-design.stories.tsx b/ui/storybook/stories/design-proposals/task-row-design.stories.tsx index 7c1b1309c56..cf91f8a5d38 100644 --- a/ui/storybook/stories/design-proposals/task-row-design.stories.tsx +++ b/ui/storybook/stories/design-proposals/task-row-design.stories.tsx @@ -224,7 +224,7 @@ const meta = { exception: "Staged design-system refresh awaiting visual review and app adoption; kept separate from production references.", implementation: "production", boundary: "fixtures", - notes: "Library specimen of refreshed IssueRow, TaskRowGroup, and TaskListColumnHeader. Task combines status and title under one header and visibility choice; Task sorting uses titles, with no status sort. All Columns starts with every column and demonstrates sibling hierarchy, sorting, pointer/keyboard column reordering, pinned column settings, visibility and reset using local fixture state. Last updated is last by default; the flyout follows the current column order. Reset restores that order and shows only Task, Task ID, and Last updated. The Columns flyout follows the Paper reference with compact 32px items, regular labels, and right-aligned checks. Sorted headers retain their background. Table typography uses sm with a 16px line height; every truncated cell uses a local marquee. The entire proposal canvas uses Paper's background token. Status tooltips use shared labels. The composition is under review and does not establish production API, preference persistence, or full accessibility acceptance.", + notes: "Library specimen of refreshed IssueRow, TaskRowGroup, and TaskListColumnHeader. Task combines status and title under one header and visibility choice; Task sorting uses titles, with no status sort. All Columns shows agent avatars, a human profile image and human fallback avatars beside assignee names. Created by remains a name. Unassigned rows have no avatar. Hiding Assignee removes its avatars. All Columns starts with every column and demonstrates sibling hierarchy, sorting, pointer/keyboard column reordering, pinned column settings, visibility and reset using local fixture state. Last updated is last by default; the flyout follows the current column order. Reset restores that order and shows only Task, Task ID, and Last updated. The Columns flyout follows the Paper reference with compact 32px items, regular labels, and right-aligned checks. Sorted headers retain their background. Table typography uses sm with a 16px line height; every truncated cell uses a local marquee. The entire proposal canvas uses Paper's background token. Status tooltips use shared labels. The composition is under review and does not establish production API, preference persistence, or full accessibility acceptance.", }, }, } satisfies Meta; diff --git a/ui/storybook/support/design-proposals/task-list-data.ts b/ui/storybook/support/design-proposals/task-list-data.ts index af5d11fb7ca..c064349d42e 100644 --- a/ui/storybook/support/design-proposals/task-list-data.ts +++ b/ui/storybook/support/design-proposals/task-list-data.ts @@ -59,6 +59,9 @@ export function columnData(issue: Issue, owner: string) { parent: treeTasks.find(candidate => candidate.id === issue.parentId), creator: rowIssue.createdByAgentId ? agent.name : "Scott", assignee: rowIssue.assigneeAgentId ? agent.name : rowIssue.assigneeUserId ? "Design" : null, + assigneeAgent: rowIssue.assigneeAgentId ? agent : null, + // Static Storybook fixture served at the same asset path as an uploaded photo. + assigneeUserAvatarUrl: rowIssue.assigneeUserId && issue.id === healthTask.id ? "/api/assets/33333333-3333-4333-8333-333333333333/content" : null, }; } @@ -79,5 +82,5 @@ export function sortValue(issue: Issue, column: TaskListColumnId) { export const reviewTaskItems: TaskListItem[] = treeTasks.map(issue => { const data = columnData(issue, ownerFor(issue)); - return { issue: data.issue, createdBy: data.creator, assignee: data.assignee, unread: issue === connectorParent || issue === healthTask }; + return { issue: data.issue, createdBy: data.creator, assignee: data.assignee, assigneeAgent: data.assigneeAgent, assigneeUserAvatarUrl: data.assigneeUserAvatarUrl, unread: issue === connectorParent || issue === healthTask }; }); diff --git a/ui/storybook/support/design-proposals/task-page-data.ts b/ui/storybook/support/design-proposals/task-page-data.ts index 9b198f6f683..9e666804752 100644 --- a/ui/storybook/support/design-proposals/task-page-data.ts +++ b/ui/storybook/support/design-proposals/task-page-data.ts @@ -7,7 +7,7 @@ const extraTasks: TaskListItem[] = [ task(460, "Collect feedback on the refreshed task list", "todo"), ].map((issue, index) => { const data = columnData(issue, index === 2 ? "Design" : "Codex"); - return { issue: data.issue, createdBy: data.creator, assignee: data.assignee, unread: index === 0 }; + return { issue: data.issue, createdBy: data.creator, assignee: data.assignee, assigneeAgent: data.assigneeAgent, assigneeUserAvatarUrl: data.assigneeUserAvatarUrl, unread: index === 0 }; }); export const initialItems = [...reviewTaskItems, ...extraTasks]; export const recentTasks = [...initialItems]