Armadilhas comuns
Antipadrões que silenciosamente derrotam o scale-to-zero e a segurança de agentes — e como evitá-los.
Esses padrões não dão erro. Eles passam no CI, parecem razoáveis em um diff e silenciosamente cancelam uma garantia pela qual você está pagando — economias de scale-to-zero ou segurança de agentes. Cada um abaixo: o que acontece, por que dói e a correção.
Código keep-alive derrota o scale-to-zero
Um agente "prestativamente" adiciona código que toca no banco de dados em um temporizador. Cada consulta periódica conta como atividade e reinicia o temporizador de suspensão, então um endpoint que você projetou para escalar a zero nunca dorme — e a ociosidade deixa de ser gratuita.
Ele chega em várias formas. Um heartbeat de frontend ou backend:
// runs forever, holds the compute awake forever
setInterval(async () => {
await pool.query("SELECT 1"); // "health check"
}, 30_000);Um monitor de uptime externo apontado para um endpoint cujo handler por acaso consulta o BD — então cada probe acorda o compute:
app.get("/health", async (_req, res) => {
await pool.query("SELECT 1"); // monitored every 60s → never idle
res.send("ok");
});Ou configurações de pool de conexões que mantêm uma conexão viva com consultas de
validação — um min acima de zero, ou um "warmer" serverless:
const pool = new Pool({
min: 2, // keeps ≥2 connections open + validated
// a validation/keep-alive query on each idle connection resets the timer
});Correção: audite os diffs escritos por agentes em busca de acesso periódico ao banco de dados. Mantenha as verificações de
liveness na camada HTTP — faça probe da aplicação, não do BD — e deixe o pool drenar
para zero (min: 0). A visão de compute do console mostra por que um endpoint não está
escalando a zero, o que revela um heartbeat perdido rapidamente.
Entregar ao agente as credenciais de main curto-circuita o ASCC
Dar a um agente a string de conexão da branch main — ou deixá-lo executar keon
autenticado como seu próprio usuário — contorna tudo o que o ASCC impõe: isolamento de
sandbox, orçamentos LEASH, verificações LINT e BLAST, portões de promoção, atribuição
e âncoras de undo. O agente agora está gravando produção diretamente, e nada do
laço que torna as mudanças de agente seguras jamais roda.
O agente nunca deveria portar uma credencial capaz de gravar em main. A promoção é
no lado do servidor por design — cp é o único gravador em produção.
Correção: cunhe uma chave de API com escopo de agente (capacidade = agent, escopo = um
projeto, expiração curta) em Settings → API keys, entregue ao agente apenas strings de
conexão de sandbox, e remova credenciais mais fortes do seu ambiente
(sem DATABASE_URL completo, sem ~/.pgpass, sem sessão keon autenticada no mesmo
shell). Veja Entregue a um agente uma credencial com escopo e a
seção SCOPE de Guardrails.
Tempestades de retry em torno do despertar
A primeira conexão após uma suspensão precisa acordar o compute, então ela é mais lenta que uma quente. Um agente que vê uma única conexão lenta pode "consertá-la" — com timeouts de cliente agressivos e laços de retry que martelam o endpoint, ou adicionando um keep-alive (armadilha #1) para que ele nunca tenha de acordar. Ambos trocam as economias para encobrir um custo pontual.
Correção: defina um connect_timeout de cliente em torno de 10s e seja paciente na primeira
conexão. O cold start é sub-segundo na mesma região quando os caminhos quentes estão em jogo — uma
conexão levemente lenta é esperada, não uma falha para se contornar com engenharia.
postgresql://…?connect_timeout=10Credenciais commitadas no código
Agentes fixam strings de conexão e chaves de API no código-fonte e em arquivos .env
que então chegam ao git. Chaves vazadas são colhidas em questão de horas após atingirem um
repositório público (ou que se tornará público depois) — a janela é medida em horas, não dias.
Correção: injete segredos pelo ambiente, nunca os comite. Mantenha arquivos .env
fora do git. Em qualquer vazamento, rotacione imediatamente em Settings → API keys
— revogue a chave exposta e cunhe uma nova.
DDL direto em main contorna o laço de promoção
Rodar uma migração direto contra main pula a captura que torna uma
mudança reversível e auditável. Você perde a migração up/down emitida (EMIT),
a entrada de ledger assinada (LEDGER) e a âncora de undo (UNDO). O esquema
mudou, mas não há registro que a plataforma possa reexecutar ou reverter.
Correção: sandbox → promote, mesmo para uma mudança de esquema "trivial". O laço de promoção
é o que produz a migração, a entrada de ledger e a âncora; um ALTER
direto não produz nenhum deles.
Gravações dentro de uma transação revertida ainda aterrissam
A captura registra as instruções que o agente executa — ela não tem
consciência de limites de transação. Instruções executadas dentro de um BEGIN … ROLLBACK
ainda são capturadas, e se aquele sandbox for promovido, elas são reexecutadas e
aplicadas. Um ROLLBACK no sandbox não as descaptura.
BEGIN;
UPDATE users SET plan = 'pro'; -- captured
ROLLBACK; -- does NOT remove it from the captureCorreção: não confie no ROLLBACK dentro de um sandbox para "desfazer" trabalho exploratório.
Se você quer o trabalho eliminado, descarte o sandbox — esse é o caminho de descarte que a captura
respeita.
Estacionar trabalho em um sandbox além do seu orçamento ou TTL
Sandboxes são efêmeros. Quando um sandbox esgota seu orçamento LEASH ele é
autodescartado (status=discarded), e qualquer trabalho não promovido nele é perdido.
Tratar um sandbox como uma branch de vida longa à qual você "vai voltar" é como as
mudanças de um dia desaparecem.
Correção: promova-o, ou recapture o trabalho em um sandbox novo. Trate sandboxes como descartáveis por padrão — o artefato durável é o promote, não o sandbox.
Interpretar mal as garantias
Três armadilhas específicas no que as verificações de fato prometem:
-
O ensaio do raio de destruição é consultivo. Ele mede o que um promote faria — locks, durações, contagens de linhas reais — mas nunca condiciona o promote. Um ensaio limpo é informação, não uma luz verde; e se um ensaio é exigido antes do promote é decisão da POLICY, não do ensaio.
-
Approve não pode substituir um block de política. Um Approve humano nunca pode deixar passar um
blockde política ou uma instrução fora da faixa. A única forma de passar por um block é uma mudança de política auditada — não há interruptor de substituição manual, por design. -
Uma bifurcação mascarada mascara valores, não formato. Em uma bifurcação mascarada os valores são mascarados, então DML dependente de dados que o agente escreve contra esses valores mascarados não corresponderá às linhas reais no pai no replay. O trabalho de faixa de esquema é independente de valor e reexecuta fielmente; DML dependente de valor desenvolvido contra dados mascarados não.
Bifurcações mascaradas
Dê a um agente (ou a um humano) o formato da produção e nada da sua PII — políticas de mascaramento, a biblioteca de funções embutidas e como branches e sandboxes mascarados permanecem selados até o mascaramento ser confirmado.
Entregue a um agente uma credencial com escopo
Provisione uma chave de API com escopo de capacidade, projeto e expiração e execute um agente de IA contra seu banco de dados com segurança.