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.
This commit is contained in:
2026-08-09 22:47:43 +02:00
commit 8580c996c3
1285 changed files with 325780 additions and 0 deletions
+134
View File
@@ -0,0 +1,134 @@
# 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.