> 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/construir/sdk-.net/skailcommand.md).

# \[SkailCommand]

Marca um método como a unidade de I/O do fluxo: executa, tem argumentos e resultado registrados no histórico da execução, e não executa de novo naquela execução depois de concluir. É onde mora tudo que a function não pode fazer: I/O, relógio, aleatoriedade.

## Assinatura

```csharp
[SkailCommand]
[SkailCommand(retryCount: 10)]
public async SkailTask NomeDoMetodo(...)      // ou SkailTask<T>
```

| Parâmetro    | Tipo   | Default | Descrição                                                                                                |
| ------------ | ------ | ------- | -------------------------------------------------------------------------------------------------------- |
| `retryCount` | `uint` | `5`     | Máximo de reentregas quando o command lança exceção. Ao esgotar, a exceção é lançada dentro da function. |

## Regras do método

| Regra                                                            | Detalhe                                                                                                                                                                                                                                                               | Quem cobra                    |
| ---------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------- |
| Método de instância, `async`, retorno `SkailTask`/`SkailTask<T>` | Como em `[SkailFunction]`                                                                                                                                                                                                                                             | Analyzer (SKAIL001, SKAIL002) |
| `public`                                                         | Sempre `public`                                                                                                                                                                                                                                                       | Analyzer (SKAIL001)           |
| Argumentos e retorno serializáveis                               | `System.Text.Json`; records e DTOs pequenos, ids em vez de entidades                                                                                                                                                                                                  | Runtime, ao registrar o passo |
| Chamado de dentro de uma function                                | Fora de uma execução durável não há contexto                                                                                                                                                                                                                          | Runtime                       |
| Não chama outro `[SkailCommand]` nem uma `[SkailFunction]`       | Quem compõe os passos é sempre a function, e commands são chamados só de dentro de functions. Se dois passos precisam ser duráveis separadamente, a function chama os dois em sequência; para reaproveitar lógica dentro do command, use métodos comuns, sem atributo | Regra do modelo               |

Dentro do corpo vale tudo que vale em C# comum: `HttpClient`, `DbContext`, `Task.Delay`, `Task.WhenAll`, `Guid.NewGuid()`, `DateTime.UtcNow`. O command não tem a restrição de determinismo da function. A única restrição é não chamar outro command nem uma function (ver a tabela acima).

## Comportamento

Quando a function alcança `await MeuCommand(...)`, o runtime confere o histórico da execução. Se este passo já foi concluído (a execução está sendo retomada), devolve o resultado daquela vez sem executar. Se não foi, executa o corpo uma vez; ao concluir, registra argumentos e retorno como um passo novo.

Se o corpo lança exceção, o runtime registra a falha e devolve a execução ao skail. Na reentrega, a function é reexecutada (passos anteriores voltam do histórico) e o command roda de novo. Isso se repete até concluir ou até esgotar `retryCount`; então a exceção é lançada no `await` da function, como qualquer exceção de `async/await`. O runtime não classifica exceções: toda exceção gera nova tentativa. Ver [Falhas, retries e idempotência](/aprender/fundamentos/falhas-retries-e-idempotencia.md).

O efeito externo é "no mínimo uma vez": se o command falhar entre executar o efeito e gravar o resultado, roda de novo. Use uma chave de idempotência derivada dos argumentos em toda chamada externa. Ver [Como escrever commands idempotentes](/construir/escrever-fluxos/como-escrever-commands-idempotentes.md).

## Quando usar

Quando o método faz algo que não pode ser perdido nem repetido sem controle (gravar, movimentar, enviar, reservar), ou quando ele produz um valor que muda entre execuções (hora atual, id gerado, resposta de API). Um bom critério: se o resultado pode diferir entre duas execuções com as mesmas entradas, é command. Um command por operação de I/O; falha de negócio como retorno, não como exceção.

## retryCount

Maior (8, 10) para integrações com instabilidade conhecida, onde repetir é seguro e barato. Zero quando o efeito não pode ser repetido e não há chave de idempotência possível: a exceção chega à function na primeira falha e você decide. Não deixe o default sem ter pensado.

## Exemplo

```csharp
public enum ResultadoCobranca { Aprovado, Recusado }

[SkailCommand(retryCount: 8)]
public async SkailTask<ResultadoCobranca> CobrarCartao(Guid faturaId, decimal valor)
{
    using var request = new HttpRequestMessage(HttpMethod.Post, "/charges")
    {
        Content = JsonContent.Create(new { amount = valor, invoice = faturaId })
    };
    request.Headers.Add("Idempotency-Key", $"cobranca-{faturaId}");

    var resposta = await _gateway.SendAsync(request);
    resposta.EnsureSuccessStatusCode();                       // falha técnica: exceção, retentada

    var corpo = await resposta.Content.ReadFromJsonAsync<RespostaGateway>();
    return corpo!.Approved ? ResultadoCobranca.Aprovado : ResultadoCobranca.Recusado;   // falha de negócio: resultado
}

[SkailCommand]
public async SkailTask<DateTime> ObterDataAtual() => await Task.FromResult(DateTime.UtcNow);
```

## Observabilidade

Cada command aparece na linha do tempo da execução no Monitor com argumentos, resultado, duração, número de tentativas e erro, e abre um span de rastreamento filho da function.

## Erros relacionados

| Erro                                | Causa                                                              |
| ----------------------------------- | ------------------------------------------------------------------ |
| SKAIL001 / SKAIL002                 | Assinatura fora das regras                                         |
| Exceção de serialização             | Argumento ou retorno não serializável (stream, entidade com ciclo) |
| Exceção do command chega à function | `retryCount` esgotado; trate na function ou ajuste                 |
| Efeito duplicado                    | Falha entre efeito e gravação sem chave de idempotência            |

## Veja também

[SkailFunction](/construir/sdk-.net/skailfunction.md), [Como tratar erros e compensar](/construir/escrever-fluxos/como-tratar-erros-e-compensar.md), [Como passar dados entre função e commands](/construir/escrever-fluxos/como-passar-dados-entre-funcao-e-commands.md).
