feat(iios): generic opaque metadata + manual ticket assign (glue prereqs)
Generic kernel primitives so app backends (e.g. a CRM support glue) can link
their domain to interactions WITHOUT the kernel learning any app vocabulary:
- thread: accept an opaque `metadata` bag on create (merged with membership),
echo it in listThreads, and filter listThreads by `?metadata[key]=value`
(equality on the opaque bag — the kernel never interprets the keys).
- support: accept opaque `metadata` on createTicket/escalate (thread bag
inherited); add SupportService.assignTo — a policy-gated manual assignment
of a ticket to a target actor (POST /v1/support/tickets/:id/assignee).
- SDK @insignia/iios-kernel-client 0.1.2: createThread(metadata),
listThreads({metadata}) filter, createTicket(metadata), escalate(metadata),
assignTicket; ThreadSummary/Ticket expose the opaque bag.
Generic-safety gate holds: no crm/customer/lead in kernel code (opaque values
only). Tests: +2 (metadata create/filter, ticket metadata + assignTo); 31 pass.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -57,6 +57,19 @@ describe('SupportService (P4)', () => {
|
||||
expect(links[0]?.threadId).toBe(threadId);
|
||||
});
|
||||
|
||||
it('stores an opaque metadata bag on the ticket; assignTo assigns to a target actor and opens it', async () => {
|
||||
const t = await support().createTicket(cust, { subject: 'help', metadata: { crmCustomerId: 'cust_1', source: 'crm-support' } });
|
||||
expect(t.metadata).toMatchObject({ crmCustomerId: 'cust_1', source: 'crm-support' });
|
||||
expect(t.state).toBe('NEW');
|
||||
|
||||
const assigned = await support().assignTo(t.id, cust, 'agent_1');
|
||||
expect(assigned?.state).toBe('OPEN');
|
||||
const handle = await prisma.iiosSourceHandle.findFirstOrThrow({ where: { externalId: 'agent_1' } });
|
||||
const actor = await prisma.iiosActorRef.findFirstOrThrow({ where: { sourceHandleId: handle.id } });
|
||||
expect(assigned?.assignedActorId).toBe(actor.id);
|
||||
expect(await prisma.iiosTicketStateHistory.count({ where: { ticketId: t.id, reasonCode: 'assigned' } })).toBe(1);
|
||||
});
|
||||
|
||||
it('transitions NEW→OPEN→RESOLVED→CLOSED with history + rejects illegal moves', async () => {
|
||||
const t = await support().createTicket(cust, { subject: 's' });
|
||||
await support().transition(t.id, cust, 'OPEN');
|
||||
|
||||
Reference in New Issue
Block a user