Files
greeneclipse 8580c996c3 Initial commit of Descrybe v2 without local scratch artifacts.
Drop one-shot tmp/axe scripts and agent i18n scratch so the Gitea tree is deployable.
2026-08-09 22:47:43 +02:00

135 lines
5.3 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 05 — Ticket detail enrichment (tags, categories, activity, auto flags)
**Agent:** 5/10 · **Series:** `support-auto`
**Migration:** `apps/api/sql/schema/031_support_ticket_detail.sql`
**Contract:** [02-contract.md](./02-contract.md) §§2, 4.4, 67
---
## Goal
Enrich support tickets for “more detailed support” without breaking existing create/list/get contracts:
| Capability | Storage / API |
|------------|---------------|
| Tags | `support_tickets.tags TEXT[]` — create + staff PATCH (`set_tags`) |
| Better categories | `support_categories` taxonomy; seeds expand beyond `billing\|bug\|account\|other` |
| Related resource refs | `related_product_id``processed_products` (tenant-checked), `related_sku` |
| Customer context snapshot | `customer_context JSONB` — server-filled on create; staff GET full; customer GET redacted |
| Disable auto-reply | `auto_reply_disabled` — staff PATCH; auto-set on human reply / customer follow-up after auto |
| Match / AI metadata | `auto_reply_status`, `auto_reply_meta`, `auto_reply_*` message columns |
| Activity timeline | `support_ticket_activity` — auto / AI / human events on **staff GET** |
---
## Migration coordination (agents 3 / 4)
**One additive series — do not fork ticket columns.**
| File | Owner | Contents |
|------|-------|----------|
| `031_support_ticket_detail.sql` | **agent 5** | Ticket rich fields, message `auto_*` columns, `support_categories`, `support_ticket_activity`, notification kind `auto_reply` |
| `032_*` (KB / templates / jobs) | **agent 3** | `support_kb_articles`, `support_reply_templates`, matcher tables/jobs — **do not** re-`ALTER` ticket/message columns from 031 |
| AI fallback | **agent 4** | Reuse `RecordAutoReplyOutcome`, message `auto_source='ai'`, statuses `ai_draft` / `ai_sent` / `failed` / `handed_off` |
Shared helpers for agents 34:
- `support.RecordAutoReplyOutcome(ctx, ticketID, status, disabled, messageID, meta, activityKind)`
- Message columns: `is_auto_reply`, `auto_source``kb|template|ai`, `auto_confidence`, `auto_ref_type`, `auto_ref_id`
- Ticket: `auto_reply_status``none|matched|ai_draft|ai_sent|skipped|failed|handed_off`
---
## API changes (additive)
### Customer create — `POST /api/support/tickets`
Existing: `subject`, `category`, `priority`, `body`.
Additive optional:
```json
{
"tags": ["woo", "export"],
"related_product_id": "uuid",
"related_sku": "SKU-123"
}
```
`related_product_id` must belong to the callers `company_id` or the API returns `invalid related_product_id`.
### List — customer + staff
Returns (in addition to prior fields): `tags`, `related_product_id`, `related_sku`, `auto_reply_disabled`, `auto_reply_status`, `resolved_by_user_id`.
**Does not** embed full `customer_context`, `auto_reply_meta`, or `activity` (detail-only).
### GET customer — `GET /api/support/tickets/{id}`
Detail fields + messages (with `is_auto_reply` / `auto_source` when present).
`customer_context` redacts `signals` and `user_email`.
`auto_reply_meta` and `activity` omitted.
### GET staff — `GET /api/admin/support/tickets/{id}`
Full snapshot + `auto_reply_meta` + `activity[]` timeline + messages including internal notes and auto metadata.
### Staff PATCH — `PATCH /api/admin/support/tickets/{id}`
Additive fields on `AdminUpdateInput`:
| Field | Notes |
|-------|-------|
| `category` | Active taxonomy slug |
| `tags` + `set_tags: true` | Replace tag set (omit `set_tags` to leave unchanged) |
| `related_product_id` / `clear_related_product` | Tenant-scoped |
| `related_sku` | Optional string |
| `auto_reply_disabled` | Staff kill-switch for auto pipeline |
---
## Activity timeline kinds
`created`, `customer_message`, `agent_message`, `system_message`, `auto_reply`, `ai_draft`, `ai_sent`, `ai_failed`, `handed_off`, `claimed`, `released`, `status_changed`, `auto_disabled`, `auto_enabled`, `note`
---
## Behavior hooks
1. **Create** — writes `customer_context`, activity `created`; leaves `auto_reply_status=none` for agent 3 matcher.
2. **Customer reply after matched/ai_sent** — sets `auto_reply_disabled=true`, `auto_reply_status=handed_off`.
3. **Staff public reply** — sets `auto_reply_disabled=true`.
4. **Staff toggles `auto_reply_disabled`** — activity `auto_disabled` / `auto_enabled`.
Create / Reply\* still **must not** call LLM completers directly (agent 4 owns `TryAutoReplyLLM`).
---
## Tests
| Test | File |
|------|------|
| Tag / SKU / category validation | `ticket_detail_test.go` |
| Context redaction | `ticket_detail_test.go` |
| Create rejects bad tags (ClientError) | `ticket_detail_test.go` |
| Existing CRUD auth / claim / CSAT | unchanged; pre-031 column fallbacks keep them green until goose up |
---
## Verify locally
```bash
# from apps/api
goose -dir sql/schema postgres "$DATABASE_URL" up
go test ./internal/support/ -count=1
```
---
## ASSUMPTIONS
- ASSUMPTION: Agent 3 lands KB in `032_*` without duplicating 031 ticket ALTERs. (**Confirmed:** `032_support_kb_auto_reply.sql` references 031.)
- ASSUMPTION: `plans.name` is stored as `customer_context.plan_slug` (plans have no slug column).
- ASSUMPTION: List payloads stay lean; UI agents read detail GET for timeline / context.
- NOTE for agent 10: two files share version `033_*` (`033_support_auto_ai_config.sql` and `033_support_auto_security_perf.sql`) — rename one before goose up.