Commit Graph
4861 Commits
Author SHA1 Message Date
Ian Couto 0f4638e6fd ci(fork): a develop volta a publicar as imagens na VPS
O sync com o upstream apagou o workflow que constrói :develop e faz pull na HostGator. Sem ele o push na develop não gera artefato e o servidor fica na versão antiga sem erro.
2026-09-17 08:03:32 -03:00
Rafael Melgaço de55029e69 Merge pull request #1015: lote 12 da triagem — 16 PRs de funil, leads, inbox, tags e agenda
Lote 12 da triagem: 16 PRs de funil, leads, inbox, tags e agenda
2026-09-16 21:45:51 -03:00
PessoaandClaude Opus 5 7b8713ff8e test(qa): a spec do Inbox segue o rótulo novo do botão de tags
O e2e do CI reprovou em qa-l12-inbox.spec.ts:247 esperando 420 s por um botão
chamado "Tag". O rótulo mudou: o #852 (@rafaelbatistazz), que entrou pela main
enquanto o lote 12 era montado, renomeou o botão para "Tags do contato" — 'o botao
de tags do contato diz de quem e a tag'. A spec foi escrita contra 778d1dcb2, antes
disso.

É interação entre lotes, e foi o CI sobre o candidato integrado que a pegou: a prova
em tela do QA rodou no SHA antigo, onde o rótulo ainda era o outro.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-16 21:13:18 -03:00
PessoaandClaude Opus 5 7a7c5e0725 ci(e2e): a parte 1 estourou o teto — navegacao e prova-painel-provedores voltam para a parte 2
Medido no run 35160549180 deste PR: a parte 1 foi cancelada aos 30 min cravados,
no caso 141 de 156, ainda passando testes — teto de tempo do job, não reprovação.
Parte 2: 15 min. Parte 3: 22 min.

A causa não são as specs novas de QA: as três que caíram na parte 1 somam 5 casos e
31 s. É o crescimento dos casos acrescentados às specs que já estavam lá (o arrasto
duplo do #919 em kanban-owner-filter, entre outros).

O bloco de comentário do próprio arquivo já nomeava prova-painel-provedores (199,4 s,
42% da parte 1) como a alavanca seguinte, e a previsão anterior (21,6 / 22,4 min) não
se confirmou — a folga está toda na parte 2. As duas specs mais caras vão para lá:
tirar 438 s da parte 1 devolve ~7,3 min, deixando ~22 min contra ~23 min na parte 2.

Não subi timeout-minutes de propósito: o teto é o que denuncia o crescimento.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-16 20:42:19 -03:00
Rafael Melgaço 491a726a3e Merge pull request #1018: as três decisões do dono sobre fork, autoria e PR parcial
docs(triagem): três decisões do dono sobre branch de fork, autoria em porte e PR parcial
2026-09-16 20:01:24 -03:00
PessoaandClaude Opus 5 2a847f6f0c Merge main (v1.30.0) no lote 12
A base andou com o lote 14, o revert do #1000, os guias do assistente (#927) e o
corte da v1.30.0. Entra por merge, como a doutrina pede para a base que muda durante
o lote; a prova do lote é refeita por cima, pelo CI do PR.

Conflito, um só, em `lib/i18n/dicionario.ts`: os dois lados acrescentam entradas
antes do `};` final, e o fecho é linha comum fora do bloco de conflito. Resolvido
ficando com os DOIS lados, na ordem — o vocabulário de etiquetas do lote 12 e as
frases do acervo vindas da main. Medido depois de resolver: 5.180 chaves, zero
duplicadas (chave repetida quebraria o objeto milhares de linhas abaixo, e só o tsc
veria).

DESKCOMM_GOV_INVARIANTS_EDIT=1 usado com medição: o merge traz
`tests/invariants/followup-reactivity.test.ts` e `tests/invariants/gov-4-routing.test.ts`,
e os dois são IDÊNTICOS à origin/main (`git diff origin/main -- <arquivo>` vazio nos
dois). Ou seja, não é edição de invariante deste lote: é a main entrando. Os
invariantes que o lote de fato escreveu já entraram nos merges dos grupos.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-16 20:01:17 -03:00
Rafael Melgaço 59aa195819 Merge pull request #1022 from melgarafael/release/1.30.0
Release 1.30.0
v1.30.0
2026-09-16 19:52:23 -03:00
deskcomm-release[bot] bbb7b9b07a release(1.30.0): a versão montada a partir dos fragmentos declarados 2026-09-16 22:26:20 +00:00
Rafael Melgaço e1f5e7acf9 Merge pull request #927 from melgarafael/fix/guias-alcance-e-yaml
fix(skills): os guias do assistente em qualquer pasta, e dois cabeçalhos que CLI estrito descartava
2026-09-16 19:21:47 -03:00
Rafael Melgaço d46655ed97 Merge pull request #1021 from melgarafael/fix/segurar-acervo-do-whatsapp-ate-decisao-999
revert: o acervo do histórico do WhatsApp (#1000) sai da main até a decisão da #999
2026-09-16 19:21:43 -03:00
PessoaandClaude Opus 5 74cd922169 docs(triagem): delegar o merge não delega fechar PR, e a receita do 8-0 sai do merge-base
Última passada da conferência (critério: só o que levaria a agir errado):

- Lembrete 4 do comando e a fronteira: a exceção 'o mantenedor delega o merge'
  suspendia a fronteira inteira, e logo abaixo o comando descreve o fechamento do PR
  parcial. Um agente com merge delegado concluiria que pode fechar. A delegação vale
  para o que ela nomeia.

- 8-0: a receita comparava a árvore do PR com a main de hoje (origin/main..head e
  git checkout head -- .). Isso lista como 'apagado pelo autor' todo arquivo que a
  main criou depois da base do PR, e devolve à versão velha todo arquivo que a main
  mudou no intervalo. Com o commit saindo com --author do contribuidor (D2), as
  reversões iriam para o nome dele. A receita nova aplica o diff a partir do
  merge-base, excluindo o arquivo que não entra, com --3way. Provado num repositório
  descartável: a receita antiga mandava replicar a deleção de um arquivo criado pela
  main e revertia outro; a nova traz exatamente o que o autor mudou, apagou e criou, e
  preserva o que a main ganhou.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-16 18:52:52 -03:00
PessoaandClaude Opus 5 a278f3de18 revert: o acervo do histórico do WhatsApp (#1000) sai da main até a decisão da #999
O #1000 (@webtecnica) liga `noweb.store { enabled, fullSync }` em toda
sessão nova do WAHA. A triagem de issues registrou isso como decisão de
produto pendente com o mantenedor (Decisão PRs - rafael/issue-999) e
pediu no #1011, às 20:53Z, que o #1000 ficasse segurado até a resposta:
o histórico guardado no WAHA tem dado pessoal que a anonimização do CRM
não alcança, e disco e memória da sincronização completa não foram
medidos. O lote 14 foi mesclado às 21:19Z sem que a triagem de PRs
relesse o aviso — o erro é da triagem, não do contribuidor.

Nenhuma versão foi publicada com ele: o PR de release da 1.30.0 (#1020)
foi fechado antes do merge. Este revert devolve o comportamento da
v1.29.0 (sessão nasce sem store), que é o que está em toda VPS hoje, e
leva junto o teste e o fragmento do #1000, que descrevem o comportamento
retirado. O trabalho segue no histórico (bf7e34dd2) e volta pela
resposta do mantenedor: reverter este revert, ou ajustar para a opção
escolhida.

Medido: `git revert -m 1 c0ae38d9f` sem conflito; o conteúdo de
lib/waha/client.ts volta a ser o do primeiro pai do merge (diff vazio,
conferido abaixo no corpo do PR).

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-16 18:49:17 -03:00
PessoaandClaude Opus 5 98869c78e0 docs(triagem): o critério da proveniência é tudo ter entrado, não a sobrevivência de arquivo
Rodada final da conferência, só com o que levaria um agente a agir errado:

- 8-quinquies: a sonda de sobrevivência decidia entre proveniência e PR parcial. Ela
  não mede se o conteúdo entrou: git cat-file -e dá verdadeiro para arquivo que já
  existia na main, e um PR que só modifica arquivos existentes sai com 100% mesmo sem
  nada ter entrado — o agente faria -s ours num PR parcial e fecharia como incorporado
  um PR que tinha destino. O critério passa a ser o de D3 (tudo o que o PR trazia
  entrou?), lido por mudança; a sonda fica como apoio.
- 12-ter: a segunda razão para reimplementar ('carregava um defeito') não tinha a
  restrição da primeira. Defeito que um commit por cima do head conserta vai por cima,
  na branch do PR ou no head mesclado; reimplementar só quando nem isso resolve.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-16 18:44:59 -03:00
PessoaandClaude Opus 5 954a0c0816 docs(triagem): terceira conferência — o 12-ter não é o caminho do PR não editável
A terceira varredura achou um erro que a segunda rodada introduziu e quatro
imprecisões:

- 12-ter: a premissa passou a dizer que PR sem edição por mantenedores cai na
  reimplementação com proveniência. Não cai: o 8-bis mescla o head numa branch nossa,
  os commits dele ficam como ancestrais e o PR fecha como incorporado. O 12-ter é só
  para conteúdo reimplementado.
- resposta-ao-contribuidor: o molde de PR parcial dizia 'estou fechando' sem ressalvar
  que fechar é de quem tem a autoridade naquela rodada; e o cabeçalho não apontava o
  12-ter como um dos passes que usam o molde.
- depois-do-pr (skill do contribuidor): a promessa de autoria valia só para PR
  'reconstruído por conflito grande' — conflito se resolve por merge, e a autoria vale
  também para recorte, reimplementação e commit misto; e a lista de destinos que mantêm
  o PR aberto esquecia o de extensão.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-16 18:35:30 -03:00
PessoaandClaude Opus 5 5515acb28c docs(triagem): cinco trechos que a revarredura achou ainda anteriores a D1 e D3
Uma segunda varredura, sobre o estado deste PR, achou cinco pontos que a primeira
passada não alcançou porque não mudavam de palavra, só de alcance:

- 8-0: o resumo do 8-bis descrevia só a branch nossa; e a exceção do arquivo que não
  pode entrar precisa valer também para o PR editável (remoção empurrada na branch
  dele não tira o blob do histórico).
- 12-ter: a premissa dizia que toda reconciliação produz branch nossa; depois de D1,
  isso é só o caso do PR não editável ou do trabalho que separou escopo.
- Fronteira: 'todo fechamento sai com convite de reabrir' contradizia o fechamento
  de D3, em que convidar a reabrir o descartado desmente o descarte.
- depois-do-pr (skill do contribuidor): prometia a branch dele em todo PR editável,
  sem o segundo caso de D1 (trabalho que separa escopo vai numa branch nossa).
- Template de PR: 'a gente corrige o histórico' lia como reescrever commits; o
  mecanismo é o .mailmap, sem reescrever nada.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-16 18:26:13 -03:00
Rafael Melgaço 539a0064c8 Merge pull request #1011 from melgarafael/integracao/triagem-16set-l14
Lote 14 da triagem (16/09): 13 PRs, com o resgate do plantão e do arquivar conversa
2026-09-16 18:19:19 -03:00
PessoaandClaude Opus 5 6eff45e4ed test(skills): a suíte do instalador volta ao test:shell, e o caso do pipe não mede o SIGPIPE do cat
- package.json: instalar-guias.test.sh volta ao test:shell, junto com o
  colisao-de-migration.test.sh e o test:db:update que vieram da main.
- O caso 14 (--help pelo pipe) passava o script por `cat |`. Com pipefail, o
  cat leva SIGPIPE quando o bash sai do --help antes de ler tudo: num contêiner
  ubuntu:24.04 com pipe de 8 KB o caso dava 141 e 1 de 83 vermelho. Agora o
  script chega por redirecionamento da entrada padrão, com o mesmo $0 = bash.
  Medido: 83 de 83 no macOS e no contêiner.
- O cabeçalho do script dizia que o README ensina '| bash -s -- --help'; o
  README ensina '| bash'. A frase agora diz as duas formas.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-16 18:16:26 -03:00
Pessoa aa98bfcbca Merge remote-tracking branch 'origin/main' into fix/guias-alcance-e-yaml 2026-09-16 18:15:10 -03:00
PessoaandClaude Opus 5 c96918fb1d chore(test): devolve a linha test:shell à base antes de trazer a main
A main mudou a mesma linha (colisao-de-migration.test.sh). Com conflito, o
commit do merge passaria pelo pre-commit do gov-loop, que compara tudo o que
chega da main com o HEAD e acusa como edição desta branch um invariante que a
própria main alterou. Devolvendo a linha, o merge entra limpo; o teste do
instalador volta no commit seguinte, junto com as suítes da main.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-16 18:15:10 -03:00
PessoaandClaude Opus 5 7ee2b6e7d6 docs(triagem): três pontos em que a triagem ainda contrariava D1 e D3
O verificador cético achou três trechos que o commit anterior deixou em
desacordo com as decisões do dono de 16/09/2026.

Fronteira (D3): a célula dizia que o PR parcialmente incorporado "fecha apenas
se o resto foi descartado". Isso invertia a condição: sem destino escrito e sem
descarte declarado, mandava manter aberto um PR sem destino. Agora diz o que o
D3 diz: fica aberto só se o que sobrou tem destino escrito no próprio PR; se foi
descartado, fecha.

3-quater (D1): a linha "Run anterior ao conserto" mandava dar merge da main na
branch do PR sem as condições do D1. Agora exige que o PR permita edição por
mantenedores, aviso no PR antes e nada de --force (passe 8); sem a permissão, o
caminho é close+reopen (modo 18).

Modo 18 (D1): dizia que, sem workflow_dispatch, o único evento novo possível era
close+reopen. Num PR que permite edição, empurrar o merge da main na branch dele
também é evento novo, sem o e-mail de "fechado". O aviso ao contribuidor fica
para o close+reopen, e o passe 1 depois do evento vale para os dois caminhos.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-16 18:12:12 -03:00
PessoaandClaude Opus 5 922aa55785 docs(triagem): três decisões do dono sobre branch de fork, autoria e PR parcial
O dono do produto (Rafael) decidiu três coisas em 16/09/2026, e a doutrina
versionada dizia o contrário ou não dizia nada. Este commit alinha a triagem, o
comando, a resposta ao contribuidor, o CONTRIBUTING, o template de PR e a skill
de contribuição.

D1 — empurrar para a branch do PR do contribuidor é permitido. A proibição
anterior (a linha "empurrar para a branch do fork alheio" na Fronteira) foi
sobreposta por engano. Condição: o PR permite edição por mantenedores
(gh pr view <n> --json maintainerCanModify --jq .maintainerCanModify → true).
Sempre commit novo ou merge da main para dentro; nunca --force, rebase ou
reescrita dos commits do autor; aviso no PR antes de empurrar. Sem a permissão,
ou quando o trabalho separa escopo, o caminho é uma branch nossa. Muda o passe 8,
o 8-bis, o fragmento que falta, a Fronteira, o modo da prévia, o lembrete 4 do
comando e, do lado de quem contribui, o CONTRIBUTING, o template de PR e a skill
deskcomm-contribuir (traga a branch com git pull --no-rebase antes de empurrar).

D2 — trabalho do contribuidor entra com a autoria dele ("não quero créditos,
quero só a evolução do sistema"). Commit que leva trabalho dele (portado,
recortado ou reimplementado) sai com git commit --author, com o nome e o e-mail
que ele usa nos próprios commits. O acréscimo só nosso, em commit separado, fica
com a nossa autoria. Co-authored-by deixa de ser a forma principal de crédito.
Muda o passe 8, o 12-ter, o modo 46 e o depois-do-pr (que prometia "coautor").

D3 — PR parcialmente incorporado fica aberto só se o que sobrou tem destino
(decisão pendente, acompanhamento planejado, espera pelo autor, destino de
extensão), escrito no PR. Se o resto foi descartado, o PR fecha dizendo o que
entrou, com o link, e por que o resto não entra. Não substitui o merge de
proveniência quando o conteúdo entrou inteiro. Muda o 8-0 (o PR reconstruído
fecha, e sem -s ours, que levaria o blob proibido para a main), o 8-quinquies
(que mandava sempre fechar com sobrevivência baixa), o 12-ter, a Fronteira, o
modo 46, o lembrete 4 e ganha um molde em resposta-ao-contribuidor.md.

O espelho .claude/skills foi regenerado por pnpm skills:sync, não editado à mão.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-16 18:03:57 -03:00
PessoaandClaude Opus 5 b4e3a8aa82 test(qa): telefone sintético das specs do lote 12 sai de Math.random
O CodeQL do #1015 abriu dois alertas de severidade alta ("insecure randomness")
nas duas specs que semeiam contatos com número de telefone gerado por Math.random.
Não é segredo nem token — é fixture —, mas o alerta é novo e reprova o check. O
conserto é o padrão que as specs da main já usam: node:crypto (randomInt), em vez
de dispensar o alerta.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-16 18:00:55 -03:00
PessoaandClaude Opus 5 2d301c0724 test(qa): a spec do funil do lote 12 passa no lint
O job verify do #1015 reprovou no Lint: 'etapas' declarado com let e nunca
reatribuído (prefer-const), num arquivo que chegou com a prova em tela e não
passou pelo lint antes de entrar. Sai também o import de Page, que ninguém usa.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-16 18:00:04 -03:00
PessoaandClaude Opus 5 772d099193 fix(skills): trocar de fonte não deixa guia ligado, o Passo 0 sempre responde e o --help chega pelo pipe
Trocar a fonte dos guias (da cópia para --fonte, ou o inverso) deixava ligado,
nas três pastas globais, o guia que só a fonte anterior tinha: a varredura de
obsoletos só reconhecia links para a fonte ATUAL, e o registro .deskcomm-fonte
era reescrito com ela — então o --remover seguinte também não o reconhecia,
anunciava sucesso e o guia seguia carregando. A varredura e o --remover passam
a cobrar todas as fontes registradas, e a fonte nova entra no registro antes
da primeira ligação, para uma execução interrompida no meio não sumir do
--remover. O link que a pessoa fez à mão para o deskcomm-* de outro
repositório continua de fora: só casa o link para uma fonte que este script
registrou.

O Passo 0 do deskcomm-contribuir procurava o quem-sou.sh por caminho relativo
à pasta atual e, sem achar em lugar nenhum, não imprimia nada — numa subpasta
do clone ou fora de clone sem a instalação global, o gate "se a resposta
começar com..." ficava sem resposta. O bloco agora procura na raiz do clone
(git rev-parse) e, sem script, sai com erro NÃO MEDIDO que diz onde procurou e
como sair dali; o guia define o que fazer com essa saída.

O --help relia o próprio arquivo por $0, que vale "bash" no curl | bash -s --
--help que o README ensina: saía vazio, com exit 0. O cabeçalho virou o texto
de um heredoc que o --help imprime, e segue sendo o topo do arquivo.

Testes: instalar-guias ganha o --help pelo pipe e os casos 20 (troca de fonte
nas duas direções, com o link feito à mão) e 21 (troca interrompida);
deskcomm-contribuir ganha o caso 7, que extrai o bloco do SKILL.md e o executa
em bash e zsh.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-16 17:56:32 -03:00
PessoaandClaude Opus 5 4448efec21 ci(e2e): as seis specs da prova em tela do lote 12 entram nas partes do CI
`e2e-cobertura-completa` reprova spec do disco que não esteja em exatamente uma
lista. As specs de QA dos lotes anteriores rodam no CI (precedente na parte 3), e
estas também: além de guardar o lote contra regressão, `qa-l12-funil` é a prova de
tela do conserto da recusa do #935.

Distribuídas pelas três partes, e não somadas numa só: são 26 casos com ~23 logins,
e o teto de logins por IP numa janela de 5 minutos já derrubou uma parte inteira.

O mapa de jornadas registra o L12.QA.4 como consertado, apontando a spec que prova.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-16 17:49:23 -03:00
PessoaandClaude Opus 5 519819daaa fix(leads): a recusa do arrasto para a perda diz a saída, como o fragmento prometia
Achado do QA em tela do lote 12 (L12.QA.4, #935). O fragmento de versão promete ao
operador que, ao arrastar um card para a etapa de perda sem motivo, "a tela avisa" e
diz o que fazer — usar "Marcar como perdido" no menu do card. A tela mostrava só
"Informe o motivo da perda.", e quem arrastou não tem onde digitar motivo no arrasto.
O CHANGELOG da versão afirmaria uma saída que a tela não dá.

A recusa do arrasto e do lote sai de MOTIVO_DA_PERDA_OBRIGATORIO, que agora nomeia o
item de menu. A rota /lose — o próprio caminho do menu, onde a janela já pede o
motivo — mantém o texto curto, que é o que faz sentido ali.

O teste unitário afirmava só `toContain("motivo")`, que passava com a recusa sem
saída nenhuma; agora ele exige o nome do item de menu, e um caso novo lê
KanbanCardActions.tsx e prende a frase ao rótulo real: renomear o menu reprova,
em vez de deixar a recusa mandando procurar algo que não existe.

O fragmento passa a citar a frase exata da tela, e a spec do QA afirma a saída.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-16 17:47:51 -03:00
PessoaandClaude Opus 5 db14bab747 Merge qa/lote-12: a prova em tela do lote 12
Quinze casos dirigidos pela tela num ambiente fresco do baseline.sql, com a
evidência em evidence/triagem-16set-l12/ e a seção do lote no mapa de jornadas.
Onze PASS pela tela, um FALHOU (#935: a recusa não diz o que fazer, e o fragmento
promete que diz — consertado no commit seguinte), dois reportados com ressalva e o
que ficou NÃO MEDIDO escrito com o porquê.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-16 17:44:50 -03:00
PessoaandClaude Opus 5 439a5aeee3 Merge main (v1.29.0) no lote 12
A main andou com o lote 13 publicado como v1.29.0 e o PR #1003. Entra por merge,
como a doutrina pede para a base que muda durante o lote; a prova do lote é refeita
por cima.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-16 17:44:36 -03:00
PessoaandClaude Opus 5 0e28777723 test(qa): a prova em tela do lote 12, com o que ficou NAO MEDIDO escrito
Dezesseis PRs medidos pela tela no SHA 778d1dcb2, em banco montado so pelo
supabase/baseline.sql num Supabase pg17 proprio, com WhatsApp, IA, Resend,
Google e Redis AUSENTES — o estado de um primeiro deploy.

O que fecha buraco declarado no mapa de jornadas:

  · L12.G2.1 era NAO MEDIDO porque so o PostgREST decide se `.neq("tags","{}")`
    sobre coluna text[] e aceita, e o duble do teste de unidade define ele
    mesmo a sintaxe. Medido: 200, com corpo, numa organizacao com contato SEM
    tag e contatos COM tag.
  · J24.3 e J24.4 (#955) eram NAO COBERTO pela tela: a tela de etiquetas foi
    para o lote sem ninguem ter clicado nos botoes uma vez. Os tres foram
    clicados — renomear reescreve a regra de automacao na mesma operacao,
    juntar nao duplica, e o aviso de excluir (que ninguem tinha visto) diz
    quantas regras continuam escrevendo a etiqueta.
  · J4.39 (#948) pedia "o quadro com >=12 tags distintas". Quadro com 13, menu
    oferece 10, e digitar "verao" com "vip" na lista deixa o campo com o texto
    INTEIRO.

O que ficou de fora, com o motivo: o comportamento do #942 (falta chave de IA),
a URL de autorizacao do #933 (falta credencial de app do Google), o recorte do
#915 (a visao Semana nao cruza a borda — so a visao Dia cruza) e o painel de
etiquetas em espanhol.

Um achado que nao virou conserto: a recusa da perda no arrasto e no lote diz
"Informe o motivo da perda." e nao diz ONDE — o fragmento do lote promete a
recusa "com o que fazer". O texto e do servidor e serve aos tres caminhos, e
qual informacao ocupa o slot de descricao do toast e decisao de produto.

As specs nao sao gate: nenhuma esta em SPECS_PARTE_* do e2e.yml, e o reenvio
que elas fazem no preparo e no login existe contra a INFRA desta maquina
(PostgREST 503 PGRST002 sob carga), nunca contra o produto — cada tentativa
fica registrada em medicoes.txt com o prefixo [ambiente].

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-16 17:04:18 -03:00
Pessoa 1f14307118 Merge origin/main (v1.29.0 + doutrina de triagem) no lote 14 2026-09-16 16:59:34 -03:00
Rafael Melgaço 670433cb6d Merge pull request #1003 from melgarafael/triagem/licoes-74-77
docs(triagem): sete modos de falha dos lotes 11 e 12
2026-09-16 16:59:05 -03:00
Rafael Melgaço f969be97ac Merge pull request #1006 from melgarafael/release/1.29.0
Release 1.29.0
v1.29.0
2026-09-16 16:58:13 -03:00
PessoaandClaude Opus 5 d40d4502a7 docs(contributing): o congelamento de tests/invariants passa a estar escrito
Prometido ao @Gervanno no #1005, onde o PR reprovou um invariante do dreno sem
ter tocado no arquivo: o conserto dele muda o comportamento que a lei afirma, e
o vermelho aparece lá. O guarda é um hook LOCAL do mantenedor
(core.hooksPath=loop/hooks), então quem contribui de fora não o vê reprovar —
vê a integração travar depois, sem saber por quê.

O texto diz as duas coisas que faltavam: que o arquivo é congelado por um hook
que o fork não roda, e que reprovar um invariante sem tocá-lo significa duas
regras concorrentes, não descuido. E diz o que NÃO fazer: apagar ou afrouxar a
asserção.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-16 16:52:18 -03:00
PessoaandClaude Opus 5 2f4a40c4ae fix(inbox): arquivar diz o que faz, e a aba passa a ser medida por comportamento
Dois itens do dossiê do #995 (@webtecnica), os dois mecânicos:

1. **A confirmação prometia menos do que o código faz.** O corpo do PR diz que
   arquivar "tira o atendimento da fila viva sem encerrá-lo", e o banco discorda:
   `fn_conversation_set_status` trata `archived` como terminal — grava
   `service_closed_at`, incrementa a revisão e, por consequência, desfaz a pausa
   do automático. Um atendente que leia "arquivar = tirar da vista, volto depois"
   encerraria o atendimento sem saber, e o robô voltaria a responder no próximo
   "oi" do cliente. A confirmação passa a dizer isso quando a conversa ainda está
   aberta (em conversa já encerrada, o texto curto continua). O fragmento perdeu
   a frase "o atendimento continua exatamente onde estava".

2. **O caso mais importante da aba era medido por TEXTO-FONTE**, com a função
   exportada ao lado. Isso falha nos dois sentidos: mover a decisão para fora do
   `switch` deixando o literal no bloco mantinha o teste verde com a aba
   quebrada; trocar o `switch` por um mapa reprovaria um refactor correto. Agora
   a pergunta é feita a `tabToFilter("archived")`.

   O controle positivo é da SONDA e não de outra aba: medi que "Fechadas" também
   devolve string (`{ status: "closed" }`), então usá-la como contraste provaria
   o contrário do que eu queria dizer. Está escrito no teste.

Medido: pnpm vitest run tests/unit/arquivadas-aba-e-contagem.test.ts → 9 passed
(com a primeira versão do controle: 1 failed | 8 passed — a minha suposição
errada sobre a aba Fechadas); inbox-header-nao-trava + i18n-espanhol +
fragmentos-de-release → 42 passed; pnpm typecheck → 0 erros.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-16 16:46:27 -03:00
PessoaandClaude Opus 5 b3579666ac Merge PR #995: feat(inbox): arquivar conversa e aba "Arquivadas" (#923) (@webtecnica)
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-16 16:40:36 -03:00
PessoaandClaude Opus 5 b617a48e35 fix(followup): o caso do #954 passa a se chamar pelo que mede, e o PR ganha fragmento
Dois itens do dossiê do #954 (@KauaAmaral, primeira contribuição):

1. O caso novo do invariante se chamava "cancel_on_reply=true também cancela uma
   espera fixa active", e o caminho que ele exercita NÃO lê tipo de nó nenhum:
   `esperaAtiva` filtra por `status === 'active'`. O rótulo prometia menos do que
   o código faz — quem lesse o teste procuraria uma condição de nó inexistente.
   A amplitude é deliberada (a chave na tela diz "cancelar se o lead responder",
   sem ressalva), então o nome passa a dizer isso.

2. O PR não trazia fragmento, e muda comportamento visível a quem opera. Escrito
   pela triagem, como manda o passe 12: contribuidor de fora não conhece
   `.changes/`. `nada_mudou` é o impacto certo — os dois lados são conserto de
   coisa quebrada, e a chave já estava na tela prometendo o que agora acontece.
   O texto inclui o que o conserto NÃO faz: rascunho que já ficou com id
   repetido não se cura sozinho, e a issue #586 diz como sair disso pela tela.

DESKCOMM_GOV_INVARIANTS_EDIT=1: a edição em tests/invariants/ é o rótulo de um
caso (string do `it`), sem tocar asserção nenhuma. Nenhum caso desapareceu:
13 na main, 14 com este PR.

Medido: pnpm vitest run tests/unit/fragmentos-de-release.test.ts → 32 passed.

Closes #586

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-16 16:39:56 -03:00
PessoaandClaude Opus 5 2e4eafcf2a Merge PR #720 + reconciliação: o plantão deixa de se desligar sozinho (@paulolimajr77)
Ponte aprovada pelo dono (decisão 18.3 = b): entra agora, e a peça de presença
de verdade é a issue #996. O conflito era modify/delete — a rota de cron
`attendant-heartbeat`, que o PR apaga e a main tinha editado —, resolvido PELA
DELEÇÃO: a melhoria de log da main vive dentro da função que deixa de existir.

Quatro consertos por cima, três deles dívida NOSSA (o PR foi escrito contra uma
main de 4 dias atrás e não podia enxergá-los):

- `vercel.ts` continuava agendando a rota apagada. O gate de inventário de jobs
  compara `vercel.ts` com `docker/scheduler/entrypoint.sh`, e a divergência
  reprovaria o lote — medida na árvore mesclada: 25 rotas contra 24.
- O cabeçalho de `attendants/availability/[user_id]/route.ts` descrevia o mundo
  antigo em três afirmações, todas falsas depois deste PR.
- `CLAUDE.md` nomeava o cron como rota viva.
- O fragmento não contava o outro lado da regra: chave ligada + jornada vazia =
  24 horas. Quem hoje "caía" às 22h passa a ficar elegível de madrugada até
  publicar uma jornada — e isso é texto de tela para quem opera.

DESKCOMM_GOV_INVARIANTS_EDIT=1, com a medição: o PR toca
tests/invariants/gov-4-routing.test.ts **só em comentário**. Provado removendo
comentários dos dois lados e comparando o código restante — idêntico. O que o
comentário novo diz é por que `last_heartbeat_at` fica na asserção mesmo sem
ninguém escrever nela: é reserva declarada, e tirá-la exige decisão, não
descuido. Nenhuma asserção foi enfraquecida, removida ou trocada por test.fails.

Medido: pnpm vitest run tests/unit/gatilho-dos-jobs-de-entrega.test.ts
tests/unit/cron-audita-so-quando-ha-efeito.test.ts → exit=0, 49 passed.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-16 16:38:55 -03:00
PessoaandClaude Opus 5 5b38a1d025 Merge PR #980: fix(branding): leitura que falhou não vira fato memoizado por um TTL (#895) (@webtecnica)
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-16 16:35:37 -03:00
PessoaandClaude Opus 5 b3a07b5bde Merge PR #961: fix(acervo): a ação "editar conteúdo" nasce do tipo do material (@webtecnica)
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-16 16:35:34 -03:00
PessoaandClaude Opus 5 797d9b638b Merge PR #960: docs(changes): fragmento de release do material arquivado marcado (@webtecnica)
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-16 16:35:31 -03:00
PessoaandClaude Opus 5 f7b5625190 test(branding): o JID do WhatsApp entra declarado, e o vermelho do #914 some
O #914 (@webtecnica) faz a catraca de marca enxergar host de terceiro, não só a
marca — e a própria régua nova reprovou o PR: `s.whatsapp.net` aparecia sem
declaração. Medido: o host existe em `lib/waha/resolve-contact-whatsapp-id.ts:38`
desde ANTES deste PR (`jid.endsWith("@s.whatsapp.net")`), ou seja, não é código
novo que o PR trouxe — é a régua nova alcançando o que já estava lá. Dívida do
PR, conserto de uma linha, e a triagem faz.

Duas entradas, porque a guarda cobra as duas e está certa em cobrar:
- `HOSTS_DECLARADOS` com categoria PROTOCOLO e o motivo: é o sufixo do JID que o
  WhatsApp manda, não destino de chamada (o código fala com o WAHA) nem texto de
  tela. Trocar pela marca do revendedor faz o CRM deixar de reconhecer o
  identificador do protocolo.
- A lista fechada de "só FORNECEDOR pode crescer": categoria que não é destino
  de chamada só cresce por decisão escrita no diff. Esta é a decisão.

Medido: pnpm vitest run tests/unit/branding.test.ts → exit=0, 39 passed (antes
do segundo ajuste: 1 failed | 38 passed, no caso do conjunto fechado).

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-16 16:34:44 -03:00
deskcomm-release[bot] 4ed2765403 release(1.29.0): a versão montada a partir dos fragmentos declarados 2026-09-16 19:31:46 +00:00
PessoaandClaude Opus 5 fece002d9d Merge PR #964: fix(mídia): a leitura de mídia usa o endereço do provedor do ponto e recusa destino interno (@webtecnica)
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-16 16:31:04 -03:00
PessoaandClaude Opus 5 87dbf406bf Merge PR #954: Fix follow-up editor IDs and cancel-on-reply (@KauaAmaral)
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-16 16:30:59 -03:00
PessoaandClaude Opus 5 7d3b871f83 fix(skills): a adoção não engole clone de trabalho e o --remover para de apagar link alheio
Rodada de reconserto do PR #927, sobre o que a verificação achou no trabalho já
commitado (d9/c13, R2, R3, R4, R5). Cada defeito foi reproduzido no código de
2873183ce antes do conserto, e cada conserto foi sabotado depois — previsto =
medido em todas as oito sabotagens.

R2/d9 — `eh_copia_de_versao_anterior` adotava QUALQUER pasta apontada por
DESKCOMM_GUIAS_HOME que fosse um clone raso, limpo e do repositório do produto.
Medido no código anterior, num clone de trabalho nessas condições: ele saía da
branch da pessoa (HEAD destacado), ganhava a marca `deskcomm.guias` para sempre
e, na execução seguinte, tinha o não commitado descartado pelo `checkout
--force`. Adotar não é contabilidade: é passar a fazer `checkout --force` ali
dentro. Agora só a pasta que o script escolhe sozinho — o padrão
`~/.deskcomm/guias` — entra na conversa, e quem instalou com a versão anterior
deste script usou exatamente essa: a cura do R2 anterior continua inteira.

R3/d9-c13 — os três sinais que seguram a adoção (remoto, raso, limpo) não
tinham catraca: apaguei cada um, um por vez, e os 56 casos seguiam verdes,
porque o clone do caso 13 falha em DOIS sinais ao mesmo tempo e não distingue
qual reprovou. Agora há um caso por sinal, cada clone falhando em um só. O
comentário do caso 16 afirmava o contrário — era novo e falso — e saiu.

R4 — o `--remover` seguia no teste largo de `eh_nosso` e apagava o link que a
PESSOA fez à mão para o `deskcomm-*` de outro clone; o cabeçalho prometia o
contrário, e o conserto da rodada anterior só tinha alcançado a varredura de
obsoletos. A instalação passa a anotar em cada pasta global, num
`.deskcomm-fonte`, de onde saíram as ligações daquela pasta, e o `--remover`
cobra essa fonte. Sem o registro — instalação feita por uma versão anterior a
ele — a única fonte que o script pode ter usado sem ninguém lhe dizer é a cópia
padrão, e é ela que responde.

R5 — o comando de desfazer que o README e a nota pública da versão ensinam não
tinha gate nenhum: revertê-lo para `| bash --remover` deixava tudo verde. O caso
novo não grepa a frase — ele EXTRAI o comando de cada documento que o menciona e
o EXECUTA, trocando só o curl pelo script deste repo, com piso de dois
documentos para não virar verde no dia em que o fragmento for consumido pelo
corte da release (o texto passa para o CHANGELOG, que entra na varredura).

tests/shell/instalar-guias.test.sh: 56 → 67 casos. Sabotagens, previsto =
medido: S1 sem a checagem de caminho → 1; S2 sem o sinal raso → 1; S3 sem o
sinal limpo → 1; S4 sem o sinal remoto → 1; S5 `--remover` no teste largo → 2;
S6 sem o registro → 1 (o caso 5, que desfaz um `--fonte`); S7 README sem o
`bash -s --` → 1; S8 fragmento sem o `bash -s --` → 1.

NÃO consertado aqui: R1 — os commits desta branch não estão no GitHub, e os
checks verdes que o PR exibe rodaram sobre o código pré-conserto. Esta sessão
não tem permissão de push.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-16 16:30:57 -03:00
PessoaandClaude Opus 5 c0ae38d9f6 Merge PR #1000: fix(whatsapp): a sessão do CRM nasce com o acervo do histórico do número (@webtecnica)
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-16 16:30:52 -03:00
PessoaandClaude Opus 5 abd95c7bae Merge PR #914: test(branding): a catraca passa a pegar host de terceiro, não só a marca (@webtecnica)
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-16 16:30:45 -03:00
Pessoa af6e9596d6 Merge origin/main (lote 13, 23c4afefe) no lote 14 2026-09-16 16:30:08 -03:00
Rafael Melgaço 23c4afefe2 Merge pull request #1002 from melgarafael/integracao/triagem-16set-l13
Lote 13 da triagem (16/09): 13 PRs de sete contribuidores, com os consertos da triagem
2026-09-16 16:24:16 -03:00
PessoaandClaude Opus 5 e5ae9fbf3c fix(i18n): a resolução do dicionário refeita pelo caminho de três pontas
O merge do #930 sobre o #926 conflita no apêndice do dicionário, e a minha
primeira resolução (concatenar os dois lados) QUEBROU o arquivo: os dois blocos
terminam numa entrada cujo `},` de fecho era a LINHA COMUM, que o git põe depois
do `>>>>>>>`. Concatenar deixou a última entrada do lado de cima aberta.

É a armadilha 8-ter do triagem/TRIAGEM.md, e ela não aparece na leitura do diff:
quem a pegou foi o `tsc` (TS1005 na linha do fecho do objeto, 8 mil linhas
abaixo do estrago).

Refeito com `git merge-file` contra a base comum (54530c9fd), fechando o bloco
de cima antes do de baixo começar. Conferido por varredura de profundidade de
chaves (o objeto fecha) e por `pnpm typecheck` → 0 erros.

Nenhuma chave repetida entre os dois lados (22 do #926, 1 do #930, interseção
vazia) — o outro modo de falha deste arquivo, o TS1117.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-16 16:20:32 -03:00