From ed636bdbd5bc4cbab6cdd6c4a6a234c56b6a3a6c Mon Sep 17 00:00:00 2001 From: Oleg Date: Wed, 5 Aug 2026 18:05:01 +0000 Subject: [PATCH] Restrict agent ticket visibility to strictly their own assigned tickets MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Previous behavior also showed unassigned tickets to every agent (so new tickets stayed discoverable to pick up), but the actual want here is stricter: an agent sees only what's assigned to them, period. New tickets are now only visible to admins until explicitly assigned to an agent — an assign-then-work model rather than self-service pickup. Co-Authored-By: Claude Sonnet 5 Claude-Session: https://claude.ai/code/session_012o9j9RezxbZVKQMrB7oRLY --- src/lib/tickets/service.ts | 11 ++++------- src/lib/tickets/visibility.ts | 4 ++-- 2 files changed, 6 insertions(+), 9 deletions(-) diff --git a/src/lib/tickets/service.ts b/src/lib/tickets/service.ts index 844be3e..8e5ca01 100644 --- a/src/lib/tickets/service.ts +++ b/src/lib/tickets/service.ts @@ -417,10 +417,9 @@ export async function assignTicket(ticketId: string, assigneeId: string | null) * body in the thread — a plain LIKE search, not FTS5 (simpler and * sufficient at this ticket volume; revisit if it gets slow). * - * `visibility` restricts the result to what an agent should see: tickets - * assigned to them, plus unassigned ones (so new/unclaimed tickets are - * still discoverable to pick up) — not the whole board. Admins see - * everything, so pass `undefined`/role "admin" for that case. + * `visibility` restricts the result to what an agent should see: only + * tickets assigned to them, not the whole board. Admins see everything, + * so pass `undefined`/role "admin" for that case. */ export async function listTickets( query?: string, @@ -458,9 +457,7 @@ export async function listTickets( let filteredRows = idsToKeep ? rows.filter((r) => idsToKeep.has(r.ticket.id)) : rows; if (visibility?.role === "agent") { - filteredRows = filteredRows.filter( - (r) => r.ticket.assigneeId === null || r.ticket.assigneeId === visibility.id, - ); + filteredRows = filteredRows.filter((r) => r.ticket.assigneeId === visibility.id); } const tagsMap = await getTagsForTickets(filteredRows.map((r) => r.ticket.id)); diff --git a/src/lib/tickets/visibility.ts b/src/lib/tickets/visibility.ts index c5771d1..a75266f 100644 --- a/src/lib/tickets/visibility.ts +++ b/src/lib/tickets/visibility.ts @@ -1,10 +1,10 @@ import type { TicketDTO } from "./types"; -/** Agents see tickets assigned to them plus unassigned ones (so new tickets are still discoverable to pick up); admins see everything. */ +/** Agents see only tickets assigned to them, nothing else — admins see everything. */ export function isTicketVisibleTo( ticket: Pick, user: { id: string; role: "admin" | "agent" }, ): boolean { if (user.role === "admin") return true; - return ticket.assigneeId === null || ticket.assigneeId === user.id; + return ticket.assigneeId === user.id; }