Contacto
Gabs

Caso de estudo de Gabriel Espinheira, engenheiro full-stack sénior

Relay, o meu próprio harness de IA

Uma app para macOS onde modelos de quatro fornecedores escrevem, desenham, falam e renderizam numa só pasta de projeto, com planos que já pago. Construí-a a dirigir agentes de programação: primeiro commit a 28/09/2026, v0.9.0 a 08/10/2026.

Uma ferramenta privada que só corre no meu Mac. Disponível para emprego a tempo inteiro e contratos.

O que o repositório diz

236

commits em 12 dias, a partir de 28/09/2026

7

versões marcadas em quatro dias, até à v0.9.0

163.636

linhas de TypeScript de produto

3.983

testes declarados em 543 ficheiros

73

ferramentas que os agentes chamam por MCP

4

agentes de chat, mais 9 geradores de media

Contado a partir do repositório a 09/10/2026, no commit d0aa1e5. Os números de testes são declarações no código, não uma execução de testes.

Bons modelos, nenhum estúdio

O meu estúdio, a SharpHaw, faz imagens, vozes, filmes e textos para clientes todas as semanas. O modelo que quero para cada trabalho está numa app diferente. Um modelo sozinho esquece o prompt por trás da imagem da semana passada, não consegue manter uma personagem igual entre planos e nunca olha para a frame que acabou de renderizar.

O Relay é o harness à volta desses modelos. Acrescenta cinco coisas que os modelos sozinhos não têm, e cada funcionalidade desta página responde a uma delas.

Estrutura
traços e briefings transformam-se no prompt
Proveniência
cada ficheiro guarda o prompt exato que o criou
Consistência
uma personagem ou uma voz mantém-se igual entre planos
Escolha
mudar de modelo a meio da sessão, criar ramos, comparar versões
Feedback
os agentes renderizam e inspecionam as suas próprias frames

Um projeto, todos os meios

Um projeto é qualquer pasta no meu Mac. O Relay guarda as sessões, os prompts e os media numa pasta relay/ ao lado dos ficheiros. Cada filme abaixo é uma gravação real do Relay 0.9.

01

Todos os agentes num só compositor

O Claude Code, o Codex, o Grok e o Antigravity correm atrás de um só contrato de fornecedor, cada um no seu próprio CLI com sessão iniciada. O Claude Code corre através do Agent SDK e o Codex através de um app server partilhado. O Grok e o Antigravity partilham um único adaptador Agent Client Protocol. Mude de agente a meio da conversa e o novo recebe a transcrição, cortada para caber em 24.000 caracteres.

A página Router do Relay: os modelos que o Auto pode escolher, agrupados por agente com os seus preços, e a primeira e a segunda escolha da tabela de encaminhamento por tarefa.
02

O Auto escolhe o modelo para cada mensagem

Antes de cada vez no Auto, um modelo encaminhador escolhe o agente, o modelo e o esforço. Escolhe entre os modelos que ativei, conforme a rapidez com que cada subscrição está a ser gasta e uma tabela de encaminhamento feita a partir de benchmarks públicos. A resposta só conta se indicar uma dessas opções, e qualquer falha corre a vez no modelo por defeito.

03

Um estúdio onde o prompt fica com o ficheiro

Um artefacto é um ficheiro markdown cujo corpo é o prompt. Cada geração escreve um registo de execução ao lado do resultado, com o prompt, os parâmetros, o modelo e o tempo, para que qualquer imagem ou voz possa ser rastreada e refeita. As personagens constroem-se a partir de traços, e escrever o nome de uma personagem num prompt anexa a sua ficha.

04

Filmes escritos em código

Um filme é uma página HTML cuja função de seek pinta qualquer frame como função pura do tempo, com molas resolvidas em forma fechada. O Relay pré-visualiza-o ao vivo, renderiza-o com oito subframes de motion blur e mistura o som a −14 LUFS. Os agentes fazem lint aos seus próprios filmes, provam que cada frame se renderiza igual duas vezes e leem folhas de contacto do resultado.

05

Quadros onde os agentes desenham

Os canvas são quadros Excalidraw com cartões para artefactos, sessões e links. Os agentes leem-nos e desenham neles com seis ferramentas, e as minhas edições e as dos agentes fundem-se elemento a elemento. O quadro deste filme é o plano desta página, desenhado por um agente a partir de um só prompt.

Uma rotina no Relay: as suas instruções, um horário de dias úteis às 09:00 em Lisboa, as próximas cinco execuções e exatamente o que o agente recebe.
06

Trabalho que corre sem mim

As rotinas correm instruções num horário dentro de um projeto, e o agendador lê o relógio a cada 15 segundos porque os temporizadores param quando um Mac adormece. Os subagentes dividem o trabalho de uma sessão entre agentes e reportam de volta. Os Bits são assistentes sempre ativos com memória própria. Só alteram um projeto através de uma tarefa, e só com o acesso que lhes dou.

Um daemon, clientes finos

Um só processo Node é dono de todos os projetos, sessões, agentes e ficheiros. O browser e a app Electron só o mostram. Os comandos seguem por HTTP, e cada alteração volta como um evento, reproduzido a partir de um buffer de 10.000 quando uma janela volta a ligar-se. Fechar todas as janelas nunca para uma vez.

O estado da app vive em SQLite, integrado no Node, com pesquisa de texto completo mantida em sincronia por triggers. O conteúdo dos projetos vive em ficheiros: prompts, registos de execução, filmes e gravações ficam na pasta do projeto, onde qualquer agente os pode ler.

Um registo de execução é um ficheiro, não uma linha.
  • Separadores do browser
  • A app Electron

Comandos HTTP, eventos SSE

Daemon do Relay · API Hono, registo de eventos, ferramentas MCP, terminais

Agentes · Claude Code, Codex, Grok, Antigravity

Geradores · GPT Image, Qwen3-TTS, ElevenLabs, Stable Audio, movimento

SQLite para o estado da app

Pastas de projeto para o conteúdo

Construído como se a web fosse hostil

O daemon corre agentes que executam comandos, numa porta a que qualquer página web chega. O README diz a todos os agentes para tratarem a web e o output dos modelos como hostis, e estas são as defesas que daí resultam.

Só local

O daemon liga-se a 127.0.0.1, aceita apenas o seu próprio cabeçalho Host contra DNS rebinding e rejeita pedidos de outros sites. Cada chamada precisa de um cookie HttpOnly ou do token de arranque, comparado em tempo constante.

Os agentes têm as suas próprias chaves

O endpoint MCP do Relay só aceita tokens por sessão guardados em memória, nunca o token de arranque. Os subagentes não podem delegar mais, e o conjunto de ferramentas de um Bit não tem shell nem ferramentas de projeto.

Os ficheiros ficam no projeto

Cada caminho é resolvido dentro da raiz do projeto antes de uma leitura ou escrita, nada escreve em .git, e o que se apaga vai para o Lixo.

O browser espera por uma pessoa

No browser do próprio Relay, as submissões, envios, pagamentos e eliminações de um agente esperam pelo meu Permitir, e o Permitir recusa se a página mudou entretanto.

Só endereços públicos

As pré-visualizações de links e os pedidos dos agentes só chegam a endereços públicos, verificados de novo na resolução DNS para que o rebinding não os troque.

Os filmes escritos por agentes correm em sandbox

Um filme é JavaScript escrito por um agente, por isso carrega numa frame em sandbox através de URLs de capacidade assinados por arranque e que expiram ao fim de seis horas.

Uma sessão do Codex no Relay a pedir autorização antes de correr um comando de shell: o comando, e depois Allow once, Allow for this session e Decline.
No modo de pedido, um agente espera por mim antes de correr um comando. Uma sessão real do Codex a rever um componente deste site.

As verificações que cada alteração tem de passar

As instruções do repositório dizem a todos os agentes de programação para escreverem primeiro o teste que falha. Uma alteração está feita quando passam o lint, os tipos e o conjunto completo de testes, e o hook de pre-push corre os três em cada push.

Um agente falso para testes reais

Um fornecedor falso e determinístico, guiado por palavras-chave no prompt, corre vezes inteiras de ponta a ponta sem gastar um crédito: ferramentas, aprovações, perguntas e falhas.

Regras de design como teste

Um teste aplica o sistema de design com sete listas de exceções de contagem exata. Um novo controlo em bruto fá-lo falhar, e também corrigir um antigo sem baixar a contagem.

Planos que admitem desvios

Cada um dos 30 planos de design termina com o que foi construído, como foi verificado e onde a construção se afastou do plano. O código ganha ao plano.

Uma versão recusada passou a barreira

A 08/10/2026, um merge de versão chegou ao main sem o seu commit de versão, e o fluxo de publicação recusou marcá-lo e abriu uma issue. Seguiu-se uma nova verificação nos pull requests de versão, e a v0.9.0 saiu cerca de dez minutos depois.

Doze dias, um autor, muitos agentes

Todos os commits têm o meu nome, e o trabalho aconteceu em sessões de agentes. Os meus registos locais têm cerca de 250 sessões do Claude Code e 164 do Codex no repositório. O Relay corre sessões sobre si próprio desde 30/09/2026.

Sou responsável por código que não escrevi através do que o rodeia. Cada funcionalidade grande começa com um plano escrito, todos os agentes leem as mesmas regras e as verificações acima têm de passar antes de qualquer push.

O primeiro código do Relay chegou quatro horas depois do esqueleto, num commit de 17.941 linhas com 216 testes. Os filmes dos casos de estudo SharpOS e Taste Vault foram compostos e renderizados no motor de movimento do Relay e depois codificados para a web.

Commits por dia de 28/09 a 09/10/2026, sem merges. O dia mais alto, 01/10, teve 50.
Commits por dia de 28/09 a 09/10/2026, sem merges. O dia mais alto, 01/10, teve 50.
DiaCommits
28/093
29/0919
30/0918
01/1050
02/1017
03/1017
04/1018
05/1027
06/1030
07/1024
08/107
09/106

Uma decisão difícil: filmes como função do tempo

Problema

Os agentes tinham de fazer motion graphics com texto, interface e marca exatos, e pré-visualizá-los ao vivo. Cada frame tinha de sair igual todas as vezes, para que as renderizações, as verificações e as correções fossem de confiança.

Opções

Um modelo de vídeo não consegue manter texto exato nem uma marca, e cada correção obriga a pagar uma nova geração. O Remotion precisa de um bundle React por filme e de uma licença de empresa paga para o meu estúdio.

Escolha

Uma página HTML com uma função de seek e um pequeno motor incluído no código. Cada valor animado é uma mola amortecida avaliada no instante t, sem estado entre frames, para que qualquer frame possa ser pintada por qualquer ordem. Isso torna possíveis o motion blur, a renderização em paralelo e uma verificação de determinismo.

Temporizadores, animation frames, transições CSS e números aleatórios ficam proibidos pelo lint.

Custo

Os agentes têm de escrever num estilo restrito e passar o lint. A renderização é offline e pesada: os filmes deste site levaram 2 a 8 minutos cada a 1080p, e 83 a 131 minutos com supersampling a dobrar.

Uma decisão mais pequena: um daemon Node num repositório Bun

O repositório corre em Bun, mas o daemon corre em Node com o seu SQLite integrado, porque o Claude Agent SDK e o conjunto de testes visam o Node. O custo são dois runtimes num repositório, nenhuma API do Bun no servidor e uma API experimental de SQLite guardada atrás de uma só função.