```yaml
id: UJ-011
title: Queue one content translation
actors: [administrator]
surface: admin-spa
goal: Administrator queues one source post for one target language and fresh job state tracks it.
trigger: Activate Generate for one missing language in content editor at /wp-admin/admin.php?page=international-press-zone#/translations/posts.
fixtures: [authenticated-admin, source-post, active-target-language, configured-translation-api]
success_state:
  visible: "Translation queued to <language>" renders and language status becomes processing.
  durable: Exactly one pending or processing job exists for source post and target language.
  persistence: Fresh jobs read returns that exact job and status.
source_specs: [admin/src/pages/content-translate.js, includes/API/TranslateController.php, includes/API/TranslateJobsController.php]
readiness: draft
canonical: false
blockers: []
```

## Path

| Hop | Evidence |
|---|---|
| UI trigger | `admin/src/pages/content-translate.js:735` |
| Request construction | `admin/src/pages/content-translate.js:777` |
| Endpoint auth and parse | `includes/API/TranslateController.php:148` |
| Authoritative write | `includes/API/TranslateController.php:806` |
| Response | `includes/API/TranslateController.php:533` |
| Terminal render | `admin/src/pages/content-translate.js:784` |

## Happy path

### H1
- Setup: Administrator authenticated; source post exists; target language active and missing; API credentials valid; no open duplicate job.
- Action: Open source post translation editor and activate `Generate` for target language.
- Request: `POST /wp-json/international-press-zone/v1/translate-async`.
- Response: `200` with `{success:true,jobs:[<job>],already_queued:[]}`.
- Visible: `Translation queued to <language>` renders; target tab becomes processing.
- Durable: One `wp_presszone_international_jobs` row links source post and target with pending or processing status and exact character count.
- Fresh read: `GET /wp-json/international-press-zone/v1/translate-jobs/{id}` returns `200` with exact post, target, and status.
- Forbidden: No duplicate job, immediate translated post, source post mutation, or second API submission occurs.
- Evidence:
  - Setup: `includes/API/TranslateController.php:426`
  - Action: `admin/src/pages/content-translate.js:735`
  - Request: `admin/src/pages/content-translate.js:777`
  - Response: `includes/API/TranslateController.php:533`
  - Visible: `admin/src/pages/content-translate.js:784`
  - Durable: `includes/API/TranslateController.php:806`
  - Fresh read: `includes/API/TranslateJobsController.php:450`
  - Forbidden: `includes/API/TranslateController.php:784`

## Alternate and failure paths

### A1
- Setup: Administrator authenticated; source post and target valid; identical pending job already exists.
- Action: Activate `Generate` again for same target.
- Request: `POST /wp-json/international-press-zone/v1/translate-async`.
- Response: `200` with `{success:true,jobs:[],already_queued:[<target-language>]}`.
- Visible: Queue acknowledgement renders without a second processing job.
- Durable: Open-job projection remains exactly one row for source and target.
- Fresh read: Jobs GET returns the original job ID and no duplicate.
- Forbidden: No duplicate job or second backend translation request occurs.
- Evidence:
  - Setup: `includes/API/TranslateController.php:784`
  - Action: `admin/src/pages/content-translate.js:735`
  - Request: `admin/src/pages/content-translate.js:777`
  - Response: `includes/API/TranslateController.php:533`
  - Visible: `admin/src/pages/content-translate.js:784`
  - Durable: `includes/API/TranslateController.php:784`
  - Fresh read: `includes/API/TranslateJobsController.php:185`
  - Forbidden: `includes/API/TranslateController.php:784`

## Permissions and boundaries

### P1
- Setup: Actor lacks `manage_options`; source and target otherwise valid.
- Action: Submit async content translation.
- Request: `POST /wp-json/international-press-zone/v1/translate-async`.
- Response: `403` with `{code:"rest_forbidden",message:"Sorry, you are not allowed to do that.",data:{status:403}}`.
- Visible: REST denial exposes no job, content, credential, or backend response.
- Durable: No job or post state changes.
- Fresh read: Protected job list returns permission denial.
- Forbidden: No backend request, job insert, translated post, or content disclosure occurs.
- Evidence:
  - Setup: `includes/API/TranslateController.php:961`
  - Action: `includes/API/TranslateController.php:152`
  - Request: `includes/API/TranslateController.php:148`
  - Response: `includes/API/TranslateController.php:963`
  - Visible: `includes/API/TranslateController.php:963`
  - Durable: `includes/API/TranslateController.php:963`
  - Fresh read: `includes/API/TranslateJobsController.php:757`
  - Forbidden: `includes/API/TranslateController.php:963`

## Source specs

- `admin/src/pages/content-translate.js`
- `includes/API/TranslateController.php`
- `includes/API/TranslateJobsController.php`
