
# Sinais do fluxo de trabalho

Os fluxos de trabalho podem pausar e aguardar uma entrada externa usando sinais. Os métodos do construtor `wait_for_signal()` e `wait_for_signals()` inserem etapas que bloqueiam o fluxo de trabalho até que seu aplicativo, um operador ou outro sistema envie o sinal esperado por meio de `Queuety::signal()`.

Para fluxos com participação humana, Queuety também inclui:

- `wait_for_approval()` para um conteúdo de aprovação com namespace
- `wait_for_input()` para entrada humana estruturada
- `wait_for_decision()` para resultados explícitos de aprovação/rejeição

Para ver um exemplo mais amplo, de ponta a ponta, que combina execuções de agentes com etapas de aprovação humana, consulte [Orquestração de agentes](https://queuety.inline0.dev/pt/docs/workflows/agent-orchestration).

## O método `->wait_for_signal()`

Adicione uma etapa de espera por sinal a um fluxo de trabalho:

```php
use Queuety\Queuety;

$workflow_id = Queuety::workflow( 'content_approval' )
    ->then( DraftContentHandler::class )
    ->wait_for_signal( 'approved' )
    ->then( PublishContentHandler::class )
    ->dispatch( [ 'post_id' => 99 ] );
```

Quando o fluxo de trabalho chega à etapa `wait_for_signal`, seu status muda para `waiting_for_signal` e ele é pausado. Nenhum trabalho é executado nem recurso é consumido durante a espera.

Por padrão, o conteúdo de um único sinal é mesclado ao estado de nível superior do fluxo de trabalho. Se quiser que o conteúdo receba um namespace, passe um `result_key`:

```php
$workflow_id = Queuety::workflow( 'content_approval' )
    ->then( DraftContentHandler::class )
    ->wait_for_signal( 'approved', 'approval' )
    ->then( PublishContentHandler::class )
    ->dispatch( [ 'post_id' => 99 ] );
```

Após a chegada do sinal, `$state['approval']` contém a matriz do conteúdo do sinal.

## Aguardar vários sinais

Use `wait_for_signals()` quando o fluxo de trabalho precisar ser desbloqueado por qualquer um de vários sinais possíveis, ou quando precisar coletar todos os sinais necessários antes de continuar.

```php
use Queuety\Enums\WaitMode;

$workflow_id = Queuety::workflow( 'moderation_gate' )
    ->then( SubmitForReviewHandler::class )
    ->wait_for_signals(
        [ 'editor_approved', 'legal_approved' ],
        WaitMode::All,
        'approvals'
    )
    ->then( PublishContentHandler::class )
    ->dispatch();
```

- `WaitMode::All` aguarda até que todos os sinais configurados tenham sido registrados.
- `WaitMode::Any` retoma a execução assim que chega o primeiro sinal configurado.

Com um `result_key`, uma espera por vários sinais armazena uma matriz associativa indexada pelo nome do sinal:

```php
[
    'editor_approved' => [ 'by' => 'editor@example.com' ],
    'legal_approved' => [ 'by' => 'legal@example.com' ],
]
```

Sem um `result_key`, os conteúdos dos sinais correspondentes são mesclados ao estado de nível superior na ordem em que a etapa foi configurada.

## Correspondência e correlação

As esperas por sinal podem ignorar eventos não relacionados, comparando o conteúdo do sinal ou correlacionando-o com uma chave compartilhada.

```php
$workflow_id = Queuety::workflow( 'review_gate' )
    ->wait_for_signal(
        name: 'review_ready',
        result_key: 'review',
        match_payload: [ 'source' => 'editorial' ],
        correlation_key: 'task_id',
    )
    ->dispatch( [ 'task_id' => 'task-42' ] );
```

Nesse exemplo, a espera só é retomada quando:

- o nome do sinal é `review_ready`
- o conteúdo inclui `source: editorial`
- `task_id` do conteúdo corresponde a `$state['task_id']`

Isso é útil quando vários sistemas externos ou agentes publicam os mesmos nomes de evento, mas apenas um conteúdo pertence à execução atual do fluxo de trabalho.

## Auxiliares de aprovação e entrada humana

`wait_for_approval()` e `wait_for_input()` são wrappers convenientes para esperas por sinal. Funcionam como `wait_for_signal()`, mas usam por padrão uma chave de resultado com namespace.

```php
$workflow_id = Queuety::workflow( 'blog_review' )
    ->then( GenerateDraftHandler::class )
    ->wait_for_approval()
    ->wait_for_input()
    ->then( HandleDecisionHandler::class )
    ->dispatch();
```

Após o sinal `approval`, `$state['approval']` contém o conteúdo da aprovação. Após o sinal `input`, `$state['input']` contém o conteúdo da entrada.

Use `wait_for_decision()` quando quiser que uma única etapa retome a execução após uma aprovação ou rejeição:

```php
$workflow_id = Queuety::workflow( 'legal_review' )
    ->then( DraftContractHandler::class )
    ->wait_for_decision( result_key: 'review' )
    ->then( HandleReviewDecisionHandler::class )
    ->dispatch();
```

Após a chegada do sinal, `$state['review']` contém:

```php
[
    'outcome' => 'approved', // or 'rejected'
    'signal'  => 'approved', // or 'rejected'
    'data'    => [ 'reviewer' => 'ops@example.com' ],
]
```

## Enviar um sinal

Envie um sinal para um fluxo de trabalho usando `Queuety::signal()`:

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

Quando o sinal corresponder ao que o fluxo de trabalho está aguardando, o fluxo retoma a execução e avança para a próxima etapa.

Para fluxos com participação humana, a fachada também oferece auxiliares semânticos:

```php
Queuety::approve_workflow( $workflow_id, [ 'reviewer' => 'editor@example.com' ] );
Queuety::reject_workflow( $workflow_id, [ 'reason' => 'needs citations' ] );
Queuety::submit_workflow_input( $workflow_id, [ 'notes' => 'Ship after copy edit' ] );
```

### Dados do sinal

Você pode passar uma matriz de dados junto com o sinal. Os dados são mesclados ao estado do fluxo de trabalho e ficam disponíveis nas etapas seguintes:

```php
Queuety::signal( $workflow_id, 'approved', [
    'approved_by' => 'admin@example.com',
    'approved_at' => time(),
] );
```

Na próxima etapa:

```php
class PublishContentHandler implements Step {
    public function handle( array $state ): array {
        // $state['approved_by'] is 'admin@example.com'
        // $state['approved_at'] is the timestamp
        // $state['post_id'] is 99
        wp_update_post( [
            'ID'          => $state['post_id'],
            'post_status' => 'publish',
        ] );
        return [ 'published' => true ];
    }

    public function config(): array {
        return [];
    }
}
```

As chaves que começam com `_` (sublinhado) são reservadas para uso interno e não são mescladas ao estado.

## Sinais preexistentes

Os sinais podem ser enviados antes que o fluxo de trabalho chegue à etapa de espera. Quando um sinal chega, ele é registrado na tabela `queuety_signals` independentemente do status atual do fluxo de trabalho. Quando o fluxo chega à etapa `wait_for_signal`, procura sinais preexistentes e continua imediatamente se encontrar algum.

Isso significa que a ordem não importa:

```php
// Dispatch workflow
$workflow_id = Queuety::workflow( 'order_fulfillment' )
    ->then( PrepareOrderHandler::class )
    ->wait_for_signal( 'payment_confirmed' )
    ->then( ShipOrderHandler::class )
    ->dispatch( [ 'order_id' => 123 ] );

// Signal arrives before the workflow reaches the wait step
Queuety::signal( $workflow_id, 'payment_confirmed', [ 'transaction_id' => 'txn_abc' ] );

// When the workflow finishes PrepareOrderHandler and reaches the wait step,
// it sees the pre-existing signal and continues immediately.
```

## Status do fluxo de trabalho durante a espera por sinal

Enquanto aguarda um sinal, o status do fluxo de trabalho é `WorkflowStatus::WaitingForSignal` (`waiting_for_signal`):

```php
$state = Queuety::workflow_status( $workflow_id );
echo $state->status->value; // 'waiting_for_signal'
echo $state->wait_type;     // 'signal'
echo $state->wait_mode;     // 'all' or 'any'
print_r( $state->waiting_for ); // ['approved']
print_r( $state->wait_details ); // matched/remaining, correlation info, result key, ...
```

## Caso de uso: aprovação humana

Crie um fluxo de aprovação em que um revisor humano precisa aprovar o conteúdo antes de ele ser publicado:

```php
$workflow_id = Queuety::workflow( 'blog_review' )
    ->then( GenerateDraftHandler::class )
    ->then( NotifyReviewerHandler::class )
    ->wait_for_decision( result_key: 'review' )
    ->then( HandleDecisionHandler::class )
    ->dispatch( [ 'topic' => 'PHP middleware patterns' ] );
```

O `NotifyReviewerHandler` envia um e-mail ou uma mensagem por Slack com links para aprovar/rejeitar. Esses links chamam seu controlador, que envia o sinal:

```php
// In your approval controller
Queuety::approve_workflow(
    $workflow_id,
    [
        'reviewer'    => $current_user->user_email,
        'reviewed_at' => time(),
    ],
    'approved'
);
```

A etapa `HandleDecisionHandler` lê `$state['review']['outcome']` e publica ou arquiva o rascunho.

## Caso de uso: retorno de chamada API externo

Aguarde um webhook de um serviço externo:

```php
$workflow_id = Queuety::workflow( 'generate_video' )
    ->then( SubmitToRenderServiceHandler::class )
    ->wait_for_signal( 'render_complete' )
    ->then( DownloadVideoHandler::class )
    ->then( NotifyUserHandler::class )
    ->dispatch( [ 'template_id' => 'promo_v2', 'user_id' => 42 ] );
```

Quando o serviço externo fizer uma requisição ao endpoint do seu webhook:

```php
// In your webhook handler
$payload = json_decode( file_get_contents( 'php://input' ), true );
$workflow_id = (int) $payload['metadata']['workflow_id'];

Queuety::signal( $workflow_id, 'render_complete', [
    'video_url'  => $payload['output_url'],
    'duration'   => $payload['duration_seconds'],
] );
```

## Vários pontos de espera por sinal em um fluxo de trabalho

Um fluxo de trabalho pode aguardar sinais em vários pontos:

```php
Queuety::workflow( 'multi_approval' )
    ->then( CreateProposalHandler::class )
    ->wait_for_signal( 'manager_approval' )
    ->then( EscalateToDirectorHandler::class )
    ->wait_for_signal( 'director_approval' )
    ->then( ExecuteProposalHandler::class )
    ->dispatch( [ 'proposal_id' => 7 ] );
```

Cada etapa de espera por sinal é independente. Você pode combinar `wait_for_signal()`, `wait_for_signals()`, `wait_for_approval()`, `wait_for_input()` e `wait_for_decision()` no mesmo fluxo de trabalho.
