
# PHP API

Todo en Queuety está disponible mediante la clase estática `Queuety\Queuety`. Es el único punto de entrada para despachar trabajos, crear flujos de trabajo, controlar máquinas de estados, gestionar colas y acceder a subsistemas internos.

```php
use Queuety\Queuety;
```

## Acceso al núcleo independiente

El entorno de ejecución de Queuety, respaldado por una base de datos, se puede inicializar fuera de WordPress.

Esta vía independiente está pensada para el núcleo de orquestación:

- trabajos y workers (los grupos de workers también necesitan las constantes `DB_HOST`, `DB_NAME`, `DB_USER` y `DB_PASSWORD`; véase más abajo)
- flujos de trabajo y señales
- máquinas de estados y eventos
- artefactos, registros, métricas y programaciones

No sustituye al adaptador WordPress. `Queuety::on_action()`,
`replace_wp_cron()`, el inicio del plugin y la ruta de registro real de `wp queuety ...`
siguen requiriendo WordPress.

### `Queuety::init( Connection $conn )`

Inicialice Queuety con una conexión directa respaldada por una base de datos.

```php
use Queuety\Connection;
use Queuety\Queuety;

$conn = new Connection(
    host: '127.0.0.1:3306',
    dbname: 'wordpress',
    user: 'root',
    password: 'secret',
    prefix: 'wp_',
);

Queuety::init( $conn );
```

El argumento opcional `table_prefix` cambia la parte Queuety de los nombres de las tablas del entorno de ejecución para cada conexión:

```php
$conn = new Connection(
    host: '127.0.0.1:3306',
    dbname: 'wordpress',
    user: 'root',
    password: 'secret',
    prefix: 'wp_',
    table_prefix: 'themequeue_',
);
```

Así, tablas como `queuety_jobs` se resuelven como `wp_themequeue_jobs`.

### `Queuety::ensure_schema()`

Asegúrese de que existan las tablas del entorno de ejecución Queuety para la conexión activa.

```php
Queuety::init( $conn );
Queuety::ensure_schema();
```

Para consultar el modelo completo y el límite WordPress, consulte [Uso independiente](https://queuety.inline0.dev/es/docs/features/standalone-use).

## Trabajos

### `Queuety::dispatch( $handler, $payload )`

Despacha un trabajo. Acepta una cadena con el nombre de un controlador o una instancia de `Contracts\Job`. Devuelve un generador `PendingJob` para encadenar opciones.

```php
// Handler name + payload (original API)
Queuety::dispatch( 'send_email', [ 'to' => 'user@example.com' ] );

// Contracts\Job instance (v0.6.0+)
Queuety::dispatch( new SendEmailJob( to: 'user@example.com' ) );

// With options
Queuety::dispatch( 'send_email', [ 'to' => 'user@example.com' ] )
    ->on_queue( 'emails' )
    ->with_priority( Priority::High )
    ->delay( 300 )
    ->max_attempts( 5 )
    ->rate_limit( 10, 60 )
    ->unique()
    ->after( $other_job_id );

// Get the job ID
$job_id = Queuety::dispatch( 'send_email', $payload )->id();
```

Cuando se pasa una instancia de `Contracts\Job`, el serializador extrae el FQCN como nombre del controlador y las propiedades públicas como carga útil. Se ignora el parámetro `$payload` cuando `$handler` es una instancia de `Job`.

### Métodos de `PendingJob`

| Método | Descripción |
|---|---|
| `on_queue( string $queue )` | Establece la cola de destino |
| `with_priority( Priority $priority )` | Establece el nivel de prioridad |
| `delay( int $seconds )` | Retrasa la disponibilidad del trabajo |
| `max_attempts( int $max )` | Número máximo de intentos de reintento |
| `rate_limit( int $max, int $window )` | Límite de frecuencia: máximo de ejecuciones por intervalo de segundos |
| `unique()` | Evita despachos duplicados |
| `after( int $job_id )` | Espera a que termine primero otro trabajo |
| `concurrency_group( string $group, int $limit )` | Añade el trabajo a un grupo compartido de admisión de workers |
| `cost_units( int $units )` | Añade metadatos de coste de ejecución relativo al despacho |
| `id()` | Fuerza el despacho y devuelve el ID del trabajo |

`concurrency_group()` y `cost_units()` se integran en el modelo general de [Gestión de recursos](https://queuety.inline0.dev/es/docs/features/resource-management), por lo que el mismo despacho puede participar en límites de concurrencia basados en recuentos, presupuestos ponderados de cola o proveedor y heurísticas de admisión de workers.

### `Queuety::dispatch_sync( $handler, $payload )`

Ejecuta un trabajo de forma síncrona en el proceso actual sin añadirlo a la cola.

```php
// With a Job instance
Queuety::dispatch_sync( new SendEmailJob( to: 'user@example.com', subject: 'Hi', body: 'Hello' ) );

// With a handler name and payload
Queuety::dispatch_sync( 'send_email', [ 'to' => 'user@example.com' ] );
```

Se llama directamente al método `handle()` del trabajo. No se crea ninguna fila en la base de datos, no interviene ningún worker ni se aplica middleware.

### `Queuety::batch( $jobs )`

Despacha varios trabajos con una sola instrucción INSERT de varias filas.

```php
$job_ids = Queuety::batch( [
    [ 'handler' => 'send_email', 'payload' => [ 'to' => 'a@example.com' ], 'queue' => 'emails' ],
    [ 'handler' => 'send_email', 'payload' => [ 'to' => 'b@example.com' ], 'priority' => 2 ],
] );
```

### `Queuety::create_batch( $jobs )`

Crea un `BatchBuilder` para despachar un grupo de trabajos con callbacks y seguimiento del progreso. Consulte [Procesamiento por lotes](https://queuety.inline0.dev/es/docs/features/batching) para ver todos los detalles.

```php
$batch = Queuety::create_batch( [
    new ImportUsersJob( $chunk_1 ),
    new ImportUsersJob( $chunk_2 ),
] )
    ->name( 'Import users' )
    ->then( ImportCompleteHandler::class )
    ->catch( ImportFailedHandler::class )
    ->finally( ImportCleanupHandler::class )
    ->allow_failures()
    ->on_queue( 'imports' )
    ->dispatch();
```

### Métodos de `BatchBuilder`

| Método | Devuelve | Descripción |
|---|---|---|
| `name( string $name )` | `self` | Establece un nombre para el lote |
| `on_queue( string $queue )` | `self` | Establece la cola para todos los trabajos del lote |
| `then( string $handler_class )` | `self` | Controlador que se llama cuando el lote termina correctamente |
| `catch( string $handler_class )` | `self` | Controlador que se llama cuando falla el primer trabajo del lote |
| `finally( string $handler_class )` | `self` | Controlador que se llama cuando termina el lote, tanto si tiene éxito como si falla |
| `allow_failures()` | `self` | Permite que se ejecute el callback `then` incluso si hay errores |
| `dispatch()` | `Batch` | Crea la fila del lote y todos los trabajos; devuelve el objeto de valor `Batch` |

### `Queuety::find_batch( $id )`

Busca un lote por ID. Devuelve un objeto de valor `Batch` o `null`.

```php
$batch = Queuety::find_batch( $batch_id );

if ( null !== $batch ) {
    echo $batch->progress() . '% complete';
}
```

## Control de workers

### `Queuety::run_worker( $queue = 'default', $once = false )`

Ejecuta directamente un ciclo de worker.

```php
Queuety::run_worker( 'default' );
Queuety::run_worker( 'critical,default', true );
```

### `Queuety::run_worker_pool( $workers, $queue = 'default' )`

Ejecuta directamente un grupo de workers de tamaño fijo. Requiere las extensiones `pcntl` y `posix`. El grupo lee las constantes WordPress `DB_HOST`, `DB_NAME`, `DB_USER` y `DB_PASSWORD` para sus conexiones secundarias (solo los prefijos de tabla proceden de la conexión inicializada); defínalas al ejecutar un grupo fuera de WordPress ([#5](https://github.com/inline0/queuety/issues/5)).

```php
Queuety::run_worker_pool( 4, 'providers' );
```

### `Queuety::run_auto_scaling_worker_pool( $min_workers, $max_workers, $queue = 'default' )`

Ejecuta directamente un grupo de workers adaptativo. Requiere lo mismo que `run_worker_pool()`.

```php
Queuety::run_auto_scaling_worker_pool( 2, 6, 'providers' );
```

### Objeto de valor `Batch`

| Método/propiedad | Tipo | Descripción |
|---|---|---|
| `$id` | `int` | ID del lote |
| `$name` | `string\|null` | Nombre opcional del lote |
| `$total_jobs` | `int` | Número total de trabajos del lote |
| `$pending_jobs` | `int` | Trabajos que siguen pendientes |
| `$failed_jobs` | `int` | Número de trabajos fallidos |
| `$failed_job_ids` | `array` | ID de los trabajos fallidos |
| `$created_at` | `DateTimeImmutable` | Fecha de creación del lote |
| `$finished_at` | `DateTimeImmutable\|null` | Fecha de finalización del lote |
| `$cancelled_at` | `DateTimeImmutable\|null` | Fecha de cancelación del lote |
| `progress()` | `int` | Porcentaje completado (0-100) |
| `finished()` | `bool` | Indica si el lote ha terminado |
| `has_failures()` | `bool` | Indica si algún trabajo ha fallado |
| `cancelled()` | `bool` | Indica si el lote se ha cancelado |

### `Queuety::chain( $jobs )`

Crea un `ChainBuilder` para ejecutar trabajos en secuencia. Cada trabajo depende de que el anterior termine correctamente.

```php
$first_job_id = Queuety::chain( [
    new FetchDataJob( $url ),
    new ProcessDataJob(),
    new NotifyCompleteJob(),
] )
    ->on_queue( 'pipeline' )
    ->catch( ChainFailedHandler::class )
    ->dispatch();
```

### Métodos de `ChainBuilder`

| Método | Devuelve | Descripción |
|---|---|---|
| `on_queue( string $queue )` | `self` | Establece la cola de todos los trabajos de la cadena |
| `catch( string $handler_class )` | `self` | Controlador que se llama cuando un trabajo de la cadena queda enterrado de forma permanente |
| `dispatch()` | `int` | Crea todos los trabajos con dependencias secuenciales; devuelve el ID del primer trabajo |

### `Queuety::retry( $job_id )`

Reintenta un trabajo específico. Lo restablece al estado `pending`.

```php
Queuety::retry( 42 );
```

### `Queuety::retry_buried()`

Reintenta todos los trabajos enterrados. Devuelve el número de trabajos reintentados.

```php
$count = Queuety::retry_buried();
```

### `Queuety::purge( $older_than_days )`

Elimina los trabajos completados. Devuelve el número de trabajos eliminados.

```php
$count = Queuety::purge();     // uses QUEUETY_RETENTION_DAYS
$count = Queuety::purge( 30 ); // purge jobs older than 30 days
```

## Colas

### `Queuety::stats( $queue )`

Obtiene el recuento de trabajos agrupados por estado.

```php
$stats = Queuety::stats();          // all queues
$stats = Queuety::stats( 'emails' ); // specific queue
// ['pending' => 12, 'processing' => 3, 'completed' => 150, 'failed' => 2, 'buried' => 1]
```

### `Queuety::buried( $queue )`

Obtiene todos los trabajos enterrados.

```php
$jobs = Queuety::buried();
$jobs = Queuety::buried( 'emails' );
```

### `Queuety::pause( $queue )`

Pausa una cola para que los workers la omitan.

```php
Queuety::pause( 'emails' );
```

### `Queuety::resume( $queue )`

Reanuda una cola pausada.

```php
Queuety::resume( 'emails' );
```

### `Queuety::is_paused( $queue )`

Comprueba si una cola está pausada.

```php
if ( Queuety::is_paused( 'emails' ) ) {
    // Queue is paused
}
```

## Flujos de trabajo

### `Queuety::workflow( $name )`

Empieza a crear un flujo de trabajo. Devuelve un `WorkflowBuilder`.

```php
$workflow_id = Queuety::workflow( 'generate_report' )
    ->then( FetchDataHandler::class )
    ->then( CallLLMHandler::class )
    ->then( FormatOutputHandler::class )
    ->dispatch( [ 'user_id' => 42 ] );
```

### Métodos de `WorkflowBuilder`

| Método | Descripción |
|---|---|
| `then( string $handler_class, ?string $name = null )` | Añade un paso secuencial. El nombre es opcional y sirve para la ramificación condicional. |
| `parallel( array $handler_classes )` | Añade un grupo de pasos paralelos. |
| `for_each( string $items_key, string $handler_class, ?string $result_key = null, ForEachMode $mode = ForEachMode::All, ?int $quorum = null, ?string $reducer_class = null, ?string $name = null )` | Añade un paso dinámico de iteración que crea una tarea ramificada por cada elemento del estado del flujo de trabajo. |
| `repeat_while( string $target_step, ?string $state_key = null, mixed $expected = true, ?string $name = null, ?string $condition_class = null, ?int $max_iterations = null )` | Vuelve a un paso anterior con nombre mientras coincida una clave del estado público o una clase `RepeatCondition`. |
| `repeat_until( string $target_step, ?string $state_key = null, mixed $expected = true, ?string $name = null, ?string $condition_class = null, ?int $max_iterations = null )` | Vuelve a un paso anterior con nombre hasta que coincida una clave del estado público o una clase `RepeatCondition`. |
| `run_workflow( string $workflow_name, WorkflowBuilder $sub_builder )` | Añade un paso para ejecutar un flujo de trabajo. |
| `delay( int $seconds = 0, int $minutes = 0, int $hours = 0, int $days = 0 )` | Añade un paso de espera persistente. Se suman todas las duraciones. |
| `wait_for_signal( string $name, ?string $result_key = null, ?string $step_name = null, array $match_payload = [], ?string $correlation_key = null )` | Añade un paso de espera de señal. `match_payload` y `correlation_key` son opcionales y permiten ignorar eventos no relacionados. |
| `wait_for_signals( array $signal_names, WaitMode $mode = WaitMode::All, ?string $result_key = null, ?string $name = null, array $match_payload = [], ?string $correlation_key = null )` | Espera todas o cualquiera de varias señales, con coincidencia opcional de carga útil y correlación. |
| `wait_for_approval( string $signal_name = 'approval', ?string $result_key = 'approval', ?string $name = null, array $match_payload = [], ?string $correlation_key = null )` | Espera una señal de aprobación humana. |
| `wait_for_decision( string $approve_signal = 'approved', string $reject_signal = 'rejected', ?string $result_key = 'decision', ?string $name = null, array $match_payload = [], ?string $correlation_key = null )` | Espera una aprobación o un rechazo y almacena una carga útil de decisión estructurada. |
| `wait_for_input( string $signal_name = 'input', ?string $result_key = 'input', ?string $name = null, array $match_payload = [], ?string $correlation_key = null )` | Espera una señal de entrada humana. |
| `wait_for_workflow( int\|string $workflow, ?string $result_key = null, ?string $name = null )` | Espera a que termine un ID de flujo de trabajo o el ID almacenado en el estado público. |
| `wait_for_workflows( array\|string $workflows, WaitMode $mode = WaitMode::All, ?string $result_key = null, ?string $name = null, ?int $quorum = null )` | Espera a que terminen todos, cualquiera o un quórum de varios flujos de trabajo. |
| `wait_for_workflow_group( string $group_key, WaitMode $mode = WaitMode::All, ?int $quorum = null, ?string $result_key = null, ?string $name = null )` | Espera a un grupo de flujos de trabajo iniciado previamente mediante su clave de grupo interna. |
| `start_workflows( string $items_key, WorkflowBuilder $workflow_builder, string $result_key = 'started_workflow_ids', string $payload_key = 'item', bool $inherit_state = true, ?string $name = null, ?string $group_key = null )` | Despacha un flujo de trabajo de nivel superior por cada elemento de ejecución y, de forma opcional, etiqueta el conjunto iniciado con una clave de grupo persistente. |
| `start_agents( string $items_key, WorkflowBuilder $workflow_builder, string $result_key = 'agent_workflow_ids', string $payload_key = 'agent_task', bool $inherit_state = true, ?string $name = null, ?string $group_key = null )` | Alias orientado a agentes de `start_workflows()`. |
| `wait_for_agents( array\|string $workflows = 'agent_workflow_ids', WaitMode $mode = WaitMode::All, ?string $result_key = 'agent_results', ?string $name = null, ?int $quorum = null )` | Alias orientado a agentes de `wait_for_workflows()`. |
| `wait_for_agent_group( string $group_key = 'agents', WaitMode $mode = WaitMode::All, ?int $quorum = null, ?string $result_key = 'agent_results', ?string $name = null )` | Alias orientado a agentes de `wait_for_workflow_group()`. |
| `version( string $version )` | Etiqueta la definición del flujo de trabajo con una versión de aplicación para inspecciones y exportaciones. |
| `idempotency_key( string $key )` | Hace persistente el despacho del flujo de trabajo mediante una clave proporcionada por quien realiza la llamada. Los despachos posteriores con la misma clave devuelven el ID de flujo de trabajo original. |
| `max_transitions( int $max )` | Falla el flujo de trabajo si se completan más de `$max` transiciones de paso. |
| `max_for_each_items( int $max )` | Falla el flujo de trabajo si un paso `for_each()` intenta ampliar más de `$max` elementos. |
| `max_state_bytes( int $bytes )` | Falla el flujo de trabajo si el estado público supera `$bytes`. |
| `max_cost_units( int $max )` | Falla el flujo de trabajo si los pasos completados consumen más de `$max` unidades de coste. |
| `max_started_workflows( int $max )` | Falla el flujo de trabajo antes de despachar más de `$max` flujos de trabajo secundarios. |
| `compensate_with( string $handler_class )` | Asocia un controlador de compensación al último paso añadido. |
| `compensate_on_failure()` | Ejecuta automáticamente las compensaciones de los pasos completados cuando falla el flujo de trabajo. |
| `on_queue( string $queue )` | Establece la cola de todos los pasos. |
| `with_priority( Priority $priority )` | Establece la prioridad de todos los pasos. |
| `max_attempts( int $max )` | Establece el número máximo de intentos de reintento por paso. |
| `on_cancel( string $handler_class )` | Registra una clase de controlador de limpieza para la cancelación. Consulte [Cancelación](https://queuety.inline0.dev/es/docs/workflows/cancellation). |
| `prune_state_after( int $steps = 2 )` | Activa la poda automática del estado. Elimina las salidas de pasos anteriores al umbral. Consulte [Poda del estado](https://queuety.inline0.dev/es/docs/features/state-pruning). |
| `must_complete_within( int $seconds = 0, int $minutes = 0, int $hours = 0, int $days = 0 )` | Establece un plazo para el flujo de trabajo. Se suman todas las duraciones. Consulte [Plazos](https://queuety.inline0.dev/es/docs/workflows/deadlines). |
| `on_deadline( string $handler_class )` | Registra un controlador que se llama cuando se supera el plazo del flujo de trabajo. Consulte [Plazos](https://queuety.inline0.dev/es/docs/workflows/deadlines). |
| `dispatch( array $initial_payload = [] )` | Despacha el flujo de trabajo. Devuelve el ID del flujo de trabajo. |

### `Queuety::workflow_status( $workflow_id )`

Obtiene el estado del flujo de trabajo. Devuelve un objeto `WorkflowState` o `null`.

```php
$state = Queuety::workflow_status( $workflow_id );
echo $state->name;          // 'generate_report'
echo $state->status->value; // 'running'
echo $state->current_step;  // 1
echo $state->total_steps;   // 3
print_r( $state->state );   // accumulated public state array
echo $state->wait_type;     // 'signal' or 'workflow' while blocked
echo $state->wait_mode;     // 'all', 'any', or 'quorum'
echo $state->definition_version; // 'research.v2'
echo $state->definition_hash;    // '4c7a...'
echo $state->idempotency_key;    // 'brief:42:research'
echo $state->artifact_count;     // 2
print_r( $state->budget );       // configured workflow budget + current usage
print_r( $state->wait_details ); // matched/remaining blockers, result key, correlation info
print_r( $state->artifact_keys ); // ['draft', 'citations']
```

`WorkflowState` también expone:

- `waiting_for`: los nombres de señales o los ID de flujo de trabajo que bloquean actualmente la ejecución
- `current_step_name`: el nombre configurado para el paso actual, si lo hay
- `wait_mode`: el modo de espera actual (`all`, `any` o `quorum`) mientras está bloqueado
- `wait_details`: datos de inspección adicionales, como los bloqueadores coincidentes y restantes
- `definition_version`: el valor establecido por `version()`, si lo hay
- `definition_hash`: un hash determinista de la definición del flujo de trabajo
- `idempotency_key`: la clave de despacho persistente, si la hay
- `budget`: los límites configurados y el uso actual, si las protecciones están habilitadas
- `artifact_count`: número de artefactos almacenados para el flujo de trabajo
- `artifact_keys`: claves de los artefactos almacenados para inspeccionarlos rápidamente

### `Queuety::retry_workflow( $workflow_id )`

Reintenta un flujo de trabajo fallido desde el paso en el que falló.

```php
Queuety::retry_workflow( $workflow_id );
```

Lanza `RuntimeException` si el flujo de trabajo ya se ha compensado tras un fallo.

### `Queuety::pause_workflow( $workflow_id )`

Pausa un flujo de trabajo en ejecución. El trabajo que ya esté en cola para el paso actual aún puede terminar, pero los pasos posteriores no se ponen en cola hasta que reanudes el flujo de trabajo.

```php
Queuety::pause_workflow( $workflow_id );
```

### `Queuety::resume_workflow( $workflow_id )`

Reanuda un flujo de trabajo en pausa.

```php
Queuety::resume_workflow( $workflow_id );
```

### `Queuety::cancel_workflow( $workflow_id )`

Cancela un flujo de trabajo y ejecuta los controladores de limpieza registrados. Establece el estado en `cancelled` y entierra todos los trabajos pendientes y en proceso del flujo de trabajo.

```php
Queuety::cancel_workflow( $workflow_id );
```

Lanza `RuntimeException` si el flujo de trabajo ya está `completed` o `cancelled`. Consulta [Cancelación](https://queuety.inline0.dev/es/docs/workflows/cancellation) para obtener más detalles.

### `Queuety::rewind_workflow( $workflow_id, $to_step )`

Retrocede un flujo de trabajo a un paso anterior y vuelve a ejecutarlo desde allí. El estado se restaura desde el registro de eventos `state_after` del paso indicado y se pone en cola el siguiente paso.

```php
Queuety::rewind_workflow( $workflow_id, to_step: 1 );
```

Lanza `RuntimeException` si no existe un estado registrado para el paso de destino. Requiere el [Registro de eventos del flujo de trabajo](https://queuety.inline0.dev/es/docs/workflows/event-log). Consulta [Viajes en el tiempo](https://queuety.inline0.dev/es/docs/workflows/time-travel) para obtener más detalles.

### `Queuety::fork_workflow( $workflow_id )`

Bifurca un flujo de trabajo en ejecución para crear una copia independiente con su estado actual. Devuelve el ID del nuevo flujo de trabajo.

```php
$forked_id = Queuety::fork_workflow( $workflow_id );
```

El flujo de trabajo bifurcado obtiene su propio ID y funciona de manera independiente del original. Consulta [Bifurcación](https://queuety.inline0.dev/es/docs/workflows/forking) para obtener más detalles.

### `Queuety::export_workflow( $workflow_id )`

Exporta el historial completo de ejecución de un flujo de trabajo como una matriz serializable JSON. Incluye la fila del flujo de trabajo, todos los trabajos, los eventos del flujo de trabajo y las entradas del registro.

```php
$data = Queuety::export_workflow( $workflow_id );
```

Consulta [Exportación y reproducción](https://queuety.inline0.dev/es/docs/workflows/export-replay) para conocer la estructura de JSON y sus casos de uso.

### `Queuety::replay_workflow( $data )`

Reproduce un flujo de trabajo exportado en el entorno actual. Crea un nuevo flujo de trabajo a partir de los datos de exportación y devuelve su nuevo ID.

```php
$new_id = Queuety::replay_workflow( $data );
```

Consulta [Exportación y reproducción](https://queuety.inline0.dev/es/docs/workflows/export-replay) para obtener más detalles.

### `Queuety::replace_wp_cron()`

Sustituye el sistema integrado de WordPress cron por el programador de Queuety. Intercepta `wp_schedule_event()`, `wp_unschedule_event()` y `wp_get_scheduled_event()`, y dirige los eventos recurrentes a través del trabajador Queuety.

```php
Queuety::replace_wp_cron();
```

Solo funciona cuando WordPress está cargado. Consulta [Sustitución de WP-Cron](https://queuety.inline0.dev/es/docs/features/wp-cron) para obtener más detalles.

### `Queuety::restore_wp_cron()`

Restaura el comportamiento predeterminado de WordPress cron eliminando los hooks de enlace de Queuety cron.

```php
Queuety::restore_wp_cron();
```

Consulta [Sustitución de WP-Cron](https://queuety.inline0.dev/es/docs/features/wp-cron) para conocer las limitaciones.

### `Queuety::signal( $workflow_id, $name, $data )`

Envía una señal a un flujo de trabajo. Si el flujo está esperando esta señal, se reanuda de inmediato. De lo contrario, la señal se almacena y se recoge cuando el flujo de trabajo llega al paso `wait_for_signal` correspondiente.

```php
Queuety::signal( $workflow_id, 'approved' );

// With data merged into workflow state
Queuety::signal( $workflow_id, 'approved', [
    'approved_by' => 'admin@example.com',
] );
```

Consulta [Señales de flujo de trabajo](https://queuety.inline0.dev/es/docs/workflows/signals) para obtener más detalles.

### `Queuety::approve_workflow( $workflow_id, $data = [], $signal_name = 'approval' )`

Envoltorio semántico de `signal()` para flujos de aprobación.

```php
Queuety::approve_workflow( $workflow_id, [
    'reviewer' => 'editor@example.com',
] );
```

### `Queuety::reject_workflow( $workflow_id, $data = [], $signal_name = 'rejected' )`

Envoltorio semántico de `signal()` para flujos de rechazo.

```php
Queuety::reject_workflow( $workflow_id, [
    'reason' => 'needs citations',
] );
```

### `Queuety::submit_workflow_input( $workflow_id, $data = [], $signal_name = 'input' )`

Envoltorio semántico de `signal()` para la entrada estructurada de una persona.

```php
Queuety::submit_workflow_input( $workflow_id, [
    'notes' => 'Ship after legal review',
] );
```

### `Queuety::put_artifact( $workflow_id, $artifact_key, $content, $kind = 'json', $step_index = null, $metadata = [] )`

Almacena un artefacto persistente para un flujo de trabajo.

```php
Queuety::put_artifact(
    $workflow_id,
    'research_brief',
    [ 'summary' => 'done' ],
    'json',
    metadata: [ 'source' => 'agent' ],
);
```

### `Queuety::put_current_artifact( $artifact_key, $content, $kind = 'json', $metadata = [] )`

Almacena un artefacto para el paso del flujo de trabajo que se está ejecutando.

```php
Queuety::put_current_artifact(
    'draft',
    [ 'status' => 'ready' ],
    'json',
);
```

### `Queuety::workflow_artifact( $workflow_id, $artifact_key )`

Obtiene un artefacto almacenado para un flujo de trabajo.

```php
$artifact = Queuety::workflow_artifact( $workflow_id, 'draft' );
```

### `Queuety::workflow_artifacts( $workflow_id, $include_content = false )`

Enumera los artefactos de un flujo de trabajo.

```php
$artifacts = Queuety::workflow_artifacts( $workflow_id );
```

### `Queuety::delete_workflow_artifact( $workflow_id, $artifact_key )`

Elimina un artefacto de un flujo de trabajo.

```php
Queuety::delete_workflow_artifact( $workflow_id, 'draft' );
```

## Máquinas de estados

### `Queuety::machine( $name )`

Empieza a crear una máquina de estados. Devuelve un `StateMachineBuilder`.

```php
use Queuety\Enums\StateMachineStatus;

$machine_id = Queuety::machine( 'agent_session' )
    ->state( 'awaiting_user' )
    ->on( 'user_message', 'planning' )
    ->state( 'planning' )
    ->action( PlanSessionAction::class )
    ->on( 'planned', 'completed' )
    ->state( 'completed', StateMachineStatus::Completed )
    ->dispatch( [ 'thread_id' => 42 ] );
```

### Métodos de `StateMachineBuilder`

| Método | Descripción |
|---|---|
| `initial( string $state_name )` | Establece explícitamente el estado inicial. |
| `state( string $state_name, ?StateMachineStatus $terminal_status = null )` | Empieza a configurar un estado. |
| `action( string\|array $action_class )` | Registra una acción de entrada en cola para el estado actual. Pasa un nombre de clase o una definición estructurada con `class`, `payload` y `config`; el `payload` llega a `StateAction::handle()` como `$payload`. |
| `on( string $event, string $target, string\|array\|null $guard_class = null, ?string $name = null )` | Registra una transición de evento desde el estado actual. La guarda acepta el mismo formato estructurado que `action()`. |
| `on_queue( string $queue )` | Establece la cola que se usará para las acciones de estado en cola. |
| `with_priority( Priority $priority )` | Establece la prioridad de las acciones de estado en cola. |
| `max_attempts( int $max_attempts )` | Establece el límite de reintentos de las acciones de estado en cola. |
| `version( string $version )` | Asigna a la definición de la máquina una versión a nivel de aplicación. |
| `idempotency_key( string $key )` | Hace que el despacho sea persistente para una clave proporcionada por quien realiza la llamada. Si se vuelve a despachar con la misma clave, devuelve el ID original de la máquina. |
| `dispatch( array $initial_state = [] )` | Despacha la máquina y devuelve su ID. |

Las clases de acción de entrada de estado también pueden exponer un método opcional `config()` con las mismas claves de política de recursos que admiten los pasos del flujo de trabajo: `concurrency_group`, `concurrency_limit` y `cost_units`.

### `Queuety::dispatch_state_machine_definition( $definition, $initial_state = [], $options = [] )`

Despacha un paquete serializado de definición de máquina de estados. La mayoría del código de aplicación debería preferir `machine()->dispatch()`. Este método de bajo nivel existe para que los adaptadores puedan iniciar una máquina sin depender directamente del generador.

```php
$machine_id = Queuety::dispatch_state_machine_definition(
    $definition,
    [ 'thread_id' => 42 ],
    [ 'idempotency_key' => 'thread:42' ],
);
```

### `Queuety::machine_status( $machine_id )`

Obtiene el estado actual de la máquina. Devuelve un objeto `StateMachineState` o `null`.

```php
$status = Queuety::machine_status( $machine_id );

echo $status->name;               // 'agent_session'
echo $status->status->value;      // 'waiting_event'
echo $status->current_state;      // 'awaiting_review'
print_r( $status->state );        // public machine state
print_r( $status->available_events ); // valid incoming events in the current state
```

`StateMachineState` también expone:

- `definition_version`
- `definition_hash`
- `idempotency_key`
- `error_message`
- `current_action`
- `terminal_status`

### `Queuety::machine_event( $machine_id, $event_name, $payload = [] )`

Envía un evento externo a una máquina en espera.

```php
Queuety::machine_event(
    $machine_id,
    'approve',
    [
        'approved' => true,
        'reviewer' => 'editor@example.com',
    ]
);
```

La carga útil está disponible para las guardas de transición y se combina con el estado público de la máquina cuando se acepta el evento.

### `Queuety::list_machines( $limit = 50, $status = null )`

Enumera las filas de máquinas persistidas para inspeccionarlas.

```php
$machines = Queuety::list_machines();
$waiting  = Queuety::list_machines( status: 'waiting_event' );
```

Cada fila incluye el ID de la máquina, el nombre, el estado, el estado actual, la versión de la definición, el hash de la definición, la clave de idempotencia y las marcas de tiempo del ciclo de vida.

### `Queuety::machine_timeline( $machine_id, $limit = 100, $offset = 0 )`

Obtiene la cronología persistente de eventos de una máquina.

```php
$timeline = Queuety::machine_timeline( $machine_id );
$page_two = Queuety::machine_timeline( $machine_id, 100, 100 );
```

Cada fila de evento contiene: `id`, `machine_id`, `job_id`, `state_name`, `handler`, `event`, `event_name`, `transition_name`, `queue`, `attempt`, `input`, `output`, `state_before`, `state_after`, `context`, `artifacts`, `chunks`, `error`, `duration_ms` y `created_at`.

### `Queuety::machine_trace( $machine_id )`

Obtiene un paquete de seguimiento normalizado para una UI de depuración de máquinas de estados.

```php
$trace = Queuety::machine_trace( $machine_id );
```

El seguimiento contiene `machine`, `states` agrupados, `events` sin procesar, `jobs` de acción correlacionados, `logs`, `artifacts` y `chunks`.

### `Queuety::machine_state_at( $machine_id, $event_id )`

Obtiene el estado público después de un evento específico del seguimiento de la máquina, o `null` si no se encuentra.

```php
$state = Queuety::machine_state_at( $machine_id, $event_id );
```

Consulta [Máquinas de estados](https://queuety.inline0.dev/es/docs/state-machines) para conocer el modelo del ciclo de vida y los patrones de uso.

## Plantillas de flujo de trabajo

### `Queuety::define_workflow( $name )`

Crea un generador de flujo de trabajo para usarlo como plantilla.

```php
$builder = Queuety::define_workflow( 'onboarding' )
    ->then( CreateAccountHandler::class )
    ->then( SendWelcomeEmailHandler::class );
```

### `Queuety::register_workflow_template( $builder )`

Registra una plantilla de flujo de trabajo.

```php
Queuety::register_workflow_template( $builder );
```

### `Queuety::run_workflow( $name, $payload, $options = [] )`

Despacha por nombre una plantilla de flujo de trabajo registrada. Devuelve el ID del flujo de trabajo.

```php
$workflow_id = Queuety::run_workflow( 'onboarding', [ 'email' => 'user@example.com' ] );

$workflow_id = Queuety::run_workflow(
    'onboarding',
    [ 'email' => 'user@example.com' ],
    [ 'idempotency_key' => 'onboarding:user@example.com' ],
);
```

Lanza `RuntimeException` si la plantilla no está registrada.

### `Queuety::dispatch_workflow_definition( $definition, $payload, $options = [] )`

Despacha un flujo de trabajo a partir de un paquete de definición en tiempo de ejecución.

La mayoría del código de aplicación debería preferir `run_workflow()` o `workflow()->dispatch()`. Este método de bajo nivel existe para que los adaptadores reutilizables puedan despachar una definición serializada del flujo de trabajo sin depender de los métodos internos del gestor de flujos de trabajo.

```php
$workflow_id = Queuety::dispatch_workflow_definition(
    $definition,
    [ 'email' => 'user@example.com' ],
    [ 'idempotency_key' => 'onboarding:user@example.com' ],
);
```

### `Queuety::on_action( $hook, $workflow, $map = null, $when = null, $idempotency_key = null, $priority = 10, $accepted_args = null )`

Registra una acción WordPress que despacha un flujo de trabajo.

```php
Queuety::on_action(
    'save_post',
    workflow: 'content_review',
    map: static fn ( int $post_id, object $post, bool $update ): array => [
        'post_id' => $post_id,
        'post_type' => $post->post_type,
        'update' => $update,
    ],
    when: static fn ( int $post_id, object $post ): bool => 'post' === $post->post_type,
    idempotency_key: static fn ( int $post_id ): string => "save_post:{$post_id}",
);
```

`$workflow` puede ser:

- el nombre de una plantilla de flujo de trabajo registrada
- un `WorkflowBuilder` integrado

`map`, `when` y `idempotency_key` se ejecutan de forma síncrona dentro de la solicitud actual de WordPress. Solo los pasos del flujo de trabajo se ejecutan de forma asíncrona después del despacho.

Consulta [Desencadenadores de acciones](https://queuety.inline0.dev/es/docs/features/action-triggers) para ver la guía completa.

### `Queuety::workflow_templates()`

Obtiene el registro de plantillas de flujo de trabajo.

```php
$registry = Queuety::workflow_templates();
$template = $registry->get( 'onboarding' ); // WorkflowTemplate or null
```

### `Queuety::load_workflows( $directory, $recursive )`

Carga y registra todos los archivos de flujo de trabajo de un directorio. Cada archivo `.php` debe devolver un `WorkflowBuilder`. Devuelve el número de flujos de trabajo registrados.

```php
$count = Queuety::load_workflows( __DIR__ . '/workflows/' );
$count = Queuety::load_workflows( __DIR__ . '/workflows/', recursive: true );
```

### `Queuety::load_workflow_file( $file_path )`

Carga y registra un único archivo de flujo de trabajo. Devuelve `WorkflowTemplate` o `null` si el archivo no devuelve un generador.

```php
$template = Queuety::load_workflow_file( __DIR__ . '/workflows/onboard-user.php' );
```

## Programación

### `Queuety::schedule( $handler, $payload )`

Crea una programación recurrente. Devuelve un generador `PendingSchedule`.

```php
Queuety::schedule( 'cleanup_handler' )
    ->every( '1 hour' )
    ->on_queue( 'maintenance' );

Queuety::schedule( 'nightly_report' )
    ->cron( '0 3 * * *' )
    ->on_queue( 'reports' );
```

### Métodos de `PendingSchedule`

| Método | Descripción |
|---|---|
| `every( string $interval )` | Establece una expresión de intervalo (e.g, `1 hour`, `30 minutes`) |
| `cron( string $expression )` | Establece una expresión cron de 5 campos |
| `overlap( OverlapPolicy $policy )` | Establece la política de solapamiento (predeterminada: `Allow`). Consulta [Programación](https://queuety.inline0.dev/es/docs/features/scheduling). |
| `on_queue( string $queue )` | Establece la cola de destino (predeterminada: `default`) |
| `id()` | Fuerza el despacho y devuelve el ID de la programación |

### `Queuety::scheduler()`

Obtiene la instancia interna de `Scheduler`.

```php
$scheduler = Queuety::scheduler();
$schedules = $scheduler->list();
$scheduler->remove( 'handler_name' );
$count = $scheduler->tick(); // manually trigger
```

## Registro de controladores

### `Queuety::register( $name, $class )`

Registra una clase de controlador con un nombre.

```php
Queuety::register( 'send_email', SendEmailHandler::class );
```

### `Queuety::discover_handlers( $directory, $namespace )`

Detecta automáticamente y registra las clases de controladores de un directorio. Devuelve el número de controladores registrados.

```php
$count = Queuety::discover_handlers( __DIR__ . '/handlers', 'MyPlugin\\Handlers' );
```

### `Queuety::registry()`

Obtiene el registro de controladores.

```php
$registry = Queuety::registry();
```

## Observabilidad

### `Queuety::logger()`

Obtiene la instancia de `Logger` para consultar las entradas del registro.

```php
$logger = Queuety::logger();
$logs   = $logger->for_job( 42 );
$logs   = $logger->for_workflow( 7 );
$logs   = $logger->for_handler( 'send_email', 50 );
$logs   = $logger->for_event( LogEvent::Failed, 50 );
$logs   = $logger->since( new \DateTimeImmutable( '-1 hour' ), 100 );
$count  = $logger->purge( 30 );
```

### `Queuety::metrics()`

Obtiene la instancia de `Metrics`.

```php
$stats = Queuety::metrics()->handler_stats( 60 ); // last 60 minutes
```

### `Queuety::webhook_notifier()`

Obtiene la instancia de `WebhookNotifier`.

```php
$notifier = Queuety::webhook_notifier();
$id       = $notifier->register( 'job.buried', 'https://hooks.slack.com/...' );
$webhooks = $notifier->list();
$notifier->remove( $id );
```

### `Queuety::rate_limiter()`

Obtiene la instancia de `RateLimiter`.

```php
$limiter = Queuety::rate_limiter();
$limiter->register( 'call_openai', 60, 60 );
```

## Caché

### `Queuety::cache()`

Obtiene la instancia del backend de caché activo.

```php
$cache = Queuety::cache();
$cache->set( 'my_key', 'my_value', 60 );
$value = $cache->get( 'my_key' );
```

### `Queuety::set_cache( $cache )`

Sustituye el backend de caché. Llama a `Queuety::init()` antes para usar una implementación personalizada.

```php
Queuety::set_cache( new RedisCache() );
Queuety::init( $connection );
```

### `QUEUETY_CACHE_TTL`

TTL predeterminado de la caché en segundos (predeterminado: `5`). Establece esta constante en `wp-config.php` para cambiar el TTL predeterminado que usan las operaciones internas de caché.

```php
define( 'QUEUETY_CACHE_TTL', 10 );
```

Consulta [Almacenamiento en caché](https://queuety.inline0.dev/es/docs/features/caching) para obtener detalles sobre los backends y la detección automática.

## Registro de eventos de máquinas de estados

### `Queuety::machine_events()`

Obtiene la instancia de `StateMachineEventLog`.

```php
$log = Queuety::machine_events();
```

## Registro de eventos de flujos de trabajo

### `Queuety::workflow_events()`

Obtiene la instancia de `WorkflowEventLog`.

```php
$log = Queuety::workflow_events();
```

### `Queuety::workflow_timeline( $workflow_id, $limit = 100, $offset = 0 )`

Obtiene la cronología completa de eventos de un flujo de trabajo. Devuelve una matriz de filas de eventos ordenadas por ID.

```php
$events = Queuety::workflow_timeline( $workflow_id );
$next   = Queuety::workflow_timeline( $workflow_id, 100, 100 );
```

Cada fila de evento contiene: `id`, `workflow_id`, `job_id`, `parent_event_id`, `step_index`, `step_name`, `step_type`, `handler`, `event`, `queue`, `attempt`, `input`, `output`, `state_before`, `state_after`, `context`, `artifacts`, `chunks`, `error`, `error_message` (copiado de `error.message`), `duration_ms` y `created_at`.

### `Queuety::workflow_trace( $workflow_id )`

Obtiene un paquete de seguimiento normalizado para una UI de depuración de flujos de trabajo.

```php
$trace = Queuety::workflow_trace( $workflow_id );
```

El seguimiento contiene `workflow`, `steps` agrupados, `events` sin procesar, `jobs`, `logs`, `artifacts`, `chunks`, `signals` y `wait_dependencies`.

### `Queuety::trace_input( $input )`

Establece la entrada a nivel de aplicación para el paso del flujo de trabajo que se está ejecutando.

### `Queuety::trace_output( $output )`

Establece la salida a nivel de aplicación para el paso del flujo de trabajo que se está ejecutando.

### `Queuety::trace_context( array $context )`

Combina el contexto a nivel de aplicación con el paso del flujo de trabajo que se está ejecutando.

### `Queuety::workflow_state_at( $workflow_id, $step_index )`

Obtiene el estado público después de que se completa o reanuda un paso específico del flujo de trabajo, o `null` si no se encuentra.

```php
$state = Queuety::workflow_state_at( $workflow_id, 2 );
```

Consulta [Registro de eventos del flujo de trabajo](https://queuety.inline0.dev/es/docs/workflows/event-log) para conocer los casos de uso y los comandos de CLI.

## Instancias internas

Estos métodos devuelven instancias internas para casos de uso avanzados.

| Método | Devuelve | Descripción |
|---|---|---|
| `Queuety::queue()` | `Queue` | Operaciones de cola (reclamar, liberar, enterrar) |
| `Queuety::worker()` | `Worker` | Control de procesos de trabajo |
| `Queuety::workflow_manager()` | `Workflow` | Orquestación de flujos de trabajo |
| `Queuety::machines()` | `StateMachine` | Gestión del ciclo de vida de máquinas de estados |
| `Queuety::connection()` | `Connection` | Conexión a la base de datos |
| `Queuety::batch_manager()` | `BatchManager` | Gestión del ciclo de vida de lotes (cancelar, depurar) |
| `Queuety::chunk_store()` | `ChunkStore` | Persistencia de fragmentos para pasos de transmisión |
| `Queuety::machine_events()` | `StateMachineEventLog` | Eventos de seguimiento de máquinas de estados y paquetes de seguimiento normalizados |
| `Queuety::workflow_events()` | `WorkflowEventLog` | Eventos de seguimiento de flujos de trabajo y paquetes de seguimiento normalizados |
| `Queuety::resource_manager()` | `ResourceManager` | Perfiles de admisión de trabajadores, presupuestos ponderados e inspección de memoria del sistema |
| `Queuety::cache()` | `Cache` | Instancia del backend de caché |

## Pruebas

### `Queuety::fake()`

Sustituye la cola por una simulación en memoria para las pruebas. Devuelve una instancia de `QueueFake` que registra los trabajos despachados sin acceder a la base de datos. `create_batch()` también registra metadatos ficticios de lotes; las cadenas se comprueban mediante los trabajos individuales que despachan.

```php
$fake = Queuety::fake();
SendEmailJob::dispatch( 'user@example.com', 'Hello', 'Hi!' );
$fake->assert_pushed( SendEmailJob::class );
```

### `Queuety::reset()`

Restablece todo el estado interno, incluida la simulación de cola. Llama a esto en `tearDown()`.

```php
Queuety::reset();
```

### Métodos de `QueueFake`

| Método | Descripción |
|---|---|
| `assert_pushed( string $class, ?Closure $callback )` | Comprueba que se haya insertado una clase de trabajo. La función de devolución de llamada opcional filtra los datos del trabajo. |
| `assert_pushed_times( string $class, int $count )` | Comprueba que un trabajo se haya insertado exactamente N veces. |
| `assert_not_pushed( string $class )` | Comprueba que nunca se haya insertado un trabajo. |
| `assert_nothing_pushed()` | Comprueba que no se haya insertado ningún trabajo. |
| `assert_batched( ?Closure $callback )` | Comprueba que se haya despachado un lote. La función de devolución de llamada opcional filtra los datos del lote. |
| `pushed( string $class )` | Obtiene todos los despachos registrados para una clase. Devuelve una matriz. |
| `batches()` | Obtiene todos los lotes registrados. Devuelve una matriz. |
| `reset()` | Borra todos los trabajos y lotes registrados. |

Consulta [Pruebas](https://queuety.inline0.dev/es/docs/features/testing) para ver ejemplos completos.

## Interfaces

### `Handler`

Interfaz para controladores de trabajos sencillos.

```php
interface Handler {
    public function handle( array $payload ): void;
    public function config(): array;
}
```

`config()` puede devolver `queue`, `max_attempts`, `backoff`, `rate_limit`, `concurrency_group`, `concurrency_limit` y `cost_units`.

### `Step`

Interfaz para controladores de pasos de flujo de trabajo.

```php
interface Step {
    public function handle( array $state ): array;
    public function config(): array;
}
```

`config()` puede devolver `max_attempts`, `backoff`, `concurrency_group`, `concurrency_limit` y `cost_units`.

### `Contracts\ForEachHandler`

Interfaz para controladores dinámicos de ramas de iteración.

```php
namespace Queuety\Contracts;

interface ForEachHandler {
    public function handle_item( array $state, mixed $item, int $index ): array;
    public function config(): array;
}
```

`config()` puede devolver `max_attempts`, `backoff`, `rate_limit`, `concurrency_group`, `concurrency_limit` y `cost_units`.

### `Contracts\ForEachReducer`

Reductor opcional para resultados de iteración.

```php
namespace Queuety\Contracts;

interface ForEachReducer {
    public function reduce( array $state, array $for_each ): array;
}
```

### `Contracts\Compensation`

Interfaz para controladores de compensación de pasos.

```php
namespace Queuety\Contracts;

interface Compensation {
    public function handle( array $state ): void;
}
```

### `Contracts\RepeatCondition`

Interfaz para predicados de bucle usados por `repeat_until()` y `repeat_while()` cuando una comparación de clave de estado resulta demasiado limitada.

```php
namespace Queuety\Contracts;

interface RepeatCondition {
    public function matches( array $state ): bool;
}
```

### `Contracts\StateAction`

Interfaz para acciones de entrada de estado en cola.

```php
namespace Queuety\Contracts;

interface StateAction {
    public function handle( array $state, ?string $event = null, array $event_payload = [], array $payload = [] ): array|string;
}
```

`$payload` recibe la `payload` estructurada de la definición de la acción, si la máquina se definió con una. Devuelve actualizaciones del estado público. Incluye `_event` y, opcionalmente, `_event_payload` cuando la acción debe realizar una transición inmediata.

Las acciones de estado también pueden definir un método opcional `config()`. Queuety lee `concurrency_group`, `concurrency_limit` y `cost_units` de ese método cuando la acción de entrada se pone en cola.

### `Contracts\StateGuard`

Interfaz para guardas de transición de máquinas de estados.

```php
namespace Queuety\Contracts;

interface StateGuard {
    public function allows( array $state, array $event_payload, string $event, array $payload = [] ): bool;
}
```

### `Contracts\Job`

Interfaz para clases de trabajo autónomas que se pueden despachar. Las clases de trabajo encapsulan su carga útil en propiedades públicas y la lógica de ejecución en `handle()`.

```php
namespace Queuety\Contracts;

interface Job {
    public function handle(): void;
}
```

Las clases de trabajo pueden definir opcionalmente un método `middleware()` que devuelva una matriz de instancias `Contracts\Middleware`.

### `Contracts\Cache`

Interfaz para backends de caché. Consulta [Almacenamiento en caché](https://queuety.inline0.dev/es/docs/features/caching) para ver implementaciones integradas y backends personalizados.

```php
namespace Queuety\Contracts;

interface Cache {
    public function get( string $key ): mixed;
    public function set( string $key, mixed $value, int $ttl = 0 ): void;
    public function delete( string $key ): void;
    public function has( string $key ): bool;
    public function add( string $key, mixed $value, int $ttl = 0 ): bool;
    public function flush(): void;
}
```

| Método | Devuelve | Descripción |
|---|---|---|
| `get( $key )` | `mixed` | Recupera un valor. Devuelve `null` si no se encuentra o ha caducado. |
| `set( $key, $value, $ttl )` | `void` | Almacena un valor. Un TTL de 0 significa que no caduca. |
| `delete( $key )` | `void` | Elimina un valor. |
| `has( $key )` | `bool` | Comprueba si una clave existe y no ha caducado. |
| `add( $key, $value, $ttl )` | `bool` | Establece el valor de forma atómica solo si no existe. Devuelve `true` si se estableció el valor. |
| `flush()` | `void` | Elimina todos los elementos de la caché. |

### `WorkflowEventLog`

Registra las transiciones de pasos del flujo de trabajo con el contexto completo del seguimiento. Se accede mediante `Queuety::workflow_events()`.

| Método | Devuelve | Descripción |
|---|---|---|
| `record_event( array $event )` | `void` | Registra un evento de seguimiento sin procesar |
| `record_step_started( ... )` | `void` | Registra un evento step_started con la entrada y el estado anteriores a la ejecución |
| `record_step_completed( ... )` | `void` | Registra un evento step_completed con la entrada, la salida y los estados anterior y posterior |
| `record_step_failed( ... )` | `void` | Registra un evento step_failed con el contexto del seguimiento y detalles estructurados del error |
| `get_timeline( int $workflow_id, ?int $limit = 100, int $offset = 0 )` | `array` | Obtiene los eventos de seguimiento del flujo de trabajo en orden, con paginación opcional |
| `get_trace( int $workflow_id )` | `array` | Obtiene un paquete de seguimiento normalizado |
| `get_state_at_step( int $workflow_id, int $step_index )` | `array\|null` | Obtiene el estado público después de completar o reanudar un paso específico |
| `prune( int $older_than_days )` | `int` | Elimina los eventos anteriores al número de días indicado. Devuelve el número de filas eliminadas. |

### `StateMachineEventLog`

Registra eventos del ciclo de vida de máquinas de estados, acciones, transiciones y guardas con los mismos campos de seguimiento que los eventos de flujos de trabajo.

| Método | Devuelve | Descripción |
|---|---|---|
| `record_event( array $event )` | `void` | Registra un evento sin procesar de seguimiento de máquina |
| `get_timeline( int $machine_id, ?int $limit = 100, int $offset = 0 )` | `array` | Obtiene los eventos de seguimiento de máquina en orden, con paginación opcional |
| `get_trace( int $machine_id )` | `array` | Obtiene un paquete normalizado de seguimiento de máquina |
| `get_state_at_event( int $machine_id, int $event_id )` | `array\|null` | Obtiene el estado público después de un evento específico de seguimiento de máquina |
| `prune( int $older_than_days )` | `int` | Elimina los eventos anteriores al número de días indicado. Devuelve las filas eliminadas. |

### `Contracts\StreamingStep`

Interfaz para controladores de pasos de flujo de trabajo que producen un flujo de fragmentos. Cada valor generado se guarda inmediatamente en la base de datos. Consulta [Pasos en flujo](https://queuety.inline0.dev/es/docs/workflows/streaming).

```php
namespace Queuety\Contracts;

interface StreamingStep {
    public function stream( array $state, array $existing_chunks = [] ): \Generator;
    public function on_complete( array $chunks, array $state ): array;
    public function config(): array;
}
```

| Método | Descripción |
|---|---|
| `stream( $state, $existing_chunks )` | Generador que produce fragmentos de texto. Cada valor generado activa una escritura inmediata en la base de datos. Al reintentar, `$existing_chunks` contiene los fragmentos guardados anteriormente. |
| `on_complete( $chunks, $state )` | Se llama cuando termina el flujo. Devuelve datos que se combinarán con el estado del flujo de trabajo. |
| `config()` | Configuración opcional. Admite `max_attempts`, `backoff`, `concurrency_group`, `concurrency_limit` y `cost_units`. |

### `ChunkStore`

Gestiona la persistencia de fragmentos para los pasos en flujo. Acceso mediante `Queuety::chunk_store()`.

| Método | Devuelve | Descripción |
|---|---|---|
| `get_chunks( int $job_id )` | `string[]` | Obtiene todos los fragmentos de una tarea, ordenados por índice de fragmento |
| `append_chunk( int $job_id, int $chunk_index, string $content, ?int $workflow_id, ?int $step_index )` | `void` | Añade un único fragmento |
| `clear_chunks( int $job_id )` | `void` | Elimina todos los fragmentos de una tarea |
| `chunk_count( int $job_id )` | `int` | Cuenta los fragmentos de una tarea |
| `get_accumulated( int $job_id )` | `string` | Concatena todos los fragmentos en una sola cadena |

### `Heartbeat`

Método auxiliar estático para enviar señales de actividad desde los controladores de pasos y los pasos en flujo. Consulta [Señales de actividad](https://queuety.inline0.dev/es/docs/features/heartbeats).

| Método | Descripción |
|---|---|
| `Heartbeat::init( int $job_id, Connection $conn )` | Establece el contexto de la tarea. El trabajador lo llama automáticamente. |
| `Heartbeat::beat( array $progress = [] )` | Envía una señal de actividad. Actualiza `reserved_at` y, opcionalmente, guarda datos de progreso en `heartbeat_data`. |
| `Heartbeat::clear()` | Borra el contexto de la tarea. El trabajador lo llama automáticamente. |
| `Heartbeat::current_job_id()` | Obtiene el ID de la tarea actual (para pruebas). |

### `Contracts\Middleware`

Interfaz para middleware de tareas. El middleware envuelve la ejecución de tareas en una canalización de estilo cebolla.

```php
namespace Queuety\Contracts;

interface Middleware {
    public function handle( object $job, \Closure $next ): void;
}
```

## Rasgos

### `Dispatchable`

Proporciona métodos estáticos de envío en clases de tareas que implementan `Contracts\Job`. Los argumentos del constructor se transfieren para crear la instancia de la tarea.

```php
use Queuety\Contracts\Job;
use Queuety\Dispatchable;

class SendEmailJob implements Job {
    use Dispatchable;
    // ...
}

// Dispatches a new SendEmailJob instance
SendEmailJob::dispatch( 'user@example.com', 'Subject', 'Body' );
```

| Método | Devuelve | Descripción |
|---|---|---|
| `dispatch( ...$args )` | `PendingJob` | Crea una instancia y la envía |
| `dispatch_if( bool $condition, ...$args )` | `PendingJob\|null` | La envía solo si `$condition` es verdadero |
| `dispatch_unless( bool $condition, ...$args )` | `PendingJob\|null` | La envía solo si `$condition` es falso |
| `with_chain( array $jobs )` | `ChainBuilder` | Inicia una cadena desde esta clase de tarea |

```php
// Conditional dispatch
SendEmailJob::dispatch_if( $user->wants_email, $user->email, 'Hello', 'Hi!' );
SendEmailJob::dispatch_unless( $maintenance_mode, $user->email, 'Hello', 'Hi!' );

// Chain from a job class
FetchDataJob::with_chain( [
    new ProcessDataJob(),
    new NotifyCompleteJob(),
] )->dispatch();
```

### Propiedades de las tareas

Las clases de tareas pueden declarar propiedades públicas que el trabajador lee mediante reflexión para modificar el comportamiento de reintentos y tiempos de espera.

| Propiedad | Tipo | Descripción |
|---|---|---|
| `$tries` | `int` | Número máximo de intentos antes de que la tarea quede en la cola de tareas fallidas |
| `$timeout` | `int` | Tiempo máximo de ejecución en segundos (requiere `pcntl`) |
| `$backoff` | `array` | Valores de demora crecientes en segundos para cada intento de reintento |

```php
class ImportProductsJob implements Job {
    use Dispatchable;

    public int $tries = 5;
    public int $timeout = 120;
    public array $backoff = [ 10, 30, 60, 120 ];

    // ...
}
```

### Hook `failed()`

Cuando una tarea queda en la cola de tareas fallidas tras agotar todos los reintentos, el trabajador llama a un método `failed()` en la instancia de la tarea, si está definido.

```php
public function failed( \Throwable $exception ): void {
    error_log( "Job failed: {$exception->getMessage()}" );
}
```

## Middleware integrado

Todas las clases de middleware implementan `Contracts\Middleware` y se encuentran en el espacio de nombres `Queuety\Middleware`.

| Clase | Descripción |
|---|---|
| `RateLimited( int $max, int $window )` | Aplica un límite de frecuencia de `$max` ejecuciones cada `$window` segundos |
| `Timeout( int $seconds )` | Finaliza la tarea después de `$seconds` segundos (requiere `pcntl`) |
| `UniqueJob( ?string $key )` | Evita la ejecución simultánea mediante un bloqueo de base de datos |
| `WithoutOverlapping( string $key, int $release_after )` | Evita ejecuciones superpuestas con expiración automática del bloqueo |
| `ThrottlesExceptions( int $max_attempts, int $decay_minutes )` | Limita las excepciones para evitar avalanchas de tareas cuando un servicio externo no está disponible |

Consulta [Middleware](https://queuety.inline0.dev/es/docs/features/middleware) para ver instrucciones de uso y ejemplos.

## Enumeraciones

### `Priority`

```php
enum Priority: int {
    case Low    = 0;
    case Normal = 1;
    case High   = 2;
    case Urgent = 3;
}
```

### `JobStatus`

```php
enum JobStatus: string {
    case Pending    = 'pending';
    case Processing = 'processing';
    case Completed  = 'completed';
    case Failed     = 'failed';
    case Buried     = 'buried';
}
```

### `WorkflowStatus`

```php
enum WorkflowStatus: string {
    case Running        = 'running';
    case Completed      = 'completed';
    case Failed         = 'failed';
    case Paused         = 'paused';
    case WaitingForSignal  = 'waiting_for_signal';
    case WaitingForWorkflows = 'waiting_for_workflows';
    case Cancelled      = 'cancelled';
}
```

### `StateMachineStatus`

```php
enum StateMachineStatus: string {
    case Running = 'running';
    case WaitingEvent = 'waiting_event';
    case Completed = 'completed';
    case Failed = 'failed';
    case Paused = 'paused';
    case Cancelled = 'cancelled';
}
```

### `BackoffStrategy`

```php
enum BackoffStrategy: string {
    case Fixed       = 'fixed';
    case Linear      = 'linear';
    case Exponential = 'exponential';
}
```

### `ForEachMode`

```php
enum ForEachMode: string {
    case All = 'all';
    case FirstSuccess = 'first_success';
    case Quorum = 'quorum';
}
```

### `WaitMode`

```php
enum WaitMode: string {
    case All    = 'all';
    case Any    = 'any';
    case Quorum = 'quorum';
}
```

### `LogEvent`

```php
enum LogEvent: string {
    case Started                  = 'started';
    case Completed                = 'completed';
    case Failed                   = 'failed';
    case Buried                   = 'buried';
    case Retried                  = 'retried';
    case WorkflowStarted          = 'workflow_started';
    case WorkflowCompleted        = 'workflow_completed';
    case WorkflowFailed           = 'workflow_failed';
    case WorkflowPaused           = 'workflow_paused';
    case WorkflowResumed          = 'workflow_resumed';
    case WorkflowCancelled        = 'workflow_cancelled';
    case WorkflowRewound          = 'workflow_rewound';
    case WorkflowForked           = 'workflow_forked';
    case WorkflowDeadlineExceeded = 'workflow_deadline_exceeded';
    case Debug                    = 'debug';
}
```

### `OverlapPolicy`

```php
enum OverlapPolicy: string {
    case Allow  = 'allow';
    case Skip   = 'skip';
    case Buffer = 'buffer';
}
```

Consulta [Programación](https://queuety.inline0.dev/es/docs/features/scheduling) para obtener detalles sobre el funcionamiento de cada política.

## Atributos

### `#[QueuetyHandler]`

PHP atributo 8 para el registro automático. Consulta [Atributos de PHP](https://queuety.inline0.dev/es/docs/features/attributes).

```php
use Queuety\Attributes\QueuetyHandler;

#[QueuetyHandler( name: 'send_email', queue: 'emails', max_attempts: 5 )]
class SendEmailHandler implements Handler {
    // ...
}
```

## Puente de CLI

### `Queuety::cli_command()`

Devuelve el nombre del comando raíz publicado de WP-CLI.

El comando raíz predeterminado es `queuety`. Define `QUEUETY_CLI_COMMAND` antes de que se inicie Queuety si el comando debe publicarse con otro nombre.

### `Queuety::cli_command_map()`

Devuelve el catálogo serializable de Queuety CLI para herramientas de prueba que necesitan conocer los comandos disponibles sin inspeccionar las clases de WP-CLI.

Cada entrada incluye:
- un ID de operación estable
- la ruta relativa de CLI
- la clase y el método del controlador de WP-CLI
- los posibles destinos de ejecución
- el adaptador que normaliza los argumentos sin procesar de CLI en un plan de ejecución

### `Queuety::resolve_cli_command( $path, $args, $assoc_args )`

Convierte una invocación analizada de CLI en el plan de ejecución que debe ejecutar la herramienta de prueba.

```php
$plan = Queuety::resolve_cli_command(
    [ 'workflow', 'export' ],
    [ '42' ],
    [ 'output' => '/tmp/workflow.json' ]
);

echo $plan['callable']; // Queuety\Queuety::export_workflow_to_file
print_r( $plan['arguments'] );
```

Los planes de ejecución tienen en cuenta el transporte:
- los comandos se resuelven en funciones públicas de PHP con argumentos normalizados
- el catálogo enumera de antemano todos los posibles destinos de ejecución, mientras que `callable` y `transport` identifican la rama elegida para la entrada analizada

Este es el formato estándar que Queuety usa para las herramientas de prueba de agentes: ID de operación estable, destino público de API y normalización a cargo del adaptador para aspectos exclusivos de CLI, como los valores predeterminados de la cola, la decodificación de cargas útiles de JSON, la selección de ramas del grupo de trabajadores y la gestión de archivos de exportación del flujo de trabajo.
