Vitor Bellini
Todos os posts

Fiz a IA assinar um contrato de trabalho

5 min de leitura #laravel #ia #claude-code

Codar com IA deixou de ser o problema. O problema virou fazer ela seguir os meus padrões.

Resolvi isso num plugin do Claude Code, o astro. Ele é uma esteira do contrato ao Pull Request, mas o que importa de verdade não são os comandos. São os templates que a IA é obrigada a seguir, e as regras por trás de cada um. É sobre isso que eu quero falar.

O que o setup organiza

O astro:setup é a fase 0, e faz mais do que gravar regra. Ele detecta a stack, grava o contrato no CLAUDE.md (as barreiras e os padrões de escrita) e, quando o projeto é Laravel, organiza o terreno antes de qualquer código:

  • Puxa a minha arquitetura do meu Git. Eu mantenho num repositório, o bellinivitor/laravel-architecture, um AGENTS.md com a arquitetura domain-driven que eu sigo. O setup baixa esse doc pra docs/architecture/ do projeto, pra ficar versionado e disponível offline, e registra no CLAUDE.md que é pra seguir ele.
  • Confirma o autoload. Garante que o namespace Domain\ está mapeado no PSR-4 do composer.json.
  • Cria um AGENTS.md por domínio e a estrutura de exceptions tipadas de domínio.

Com isso a IA já começa sabendo onde cada coisa vai, em vez de inventar a estrutura.

O gate (e o appzinho das regras)

Prompt é prompt. A IA lê o contrato, concorda, e meia hora depois sugere um migrate:fresh "só pra limpar o banco".

Por isso tem um gate: um hook do Claude Code que roda antes de cada comando no terminal, lê o texto e barra a execução se bater num padrão proibido. migrate:fresh, rm -rf, git push --force, truncate, composer update, nenhum passa. O prompt orienta, o código garante.

Só que eu não queria editar regra na unha, num JSON escondido. Então fiz um appzinho. É o ai-gate/: uma mini aplicação em Python puro, sem nenhuma dependência, que o setup instala no projeto. As regras ficam num SQLite, e você edita numa interface no navegador.

Rode astro:gate. Ele sobe um servidor local, abre a tela, e ali você liga, desliga, edita ou adiciona regra (regex ou texto literal). Clica em Gerar gate e pronto.

O detalhe que eu gosto: o gate gerado é autossuficiente. As regras ficam embutidas no arquivo, então ele não depende do SQLite pra rodar. Dá pra copiar e usar como CLI na VM ou no CI:

python3 astro_gate.py "$CMD" && eval "$CMD"

O SQLite é a fonte da verdade. O script é só um artefato gerado dali.

flowchart LR
    CMD([Comando]) --> MODE{Como chega?}
    MODE -->|Claude Code| HOOK[Hook PreToolUse]
    MODE -->|VM / CI| CLI[CLI: astro_gate.py CMD]
    HOOK --> GATE
    CLI --> GATE
    DB[(rules.db · SQLite)] -->|Gerar gate| GATE
    GATE[astro_gate.py<br/>lê o comando] --> CHECK{Padrão<br/>proibido?}
    CHECK -->|Sim| BLOCK["🚫 Bloqueia<br/>comando NÃO roda"]
    CHECK -->|Não| ALLOW["✅ Libera<br/>comando roda"]
    style BLOCK fill:#fde2e1,stroke:#b42318
    style ALLOW fill:#e3f5e8,stroke:#1f7a3f
    style GATE fill:#edebfb,stroke:#4738c8

O commit

Nada de mensagem genérica. O astro:commit escreve sempre assim:

<TIPO>: <Título curto e objetivo>
- <Alteração 1>
- <Alteração 2>

A regra que faz diferença: tudo no particípio passado, porque o commit descreve algo que já foi feito. "Adicionado cadastro de estoque", não "Adicionar cadastro". E um commit por mudança lógica. Se a descrição pede um "e", provavelmente são dois commits.

O Pull Request

O PR não repete a task. Ele diz ao revisor o que olhar no diff e como validar:

## O que foi feito
- <mudança técnica: arquivo, classe, endpoint, tabela>

## Impacto
<migration, contrato de API, job, cache; ou "nada além do escopo">

## Como testar
1. <preparo e passo a passo>

## Pontos de atenção
<o que olhar com cuidado, dívida deixada; ou "nenhuma">

A regra do Impacto aqui é a que mais me incomodava: nada de número que você não mediu. O texto saía sempre exagerado, com "pode pesar o banco" e cifras chutadas. Então o PR é simples, pro PO bater o olho e entender, sem inventar magnitude.

A entrega

Esse é o meu favorito. O astro:answer gera o texto que vai pro QA e pra quem sobe, num arquivo local (markdown colado do terminal quebra tudo):

### O que foi feito
### Impacto
- Dados: +X MB em 30 dias (N linhas/dia × Y KB)
- Banco: +N queries por request, índice novo
- Performance: endpoint Xms antes, Yms depois
### Como testar
### Deploy
### O que ficou de fora
### Observações

E aqui a regra se inverte em relação ao PR: na entrega, número vale mais que adjetivo. "Pode pesar o banco" não dá pra avaliar. "+2,4 GB em 30 dias" dá. Ordem de grandeza basta, o que importa é a diferença entre 2 MB e 2 GB.

E se não der pra medir na hora? Escreve a conta e a suposição: "estimo 40k linhas/dia assumindo o volume de pedidos de hoje". Suposição escrita alguém corrige. Número ausente ninguém questiona.

Um AGENTS.md por domínio

Esse resolve um problema chato: a IA mexer num domínio sem entender o que ele faz. Então todo app/Domain/{Dominio}/ ganha um AGENTS.md com um template fixo:

## Propósito e fronteira
## Linguagem (glossário)
## Modelos principais
## Estados e transições
## Regras de negócio
## Ações (casos de uso)
## Contratos expostos
## Dependências
## Exceptions
## Pontos de atenção

A regra: curto e de alto sinal. Seção vazia some, e atualiza quando o comportamento muda. Doc desatualizada engana mais que ausência.

A decisão que não é template: o plano é efêmero

Tem uma coisa que eu fiz questão de não virar arquivo. O plano do astro:plan vive na lista de tarefas da sessão, não em pastas de .md versionadas. Eu não queria 200 arquivos de planejamento poluindo o repositório. O que fica versionado é código, não cerimônia.

O código está aberto

Instala direto do GitHub:

/plugin marketplace add bellinivitor/astro
/plugin install astro@astro

O código está aberto no GitHub: https://github.com/bellinivitor/astro

E sim, 100% feito com o Claude Code. Eu descrevia, ele escrevia, e o gate segurava a mão dele quando precisava.

2 curtidas