Maps the feature onto the three existing orchestration/{routing,
assignments,orchestration} scaffold stubs. Key decisions: Assignment
refined as a version-row-per-period model (paired with a separate
append-only AssignmentHistory event log), round-robin concurrency
safety via atomic Redis INCR (reusing existing infra, not a new one),
LEAST_LOADED/SKILL_BASED tie-breaks falling back to that same cursor,
currentLoad read but never mutated by this feature, and the escalation
trigger reusing 005's existing domain-event bus rather than a new
notification path.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2.8 KiB
Phase 1 Data Model: Orchestration and Assignment
All new models use cuid() ids. Refines docs/06-database-schema.md's conceptual Assignment/
AssignmentHistory shapes (research.md).
Assignment
| Field | Type | Notes |
|---|---|---|
| id | String @id @default(cuid()) | |
| ticketId | String | FK → Ticket. Not unique — one row per assignment period (research.md) |
| agentId | String | FK → Agent |
| strategy | String | ROUND_ROBIN | LEAST_LOADED | SKILL_BASED | MANUAL | DIRECT |
| reason | String? | Free text — required-in-practice for MANUAL/DIRECT, optional for automatic strategies |
| isCurrent | Boolean @default(true) | Exactly one true row per ticketId at a time — set false when superseded |
| assignedAt | DateTime @default(now()) | |
| unassignedAt | DateTime? | Set when superseded by a later assignment |
Constraints: Index on (ticketId, isCurrent) — the exact shape "the current assignment for
this ticket" queries on. No DB-level unique on (ticketId, isCurrent: true) (Postgres partial
unique indexes aren't expressed directly in this Prisma version's schema syntax used elsewhere in
this codebase) — enforced instead by the repository's single transaction that supersedes the
prior row and inserts the new one together (same class of guarantee as 004's
conditional-update-then-insert).
AssignmentHistory
| Field | Type | Notes |
|---|---|---|
| id | String @id @default(cuid()) | |
| ticketId | String | FK → Ticket |
| agentId | String? | Null for a no eligible agent outcome (FR-008) |
| action | String | assigned | reassigned | unassigned |
| strategy | String | Same value set as Assignment.strategy |
| reason | String? | |
| actor | String | system (automatic orchestration) or an admin/agent identifier (manual) |
| createdAt | DateTime @default(now()) |
Never updated or deleted — this is the append-only audit trail FR-009/SC-003/SC-004 require.
Written in the same transaction as the Assignment row it corresponds to (or on its own, for a
"no eligible agent" outcome that produces no Assignment row at all).
Ticket / Agent (relations added by this feature)
Ticket.assignments Assignment[], Ticket.assignmentHistory AssignmentHistory[],
Agent.assignments Assignment[] — forward relations doc 06 already implied but that couldn't be
added until these models existed (same pattern every prior feature has used for its own new
back-relations).
No new fields on HierarchyNode or AgentAvailability
Round-robin's cursor lives in Redis, not Postgres (research.md) — no schema change to either
model 006-support-organization already shipped. AgentAvailability.currentLoad is read, never
written, by this feature (research.md) — no new mutation path added to it here.