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
+202
View File
@@ -0,0 +1,202 @@
# 01 — Support desk + admin AI inventory (auto-reply / AI assist)
**Agent:** 1/10 · **Mode:** read-only inventory
**Tools:** codehelper `project_context``kickoff``query`/`investigate`; Read/Grep/Shell fallback (`read_workspace_file` / `write_workspace_file` unavailable in focused MCP profile)
**Related:** `docs/admin-roles-support/*`, `docs/plan-permissions/01-feature-keys.json`
---
## Executive summary
Support desk is **shipped**: customer tickets + CSAT, staff queue/claim with `support_staff`, platform admin aggregate CSAT, agents directory. Platform AI already has a dedicated **`support` role slot** (provider / base_url / model / encrypted key) editable at **`/admin/settings` → AI roles**. LLM ticket auto-reply is an intentional **safe stub** (`TryAutoReplyLLM` → always `ErrAIAutoReplyDisabled`); Create / ReplyAsUser / ReplyAsAgent must not call Completers. No FAQ/canned/rule-based ticket auto-response exists yet (docs Ask is a separate rule tree and must not use `AIRoleSupport`).
---
## 1. Support desk — current surface
### 1.1 Schema
| Migration | Purpose |
|-----------|---------|
| `025_support_center.sql` | `support_tickets`, `support_messages`, `support_notifications` |
| `027_capabilities_support_perf.sql` | assignee / activity indexes |
| `029_staff_roles.sql` | `users.staff_role``admin\|developer\|support_staff` |
| `030_support_desk.sql` | `resolved_by_user_id`, CSAT table + token cols, unassigned index, `ticket_claimed` / `csat_requested` kinds |
**Ticket enums:** status `open|pending|resolved|closed`; category `billing|bug|account|other`; priority `low|normal|high`.
**Message `author_role`:** `user|agent|system` (authorship, not staff RBAC).
**CSAT:** `support_csat_ratings` — one rating per ticket (score 15 + comment). Public token columns exist but are **not wired**.
### 1.2 Domain packages
| Path | Role |
|------|------|
| `apps/api/internal/support/types.go` | `Ticket`, `Message`, `CSAT*`, `CreateInput`, `ReplyInput`, list scopes |
| `apps/api/internal/support/tickets.go` | `Create`, `Get`/`GetAdmin`, `ReplyAsUser`, `ReplyAsAgent`, `UpdateAdmin`, `listMessages` |
| `apps/api/internal/support/desk.go` | scopes, `Claim`/`Release`, `GetAdminForActor` |
| `apps/api/internal/support/agents.go` | agents directory via `staff_role` |
| `apps/api/internal/support/ratings.go` | `SubmitCSAT`, `AggregateCSAT`, `attachCSAT` |
| `apps/api/internal/support/ai_auto_reply.go` | **`TryAutoReplyLLM` stub** (sole LLM gate) |
| `apps/api/internal/support/errors.go` | `ErrAIAutoReplyDisabled` |
### 1.3 HTTP API
**Customer** (`RequireSession` + `RequireCompany`) — `server.go` ~494501:
| Method | Path |
|--------|------|
| GET/POST | `/api/support/tickets` |
| GET | `/api/support/tickets/{id}` |
| POST | `/api/support/tickets/{id}/messages` |
| POST | `/api/support/tickets/{id}/csat` |
| GET/POST | `/api/support/notifications` (+ read / read-all) |
**Staff desk** (`RequireSupportDesk`) — ~304313:
| Method | Path |
|--------|------|
| GET | `/api/admin/support/tickets` (`scope=inbox\|mine\|unassigned\|all`) |
| GET/PATCH | `/api/admin/support/tickets/{id}` |
| POST | `…/messages`, `…/claim`, `…/release` |
| GET | `/api/admin/support/agents` |
**Platform admin only** — ~316332:
| Method | Path |
|--------|------|
| GET | `/api/admin/support/csat?from=&to=` |
| PUT | `/api/admin/support/agents/{id}` |
| GET/PUT | `/api/admin/settings` (includes `ai_roles`) |
| PATCH | `/api/admin/users/{id}/staff-role` |
Contract design: `docs/admin-roles-support/11-support-api-contract.json` · backend notes: `12-support-backend.md` · CSAT: `13-support-ratings.md`.
### 1.4 Staff access
| Symbol | Path | Behavior |
|--------|------|----------|
| `ResolveStaffAccess` | `apps/api/internal/auth/staff.go` | `support_staff` → desk only; `admin`/`developer`/legacy platform admin → full |
| `RequireSupportDesk` | `httpapi/middleware.go` | desk routes |
| `RequirePlatformAdmin` | same | excludes `support_staff` from billing/settings/users |
| `requireSupportDesk` / `requirePlatformAdmin` | `apps/web/src/lib/admin-gate.ts` | UI gates |
Visibility: queue + claim (`inbox`/`mine`/`unassigned`; `all` = full admin). Cross-assignee GET → 404; reply on anothers claim → 409.
### 1.5 Web UI
| Persona | Routes | Client |
|---------|--------|--------|
| Customer | `/support`, `/support/new`, `/support/[ticketId]` | `$lib/support/api.ts`, CSAT via `SupportTicketRating.svelte` |
| Staff | `/admin/support`, `/admin/support/[id]` | `$lib/support/admin-api.ts` |
| Role grant | `/admin/users` | `PATCH …/staff-role` |
| AI keys | `/admin/settings` tab **AI roles** | `$lib/admin-ai-roles.ts` |
Docs: `14-support-staff-ui.md`, `15-user-support-ui.md`.
### 1.6 Plan-permission keys (tenant Support Center)
From `docs/plan-permissions/01-feature-keys.json` / `plan-feature-catalog.ts` — section `support`, defaults **ON** all public plans:
| Key | Route |
|-----|-------|
| `support.center` | `/support` |
| `support.ticket_create` | `/support/new` |
| `support.ticket_thread` | `/support/[ticketId]` |
| `shell.support_notifications` | notification bell |
No plan keys for staff desk, AI auto-reply, or CSAT (CSAT rides on thread eligibility).
---
## 2. Admin AI configuration (platform)
### 2.1 Role catalog
Stored in platform settings JSON (`company_settings` / SystemCompanyID) — **not** a dedicated SQL table.
| Role key | Constant | Purpose today |
|----------|----------|---------------|
| `processing` | `AIRoleProcessing` / `RoleProcessing` | Product pipeline chat; falls back to legacy `openai` + env |
| `vectorization` | `AIRoleVectorization` | Embeddings; env fallback |
| `docs_api` | `AIRoleDocsAPI` | Config slot only — **/docs Ask must not use it** |
| `support` | `AIRoleSupport` / `RoleSupport` | **Ticket-assist slot** — configure only; auto-reply gated by stub |
Fields per role: `provider`, `base_url`, `model`, encrypted `api_key`, `enabled`, optional `extras`. Secrets masked on GET (`api_key_last4` / masked).
### 2.2 Existing AI admin settings paths
| Layer | Path / symbol |
|-------|----------------|
| **UI** | `apps/web/src/routes/admin/settings/+page.svelte` — tabs Overview / **AI roles** / Mail / Other / Admin Users |
| **Client load/save** | `apps/web/src/lib/admin-platform-settings.ts``GET|PUT /api/admin/settings` |
| **Client AI roles** | `apps/web/src/lib/admin-ai-roles.ts``AI_ROLES`, `saveAIRoles`, `extractAIRoles`, `testAIRole` |
| **API handlers** | `apps/api/internal/httpapi/admin_settings_handlers.go``handleGetAdminSettings` / `handlePutAdminSettings` |
| **Storage / resolve** | `apps/api/internal/platformsettings/``ai_configs.go` (`publicAIConfigs`, `patchAIConfigs`, `ResolveAIConfig`), `types.go` (`AIRoles`, `AIRoleSupport`) |
| **Runtime completer** | `apps/api/internal/aiprovider/roles.go``ResolveCompleterForRole` (supports `RoleSupport` when configured) |
| **Tenant BYOK test (not platform roles)** | `POST /api/…/integrations/ai/test``handleTestAIIntegration` |
| **Optional role probe** | Client expects `POST /api/admin/settings/ai-roles/{role}/test`**not mounted**; `testAIRole` treats 404/501 as skipped |
**ASSUMPTION:** Enabling LLM ticket assist should use `ResolveCompleterForRole(..., RoleSupport)` (or `ResolveAIConfig(AIRoleSupport)`) **only** inside/behind `TryAutoReplyLLM`, never from Create/Reply handlers directly.
### 2.3 Docs Ask (non-ticket, rule-based)
`DocsAskGuide.svelte` + `$lib/docs-guide` — decision tree only. Explicitly must never call `AIRoleDocsAPI` or `AIRoleSupport`. Useful **pattern** for content-based auto-response before LLM fallback — not wired to tickets.
---
## 3. Auto-reply / AI assist — current state
| Capability | Status |
|------------|--------|
| Admin config slot `ai_roles.support` | **Exists** (UI + API + tests) |
| `aiprovider.ResolveCompleterForRole(RoleSupport)` | **Resolves** when slot configured |
| `support.TryAutoReplyLLM` | **Stub refuses** (`ErrAIAutoReplyDisabled`) |
| Call sites from Create / Reply* | **Forbidden by comments**; Create documents “do not call” |
| FAQ / keyword / canned auto-reply | **Absent** |
| Draft-assist UI for agents | **Absent** |
| Job/queue for async auto-reply | **Absent** |
| Plan/feature flag for auto-reply | **Absent** |
| `author_role=system` messages | Schema allows; **no auto-writer** |
Test lock: `ai_auto_reply_test.go``TestTryAutoReplyLLM_refuses`.
---
## 4. Gaps (richer detail / auto-response / AI fallback)
### 4.1 Richer ticket detail
- Staff `admin-api.ts` `SupportTicket` omits `csat`, `csat_eligible`, `resolved_by_user_id` — API can attach CSAT on GetAdmin but **admin UI/types ignore it**.
- No admin CSAT aggregate UI for `GET /api/admin/support/csat`.
- No attachments, linked product/feed IDs, or structured metadata on tickets/messages.
- No SLA timers, tags, macros, or customer org context beyond company name + requester email.
- Public CSAT token invite flow (`csat_token_hash`) unused.
- Message list is plain body text only (no markdown/HTML sanitization layer called out for AI drafts).
### 4.2 Auto-response from question content
- No classifier on `CreateInput.subject` / `body` or category.
- No canned/FAQ store or docs-guide-like tree for tickets.
- No `author_role=system` first-response after create.
- Create commits then returns — **no post-commit hook** for auto-reply (natural extension: after successful `Create` / `ReplyAsUser`, call a non-LLM matcher then optionally `TryAutoReplyLLM`).
### 4.3 AI fallback when auto fails
- Intended entry: implement `TryAutoReplyLLM` using `AIRoleSupport` completer; today always fails closed.
- No confidence threshold, human-handoff status (`pending`), or “AI attempted” audit fields.
- No product enable flag separate from `ai_roles.support.enabled` (comments require an **explicit** gate).
- Docs Ask must remain separate — do not reuse docs_api for ticket LLM.
---
## 5. Prior inventory note
`docs/admin-roles-support/02-current-inventory.md` is **stale** relative to shipped staff roles + CSAT (preagents 6/12/13/14). Prefer `12``15` + this doc for current support/AI state.
---
## 6. Sources (codehelper + docs)
- Investigate: `TryAutoReplyLLM`, `publicAIConfigs`, `GetAdmin`
- Query: support tickets/CSAT, admin AI settings, plan support keys
- Docs: `admin-roles-support/1115`, `plan-permissions/01-feature-keys.json`, `06-defaults-matrix`