Skip to content

fix(agent-engine): classificador do roteador não manda modelo sem provider par - #316

Closed
maugarciasa wants to merge 33 commits into
melgarafael:mainfrom
maugarciasa:claude/compassionate-wilbur-dde08a
Closed

fix(agent-engine): classificador do roteador não manda modelo sem provider par#316
maugarciasa wants to merge 33 commits into
melgarafael:mainfrom
maugarciasa:claude/compassionate-wilbur-dde08a

Conversation

@maugarciasa

Copy link
Copy Markdown
Contributor

Resumo

  • Bug reproduzido em produção (mkt.lojaconnect.com.br, org com organizations.settings.llm.provider = 'openrouter'): configurar o Roteador de intenção com "Modelo do classificador" em Automático fazia POST /api/v1/ai/routers/[id]/test devolver {"intent_name":null,"confidence":0} para QUALQUER frase, mesmo idênticas aos exemplos cadastrados.
  • Causa raiz: router-config.ts mandava o modelo default 'claude-haiku-4-5' (só existe no catálogo ai_models do provider anthropic) para o seam sem nenhum classifier_provider correspondente. Numa org OpenRouter esse id não bate com nenhuma linha do catálogo dela, a chamada ao provedor falha, e classifyIntent engole o erro (por design — nunca derruba o turno) devolvendo null em silêncio. É o MESMO padrão de defeito já documentado e corrigido no PR fix: aux classifiers borrow model without provider/credential, causing 404 on agents with non-default provider #151 (lib/agent-engine/agent/aux-model-args.ts): modelo e provider precisam viajar sempre do mesmo lugar.
  • Fix: classifierModel só é honrado quando vem PAREADO com classifierProvider explícito. Sem os dois, ambos ficam null e o seam (runModelCall) herda defaultModel+provider já configurados e consistentes da própria organização — o mesmo par que o agente principal usa.
  • Bônus: o fix é puramente de interpretação em runtime, sem mudança de schema — também corrige (sem backfill) routers criados antes deste fix e nunca tocados na tela, já que o DEFAULT da coluna ai_routers.config grava classifier_model sem classifier_provider par.

Test plan

  • pnpm typecheck — 0 erros
  • pnpm lint — 0 erros (warnings pré-existentes, nenhum novo)
  • pnpm lint:channels — ok
  • pnpm test:unit — 5224/5225 (a 1 falha, sentry-comunidade-so-erro.test.ts, é ambiental/pré-existente — confirmado reproduzindo com o fix stashado)
  • Testes novos/atualizados cobrindo o caso do bug real: router-config.test.ts (classifier_model sem classifier_provider par → classifierModel: null, reproduzindo o default de coluna da migration 0085) e intent-classifier.test.ts (classifierModel: null não manda model nem llmOverride ao seam)

🤖 Generated with Claude Code

maugarciasa and others added 30 commits August 22, 2026 19:08
O editor legado de agente rag_bot (components/ai/AgentEditor.tsx,
"caminho pré-EPIC-13") só gravava o rascunho em ai_agents. O runtime
(lib/agent-engine/agent/agent-config.ts) le system_prompt/provider/
model da versao apontada por ai_agents.published_version_id, nunca do
rascunho -- editar o prompt pela tela nao tinha efeito nenhum, em
silencio. Medido numa instalacao real: o prompt generico do seed
("Voce e um(a) atendente virtual amigavel de uma loja online...")
continuava ativo depois de varias edicoes salvas pela tela.

fn_publish_ai_agent_version (0024/0025/0026) nao serve pra este
caminho: exige credential_id nao-nulo na versao e canal WORKING --
nenhum dos dois e como rag_bot opera. fn_publish_rag_bot_version e o
par minimo: copia os campos de infraestrutura da versao publicada
atual (canal, ferramentas, orcamento -- nada disso e editavel pelo
AgentEditor.tsx) e troca so system_prompt/provider/model, vindos do
rascunho. provider nunca e hardcoded: resolve de
organizations.settings.llm.provider, o mesmo campo que
scripts/bootstrap-owner.ts grava a partir do AI_PROVIDER do
instalador -- evita repetir o bug irmao (agente seedado direto com
provider='anthropic' apesar da org ter escolhido OpenRouter).

Botao "Publicar" novo no AgentEditor.tsx, desabilitado enquanto ha
alteracao nao salva (publica sempre o que esta persistido, nunca o
que esta so na tela).

Verificado: pnpm typecheck/lint/test:unit/build limpos; pnpm test:db
real (install + update + 839 testes de invariantes, incluindo
isolamento RLS) verde; caminho feliz + 4 sabotagens + permissoes
(anon/authenticated bloqueados, so service_role) testados manualmente
contra fixtures sinteticas em pg17 efemero.

Nao medido: pnpm test:e2e e imagens-ok (precisam de infraestrutura
nao disponivel nesta maquina).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
channel_session_health nasce por channel_sessions.id (por SESSÃO), não por
phone_number (por NÚMERO). O caminho de recuperação de sessão morta é
arquivar + conectar de novo (channel-sessions/[id]/reconnect recusa sessão
arquivada e manda reconectar criando outra), e isso sempre gera um id novo
— que nascia com health_released_at null, o fail-safe de "número novo",
mesmo quando o telefone por trás já tinha passado pelo go-live manual dias
antes.

Medido em produção (instalação MKT, 2026-08-22): WAHA caiu, o dono
arquivou a sessão morta e conectou de novo — rotina. A sessão nova entrou
em hold go_live, e como ninguém sabia que precisava resolver esse aviso na
Central de novo, o outbound ficou retido por ~2h; um lead mandou mensagem
e não recebeu resposta.

evaluateSession agora checa, antes de armar o hold, se outra sessão do
mesmo phone_number nesta org (arquivada ou não) já foi liberada. Se sim,
libera de cara. Número de verdade novo continua nascendo em hold, sem
mudança de comportamento.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…porta de entrada

Os dois documentos tinham 11 contradições medidas contra o repositório: versão
do produto divergente entre AGENTS.md/package.json/CHANGELOG (três valores
diferentes), comando de gate errado (npm em vez de pnpm), instrução de schema
local que quebra (migrations em vez de baseline.sql), e um punhado de
contagens (rotas, testes, specs) já podres. Toda contagem volátil virou
comando — quem lê mede na hora em vez de confiar num número que já mentiu
quatro vezes neste repo.

Adiciona .ai/AI_BOOTSTRAP.md como porta de entrada única, com a ordem de
leitura e a regra de precedência entre os documentos. package.json ganha um
comentário `//version` explicando por que aquele campo é inerte — a versão do
produto vem do CHANGELOG.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…r de path)

Todo checkout Windows normaliza .github/workflows/e2e.yml, specs de tradução
e telas .tsx para CRLF, e oito testes deste repo não sobreviviam a isso —
todos verdes no CI Linux, todos vermelhos ou (pior) silenciosamente inertes
numa máquina Windows:

- e2e-cobertura-completa: `.` não casa `\r` em JS; as três listas de spec do
  workflow liam vazias, derrubando o controle positivo e acusando as 49 specs
  de uma vez.
- traducao-nao-defasa: o selo `<!-- traduzido-de: … -->` casa com `$`; o `\r`
  sobrando fazia duas traduções corretamente seladas reprovarem.
- marca-logo-spec-ancora-a-rota: o helper que extrai o corpo da função nunca
  achava o fecho `\n}\n` — a suíte inteira morria na coleta, 0 testes rodados.
- import-puro-sem-env: chamava `npx` direto; no Windows é `npx.cmd`, e o
  ENOENT resultante passava pelo controle positivo porque ele só checava "a
  importação falhou", não "falhou pelo motivo certo". Troca para invocar o
  CLI do tsx via `process.execPath`, mais uma asserção que reprova falha por
  runner quebrado.
- telemetria-tem-um-leitor-so, provedores-x-registry,
  marca-sem-divergencia-de-hidratacao: comparavam `path.relative()` (que
  devolve `\` no Windows) contra allowlist escrita com `/` — o desencontro
  ora derrubava o controle positivo, ora deixava a allowlist inteira INERTE
  sem avisar ninguém. Acrescenta `caminhoRelativo()` em
  tests/unit/helpers/varrer-codigo.ts como ponto único de normalização.
- vocabulario-do-funil: o regex de texto visível excluía `\n` como primeiro
  caractere depois do `>`, cego ao caso mais comum de JSX (texto na própria
  linha). Corrigido, ele achou 4 vazamentos reais de "pipeline"/"Kanban" em
  copy de usuário — consertados nos mesmos quatro arquivos: SourceDetail,
  tenant/_form, KanbanBoard, NewLeadDialog ("Ver no Kanban" → "Ver no funil"
  etc.).

Cada conserto tem um teste de mutação nos comentários do diff (spec removida
da lista / copy ruim reinjetada / runner quebrado de propósito) provando que
o gate volta a acender quando o defeito volta.

vitest run: 469/469 arquivos, 5216/5216 testes. typecheck limpo.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
send_message só ENSINAVA o modelo quando pacing vetava (outside_window/
warmup_cap/daily_cap) — o run fechava "concluído" com messages_sent: 0 e
nada reagendava. Fora do caminho determinístico de re-entrada
(runDeterministicReentry, que só cobre outside_window), o lead ficava sem
resposta até mandar outra mensagem por conta própria.

Medido em produção (instalação MKT, 2026-08-22): warmup_cap bateu no meio
de uma conversa; o cliente mandou "Preciso da planta baixa..." e não
recebeu nada, nem depois de o teto ser liberado — só reenfileirando o job
na mão é que a resposta saiu.

reagendarTurnoPorVetoDePacing reusa followup_turn (a mesma peça que
rescheduleReentry já usa pra outside_window na re-entrada determinística)
pros três códigos do pacing — todos carregam nextAllowedAt; nenhum outro
gate carrega. Idempotente por job de origem, best-effort (falha no
agendamento não deve derrubar o erro de ensino que o modelo já recebeu).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…ciasa)

Este fork passa a se atualizar e se instalar sozinho, sem depender do
repositório original (melgarafael/DeskcommCRM) nem de permissão de merge
lá. Todo ponto que apontava pro namespace/repo original agora aponta pro
fork:

- docker-compose.prod.yml: default de APP_IMAGE/WORKER_IMAGE/
  SCHEDULER_IMAGE (fallback de quem não tem a chave no .env).
- .env.hostgator.example: os mesmos três defaults, o que o install.sh
  copia pra instalação nova.
- hostgator-setup-kit/install.sh, comecar.sh, diagnostico.sh: REPO_URL
  default (clone do kit) e os exemplos de `curl | bash`.
- hostgator-setup-kit/_common.sh: IMG_NS (a constante de onde
  ultima_versao_publicada() e ghcr_status() puxam versão/manifest) e as
  duas URLs de token/manifest do GHCR que ainda citavam o namespace
  antigo direto.
- hostgator-setup-kit/update.sh: fallback de APP_IMAGE em
  image_desatualizada().
- hostgator-setup-kit/test-validators.sh, tests/shell/update-guard.test.sh:
  fixtures e asserções atualizadas pro novo namespace — pnpm test:shell
  passa de novo (rodado localmente; achado à parte, não-regressão: ".env
  continua 600" falha neste Windows/Git Bash por causa de como NTFS
  mapeia bits de permissão POSIX, mesmo sem nenhuma mudança de conteúdo).

Não editei texto cosmético (README, CONTRIBUTING, docs de growth,
narrativa histórica em comentários) — só o que o instalador/atualizador
realmente lê em runtime.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…anal

Dois achados de auditoria, mesma causa raiz: verificação de papel que
faltava numa camada, com a irmã correta do mesmo domínio provando qual era
o piso certo.

- contacts: única policy sempre foi tenant_isolation_contacts_all
  (FOR ALL, só fn_user_org_ids()), com GRANT ALL a authenticated. Qualquer
  membro do tenant, inclusive viewer (somente-leitura por doutrina),
  lia/gravava/apagava contato direto pelo PostgREST, sem passar por
  requireRole. Mesma classe de falha que a migration 0150 já corrigiu para
  canais/config de IA e deixou o resto do schema para depois. Migration
  0169 aplica o mesmo par (SELECT só-tenancy + escrita com
  fn_role_at_least('agent')), o piso que
  app/api/v1/contacts/route.ts já exige no POST/PATCH/DELETE.

- channels/partner/templates (POST), .../templates/media (POST) e
  onboarding/whatsapp/session (POST) resolviam org sem checar papel —
  viewer conseguia criar/sincronizar template aprovado pela Meta, subir
  imagem de cabeçalho e reiniciar (?restart=1 derruba antes) a sessão
  oficial de WhatsApp do tenant. As rotas irmãs (channels/templates,
  channels/official) sempre exigiram admin. Gate com requireRole("admin")
  nas 3, em vez de reimplementar a checagem inline (anti-padrão "matriz
  advisória" que a doutrina de lib/auth/require-role.ts proíbe).

Verificado: pnpm test:db (112 arquivos, 845 testes, install+update do
baseline sem erro) e pnpm test:unit (5221/5222 — a 1 falha é o flake
conhecido de lib/ui/icons.test.ts, documentado no próprio
vitest.config.ts, não relacionado). typecheck e lint limpos.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Move Não lançado -> 1.3.1 (2026-08-23) e documenta os 3 fixes desta
sessão junto do que já estava acumulado (orçamento de IA):

- editor do rag_bot salvava sem publicar
- reconexão do WhatsApp reprovava número já aquecido no go-live
- veto do pacing (anti-banimento) derrubava o turno sem reagendar

Corrige também o link de comparação [1.2.1] pra [1.3.0], que faltava, e
aponta os links novos pro fork (maugarciasa) em vez do repo original.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…gent-engine

Auditoria técnica encontrou o mesmo gap da migration 0169 (contacts) em
messages e em toda a infraestrutura do agent-engine (job_queue,
send_ledger, lead_checkpoints, metrics etc., migration 0050): RLS só de
tenant, sem checagem de papel — qualquer membro do tenant, inclusive
viewer, gravava/apagava direto pelo PostgREST.

0170 fecha messages com o mesmo piso que app/api/v1/messages/route.ts já
exige (agent+). 0171 faz a verificação tabela-por-tabela nas 31 tabelas
da 0050: 4 têm escrita confirmada por sessão (piso = o que a rota já
exige), as outras 26 ganham o mesmo piso por defesa em profundidade
(service_role sempre ignorou RLS; o que muda é o acesso direto via
PostgREST com a chave authenticated).

Verificado com pnpm test:db (Postgres efêmero, baseline em modo install
e update, 845 testes de invariantes).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
app/api/v1/admin/tenants/route.ts e .../lgpd/requests/[id]/route.ts
interpolavam input direto em .or() sem escapar , ( ) — sujeito a
injeção de filtro. O repo já tinha o fix correto em
ai/followups/queue/route.ts; só não estava replicado.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Loop for com await sequencial por lead (até 50 round-trips em série).
A ação move do mesmo arquivo já usava Promise.all — tag ficou pra trás.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
O worker tem acesso a SUPABASE_SERVICE_ROLE_KEY, chaves WAHA e AES de
credenciais de IA — RCE numa dependência ganhava root no container.
COPY --chown=node:node + USER node (node:22-alpine já traz o usuário
node). --chown no COPY evita reescrever ~200MB de node_modules numa
camada extra de chown -R.

Verificado com docker build + docker run: uid=1000(node), node_modules
legível, falha só por falta de .env (esperado sem ambiente real).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
.agents/skills/DeskcommCRM/SKILL.md era gerado automaticamente em julho
e ensinava snake_case/imports relativos — o oposto do kebab-case/alias
@/ real do repo. Com allow_implicit_invocation: true em
.codex/agents/openai.yaml, toda sessão Codex recebia a convenção
errada automaticamente. Conteúdo reescrito espelhando a doutrina real
de .claude/skills/DeskcommCRM/SKILL.md.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
path.join() no Windows devolve \; KNOWN_DEBT e ALLOWED usam /. O
desencontro fazia arquivo legítimo de lib/channels/ virar "violação
nova" e a lista de dívida inteira aparecer "obsoleta" ao mesmo tempo —
249 linhas de falso positivo. Normaliza o separador pra / em walk().
Não afeta o CI (roda em Linux), mas quebrava pnpm gov:verify local de
qualquer contribuidor no Windows.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
backup.sh existia só como script manual (cabeçalho ensinava "crontab
-e" à mão) — numa instalação self-host real, isso é "nunca ter backup
rodando". Agendado no crontab do HOST (não no container scheduler, que
é deliberadamente sem docker.sock e o backup.sh precisa de docker run
pg_dump/tar), mesmo mecanismo idempotente por marcador que
setup_update_agent_cron já usa. install.sh e update.sh chamam
setup_backup_cron automaticamente — nasce funcionando, sem passo
manual. Horário configurável via BACKUP_CRON_HOUR (default 3h).

Verificado com pnpm test:shell.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Sem CVE conhecida hoje, mas ausência de defesa em profundidade contra
XSS futura — e o produto é self-hosted por terceiros, então o header é
a única rede de segurança que independe de disciplina de código.

Aplicado em proxy.ts (não next.config.ts): a CSP precisa da URL real do
Supabase em runtime, e self-host não queima NEXT_PUBLIC_* no build (a
mesma imagem Docker serve toda instalação). HSTS só quando a request é
https (via x-forwarded-proto, o app roda atrás de Traefik em http
interno). unsafe-inline em script/style porque 4 blocos inline via
dangerouslySetInnerHTML (branding, tema) não têm nonce hoje — sem
input de usuário, mas real hardening futuro é threadar nonce.

Verificado com curl real (headers presentes, HSTS ausente em http) e
browser (sem erro de CSP no console em /login).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@tanstack/react-virtual estava instalado desde sempre e nunca importado
em produção, apesar de a spec já prever virtualização pro inbox. Sem
ela, a lista renderiza toda conversa no DOM de uma vez.

useVirtualizer com measureElement (altura dinâmica por linha), key por
id da conversa (não índice — Realtime reordena a lista, uma key por
índice atribuiria a altura medida errada), botão "Carregar mais" como
última linha virtual, e scroll automático até a conversa selecionada
quando ela sai da janela renderizada.

Testado pela tela com 300 conversas semeadas: DOM fica em ~13-21 nós
mesmo com a lista crescendo de 50 pra 100 itens carregados.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
scripts/ estava fora do pnpm typecheck (exclude explícito e literal em
tsconfig.typecheck.json, herdado sem querer do exclude do tsconfig
base). CI nunca via os erros que se acumulavam ali.

4 erros reais corrigidos: cast inválido em
prova-modelo-escolhe-retorno.ts, grupo de captura opcional sem
fallback em qa-wave-13-09.ts, campo opcional faltando no shape do MCP
tool em seed-e2e-retorno.ts, e organizationId ausente (campo
deliberadamente obrigatório) em spike-adapter-meta.ts.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
6 HANDOFF-*.md na raiz não estavam declarados em docs/index.md §8 —
drift já flagueado em docs/audits/2026-08-14-afirmacoes-de-estado.md e
nunca resolvido. Todos correspondem a épicos já mergeados na main
(confirmado via git log --merges). Movidos pra docs/handoffs/ (mesma
convenção dos irmãos já arquivados lá), índice atualizado, e os 3
docs que citavam caminho/linha desses arquivos corrigidos.

.claude/ecc-tools.json e o yaml de instincts do homunculus: confirmado
por busca direta que nada no repo os lê (nenhum .codex/agents/*.toml,
hook ou config aponta pra eles) — vestígio inerte de uma geração
batch de 2026-07-06. Removidos.

4 commits de teste vazios na main (f97683e, 87ae640, 131edf5,
c8e2055): confirmados vazios via git show --stat, mas não
reescrevemos histórico compartilhado — aceitos como estão.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Terceira perna da família 0167 (job_queue/api_audit_log): event_log
crescia sem limite, nenhuma linha jamais apagada. fn_podar_event_log
segue o mesmo molde — só status done/dead, pending/processing nunca
tocado, piso de 7 dias — chamado pelo cron data-retention já existente.

Mapeamento de consumidor feito por grep real, não suposição: a maioria
dos event_type tem handler e chega a done/dead (mensagem inbound,
mudança de lead, dispatch de IA) — esses são resolvidos por este
expurgo. Oito são órfãos sem handler nenhum (message.sending/sent/
failed/outbound, lead.won/lost/reopened/assigned) e ficam pending pra
sempre — três deles batem com NOTIFICATION_CATEGORIES já declaradas,
cheiro de feature de notificação inacabada. Por doutrina (não inventar
regra de negócio), esta migration não implementa esse consumer —
documentado no cabeçalho da 0172 como achado separado.

Retenção: EVENT_LOG_RETENTION_DAYS (default 90, piso 7 — mesmos
números da JOB_QUEUE_RETENTION_DAYS, bus operacional, não legal/LGPD).

Verificado com pnpm test:db: 112 arquivos, 851 testes, baseline em
install e update.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…inhas)

Extração de método pura, zero mudança de comportamento — cada uma das
9 funções extraídas manteve o corpo verbatim, testada individualmente.
1672 linhas era difícil de revisar com segurança no hot path que todo
turno de agente de WhatsApp passa.

9 funções extraídas: resolverAgenteDoTurno, montarPromptDoSistema,
compactarSeNecessario, escalarSeJailbreakComPromessaForaDeTabela,
fecharTurnoEGravarCheckpoint, enfileirarTurnoDoOperador,
registrarAtividadeDeCheckpoint, recalcularScoreDoNegocio,
registrarDivergenciaDeEstagio. Todas declaradas DEPOIS do fechamento
de executarTurnoDoAgente — colocá-las antes quebrava um teste que lê o
código-fonte pra confirmar que maybeCompact/classifyStage ficam dentro
do escort de handoff por orçamento (comportamento idêntico em runtime,
já que function declaration hoisteia, mas o teste é textual).

O que ficou: ~700 linhas de rawTools (tool calls do agente), com
closures que mutam estado compartilhado do turno (seq, outcomes,
pendingCitations etc.) — extrair isso exigiria virar esse estado num
objeto mutável threadado por toda parte, uma mudança bem maior e mais
arriscada de verificar à mão. Não forçado.

Verificado: 253 testes do oráculo (12 arquivos que já cobriam este
código) passando antes E depois de cada extração individual, suíte
completa (5223 testes) e pnpm test:db (851 testes de invariantes,
incluindo os 6 que exercitam o turno fim-a-fim) verdes.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Painel de segurança do fork mostrava code scanning sem nenhuma análise.
Workflow novo, mesmo padrão de permissions do ci.yml (contents: read no
topo, security-events: write só no job que precisa escrever o
resultado). Roda em push/PR pra main e semanalmente (segunda 06h UTC,
pra pegar CVE nova em código que não mudou).

Dependabot vulnerability alerts e automated security fixes também
ligados via API (sem arquivo — são flag de repositório, não workflow).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
v3 rodou verde na primeira execução, mas com aviso de depreciação
(dezembro/2026) e forçando Node 20→24 por baixo do capô. v4 já existe
e remove os dois avisos.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
fn_podar_event_log entrou na 0172 como terceira perna da família
0167, mas o mapa retencao-de-historico ainda só desenhava job_queue e
api_audit_log — peça nova fora do diagrama fere invariante 3
(sistema-vivo). Node fn_podar_event_log + event_log com as duas
arestas (podar → rpc → delete), EVENT_LOG_RETENTION_DAYS no node de
env, README com a contagem correta (18 peças, 20 arestas).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
tests/unit/workflows-tem-permissions.test.ts exige justificativa
registrada pra todo escopo de escrita de GITHUB_TOKEN em workflow — o
codeql.yml que criei ligava security-events: write sem entrar na
allowlist ESCRITA_JUSTIFICADA, e o check verify quebrou no push
anterior. github/codeql-action/analyze precisa desse escopo pra subir
o SARIF na aba Security; init/checkout ficam só com o contents: read
do topo.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
maugarciasa and others added 3 commits August 23, 2026 13:55
.env.hostgator.example promete a chave (adicionada junto com o backup
automático), mas install.sh nunca a escrevia — sumiria no próximo
install/update mesmo se posta à mão, e o teste
test-validators.sh::sincronia pegou. envq no mesmo padrão das outras
chaves opcionais, default 3 (03h), mesmo fallback que
setup_backup_cron() em _common.sh já usa quando ausente.

Verificado com pnpm test:shell: só sobra a falha pré-existente de
permissão .env 600 no Windows/git-bash (não relacionada, já confirmada
antes via git stash contra a main limpa).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Causa raiz confirmada do e2e vermelho desde o commit anterior desta
sessão: tests/e2e/marca-logo.spec.ts falhava com "<img> tem src mas o
bitmap veio vazio" — e2e roda contra Supabase local (`supabase start`),
cujo Storage é servido em http puro (127.0.0.1:54321), e o img-src da
CSP só tinha `https:` — bloqueando silenciosamente o <img> do logo no
navegador. Confirmado batendo o histórico do workflow e2e: verde até o
commit anterior à CSP, vermelho em TODO run desde então, inclusive em
4 branches do Dependabot cujo único diff era bump de dependência (nada
de branding/logo).

Fix: soma supabaseUrl.origin explicitamente ao img-src, cobrindo o
schema real (http local ou https em produção/self-host hospedado),
sem depender só do fallback genérico https:.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…vider par

Automático (classifier_model/provider null) caía no default hardcoded
'claude-haiku-4-5', válido só no catálogo anthropic. Numa org OpenRouter
esse id não existe, a chamada falha e classifyIntent devolve null em
silêncio — POST /api/v1/ai/routers/:id/test sempre respondia confidence
0, para qualquer frase.

Mesma regra do PR #151 (aux-model-args.ts): modelo e provider viajam
juntos. Sem os dois, o loader devolve classifierModel:null e o seam
herda defaultModel+provider já consistentes da organização.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@vercel

vercel Bot commented Aug 23, 2026

Copy link
Copy Markdown

@maugarciasa is attempting to deploy a commit to the rafael-maudibrasil's projects Team on Vercel.

A member of the Team first needs to authorize it.

@ecc-tools

ecc-tools Bot commented Aug 23, 2026

Copy link
Copy Markdown
Contributor

ECC bundle files are already tracked in this repository. Skipping generation of another bundle PR.

@maugarciasa

Copy link
Copy Markdown
Contributor Author

Fechando — o alvo certo desse fix é maugarciasa/DeskcommCRM (PR #6 lá), não o upstream.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant