Files
support_backend/specs/011-agent-ticket-queue/tasks.md
T
saqib mirandClaude Sonnet 5 fb9606b6aa feat(011-agent-ticket-queue): link agents to accounts, list assigned tickets
Extends PATCH /admin/agents/:agentId with an optional userId to finally
wire Agent.userId (added in 010-identity-auth as schema-only, never
consumed by any workflow), with proactive role/duplicate-link checks
mirroring UsersService.create's own pre-check style.

Adds GET /agents/me/tickets and GET /admin/agents/:agentId/tickets,
sharing one TicketsService.listAssignedTo method, returning a dashboard-
ready summary (product, customer, priority, severity, status, SLA state)
of every ticket currently assigned to an agent — no such query existed
anywhere in the ticketing or orchestration modules before this. Backed by
a new Assignment @@index([agentId, isCurrent]).

Discovered while starting supporthub-web's 001-agent-admin-ui: its agent-
dashboard user story had no backend data source without this.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-07 13:19:13 +05:30

120 lines
5.5 KiB
Markdown

---
description: "Task list for 011-agent-ticket-queue"
---
# Tasks: Agent Ticket Queue
**Input**: Design documents from `specs/011-agent-ticket-queue/`
**Prerequisites**: [plan.md](./plan.md), [spec.md](./spec.md), [research.md](./research.md),
[data-model.md](./data-model.md),
[contracts/agent-ticket-queue-contract.md](./contracts/agent-ticket-queue-contract.md),
[quickstart.md](./quickstart.md)
**Organization**: Tasks are grouped by user story (US1 = P1 linking, US2 = P1 ticket listing).
US2 depends on a helper US1 also needs, so despite being nominally independent, build US1 first.
## Format: `[ID] [P?] [Story] Description`
All file paths are relative to `supporthub-api/` (repo root).
---
## Phase 1: Foundational (Blocking Prerequisites)
- [x] T001 Add `Assignment @@index([agentId, isCurrent])` to `prisma/schema.prisma`; generate
the migration (`prisma migrate diff` → hand-write `migration.sql``prisma migrate
deploy`, this session's established non-interactive workaround) and run
`npm run prisma:generate`
**Checkpoint**: Index in place. Both user stories can now be built.
---
## Phase 2: User Story 1 - An admin links a staff account to its agent roster entry (Priority: P1)
**Goal**: `Agent.userId` becomes settable through the existing update endpoint, with the
rejection rules FR-001 requires.
**Independent Test**: Quickstart Scenario 1.
### Tests for User Story 1
- [x] T002 [P] [US1] Integration test covering Quickstart Scenario 1 (link succeeds; non-AGENT
role rejected 400; already-linked-elsewhere rejected 409) in
`tests/integration/agent-ticket-queue.test.ts` (depends on T001)
### Implementation for User Story 1
- [x] T003 [US1] Add `AgentsRepository.findByUserId(userId)` in
`src/modules/identity/agents/repository/agents.repository.ts` — shared by this story's
own duplicate-link check and by User Story 2's agent-self route (T010)
- [x] T004 [US1] Add `userId: z.string().min(1).nullable().optional()` to `updateAgentSchema` in
`src/modules/identity/agents/schema/agents.schema.ts`
- [x] T005 [US1] In `AgentsService.update` (`src/modules/identity/agents/service/agents.service.ts`),
when `data.userId !== undefined`: if non-null, look up the target `User` (via a small
`UsersRepository.findById`) — 404 if missing, reject with a `ValidationError` if its role
isn't `AGENT`; look up any `Agent` already linked to that `userId` (T003's
`findByUserId`) — `ConflictError` if it's a different agent than `agentId` (depends on
T003, T004)
- [x] T006 [US1] Run Quickstart Scenario 1 locally and confirm all 3 steps pass
**Checkpoint**: An agent's login can now be resolved to its roster row.
---
## Phase 3: User Story 2 - An agent retrieves their own currently-assigned tickets (Priority: P1)
**Goal**: Both list endpoints return the same summarized projection, correctly scoped per
caller.
**Independent Test**: Quickstart Scenarios 2-3.
### Tests for User Story 2
- [x] T007 [P] [US2] Unit test: given a `User` id with no linked `Agent`, the service throws the
specific `NotFoundError` — in `tests/unit/identity/agent-ticket-queue-guard.test.ts`
- [x] T008 [US2] Integration test covering Quickstart Scenarios 2-3 (agent sees exactly their
own current assignments; list updates after a reassignment; no-linked-agent session gets
404 not `[]`; admin route returns the same shape for an explicit `agentId`; non-admin
calling the admin route for another agent gets 403) in
`tests/integration/agent-ticket-queue.test.ts` (depends on T006)
### Implementation for User Story 2
- [x] T009 [US2] Add `TicketsRepository.findAssignedToAgent(agentId)` in
`src/modules/ticketing/tickets/repository/tickets.repository.ts` — one query joining
current `Assignment` (via orchestration's public repository/service surface) to `Ticket`
with `product`/`customer`/`sLARun` relations (depends on T001)
- [x] T010 [US2] Add `TicketsService.listAssignedTo(agentId)` mapping each row to the
`AssignedTicketSummary` shape (data-model.md) in
`src/modules/ticketing/tickets/service/tickets.service.ts` (depends on T009)
- [x] T011 [US2] Add `GET /agents/me/tickets` (`fastify.authenticate` only; resolves `agentId`
via `agentsService`'s `findByUserId` (T003) against `request.user.id`, throwing the
FR-006 `NotFoundError` if none) and `GET /admin/agents/:agentId/tickets`
(`fastify.authenticate` + `requireRole('ADMIN')`) in `src/modules/ticketing/tickets/
controller/` + `routes/`, registered from `src/api/routes.ts` (depends on T003, T010)
- [x] T012 [US2] Run Quickstart Scenarios 2-3 locally and confirm all steps pass
**Checkpoint**: supporthub-web's agent dashboard now has a real data source.
---
## Phase 4: Polish & Cross-Cutting Concerns
- [x] T013 [P] Update `specs/011-agent-ticket-queue/checklists/requirements.md` Notes with any
implementation-time findings
- [x] T014 Run `npx tsx scripts/check-architecture.ts` and `npm run lint`/`npm run typecheck`
- [x] T015 Full regression: `npm run test:unit` then the full integration suite against real
Docker-provisioned Postgres/Redis, confirming nothing outside this feature regressed
---
## Dependencies & Execution Order
- **Foundational (Phase 1)**: No dependencies — BLOCKS both user stories
- **User Story 1 (Phase 2)**: Depends on Foundational
- **User Story 2 (Phase 3)**: Depends on Foundational and on T003 (built in Phase 2) — build
Phase 2 before Phase 3 despite the two stories being otherwise independent
- **Polish (Phase 4)**: Depends on both user stories