<!-- source-hash: 63b1778d9923d2e7914bfaebf859d9ff -->
Defines the shared type contract for the ticket management feature, including wire shapes for ticket data returned from the `POST /api/chat/agent/find-ticket` endpoint, optimistic UI helpers, error handling types, and UI constants.

## Key Components

| Export | Kind | Description |
|--------|------|-------------|
| `TicketData` | Interface | Wire shape of a ticket row from the find-ticket API, mirroring the server's `FindTicketResult` projection |
| `TicketClickupSummary` | Interface | Compact projection of a linked ClickUp task for the ticket drawer's delivery card |
| `OptimisticTicket` | Interface | Extends `TicketData` with `_optimistic: true` for pre-flight UI placeholders |
| `AnyTicket` | Type | Union of `TicketData \| OptimisticTicket` |
| `isOptimistic()` | Type Guard | Narrows `AnyTicket` to `OptimisticTicket` |
| `TicketActionErrorCode` | Type | Discriminated string union of stable server-side error codes |
| `MappedTicketActionError` | Interface | Structured error envelope with toast copy, cache hints, and retry metadata |
| `TICKET_TEXT_MAX_CHARS` | Constant | Client-side cap of `5000` chars for ticket text inputs |
| `TOAST_COPY` | Constant | Centralized `as const` map of all user-visible toast strings |

## Usage Example

```typescript
import {
  AnyTicket,
  isOptimistic,
  MappedTicketActionError,
  TICKET_TEXT_MAX_CHARS,
  TOAST_COPY,
} from './types'

// Narrow optimistic rows before rendering the drawer
function renderTicket(ticket: AnyTicket) {
  if (isOptimistic(ticket)) {
    return <SkeletonDrawer />
  }
  return <TicketDrawer data={ticket} />
}

// Validate user input before submission
if (content.length > TICKET_TEXT_MAX_CHARS) {
  throw new Error(`Max ${TICKET_TEXT_MAX_CHARS} characters allowed`)
}

// Handle a mapped server error
function handleError(err: MappedTicketActionError) {
  if (err.supportSystemDown) {
    showSupportDownBanner()
  }
  if (err.removeRowFromCache) {
    removeTicketFromList(ticketId)
  }
  toast({ title: err.message })
}

// Use centralized toast copy on success
toast(TOAST_COPY.open_success)
```

> **Note:** `TicketData` is intentionally duplicated from the server's `FindTicketResult`. If `FIND_TICKET_SELECT` gains a new column server-side, this interface must be updated to match.