> For the complete documentation index, see [llms.txt](https://docs.skail.dev/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.skail.dev/aprender/readme/proximo-nivel-command-evento-externo-e-monitor-20-minutos.md).

# Próximo nível: Command, evento externo e Monitor (20 minutos)

Adicione um command, espere um evento externo com prazo e veja os checkpoints no Monitor.

Continua o projeto do Quickstart. Em três passos você adiciona um command que grava em arquivo e lê o relógio, uma espera por evento externo com prazo, e vê os checkpoints aparecerem na linha do tempo do Monitor. Ao final você viu, na prática, os três conceitos que sustentam o skail.

## Pré-requisitos

O projeto `HelloWorld` do [Quickstart](/aprender/readme.md) rodando, com o workload `primeiroapp:v0.0.1`, e a chave, o namespace e a URL base do environment `dev` à mão.

{% stepper %}
{% step %}

### Adicione um command e veja o checkpoint

Um `[SkailCommand]` é onde mora a operação de I/O. Vamos gravar uma linha em um arquivo local e devolver o horário da gravação. Substitua `HelloWorldFunctions.cs` por:

```csharp
using Skail.Platform.Runtime.Standard;
using Skail.Platform.Runtime.Standard.Threading;

public class HelloWorldFunctions
{
    [SkailFunction]
    public async SkailTask HelloWorld()
    {
        var registradoEm = await RegistrarPassagem("inicio");
        await RegistrarPassagem($"fim (primeiro registro em {registradoEm:HH:mm:ss})");
    }

    [SkailCommand]
    public async SkailTask<DateTime> RegistrarPassagem(string etapa)
    {
        var agora = DateTime.UtcNow;                        // relógio: só dentro de command
        await File.AppendAllTextAsync("passagens.log", $"{agora:O} {etapa}\n");
        return agora;
    }
}
```

Repare no que está onde. A function `HelloWorld` só encadeia; ela não lê o relógio nem toca o disco. O command `RegistrarPassagem` faz as duas coisas e devolve um valor, que fica gravado no histórico da execução. Se a function fosse reexecutada, o command não rodaria de novo: o horário gravado voltaria do histórico.

Rode `dotnet run` e dispare com um id novo:

```bash
curl -X POST "https://SEU_SKAIL_BASE_URL/trigger/primeiroapp/v0.0.1/HelloWorld/00000000-0000-0000-0000-000000000002" \
  -H "Content-Type: application/json" \
  -H "skail-key: SUA_SKAIL_KEY" \
  -H "Skail-namespace: SEU_NAMESPACE" \
  -d '[]'
```

O que você deve ver: duas linhas em `passagens.log` e, no Monitor, a execução `...0002` com dois checkpoints na linha do tempo, um por command, cada um com o horário e o valor retornado.
{% endstep %}

{% step %}

### Espere por um evento externo, com prazo

Agora a function vai parar e esperar uma aprovação que vem de fora. Adicione a espera, com um prazo de dez minutos para o exemplo não ficar pendurado:

```csharp
using Skail.Platform.Runtime.Standard;
using Skail.Platform.Runtime.Standard.Threading;
using Skail.Platform.Runtime.Threading;

public static class Eventos
{
    public const string PedidoAprovado = "PEDIDO_APROVADO";
}

public record Aprovacao(bool Aprovado, string Quem);

public class HelloWorldFunctions
{
    [SkailFunction]
    public async SkailTask HelloWorld()
    {
        await RegistrarPassagem("inicio");

        var aprovacao = SkailTask.WaitForEvent<Aprovacao>(Eventos.PedidoAprovado, SkailContext.Current.TaskId.ToString());
        var prazo     = SkailTask.Delay(TimeSpan.FromMinutes(10));

        var vencedor = await SkailTask.WhenAny(aprovacao, prazo);
        if (vencedor == prazo)
        {
            await RegistrarPassagem("prazo vencido sem aprovacao");
            return;
        }

        var resposta = await aprovacao;
        await RegistrarPassagem(resposta.Aprovado ? $"aprovado por {resposta.Quem}" : $"recusado por {resposta.Quem}");
    }

    [SkailCommand]
    public async SkailTask<DateTime> RegistrarPassagem(string etapa)
    {
        var agora = DateTime.UtcNow;
        await File.AppendAllTextAsync("passagens.log", $"{agora:O} {etapa}\n");
        return agora;
    }
}
```

O `instanceId` da espera é o `TaskId` da execução, que é o id que você passa na URL do trigger. É assim que o disparo e a espera se encontram. `SkailContext.Current.TaskId` é uma das poucas coisas que podem ser lidas dentro da function, porque é igual em todo replay.

Rode `dotnet run` de novo (o código mudou, e o id da execução vai ser novo) e dispare:

```bash
curl -X POST "https://SEU_SKAIL_BASE_URL/trigger/primeiroapp/v0.0.1/HelloWorld/00000000-0000-0000-0000-000000000003" \
  -H "Content-Type: application/json" \
  -H "skail-key: SUA_SKAIL_KEY" \
  -H "Skail-namespace: SEU_NAMESPACE" \
  -d '[]'
```

O que você deve ver: no Monitor, a execução `...0003` com o status de aguardando evento, mostrando o nome `PEDIDO_APROVADO` e o instance id. No terminal, nada preso: o processo está livre. Você pode até parar o `dotnet run` e subir de novo; a espera continua.

Agora acorde a execução disparando o evento, com o mesmo id:

```bash
curl -X POST "https://SEU_SKAIL_BASE_URL/api/v1/fire/PEDIDO_APROVADO/00000000-0000-0000-0000-000000000003" \
  -H "Content-Type: application/json" \
  -H "skail-key: SUA_SKAIL_KEY" \
  -H "Skail-namespace: SEU_NAMESPACE" \
  -d '{ "Aprovado": true, "Quem": "maria" }'
```

O que você deve ver: a linha `aprovado por maria` em `passagens.log` e a execução concluída no Monitor. Se você esperar dez minutos sem disparar, a linha será `prazo vencido sem aprovacao`. Se nada acontecer, confira se o nome do evento e o id são exatamente iguais nos dois lados.
{% endstep %}

{% step %}

### Leia a linha do tempo

Abra a execução `...0003` no Monitor. A linha do tempo mostra, em ordem: o início da function, o command `RegistrarPassagem("inicio")` com o valor retornado, a espera por `PEDIDO_APROVADO` com o tempo que ela durou, o `Delay` de dez minutos que perdeu a corrida, o evento recebido com o payload, e o command final. Cada item é um passo do histórico da execução: é isso que o runtime consulta quando reexecuta a function.

Se você parou e subiu o processo durante a espera, note que nenhum command rodou duas vezes: os checkpoints anteriores voltaram do histórico. Essa é a durabilidade em uma imagem.

Para depurar uma execução dessas na sua IDE com os dados originais, o Monitor oferece o Time Travel Debug: [Como depurar com Time Travel Debug](/construir/testar-depurar-e-observar/como-depurar-com-time-travel-debug.md).
{% endstep %}
{% endstepper %}

## O que você viu

Command: operação de I/O cujo resultado fica gravado no histórico; no replay o skail devolve esse resultado sem executar o command de novo. Evento externo: a function hiberna sem custo e é acordada por um `POST` de qualquer sistema, correlacionado por nome e id. Monitor: cada passo visível, com valores e tempos, sem instrumentar nada.

## Próximos passos

[Próximos passos](/aprender/readme/proximos-passos.md) tem três caminhos por objetivo. Se quiser entender o que aconteceu por baixo, [Determinismo e replay](/aprender/fundamentos/determinismo-e-replay.md). Se quiser um caso real, [Fluxo de aprovação com timeout](/aprender/exemplos-completos/fluxo-de-aprovacao-com-timeout.md) é este mesmo padrão em um cenário de negócio.
