> 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/comece-aqui/use-com-ia.md).

# Use com IA

Cursor, GitHub Copilot e Claude erram muito menos em código skail quando recebem as regras certas.

Esta página dá o arquivo de regras pronto para copiar e aponta onde a documentação inteira está em texto para o assistente ler.

## A documentação em texto

[docs.skail.dev/llms.txt](https://docs.skail.dev/llms.txt) lista todas as páginas no formato que os assistentes leem. Cada página também existe em markdown puro: acrescente `.md` ao final da URL. Cole o `llms.txt` ou a URL `.md` de uma página no contexto do assistente quando quiser que ele responda com base na doc, não na memória.

{% hint style="info" %}
Assistentes que falam MCP (Claude, Claude Code, Cursor, Codex, VS Code) podem conectar esta documentação como servidor MCP: `https://docs.skail.dev/~gitbook/mcp`. Ele responde com busca e leitura das páginas publicadas.
{% endhint %}

## O arquivo de regras

Salve o bloco da linguagem do projeto na raiz do repositório como `.cursorrules` (Cursor), `.github/copilot-instructions.md` (GitHub Copilot) ou `CLAUDE.md` (Claude Code). Os três leem o arquivo automaticamente. O conteúdo resume o modelo de programação e os antipadrões que mais aparecem em revisão.

{% tabs %}
{% tab title="C#" %}

```markdown
# Regras para código skail

Este projeto usa o skail. Siga estas regras em todo código que use [SkailFunction] ou [SkailCommand].

## Modelo
- [SkailFunction] é o orquestrador. Seu corpo é reexecutado (replay) do início a cada retomada. Ele só decide e encadeia chamadas.
- [SkailCommand] é onde ficam as operações de I/O (HTTP, banco, fila, arquivo) e tudo que muda a cada execução (relógio, aleatoriedade). Depois de concluído, ele não roda de novo; o resultado gravado volta no replay.
- Todo método decorado é público, de instância (nunca static), async, e retorna SkailTask ou SkailTask<T>. Nunca Task/Task<T>.
- Declare as classes com functions e commands direto no namespace, nunca dentro de outra classe: método decorado em classe aninhada falha na execução com NotSupportedException em SkailTaskMethodBuilder.Create().
- Use sempre `await` e nunca .Result, .Wait(), .GetAwaiter().GetResult() ou .GetResult() em SkailTask.
- O runtime é configurado com `builder.UseSkail()` e o assembly precisa de `[assembly: VisibleToSkailPlatform]`.
- Não misture Task e SkailTask no caminho durável: um helper `async Task` com I/O chamado da function não grava passo nem retenta. Faça dele um [SkailCommand].
- Não declare método `async SkailTask` ou `async SkailTask<T>` sem [SkailFunction] ou [SkailCommand], nem mesmo um helper privado que agrupa commands e chama SkailTask.WhenAll: ele compila sem aviso e falha na execução com NotSupportedException ("Specified method is not supported.") em SkailTaskMethodBuilder.Create(). Escreva o trecho no corpo da function. Se ele precisar ser reaproveitado, faça dele uma [SkailFunction] pública e de instância, que roda como function filha, numa execução própria.
- Não guarde estado em campos da classe entre awaits: a instância é descartada na hibernação. Passe o estado por parâmetros e retornos de commands.
- Commands só são chamados de dentro de functions. Um [SkailCommand] não chama outro [SkailCommand] nem uma [SkailFunction]: quem compõe os passos é sempre a function. Se dois passos precisam ser duráveis separadamente, a function chama os dois em sequência. Dentro de um command, reaproveite lógica com métodos comuns, sem atributo.

## Determinismo (dentro de [SkailFunction])
- Proibido: DateTime.Now/UtcNow, DateTimeOffset.Now, Guid.NewGuid(), Random, Environment.TickCount, Environment.GetEnvironmentVariable, ler arquivo/config/banco/HTTP, Task.Delay, Thread.Sleep, Task.WhenAll/WhenAny, Task.Run, Parallel.*.
- Permitido: lógica pura, branching sobre argumentos e resultados de commands, laços (loops), ILogger, SkailContext.Current.TaskId, chamadas a commands e a outras functions, e as primitivas SkailTask.*.
- Relógio: `var agora = await ObterDataAtual();` com `[SkailCommand] public async SkailTask<DateTime> ObterDataAtual() => await Task.FromResult(DateTime.UtcNow);`
- Dentro de [SkailCommand] vale tudo (HttpClient, DbContext, Task.Delay, Guid.NewGuid(), DateTime.UtcNow, ...), menos chamar outro [SkailCommand] ou uma [SkailFunction].

## Primitivas (só dentro de functions)
- SkailTask.Delay(TimeSpan), SkailTask.DelaySeconds(ulong), SkailTask.DelayMinutes(ulong): espera durável.
- SkailTask.WhenAll(params SkailTask[]), SkailTask.WhenAny(params SkailTask[]): paralelismo. Ler resultados pela posição, não pela ordem de término.
- SkailTask.WaitForEvent(string eventName, string eventInstanceId) e WaitForEvent<T>(...): hiberna até um evento externo com o mesmo nome e o mesmo eventInstanceId.
- Não existe SkailTask.FireEvent nem cliente .NET para disparar. Eventos são disparados por HTTP: POST {SKAIL_BASE_URL}/api/v1/fire/{eventName}/{instanceId} com os headers Authorization: Bearer {SKAIL_KEY}, skail-key: {SKAIL_KEY} e Skail-namespace: {SKAIL_NAMESPACE} e o payload em JSON. Funções são iniciadas por POST {SKAIL_BASE_URL}/api/v1/trigger/{workload}/{versao}/{funcao}/{id} com os mesmos três headers e os argumentos em um array JSON. O id do trigger é um GUID: quem dispara gera uma vez, reutiliza nas novas tentativas desse disparo e guarda a relação com o id de negócio.
- O payload do fire e os argumentos que são objetos no trigger usam os nomes exatos das propriedades do tipo, em PascalCase: o runtime diferencia maiúsculas de minúsculas e, com caixa diferente, o campo fica com o valor padrão do tipo (`false`, `0`, `null`), sem erro. Quem dispara em C# usa `PostAsJsonAsync(url, valor, JsonSerializerOptions.Default)`.
- Enum em argumento de trigger ou em payload de fire vai como número no JSON. Não registre JsonStringEnumConverter no UseSkail. Se quem dispara manda o valor como texto, use um campo string no tipo.
- Não existem DelayHours, DelayDays nem ContextHolder.*.
- Não use SkailEvent.WaitForEvent (devolve Task); use SkailTask.WaitForEvent.

## Eventos e tempo
- Nomes de evento em constantes compartilhadas (public const string), MAIÚSCULAS_COM_UNDERSCORE.
- instanceId é um id de negócio estável (id do pedido, da fatura), igual nos dois lados.
- Toda espera tem prazo: `var timeout = SkailTask.Delay(prazo); var vencedor = await SkailTask.WhenAny(espera, timeout); if (vencedor == timeout) { ... }`.

## Falhas e idempotência
- Qualquer exceção em command gera nova tentativa até retryCount (default 5); a function só vê a exceção depois. Com retryCount maior que zero (inclusive o padrão), as novas tentativas ficam com o runtime: não escreva laço (loop) de retry na function para esse command, porque as tentativas se multiplicam.
- Falha de negócio (recusa, inválido) volta como resultado do command: enum ou record com o desfecho, o motivo e o dado.
- Toda classe de exceção própria lançada em command tem construtor com um único parâmetro string, repassado para base(message), para a exceção chegar à function com o tipo original, inclusive depois de uma retomada.
- Use exceção tratada na function só quando o usuário pedir ou o código existente já trabalhar assim: [SkailCommand(retryCount: 0)] no command, classe de exceção com construtor que recebe só a mensagem (string), catch com filtro de tipo (when) na function, laço (loop) com limite e SkailTask.Delay entre as tentativas, desfecho registrado num command na última tentativa e chave de idempotência mantida. O command converte timeout e 5xx na exceção que a function filtra, ou o filtro inclui TaskCanceledException, que é como chega o timeout do HttpClient.
- A operação externa executa "no mínimo uma vez": passe uma chave de idempotência (id de negócio) em toda chamada externa dentro de command.
- Um command por operação de I/O.
- Nunca capture SkailNonDeterministicException nem SkailException: a primeira indica que o código diverge do histórico (corrija o código), a segunda é falha de comunicação com o skail ou uma exceção de command que o runtime não conseguiu reconstruir (o log mostra "Exception thrown by user code cannot be persisted to durable state"; dê à classe da exceção o construtor com um único string). Num catch genérico, use `when (ex is not SkailNonDeterministicException and not SkailException)`.
- Argumentos e retornos são DTOs/records pequenos e serializáveis (System.Text.Json). Nunca entidades de ORM, streams, SkailTask ou listas enormes.

## Versionamento
- Não mude ordem, quantidade ou tipo dos awaits de uma function que tem execuções em andamento. Crie um método novo com [SkailFunction(skailMethodName: "...")] ou publique nova versão do workload (SKAIL_WORKLOAD=nome:versao) e mantenha a antiga até esvaziar (drain).

## Estilo
- Nomes em português, legíveis no Monitor: EmitirNotaFiscal, AguardarAprovacao, ObterDataAtual.
- Inclua SkailContext.Current.TaskId nos logs.
```

{% endtab %}

{% tab title="TypeScript" %}

```markdown
# Regras para código skail

Este projeto usa o skail com o SDK TypeScript (`@skail/runtime`). Siga estas regras em todo código que use @SkailFunction() ou @SkailCommand().

## Modelo
- @SkailFunction() é o orquestrador. Seu corpo é reexecutado (replay) do início a cada retomada. Ele só decide e encadeia chamadas.
- @SkailCommand() é onde ficam as operações de I/O (HTTP, banco, fila, arquivo) e tudo que muda a cada execução (relógio, aleatoriedade). Depois de concluído, ele não roda de novo; o resultado gravado volta no replay.
- Todo método decorado é público, de instância (nunca static nem private), async, e retorna Promise<T>. O `skail build` rejeita o que foge disso.
- Leia todo resultado de command, function ou primitiva com `await`, direto ou por `Skail.whenAll`/`Skail.whenAny`; nunca com `.then`.
- O entry point exporta as classes com functions e commands; o `skail build` gera a partir dele o bundle e o manifest; o host é `createSkailPlatform({ ... })` com `platform.run(signal)`.
- O runtime não infere parâmetros de construtor: as dependências entram pelo array `providers` exportado no entry point (com `useFactory`) ou são instanciadas na própria classe.
- `process.env` só pode ser lido dentro de um @SkailCommand(). No topo de um módulo que entra no bundle, no `useFactory` dos `providers` e na function, a leitura lança erro, e um erro no carregamento do módulo impede o host de subir. Leia a variável no command que a usa, inclusive a string de conexão do banco.
- Não dependa de campos da classe para guardar estado entre awaits: na hibernação a execução sai da memória. Passe o estado por parâmetros e retornos de commands.
- Commands só são chamados de dentro de functions. Um command não chama outro command nem uma function: quem compõe os passos é sempre a function. Se dois passos precisam ser duráveis separadamente, a function chama os dois em sequência. Dentro de um command, reaproveite lógica com métodos comuns, sem decorator.

## Determinismo (dentro de @SkailFunction())
- Lançam erro na hora: new Date() sem argumento, Date.now(), Math.random(), crypto.randomUUID(), crypto.getRandomValues(), setTimeout(), setInterval(), fetch(). `process` e `require` não existem. Imports estáticos de módulos Node de I/O (fs, net, crypto, process, worker_threads) são rejeitados no build.
- Não use Promise.all, Promise.race nem .then para compor esperas: só promises do runtime entram em Skail.whenAll e Skail.whenAny.
- Permitido: lógica pura, decisões sobre argumentos e resultados de commands, laços (loops), new Date(texto), Date.parse, getTime(), SkailContext.current.taskId, chamadas a commands e a outras functions, e as primitivas Skail.*.
- Relógio: leia a hora em um command que devolve a data como string ISO-8601, e use o valor devolvido na function.
- Dentro de @SkailCommand() vale tudo (fetch, banco, timers, crypto, Date), com os módulos de I/O carregados por `await import(...)`, menos chamar outro command ou uma function.

## Primitivas (só dentro de functions)
- Skail.delay(ms), Skail.delaySeconds(n), Skail.delayMinutes(n): espera durável. Resolvem undefined; valor negativo ou não finito rejeita com RangeError.
- Skail.whenAll(...): devolve os resultados na ordem dos argumentos. Skail.whenAny(...): devolve o valor de quem terminou primeiro.
- Skail.waitForEvent<T>(eventName, instanceId): hiberna até um evento externo com o mesmo nome e o mesmo instanceId (string).
- Eventos são disparados por HTTP: POST {SKAIL_BASE_URL}/api/v1/fire/{eventName}/{instanceId} com os headers Authorization: Bearer {SKAIL_KEY}, skail-key: {SKAIL_KEY} e Skail-namespace: {SKAIL_NAMESPACE} e o payload em JSON. Funções são iniciadas por POST {SKAIL_BASE_URL}/api/v1/trigger/{workload}/{versao}/{funcao}/{id} com os mesmos três headers e os argumentos em um array JSON. O id do trigger é um GUID: quem dispara gera uma vez, reutiliza nas novas tentativas desse disparo e guarda a relação com o id de negócio.
- O payload do fire e os argumentos que são objetos no trigger usam os nomes exatos das propriedades do tipo, em PascalCase (`Aprovado`, não `aprovado`): o runtime diferencia maiúsculas de minúsculas e, com caixa diferente, o campo chega `undefined`, sem erro.
- União de strings em payload de fire ou argumento de trigger vai como texto. Se o destino é uma function C#, mande o número do enum ou combine um campo string no tipo C#.

## Eventos e tempo
- Nomes de evento em constantes compartilhadas (objeto `as const`), MAIÚSCULAS_COM_UNDERSCORE.
- instanceId é um id de negócio estável (id do pedido, da fatura), igual nos dois lados.
- Toda espera tem prazo: `const resultado = await Skail.whenAny(espera, Skail.delayMinutes(prazo)); if (resultado === undefined) { ... }`. Por isso o payload do evento nunca pode ser undefined.

## Falhas e idempotência
- Qualquer exceção em command gera nova tentativa até retryCount (default 5); a function só vê a exceção depois. Com retryCount maior que zero (inclusive o padrão), as novas tentativas ficam com o runtime: não escreva laço (loop) de retry na function para esse command, porque as tentativas se multiplicam.
- Falha de negócio (recusa, inválido) volta como resultado do command: união de strings ou objeto com o desfecho, o motivo e o dado.
- Toda classe de erro lançada em command é declarada no projeto como `class X extends Error` no nível do módulo, com name definido; erro de pacote npm é capturado no command e relançado como classe do projeto.
- Use exceção tratada na function só quando o usuário pedir ou o código existente já trabalhar assim: @SkailCommand({ retryCount: 0 }) no command, classe de erro declarada no projeto como `class X extends Error` no nível do módulo, com name definido (`override name = "X"`), instanceof na function e relançar tudo que ela não reconhecer, laço (loop) com limite e Skail.delay entre as tentativas, desfecho registrado num command na última tentativa e chave de idempotência mantida. O command converte timeout e 5xx na classe do projeto que a function filtra, porque o fetch nunca lança essa classe. Erro de classe vinda de pacote npm é capturado no command e relançado como classe do projeto; se a classe do pacote define name, a function também pode comparar pelo name.
- A operação externa executa "no mínimo uma vez": passe uma chave de idempotência (id de negócio) em toda chamada externa dentro de command.
- Um command por operação de I/O.
- Nunca capture SkailError (inclui SkailNonDeterministicError, que indica que o código diverge do histórico; corrija o código). Num catch genérico, relance com `if (ex instanceof SkailError) throw ex;`.
- Argumentos e retornos são objetos pequenos e serializáveis em JSON, com datas como string ISO-8601. Nunca streams, promises ou listas enormes.

## Versionamento
- Não mude ordem, quantidade ou tipo dos awaits de uma function que tem execuções em andamento. Crie um método de nome novo, sem `uri` (o endereço vira `{workload atual}/{nome do método}`; o `uri` é literal e prende o método à versão escrita), ou publique nova versão do workload (SKAIL_WORKLOAD=nome:versao) e mantenha a antiga até esvaziar (drain).

## Estilo
- Nomes em português, legíveis no Monitor: EmitirNotaFiscal, AguardarAprovacao, ObterDataAtual.
- Inclua SkailContext.current.taskId nos logs.
```

{% endtab %}
{% endtabs %}

## Como usar no dia a dia

Peça ao assistente para "escrever uma function que..." e revise com o [Checklist de revisão de código skail](/construa/como-fazer/padroes-e-antipadroes/checklist-de-revisao-de-codigo-skail.md): os itens 1 a 5 de [Antipadrões](/construa/como-fazer/padroes-e-antipadroes/antipadroes-o-que-nao-fazer-e-por-que.md) (I/O, relógio e aleatoriedade dentro da function, pausas e concorrência da linguagem no lugar das primitivas do skail) e, em C#, o item 21 (`Task` em vez de `SkailTask`) são os que os assistentes mais erram sem as regras. Em C#, confira também o item 32 (helper `async SkailTask` sem atributo), que o analyzer não pega.

{% hint style="warning" %}
Em C#, quando o assistente sugerir `SkailTask.FireEvent`, `DelayHours`, `DelayDays` ou `ContextHolder`, ele está inventando: nenhum existe. Se ele sugerir `SkailEvent.WaitForEvent`, troque por `SkailTask.WaitForEvent`: o primeiro devolve `Task`. Aponte para esta página.
{% endhint %}

## Próximos passos

[Antipadrões](/construa/como-fazer/padroes-e-antipadroes/antipadroes-o-que-nao-fazer-e-por-que.md) é a versão comentada do arquivo de regras. [Padrões recomendados](/construa/como-fazer/padroes-e-antipadroes/padroes-recomendados.md) é a versão positiva. [Quickstart](/comece-aqui/quickstart.md) para o primeiro projeto.
