# Por que o T25 orquestra CLIs de agente em vez de ser um framework em processo

> A decisão de arquitetura que define o T25: cada papel roda um CLI real, headless, em worktree isolada, e a fábrica governa o que acontece entre os estágios.

- Por: Djan Magno
- Publicado em: 2026-09-26
- Atualizado em: 2026-09-26
- URL: https://t25.io/blog/por-que-o-t25-orquestra-clis-de-agente/
- Engenharia · arquitetura de agentes de código, orquestração de CLIs, agentes de código headless, git worktree, state machine, human in the loop

> **Resumo:** > - O T25 não é uma biblioteca que você importa para chamar um modelo dentro do seu processo. É uma fábrica que **orquestra CLIs de agente reais**, Claude Code, Codex, Kimi, Gemini e outros, cada um rodando headless em uma worktree Git isolada.
> - Nessa arquitetura de agentes de código, o que o modelo escreve é um artefato de entrada; quem decide se a tarefa avança é uma state machine determinística, com gates humanos e merge sempre humano.
> - O isolamento por processo e worktree, os artefatos endereçados por hash, a cadeia de fallback entre CLIs e o fato de custo e credencial ficarem com o usuário são consequências diretas dessa escolha.
> - A escolha tem preço: latência de spawn por estágio, dependência do comportamento dos CLIs e menos controle fino sobre o loop do modelo. Os trade-offs estão no fim do post.

## A decisão em uma frase

Quando começamos a construir o T25, a pergunta de arquitetura não era "qual modelo usar". Era: **o agente é código dentro do nosso processo, ou um processo que nós governamos?** Escolhemos a segunda opção, e ela explica quase tudo no produto, do jeito que isolamos tarefas ao jeito que auditamos decisões.

Uma biblioteca de agentes em processo roda dentro da sua aplicação: você chama uma função, o loop do agente executa na sua memória, e o resultado volta como valor de retorno. Uma fábrica que orquestra CLIs faz o contrário: cada papel do pipeline, research, planner, dev, QA, reviewer, security, é um CLI real, autenticado com a assinatura do usuário, spawnado como processo separado, recebendo um prompt montado pela fábrica e devolvendo saída que a fábrica parseia antes de decidir qualquer coisa.

Entre os dois modelos há uma diferença de postura. No primeiro, o agente é uma peça sua. No segundo, o agente é um fornecedor com contrato, e o contrato é o que permite governança.

## O que acontece entre os estágios ao orquestrar CLIs de agente

O ponto central é quem controla o que acontece entre uma saída de agente e o próximo dispatch. No T25, a resposta é o `FactoryService` (`src/factory/service.ts`): um loop sobre `task.state` em que cada iteração escolhe o adapter, executa o papel, parseia o artefato e chama `transition()`. A máquina de estados (`src/core/state-machine.ts`) centraliza as transições legais via `assertTransition`, o serviço nunca atribui estado diretamente.

Isso quer dizer que o modelo não anda sozinho pelo pipeline. Ele produz texto num estágio; o código decide se aquele texto é um artefato válido, o que ele autoriza e qual o próximo estado. Um parse failure não é retentado em silêncio: falha a tarefa com erro descritivo. Um diff que estoura `limits.max_files_changed` ou `limits.max_diff_lines` é parado por `checkDiffLimits()` depois da implementação. Um plano só pede aprovação humana se `requiresPlanApproval()` disser que pede, com base no risco configurado no `t25.yaml`.

Esse desenho é o que nos permite afirmar, num post anterior, que [o APPROVE do modelo não é o merge](https://t25.io/blog/o-approve-do-modelo-nao-e-o-merge/): o veredito do reviewer é recalculado por `evaluateReview()` a partir dos findings, e o merge continua sendo uma ação humana depois dos checks obrigatórios.

## Isolamento por worktree, isolamento por processo

Cada tarefa roda numa Git worktree própria, em `.factory/worktrees/<taskId>`, no branch `factory/<taskId>`, nunca no checkout principal. Já contamos os detalhes noutro post: guards de path contra symlink e `..`, verificação de ownership via `.factory-metadata`, locks por PID e um `cleanup()` que recusa worktree suja e nunca faz force-remove.

A decisão de orquestrar CLIs soma a isso um segundo isolamento: o do processo. O adapter de cada CLI (`src/adapters/base.ts`) faz spawn com argv puro, sem shell, nomes de branch e caminhos nunca são interpolados numa string de comando. Prompts acima de 100KB são materializados em arquivo temporário em vez de irem por argv, para não estourar `ARG_MAX`. Há timeout, abort e detecção de quota, e um provider de sandbox Docker opcional executa comandos com `network none`.

O efeito prático sobre o raio de explosão: quando um agente se comporta mal, loop de edição, diff gigante, tentativa de escrever fora do diretório, o estrago está contido num diretório descartável, num branch que ninguém fez merge, num processo que pode ser morto. Nada disso depende do modelo estar de bom humor.

## Auditabilidade como consequência

Processos que terminam deixam rastro. Cada run de agente no T25 grava log, eventos SSE e artefato persistido no store endereçado por SHA-256; o Markdown solto em `.factory/artifacts/` não é mais a fonte de verdade. As decisões do operador, aprovar, mergear, cancelar, reexecutar, arquivar, vão para um `audit.jsonl` append-only com ator e timestamp, exposto via `GET /audit` no dashboard. Em produção, tarefas, runs e leases vivem no Postgres, com leasing atômico via `FOR UPDATE SKIP LOCKED` e heartbeat de worker.

Quando a auditoria de um incidente pede "quem escreveu isso, com qual prompt, a partir de qual estado", a resposta é uma consulta, não uma reconstrução de memória. Em uma arquitetura em processo, obter o equivalente exige instrumentar você mesmo cada chamada, o que a maioria dos times adia para sempre.

## Troca de agente sem reescrita

O `t25.yaml` define, por papel, uma lista ordenada de adapters, uma cadeia de fallback, não um adapter fixo. Se o Claude Code não está no PATH, ou responde com quota esgotada, o `selectAdapter()` tenta o próximo da lista. Um adapter novo é um `CliAdapterSpec` fino (nome do binário e `buildArgs`), não uma reimplementação de runtime: a infraestrutura de spawn, timeout e log é compartilhada em `src/adapters/base.ts`.

Esse é um benefício que só existe porque o agente é um processo externo.

## Custo e credencial ficam com você

Cada CLI roda autenticado com a sessão e a assinatura do usuário, na máquina dele. O T25 não pede chave de API de provedor, não proxy o tráfego dos modelos e não vê o conteúdo das conversas além da saída que parseia. Os eventos de `usage` que os adapters emitem no fim de cada run alimentam uma estimativa de custo por run (`src/core/cost.ts`), com taxas configuráveis, para visibilidade, não para cobrança.

Para um time que já paga pelos CLIs, isso muda a conta do projeto: a fábrica soma o custo de orquestração, não substitui o contrato dos agentes. E para segurança, significa que credencial nenhuma nova é criada, armazenada ou transmitida.

## O que essa escolha custa

Nenhuma arquitetura é de graça. Três custos são reais e convivem com a gente:

1. **Latência de spawn.** Cada estágio paga o boot de um processo CLI. Para tarefas curtas, isso é proporcionalmente caro. Nossa resposta é empacotar trabalho por estágio, o pipeline inteiro roda poucos dispatches por tarefa, e tratar o tempo de máquina como barato comparado ao tempo de revisão humana.
2. **Dependência de CLIs de terceiros.** Mudanças de versão, flags e formatos de saída quebram adapters. Mitigamos com parsers tolerantes (compatibilidade com saídas legadas), detecção de versão no boot e testes de contrato, mas a fragilidade existe e volta de tempos em tempos.
3. **Menos controle sobre o loop do modelo.** Quando o agente é uma biblioteca no seu processo, você enxerga cada chamada de tool, cada token, e pode intervir no meio do raciocínio. Quando é um processo, você governa as bordas, prompt que entra, saída e logs que saem, e o meio é território do CLI. Para governança isso chega; para pesquisa em comportamento de modelo, não.

Há casos em que o framework em processo é a escolha certa: protótipos, experimentos de orquestração, produtos cujo core é o próprio agente. Se o seu problema é "preciso de um loop de agente customizado dentro do meu serviço", uma biblioteca serve melhor. Se o problema é "preciso que uma equipe entregue software com agentes sem abrir mão de revisão, escopo, isolamento e merge humano", a fábrica processo-externo é o desenho que sustenta isso.

## Perguntas frequentes

### Orquestrar CLIs não é mais lento que chamar uma API diretamente?

É, por estágio: há boot de processo e materialização de prompt. A comparação justa não é por chamada, é por tarefa entregue. Uma chamada de API barata que produz código sem spec, sem worktree isolado e sem veredito auditável sai mais cara na revisão do que um dispatch com overhead de spawn.

### E se o CLI que eu uso sair do ar ou mudar radicalmente?

A cadeia de fallback por papel existe exatamente para isso. Como o contrato entre a fábrica e o agente é a saída parseada, trocar o executor de um papel não muda a política, o mesmo argumento do post sobre [o que é uma software factory](https://t25.io/blog/o-que-e-uma-software-factory-para-agentes-de-codigo/): a fábrica decide o que acontece com o código, quem escreve é substituível.

### O T25 roda os agentes com as minhas credenciais. Isso é seguro?

O T25 roda local, na sua máquina, com os CLIs que você já autenticou. Nenhuma credencial é enviada a um servidor nosso, o control plane é local, o Postgres é seu. O que a fábrica centraliza é política, não segredo. O isolamento por worktree e as regras de workspace detalham a parte defensiva.

### Dá para usar o T25 como biblioteca dentro do meu serviço?

Não é o desenho. O T25 é um harness standalone com API REST/SSE, CLI e dashboard sobre o mesmo `FactoryService`. Se você precisa embutir um loop de agente num processo seu, o que recomendamos é usar esse loop onde ele brilha e deixar com a fábrica o que exige governança, worktree, gates e merge.

### Onde leio mais sobre a arquitetura?

A [documentação](https://t25.io/docs/) tem o guia de arquitetura e o modelo de domínio, e o blog já cobriu o isolamento por worktree e o gate de aprovação em profundidade. O código do T25 é fechado; a documentação descreve o comportamento que citamos aqui.
