Versão
v1.2.24
O que foi entregue, em números
- 20
- PRs integrados
- 24,312
- Linhas adicionadas
- 3,586
- Linhas de código
- Código
- 3,586
- Testes
- 4,708
- Docs
- 16,018
- Soma · código + testes + docs
- 24,312
de 20 integrados
- bug 14 70%
- feature 0 0%
- polish 0 0%
- docs 3 15%
- groundwork 0 0%
- unclassified 3 15%
desde v1.2.23 · tag em
A carta
A carta do fundador, na íntegra
Oi,
A v1.2.24 não traz nenhuma funcionalidade nova, de propósito. Cada mudança nela conserta algo que quebrou em produção nos dois dias depois que a v1.2.23 foi lançada, boa parte disso primeiro na minha própria máquina.
O pior foi o cockpit. O guard do daemon de todo cockpit aberto matava e reiniciava o daemon compartilhado a cada 6–14 segundos, imprimia cada morte por cima da tela do cockpit, e nunca coletava (reaped) o que tinha matado — até 5.242 processos <defunct> lotarem a tabela de processos por usuário (#6351). O segundo problema era mais silencioso e igualmente ruim: abrir a janela de inspeção da ACE rodava claude --resume na própria conversa da ACE, o que inicia um segundo Claude em vez de se conectar ao primeiro, e em 2026-09-15 dois controladores agiram sobre uma mesma lane por horas (#6349).
Ao redor disso, quatro alarmes param de soar à toa: um aviso de "commits atrás" num binário promovido minutos antes (#6338), um "CHECKOUT IS DEAD" em todo deploy da main enquanto o checkout estava saudável (#6331), um soluço do GitHub num merge lido como recusa (#6353), e tickets terminados desenhados como épicos vazios (#6354). A fonte de destinatários do Supabase do Release Letters ganha a função de banco de dados que ela vinha chamando o tempo todo (#6341).
Depois que você atualizar, encerre e reabra todo cockpit aberto. Se algo aqui ainda quebrar para você, responda e me conte. Eu leio cada uma.
Um abraço, Alex
O daemon para de se matar
O guard de identidade de todo cockpit aberto comparava a build do daemon compartilhado com a build do cockpit que estava rodando. Um respawn sempre inicia o binário em disco, então um cockpit aberto antes de uma atualização nunca conseguia ficar satisfeito, e dois cockpits em duas builds ficavam se revezando para matar o daemon um do outro a cada 6–14 segundos. O guard agora compara contra a build instalada, lida a partir do próprio binário — seu carimbo do linker, ou sua revisão de VCS — e só recai na build de quem chamou quando isso não pode ser lido. Um cockpit mais antigo que a instalação é avisado disso em vez de ter qualquer coisa morta: ⚠ STALE COCKPIT — this cockpit runs vX, the installed FlowForge is vY — quit and relaunch (#6351).
Uma resposta lenta isolada não mata mais um daemon. Uma única resposta de saúde mais lenta que um segundo costumava contar como emperrado, então daemons ainda em inicialização eram mortos no meio do boot. Um daemon agora só é morto como sem resposta depois de três falhas seguidas cobrindo pelo menos 90 segundos, nunca enquanto tem menos de 45 segundos de vida, e o timeout do handshake é de 5 segundos. Só o guard ao vivo mantém essa contagem entre os ticks; um lançamento, uma nova tentativa pela tecla D ou uma chamada da CLI nunca mata por causa de uma única falha (#6351).
Daemons mortos nunca eram coletados (reaped), e uma sondagem de vivacidade kill(pid, 0) tem sucesso num zumbi — foi daí que veio o "ainda vivo depois do SIGKILL", e assim 5.242 processos <defunct> lotaram a tabela de processos por usuário. Todo daemon gerado agora é coletado, um zumbi conta como encerrado (lido a partir do estado do processo no macOS e no Linux), e o flowforge doctor ganhou uma linha daemon-zombies que fica vermelha quando um processo flowforge tui tem filhos <defunct>, nomeando cada cockpit e sua contagem (#6351).
E o guard para de escrever por cima da tela. Enquanto o cockpit roda, qualquer coisa escrita no stderr vai para ~/.flowforge/logs/tui-<repo>.stderr.log e é restaurada quando o cockpit sai; as próprias linhas do guard vão para ~/.flowforge/logs/daemon-guard.log (#6351).
Uma ACE, nunca duas
A ACE do cockpit é headless — um processo de stream sem pane — e sua janela de inspeção rodava claude --resume <sessão da ACE>. Isso não se conecta à ACE que está rodando: inicia um segundo Claude com as mesmas ferramentas sobre uma cópia da conversa. Em 2026-09-15 isso deixou a ACE e seu fork agindo os dois como controlador numa mesma lane por horas (#6349).
A janela agora decide uma coisa quando abre: já existe um processo rodando essa sessão? Se o transporte do cockpit está de pé, ou qualquer processo Claude vivo nomeia a sessão, ou a tabela de processos não pode ser lida, a janela abre uma visão somente leitura que acompanha a transcrição da ACE conforme ela é escrita. A borda dela mostra ⎋ READ-ONLY live ACE view · type in the cockpit rail, sua primeira linha diz que nada digitado ali chega até a ACE, avisos de job em segundo plano aparecem como ⚙ background job finished: … em vez de como suas próprias palavras, e Ctrl-C fecha a janela de forma limpa. O claude --resume é recusado, e o log de atividade nomeia os pids vivos. Só quando nada está rodando é que a janela retoma a sessão como antes (#6349).
O flowforge doctor ganhou uma linha claude-session-fork: vermelha quando qualquer sessão do Claude tem dois ou mais processos vivos, nomeando cada pid e como ele segura a sessão, e ⚠ unknown quando a tabela de processos não pode ser lida (#6349).
A janela também era difícil de achar. Ela até aterrissava na janela 1, mas a barra superior não mudava, nenhuma aba acendia, e nada podia ser clicado ou alcançado por ciclo — então depois que você clicava para outro lugar, Ctrl-b w era o único caminho de volta, a menos que você soubesse o dígito de prefixo. Agora ela tem sua própria aba ^b1 ACE, presente só enquanto a janela existe, acesa enquanto você está nela, clicável, e uma parada no ciclo Alt-←/→ (#6344). Um alvo de tmux sem prefixo caía para um prefixo de nome, então uma janela só de nome parecido com ace-inspect-x podia ser sondada, pré-morta ou coletada na inicialização do cockpit no lugar da janela de verdade; agora cada um desses alvos casa exatamente (#6344, #6347).
Sinais que param de soar à toa
"N commits atrás" num binário atual. Um binário promovido minutos antes lia 3 commits behind — cut a release: os três commits eram merges — uma sincronização de down-merge e o corte da versão — que não mudaram nenhum arquivo. O aviso agora pergunta se a árvore é diferente antes de chamar um binário de obsoleto, tanto no caminho da lane de release quanto no da build de desenvolvimento, e ainda conta commits quando de fato é o caso (#6338).
"CHECKOUT IS DEAD" em todo deploy da main. O gate de checkout do deploy do site prova que o handler de checkout recebe corpos de requisição em duas camadas: as próprias respostas do handler a sondas que dependem do corpo, e uma contagem de invocações do CloudWatch. O papel (role) do deploy nunca recebeu a permissão de leitura do CloudWatch, então toda chamada era negada — e o script transformava uma leitura negada em invocations=0, um alarme de receita por causa de uma permissão de monitoramento. Uma leitura negada ou com erro, ou uma leitura bem-sucedida sem nenhum ponto de dado enquanto o handler respondia toda sonda, agora é relatada como não consegue verificar, e o veredito recai sobre as respostas do handler que dependem do corpo. Uma queda real — o handler não recebendo o corpo, como no #4770 — ainda falha o gate (#6331).
Um soluço do GitHub no merge agora tem nova tentativa, em vez de aparecer como recusa. Um erro transitório do GitHub no passo de merge — um "Something went wrong" do GraphQL, um HTTP 5xx, uma conexão derrubada, um transporte travado, o limite secundário de taxa — era lido como Unrecognised refusal e pedia para um humano repassar um erro que não precisava de humano nenhum. Agora ele tem até quatro novas tentativas, com backoff de 2 s, 4 s e 8 s, e a cabeça do pull request é relida antes de cada tentativa: uma cabeça que se moveu interrompe a nova tentativa em vez de mesclar um commit que ninguém reconferiu. Uma recusa de verdade é devolvida já na primeira tentativa e nunca tem nova tentativa. Quando as tentativas se esgotam, o cockpit nomeia a queda do lado do GitHub e a contagem de tentativas (#6353).
Um ticket fechado sem filhos aparece como CONCLUÍDO. Seis tickets únicos e fechados carregando uma marca [META] apareciam na War Room como épicos a 0%, com a mensagem "nenhum ticket neste board — aperte U num ticket para adicionar um". Um épico fechado e sem filhos agora renderiza 100%, ✓ 1, (closed, done), e sua linha de nota nunca mais oferece para adicionar um ticket. Um épico aberto e sem filhos — um contêiner esperando seu primeiro filho — continua igual, e um ticket [META] com um filho de verdade continua sendo um épico (#6354).
Um merge podia fechar um ticket que ainda era dono de outro PR aberto. Quando a leitura do GitHub feita pelo guard de posse falhava — um rate limit, um 5xx, uma conexão derrubada — ele tratava a falha como "ninguém é dono deste ticket" e deixava o merge fechá-lo mesmo assim, sem nada no log, no Result ou no ticket dizendo que o guard nunca rodou. Agora ele falha fechado: uma leitura que falhou bloqueia todo auto-close que aquela execução faria, nomeia a causa no Result, no log e como comentário em cada ticket que teria fechado, e o internal/autoclose entrou no perímetro do wall degradehonesty, para que uma futura degradação silenciosa ali seja pega por construção, não por memória (reportado por um cliente, #6360).
Release Letters: destinatários numa única função fixa
O flowforge marketing send não conseguia ler destinatários de uma fonte Supabase. O adaptador chamava POST /rest/v1/rpc/flowforge_marketing_recipients, nenhuma migração jamais tinha criado essa função, a produção respondia 404 PGRST202 — e o flowforge marketing get ainda dizia ready (#6341).
A migração 0077_marketing_recipients_rpc.sql cria public.flowforge_marketing_recipients() como uma função fixa e sem parâmetros: stable, security definer, um search_path vazio, execução revogada de todo papel (role) e concedida só ao service role. Ela devolve contas confirmadas e não excluídas com seu estado de consentimento, e nunca um endereço que esteja recusado no momento ou que já tenha cancelado a inscrição alguma vez — esses ficam no banco de dados em vez de atravessar a rede. A migração recusa fazer commit se qualquer atributo ou concessão estiver errado, ou se existir uma sobrecarga (overload) que aceite argumentos (#6341).
A alternativa — rodar uma consulta SQL configurada dentro de uma transação somente leitura — foi rejeitada na revisão de segurança: somente leitura não é uma sandbox, e uma consulta rodada desse jeito ainda pode ler segredos e travar outras sessões. Então o adaptador não envia consulta nenhuma, recusa uma marketing.recipients.supabase.query configurada, e a prontidão lê partial enquanto essa chave estiver definida. Uma função ausente é nomeada junto com a migração que a cria, um resultado que o servidor truncou é recusado em vez de enviado incompleto, e o flowforge marketing preflight nomeia a função ausente na sua checagem recipients.count (#6341).
Colado
Colado é um termo do jiu-jitsu — uma posição está colada quando está justa, perfeitamente ajustada, sem folga, pressão máxima. É o nome para a família de gates da FlowForge que não deixam espaço nenhum entre a regra e o trabalho. Esta versão fecha estas lacunas:
- Abrir a janela de inspeção da ACE não pode mais iniciar uma segunda ACE — retomar é recusado enquanto a ACE roda, e uma tabela de processos ilegível conta como estando rodando (#6349).
- Uma nova tentativa de merge não pode mais aterrissar um commit que ninguém reconferiu — a cabeça é relida antes de cada nova tentativa, e uma cabeça que se moveu interrompe (#6353).
- A leitura de destinatários não pode mais rodar uma consulta que alguém configurou — o conjunto é fixo numa única função revisada, e uma consulta configurada é recusada (#6341).
- Um guard não pode mais matar um daemon por causa de uma resposta lenta isolada — três falhas ao longo de 90 segundos, nunca dentro da tolerância de boot (#6351).
Coisas singelas que mudam tudo
Nem toda mudança merece um grande destaque. Cada uma cabe numa linha, e você a sente todo dia.
- A aba da ACE acende enquanto você está na janela de inspeção, e um clique te leva de volta (#6344).
- Um binário recém-promovido para de te dizer para cortar uma versão (#6338).
- Tickets terminados param de aparecer como épicos vazios na War Room (#6354).
Nos bastidores
- Um teste instável (flaky) que recusava o terceiro corte da v1.2.23 está corrigido. Um dublê de teste (test double) compartilhado entre o fan-out do company-board era escrito de forma concorrente sem trava; agora está protegido. Nenhum código de produção mudou (#6329).
Um passo de configuração
- Encerre e reabra todo cockpit aberto depois que você atualizar. Um cockpit iniciado num binário mais antigo mantém o guard antigo até reiniciar, então o loop de mortes só termina de vez quando todo cockpit aberto estiver rodando essa build. O aviso
⚠ STALE COCKPITe a linhadaemon-zombiesdo doctor apontam para qualquer um que ainda não esteja (#6351). - Uma fonte de destinatários no Supabase precisa da função de destinatários naquele projeto, e nenhuma chave
supabase.query. Aplique a migração0077_marketing_recipients_rpc.sql(ela precisa da0070primeiro), removamarketing.recipients.supabase.queryse você tiver definido uma, e oflowforge marketing test recipientsdeve então passar com uma contagem (#6341).
Ainda não
- O papel (role) do deploy ainda não consegue ler a métrica de checkout. Até que essa permissão seja aplicada ao papel, a metade do CloudWatch do gate relata não consegue verificar em todo deploy e o veredito recai sobre as respostas do handler; uma queda real ainda falha (#6331).
- Um fork criado depois que a ACE roda
/clearnão é agrupado pela linha do doctor. A própria janela de inspeção ainda recusa fazer fork nesse caso; só a linhaclaude-session-forkpode deixar de perceber isso (#6349).
Agentes
Nenhuma mudança de elenco nesta versão: a FlowForge instala os mesmos 44 especialistas que a v1.2.23 entregou (git diff --name-status -M v1.2.23 origin/release/1.2.24 -- v3/packages/core/agents está vazio).
Atualizar
flowforge updatenuma instalação já existente, oucurl -fsSL https://get.flowforgesoft.com/install.sh | shpara uma nova. Os dois caminhos gravam o mesmo binário de release em/usr/local/bine~/.flowforge/binjuntos. Depois, encerre e reabra todo cockpit aberto.
Os números desta página são uma leitura pontual de v3/site/src/data/release-stats.json, chave "v1.2.24" (computed_at: 2026-09-17T02:36:34Z), intervalo v1.2.23 (5002fc80d, com tag em 2026-09-15T02:16:30-04:00) → lane head 96726ce42 — a v1.2.24 ainda não tem tag, e estas cifras serão re-derivadas na tag antes do corte.