Skip to content

A corrente do cliente: do extrato à meta travada, e o mês que agora fecha - #59

Merged
BarryBits merged 22 commits into
mainfrom
feat/partes-relacionadas-e-regra-token
Aug 20, 2026
Merged

A corrente do cliente: do extrato à meta travada, e o mês que agora fecha#59
BarryBits merged 22 commits into
mainfrom
feat/partes-relacionadas-e-regra-token

Conversation

@BarryBits

Copy link
Copy Markdown
Owner

21 commits acumulados nesta branch. O eixo é a jornada canônica do cliente
(docs/valor/pente-fino/PLANO-DE-CORRECAO.md §0): tudo aqui serve a corrente que
vai do extrato até a meta travada.

O que fecha nesta leva

E3-B — o switch da antecipação. O extrato credita o líquido; a face está no
borderô, que a maior parte do ICP não tem em mãos. Um campo de formulário
trancava a apuração inteira para todo cliente que desconta duplicata. A face
deixa de ser obrigatória: quem sabe a taxa declara % ao mês + prazo, e o sistema
deriva a face do líquido (face = líquido / (1 − taxa × prazo/30)).

A armadilha está tratada estruturalmente: o caminho da taxa é função irmã do
motor multinível e nunca chama valorBrutoDe — onde o líquido do extrato virava
face e o deságio saía errado, em silêncio e para menos. Provado por mutação.

E5-B — a meta do pacing vem do plano vigente. lerMeta ordenava por
version desc sem filtro de status; a primeira edição do plano feita depois
de a dona travar a meta roubava a referência de 4 telas (Cockpit, Capital de
Giro, Simulador, motor de Iniciativas), sem nada dizer.

A1 (já na branch) — o modelo morto de 30 análises fora do sistema, a escada
de publicação aposentada, a cobrança dos 12 meses pelo nome, e a dona travando a
própria meta.

Segurança (já na branch) — 31 policies que liberavam por estar logado.

Banco

20260820030000 amplia o CHECK de dre_detalhamento.modo com 'por_taxa'.
Já aplicada no remoto e conferida no banco vivo (prova não-destrutiva com
controle registrada no SNAPSHOT.md). As de 20/ago também já estão aplicadas.

⚠️ db/snapshot/ segue 🔴 obsoleto (dump de 18/ago) — pede re-dump com Docker,
e o db/ESTRUTURA.md só deve ser regerado depois dele.

Prova

tsc zero erros · 1922 testes verdes · build limpo · zero erros de eslint
novos nos arquivos tocados.

O que NÃO está provado

O check ao vivo. Nada aqui foi visto rodando — e o metodo.md diz que o gate
é o check ao vivo. O roteiro está no handoff: subir OFX → categorizar → ver a
faixa cobrar os meses pelo nome → fechar um mês com antecipação pelo modo taxa
→ travar a meta mexendo no número → o Cockpit popular → perguntar ao chat.

Duas armadilhas do roteiro: o dossiê tem TTL de 24h (feche um mês antes de
perguntar ao chat), e o visual do switch da antecipação ainda não teve um par de
olhos humanos.

🤖 Generated with Claude Code

BarryBits and others added 21 commits August 18, 2026 01:18
…ticos que sustentam os números

Três frentes: o cenário macro do Brasil (fontes primárias, corte 17/ago), o que o
ATR OS de fato faz (verificado no código com arquivo:linha) e a oferta de valor
que liga os dois — mais o pente fino da jornada de um cliente simulado.

Os cinco scripts diag-* são READ-ONLY e produziram os números da E2. O
diag-grupos-por-transacao também serve como ferramenta comercial: roda no
primeiro extrato e estima o esforço de onboarding ANTES de fechar contrato.

Achados medidos que mudaram conclusões:
- linhas/grupo varia 6,6x entre clientes reais (Vertimetal B2B 29,8 x Prudente
  B2C com PIX nominal 4,5) — o custo de categorização é função do MODELO DE
  NEGOCIO do cliente, não do volume de transações;
- a cauda é cardinalidade real, não bug de normalização (prefixo colapsa só 10%);
- 311 decisões cobrem 80% das linhas; as 1.497 seguintes cobrem os 20% restantes.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
… contraparte no histórico

O detector de partes relacionadas era bom e estava desligado da tomada: a tabela
que ele lê (client_socios) NÃO tinha escritor nenhum no código — as 8 linhas
existentes foram inseridas à mão por SQL. E o detector só rodava em
confirmarImportacao, então cadastro novo nunca alcançava o histórico já
importado. Medido antes: 16 de 18.097 transações com contraparte marcada.

socios-do-qsa.ts (puro): extrai sócios PF de companies.dados_receita.qsa,
converte a máscara da Receita (***939098** -> 939098) para o CHECK de 6 dígitos,
ignora sócio PJ (é empresa_grupo, não client_socios) e inclui o representante
legal, que era descartado. Empresa cadastrada à mão (sem enriquecimento) degrada
para nome só, com confiança média.

sincronizar-socios.ts: upsert idempotente pelo índice único (client_id, upper(nome)).
Sócio de fonte 'manual' NUNCA é sobrescrito — o comentário da tabela no banco diz
que sócio de fato não registrado é comum em PME, e é justamente esse caso.

varredura-contraparte.ts: espelha aplicarRegrasNoProjeto. Só toca linhas com
contraparte_tipo IS NULL e grava alta e média — o schema previu as duas
(CHECK IN ('alta','media')) e o gate JÁ renderiza média como "identificada pelo
nome, confira". Persistir média não decide nada: nada age sem contraparte_porta,
que é escolha do dono. E é o que torna eficaz a trava da regra por token.

O sync fica em CompanyService.criar/atualizar, condicionado a dados_receita no
payload: são QUATRO telas que enriquecem empresa e todas afunilam ali. No caminho
de leitura não dava — a policy client_socios_insert exige consultor+, e o leitor
também roda na ingestão feita pelo cliente.

Aplicado em dado real: contraparte socio 5 -> 795, empresa_grupo 9 -> 193,
propria 2 -> 57. Os 795 batem exatamente com a medição manual que o autor do
detector deixou no cabeçalho do arquivo.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
As 49 regras genéricas da casa são REGEX; as regras que o CLIENTE cria eram só
igualdade exata. O sistema sabia casar por padrão, mas só para si mesmo.

Medido em produção: PRUDENTE & SILVA tem 1.358 grupos de uma ocorrência (75% das
decisões, 17% das linhas), e 1.157 deles (85%) contêm o token MAQUININHA com 100%
de pureza de sentido — 1.157 clientes diferentes pagando pelo MESMO canal, todos
venda no balcão. Prefixo não resolve (4%): o nome da pessoa vem primeiro.
Com token, 1.808 decisões caem para ~529.

match_type ('exact' | 'contains') com DEFAULT 'exact' — as 151 regras existentes
seguem casando por igualdade, byte a byte. Verificado no banco após a migration.

filtroDeRegra() é fonte única dos três pontos de aplicação (importação, varredura
e lote da tela), e carrega A TRAVA: regra 'contains' só toca linha SEM contraparte
detectada. Sem isso, uma regra ampla em entradas varreria aporte de sócio para
dentro da receita e contaminaria margem, ponto de equilíbrio e a meta do Plano de
Voo — são 1.029 lançamentos protegidos por essa cláusula hoje.

escaparParaIlike() fecha o outro furo: '%' e '_' no pattern do cliente virariam
curinga e a regra pegaria mais do que ele viu no preview.

Precedência exact -> contains resolvida no sort em TS: depender da ordenação
alfabética do Postgres ('contains' < 'exact') seria acidente, não desenho.

O preview é obrigatório e mostra o que protege: "1.157 lançamentos, R$ 134.012,
todos entrada — e 900 movimentações de sócio que eu não vou tocar".

MatchType já existia declarado com 'starts_with' e 'regex' e nunca fora usado.
Os dois saíram: starts_with foi MEDIDO e cobre 4%; regex vindo do usuário é ReDoS.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…rio projeto

Decisão do Lucas (18/ago): o ATR OS é SaaS de uso próprio. Todo o sistema é do
cliente; dentro de um projeto quem manda é ele — contrata, cadastra a própria
empresa e sócios, categoriza, fecha o mês e trava a própria meta. O consultor
deixa de ser etapa: é ferramenta externa opcional, só enxerga se o cliente ligar,
e sem nenhuma limitação diferente da do cliente. Owner/admin veem todos.

Isso INVERTE a hierarquia atual (owner > admin > consultor > cliente, onde o
consultor tem mais poder que o cliente no projeto): a autoridade passa a vir de
posse do projeto, não de degrau.

Levantado contra a RLS: as perguntas 1 a 5 da cadeia o cliente já faz sozinho,
mas 6 e 7 — "pra onde eu vou" e "o que eu faço" — estão trancadas em
canOperateProject e no gate de Iniciativas.

Raio medido: 104 das 348 policies codificam o bypass de 3 papéis, em 47 tabelas.
A boa notícia é que project_members (com granted_by) JÁ é o mecanismo que o
modelo pede — a execução é repoint, não invenção. A pegadinha: a view
client_project_access filtra role='cliente' e 242 policies a consomem, então do
jeito que está ela exclui o consultor de tudo.

Fica decidida e NÃO executada — próxima sessão. Uma pergunta aberta registrada
no doc: consultor lê ou também escreve (muda de 1 para 2 policies por tabela).

ESTRUTURA.md regerado: client_socios 1 -> 4 usos, transactions 77 -> 81,
companies 24 -> 25.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…aaS obriga a reler

A decisão de que o ATR OS é SaaS sem consultor chegou no FIM da sessão. F2, F3 e
o pente fino foram escritos com o consultor como premissa silenciosa: não estão
errados nos fatos (tudo verificado no código), mas estão errados no julgamento —
toleravam como normal um passo que agora não tem dono.

O handoff registra item por item o que precisa ser relido (F1 não muda; F2 §5.3 e
§7, F3 §6 e §8, E1 e E2 mudam) e a consequência mais dura: sob SaaS, as 4 a 29
horas de categorização da E2 são INTEIRAMENTE do cliente, sozinho, antes de ver
qualquer valor. Isso promove a regra por token de otimização a existencial.

E levanta a pergunta que a trilha nunca fez porque havia um consultor implícito
para traduzir: o cliente sozinho consegue INTERPRETAR o que vê? Nas etapas E4-E6
esse vira o critério principal.

Inclui: as 9 etapas com estado, o que já foi aplicado em produção (migration +
varredura de contraparte em dado real), o aviso de que o db/snapshot está atrás
do banco, as 2 perguntas que travam trabalho, e os gotchas de build/lint
descobertos na marra (o build type-checa scripts/; o hook de commit precisa de
mais de 2 min).

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…stava quebrado

Os três scripts de diagnóstico da leva de regra por token anotam o callback do
`.find()` com um tipo `ProjetoRef` que nunca foi declarado em lugar nenhum.
Estão commitados desde a sessão anterior, e como o `npm run build` type-checa
`scripts/` junto com `src/`, a branch não compilava.

Declarado em cada arquivo, no mesmo formato que o irmão que já compilava
(`diag-grupos-por-transacao.ts` faz o cast inline equivalente).

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…o próprio projeto

Executa a lei `docs/atros-v3/saas-sem-consultor.md` (decisão de 18/ago). As três
migrations já estão APLICADAS em produção; o snapshot foi re-dumpado e confere.

O que inverte
-------------
`canOperateProject` deixa de exigir `PROJECT_OPERATOR_ROLES` e passa a exigir
VÍNCULO: membro do projeto opera, owner/admin operam qualquer um. A linha
`// cliente/sdr nunca operam` morreu. Ler e escrever passaram a ter a mesma
regra de autorização, então a função delega para `canAccessProject`.

O que a lei não tinha mapeado
-----------------------------
Ela descreve 3 padrões de bypass; existem 5. O que faltava — 36 policies em 22
tabelas fixando `pm.role = 'consultor'` — é invisível ao grep dela e era
justamente o que tornaria a inversão INERTE: o dono passaria no app e apanharia
da RLS, em silêncio. Números corrigidos: 367 policies (não 348), 120 bypasses
(não 104), 97 policies a repointar (não 242 — a doc contou linhas).

O gate de qualidade estava fora do caminho de escrita
-----------------------------------------------------
`planoVooDestravado` (12 meses da janela fechados) só existia no render de
`page.tsx`. As actions e a rota aceitavam qualquer chamada. Enquanto o cliente
não operava isso não vazava; no minuto da inversão viraria meta calibrada em
dado ruim. Agora está em `generate` e em `approve`.

As migrations
-------------
  20260818010000  os 2 INSERTs do caminho de escrita do Plano de Voo
  20260818020000  view `project_access` + 139 policies reescritas
  20260818030000  o dono cadastra e conserta o próprio cadastro

Decisões travadas: consultor escreve igual ao cliente (1 policy por tabela);
DELETE de dado financeiro apurado segue admin-only, cadastro abre pro dono.

Ferramental em `scripts/saas/` (read-only)
------------------------------------------
139 policies não se escreve à mão. `transformar-policies.ts` divide a expressão
booleana em disjuntos de topo e remove só o certo, provando balanceamento de
parênteses; `auditar-acesso-do-dono.ts` é o gate (dono bloqueado 177 → 142,
37 destravadas, zero regressões); `verificar-dump.ts` confere contra o dump, não
contra a migration.

Nota: `plano-voo/actions.ts` também perde aqui a limpeza morta de
`cockpit_actions` — o conserto do Cockpit vem no commit seguinte.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…m por estar logado

Achado ao mapear o terreno do SaaS, não estava em lista nenhuma. Já APLICADO em
produção; o snapshot foi re-dumpado e confere.

O problema
----------
31 policies RLS liberavam acesso por simplesmente ESTAR LOGADO
(`auth.role() = 'authenticated'`, `USING (true)`). Como policies permissivas se
somam por OR, elas anulavam todo o escopo por vínculo das vizinhas: não adianta
a policy do lado exigir `project_members` se a outra libera geral.

Sob o modelo antigo — só a equipe da ATR tinha login — era desleixo contido. Com
o signup self-service em produção, é vazamento entre clientes:

  · `projects` SELECT+UPDATE+INSERT: qualquer cliente logado listava TODOS os
    projetos da plataforma e dava UPDATE em qualquer um — inclusive no
    `data_inicio`, que define a janela de 12 meses da meta
  · `profiles` USING (true): nome, e-mail e papel de todo mundo
  · `categorization_rules` / `learned_rules` ALL: a inteligência de
    categorização de um cliente legível E gravável por outro
  · `activity_log` INSERT WITH CHECK (true), sem nenhum inserter no código —
    servia só pra forjar log em nome de outro
  · `class_access` idem: auto-concessão de acesso a curso

Resultado: 31 → 8. As 8 que ficam são SELECT em catálogo global sem chave de
tenant (`dre_categories`, `financial_benchmarks`, `class_*`, `library_content`,
`subcategories`, `categorization_patterns`) — desenho correto, não vazamento.

A armadilha do subselect
------------------------
`project_members` tem RLS own-row (`project_members_self`): subselect nela dentro
de policy roda como o usuário, então procurar o vínculo de OUTRA pessoa volta
vazio SEM ERRO. A policy de `profiles` precisa enxergar o colega de projeto — por
isso usa a view `project_access`, que roda como owner e atravessa. Escrita com a
tabela, passaria no review e negaria tudo em silêncio.

Regra que fica: precisa ver vínculo de terceiro ⇒ use a view, nunca a tabela.

Gate: `npx tsx scripts/saas/auditar-acesso-do-dono.ts --vazamento`.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Bug latente de três camadas, achado ao ir escrever a policy de DELETE que falta
em `cockpit_actions`. A policy acabou não sendo o conserto.

1. A limpeza mirava o alvo errado
   `approvePlanoVooV2Action` fazia `.delete().eq("snapshot_id", snapshotId)` — o
   snapshot que está sendo aprovado AGORA, que nunca tem linha, porque re-aprovar
   o mesmo snapshot é barrado antes. Código morto. E inerte de qualquer forma:
   `cockpit_actions` não tem policy de DELETE, então o delete apagava zero linhas
   sem erro.

2. O Cockpit não filtrava por plano
   `api/projetos/[id]/cockpit` lia por `project_id`. Aprovar um segundo plano
   arquiva o anterior mas não mexe nas ações dele ⇒ o dono veria 24 ações, metade
   de um plano que já não vale.

3. A FK é ON DELETE SET NULL
   Excluir planos deixa ações órfãs com `snapshot_id` nulo, para sempre.

O conserto é ler, não apagar
----------------------------
Nasce `plano-voo/snapshot-vigente.ts` — resolvedor único do snapshot aprovado
vigente, recebendo o client por parâmetro (o Cockpit usa o do usuário, o motor do
HTR usa o service client). O Cockpit filtra por ele. Preserva o histórico — as
ações antigas e o progresso marcado nelas continuam na tabela — e resolve as três
camadas sem policy nova.

Quarta camada, achada no caminho
--------------------------------
`pe35-auto-gen-service` resolvia o snapshot por `created_at` desc SEM filtrar
status (o comentário dizia "snapshot ativo"). Podia grudar a escalação do HTR num
rascunho, que o Cockpit — agora lendo por vigente — jamais mostraria. E
`getSemanaAtualDoPV` contava as ações do projeto inteiro, somando semanas de plano
arquivado para decidir a próxima.

`cockpit_actions` tem zero linhas em produção (conferido no seed.sql): o bug era
latente e o fix não tem risco de dado. Os 4 testes novos travam o FILTRO, que é o
que era o bug — se alguém tirar o `status='aprovado'` ou voltar a ordenar por
`created_at`, eles caem.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…r por IA

Os dois estavam rotulados como "baratos" no backlog. Nenhum era o que dizia.

G-05 — eram 8 lugares em 6 arquivos, não 2
-------------------------------------------
E três propagavam DECISÃO REVOGADA, não só número velho:

  plano-voo-gate.ts:6            "≥3 snapshots mensais ATIVOS"
  iniciativas-gate.ts (×2)       "≥3 meses" + "o ato explícito do consultor"
  useProjectSidebarBadges (×2)   "≥3 meses" + "gate D-3"
  Sidebar.tsx:145                "≥3 meses + folha"
  htr-consultant-agent.ts:62     "≥3 snapshots + folha"
  financial-snapshot-service:86  citava `learned_rules`, removida abaixo

Folha deixou de ser gate em 2026-06-05 (que é exatamente a revogação da D-3), e
sob o SaaS quem aprova o plano é o DONO, não o consultor. O gate real é 12 desde
2026-07-28, e por COBERTURA da janela, não por contagem.

A armadilha: dois candidatos óbvios estavam CERTOS
--------------------------------------------------
Um replace de "≥3 → 12" varrendo o repo teria corrompido dois comentários
corretos, que descrevem outra regra com um 3 real:

  auto-conclusion-service.ts:91  é literalmente `snapshotsTotal >= 3`
  module-service.ts:51 + dre-publishing-service.ts:6
                                 rascunho (<3) → preliminar (3-11) → consolidada (12+)

Regra que fica: o número na prosa só é erro se o literal no código disser outra
coisa. Conferir o literal antes de trocar o texto.

G-08 — o categorizador por IA, e a cascata que ninguém tinha mapeado
--------------------------------------------------------------------
Decisão do Lucas (17/ago): a categorização do ATR OS é determinística, sem IA.
`ai-categorizer.ts` tinha zero callers — 210 linhas removidas.

Matar ele torna morto o `CATEGORY_MAP`, que existia só para alimentá-lo, o que
por sua vez mata `FINANCIAL_SUBCATEGORIES`, que existia só para montar o
`CATEGORY_MAP`. `constants/financial-categories.ts` foi de 123 para 25 linhas;
sobra `FINANCIAL_CATEGORIES`, com 9 consumidores vivos.

O DROP de `learned_rules` ficou para depois, por decisão do Lucas. A migration
NÃO fica em `supabase/migrations/`: migration não aplicada quebra o oráculo de
frescor (marca o dump como obsoleto sem ele estar) e um `supabase db push` a
aplicaria sem perguntar. O SQL, a prova de que é seguro e a cauda obrigatória
estão parqueados no G-08 do `docs/valor/gaps-backlog.md`.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
… como e o que falta

Re-dump
-------
Duas rodadas no dia, ambas com guarda de tamanho (dump em temporário, validação,
só então instalar — a lição do dump vazio que quase virou perda de dado).

  Views      12 → 13   (`project_access`)
  Policies  362 → 353
  Tabelas · Funções · Triggers · Índices    iguais

Os dois deltas foram PREVISTOS antes de rodar o dump e bateram exato: +1 view /
0 policies na leva do SaaS (146 pares DROP+CREATE com saldo zero — nada se
perdeu, nada duplicou), −9 policies na leva do vazamento. Prever o delta é o que
separa "a migration rodou" de "a migration fez o que eu quis".

Oráculo de frescor: AUTORITATIVO (`ultima_migration_incluida = 20260818050000`).
Gate contra a realidade: `npx tsx scripts/saas/verificar-dump.ts` — 0 policies na
view antiga, 108 na `project_access`, 0 policies abertas fora de catálogo global.

Ninguém foi trancado do lado de fora: a conferência de quem perderia o bypass por
papel global voltou VAZIA — não existe um único `profiles.role` em
('consultor','sdr') no banco. O bypass que aparecia em 120 lugares não protegia
uma pessoa, o que confirma a decisão do SaaS por outro caminho.

Handoff + backlog
-----------------
`handoff-2026-08-18b` traz o placar G-01…G-10 com todos os itens REVERIFICADOS no
código vivo (não repassados da doc), o mapa dos 5 padrões de bypass, os quatro
defeitos achados no caminho e o que falta, em ordem.

`gaps-backlog.md`: G-05 fechado, G-08 na metade, ambos com a investigação que
mostrou que nenhum dos dois era o que o backlog dizia.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…ssaram a mentir

O handoff estava em formato de relatório de sessão. Reescrito para o que a
próxima sessão precisa primeiro: E3 é onde retoma, o rito, a régua e as 9 etapas
logo na abertura; o que a sessão fez virou contexto abaixo.

E0 — o veredito da régua ficou falso
------------------------------------
Dizia "reprovado no item 1 e no 3", porque as perguntas 6 e 7 estavam trancadas
atrás de `canOperateProject`. O G-09 foi executado: os itens 1 e 3 passam NO
CÓDIGO (falta o check ao vivo). O que segue reprovado é o item 4 — e agora se
sabe onde (abaixo).

E1 — aquele 🟡 nunca existiu
----------------------------
"Distribuição da conta nova — exige consultor" saiu de uma leitura incompleta da
RLS: olhou `pending_ofx_update`/`_delete` (staff-only) e não viu as irmãs
`pending_ofx_cliente_*`, que já estavam na baseline. Policies permissivas se
somam por OR — basta uma dar caminho.

E a UI expõe ao cliente: CasaDoGrupoShell → IngestaoCasaTab:139 →
GroupIngestionTab, que renderiza o DistribuirContaPendenteModal (:292) com o
botão "Distribuir" (:376). O diretório `forense/` engana — é pasta de
componentes, não existe rota forense.

Selo corrigido para ✅, e os ~10 min saem do consultor e entram no tempo dela.

O achado que a E4–E6 vai encontrar em cada tela
-----------------------------------------------
A pergunta nova do pente fino é "o cliente sozinho consegue interpretar o que
vê?". Há resposta parcial, e é NÃO: 9 textos de tela mandam o cliente falar com
um consultor que o modelo removeu. Sete em caminho vivo — a tela do Plano de Voo
(AtoVCaminho.tsx:121,167), a narrativa de pacing do Cockpit
(pacing/pacing-core.ts:513), a ingestão (ofx-scan-service.ts:460), o acesso
(actions/conta/membership.ts:67) e o e-mail ao cliente (email-service.ts:268).
As outras 2 estão num serviço sem callers.

O sistema deixou o dono fazer tudo; o texto ainda aponta para alguém que não
existe. Entra no backlog ANTES da E6 — senão a E6 sela 🔴 em tela que só precisa
de copy nova. (Não confundir com "Consultor IA", o chat: é nome de feature.)

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…removeu

O G-09 tirou o consultor do modelo (a autoridade passou a vir do vinculo), mas
a copy continuou apontando pra alguem que nao existe mais. Sob SaaS, tela que
manda "falar com o consultor" e trabalho sem dono — o 21 lugares varridos aqui.

O que mudou, por classe:

- Cockpit (pacing-core): o veredito de risco mandava "sentar com o consultor
  para recalibrar". Agora termina em acao-raiz de verdade (R4): alavanca nova,
  e o caminho e Iniciativas.
- Plano de Voo (relatorio, ato V, PDF): a legenda de "atraso critico" repetia o
  mesmo; a meta dizia-se "calibrada pelo consultor" quando quem calibra e o
  motor com a historia da propria empresa; e o dia a dia era "conduzido junto ao
  seu consultor" quando na verdade acontece no Cockpit e nas Iniciativas.
- Capa e fecho do documento: o bloco rotulado "Consultor responsavel" mostra, no
  codigo, quem GEROU o documento (fetchConsultor(userId)) — que sob SaaS e o
  proprio dono. Trocar por "Suporte ATR" mentiria igual; o rotulo honesto nos
  dois mundos e "Gerado por". Fallbacks de nome "Consultor ATR" -> "ATR OS".
- Ingestao, acesso e e-mail: o erro de distribuicao de OFX mandava "avise seu
  consultor"; o de acesso ao grupo, "fale com o consultor" — quando hoje o dono
  convida em Minha Conta > Empresa > Pessoas (inviteToGroupAction). O rodape do
  e-mail idem.
- Proximos passos do relatorio: "recalibragem ... em nova rodada de consultoria".

Deixado de proposito, porque nao e copy:

- "Parecer do consultor" — campo humano real, escrito no WorkflowControls e
  escondido do dono por padrao (AnalysisPageShell:59). Decisao de produto.
- email-service.ts:216 ("seu consultor responsavel e X") — X vem de
  project.created_by, que num signup self-service e o proprio cliente. O defeito
  ali e a FONTE da identidade, nao a frase; remendar a copy esconderia o bug.
- ofx-import-service.ts:20 — servico sem nenhum caller (codigo morto).
- COMPROMISSO_ATR — promessa comercial de acompanhamento humano. Nao e minha
  decisao mudar o que a ATR promete.

Prova: eslint com 0 erros novos (os 3 hits nas linhas tocadas sao
text-[var(--...)] que ja existiam no HEAD); tsc --noEmit EXIT=0; suite 181
arquivos / 1.885 testes verde. Build nao rodado de proposito: dev server no ar
em :3000, e next build derruba as rotas dele.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Eram 21 lugares, nao 9 — a contagem anterior grepou frases prontas e nao pegou
a familia de ROTULOS, onde estava o pior caso: a capa do Plano de Voo dizia
"Consultor responsavel" sobre o nome de quem GEROU o documento, que sob SaaS e o
proprio dono. Vira "Gerado por".

Registra tambem a decisao do Lucas de manter o COMPROMISSO_ATR (e oferta, nao
defeito) pra E6 nao re-levantar, e o motivo de 3 outros itens NAO terem sido
tocados.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…quem veio busca-lo

O motor de fechamento e solido: amortizacao SAC/Price, match de parcela com
tolerancia, snapshot imutavel versionado, stale-by-event. O caminho do cliente
nao e.

O achado central: pra separar receita de custo do dinheiro, o gate pede o VALOR
DE FACE da antecipacao. O OFX credita o liquido; a face mora no borderô do
banco. Sem ela o mes nao fecha. E a E0 diz, textualmente, que ninguem na Casa
Pilar sabe quanto o desconto de duplicatas custa — o sistema pergunta a cliente
exatamente o que ela veio perguntar ao sistema. Sozinha, ela trava ali.

Medido em producao (scripts/diag-fechamento-e3.ts, somente leitura):

- 91% dos meses fechados tinham pendencia que BLOQUEIA o fechamento; na
  Vertimetal, 31 de 31. Na Di Forni, 0 de 3 — o gate e funcao do PERFIL do
  cliente, nao do sistema, mesmo padrao que a E2 achou na cauda.
- 97% dos meses foram fechados mais de uma vez. Mas a distribuicao desmente a
  leitura obvia: 27 meses em v5, com ~30 fechamentos no MESMO DIA, quatro vezes.
  Nao e o cliente reapurando — e o historico inteiro sendo refechado em massa
  por drift de catalogo/motor, via script com service key. Sob SaaS nao existe
  quem rode esse script.
- 0 projetos passaram de 'rascunho' na escada de publicacao — e o motivo esta no
  codigo: ResultsTab declara o prop onPublishDRE e nunca o usa; PublishDREModal
  nao tem ponto de montagem. A cascata que auto-conclui as analises #2/#5/#8/#10
  nunca dispara, e a ferramenta que da contexto a IA le justamente essas.

Custo: ~5h para a Casa Pilar, ~40% delas fora do sistema (cacando o valor de
face no banco). Acumulado E1+E2+E3 = ~11h30, e ela ainda nao viu uma resposta.

7 achados (E3-A..E3-G). A maior alavanca e barata: herdar prazo e tipo da
antecipacao do ultimo mes declarado, no mesmo padrao sugerir+confirmar que a
auto-linkagem de parcelas ja usa.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…de começar

Fecha a varredura da jornada do cliente (E0-E8), iniciada em 17/ago. Nenhuma
linha de src/ foi tocada: foi auditoria, não conserto, por decisão do Lucas.

O VEREDITO: o produto não está imaturo — está desconectado.

A cadeia das 7 perguntas quebra em quatro dos sete elos. Mas dos 24 achados
críticos, 10 são fiação: a peça existe, está correta, e lê a fonte errada — ou
não está ligada em nada. Três consertos já estão escritos no repositório e nunca
foram repontados (snapshotAprovadoVigente, consultorPronto, o padrão .range()).

Os achados que sustentam isso:
- E6-A: a dona gera o Plano de Voo e não consegue travar a meta nem abrir o
  documento. documento/page.tsx é o único mount de WorkflowControls, único
  caller de approvePlanoVooV2Action, atrás de um redirect por PRIVILEGED_ROLES.
  Isso derruba o critério 3 do E0 ("nenhum elo exige alguém da ATR"), que o
  handoff de 18/ago dava como aprovado.
- E6-C/E6-D: o Consultor IA nunca terá ferramentas — teto estrutural de 10
  análises concluídas contra um limiar de 30. A régua substituta (consultorPronto)
  existe, a flag está ON global desde maio, o dossiê é carimbado, e as duas rotas
  de chat não a leem.
- E4-A/E4-C: a Curva ABC soma débito cru de 32 meses sem as colunas da DRE; o
  Ponto de Equilíbrio calcula sobre o mês corrente parcial. Ambos saem, e saem
  errados.
- E5-A/E4-J: a concentração de clientes é calculada sobre 1.000 linhas, sem
  order — em duas telas. O teto do PostgREST está documentado no próprio repo e
  contornado em 11 outros leitores.
- E7-A/E7-B: o sistema nunca chama a cliente de volta. O lembrete mensal foi
  construído e está parado por quatro bloqueios empilhados, incluindo gravar numa
  tabela sem nenhum leitor.

Entregue:
- RELATORIO-TECNICO.md — o compilado autocontido: método, custos medidos, a
  tabela mestra dos 52 achados, o backlog em 6 blocos, decisões travadas.
- README.md — porta de entrada da pasta.
- 04 a 08 — as etapas novas (diagnóstico, simulador, plano/cockpit/IA, cadência,
  veredito).
- Anexos de evidência (arquivo:linha) em E1-E7, para reauditar sem refazer.
- E3-B registrado como decisão travada: o switch face x taxa no gate, com a
  fórmula e a armadilha do religamento do Nível 1 (que erra em silêncio, para
  menos).

Correções aplicadas na conferência final — um checador de caminhos sobre as 89
referências citadas pegou três erros, dois herdados dos docs originais:
GiroView.tsx:281-286 -> :265-268; lib/partes-relacionadas/receita-ficha.ts não
existe (é lib/receita-ficha.ts); e E2-A já tinha sido executado em 66771e8.

Handoff canônico novo (handoff-2026-08-20) com a recomendação de sequência:
começar pela Fatia A (E6-D, E6-A, E6-B) porque a Vertímetal já tem 31 meses
fechados e permite o check ao vivo que a trilha inteira ficou devendo — e não
pelo achado mais grave (E3-B), que só se prova num cliente em onboarding.

Fora do commit por decisão: docs/business/*.md (proposta comercial).
Dívida declarada: E5b (Caixa & Projeção) não foi auditada — em redesenho.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Executa o item 1, a cobrança dos 12 meses e o E6-A ampliado do plano de
correção do pente fino (docs/valor/pente-fino/PLANO-DE-CORRECAO.md).

O QUE ESTAVA QUEBRADO, na ordem em que a dona encontrava:

1. Ela fechava meses e o sistema NUNCA dizia quantos faltavam nem quais.
   `consultorPronto` recebia a contagem e a lista era descartada na chamada
   — a tela era estruturalmente incapaz de nomear os meses.
2. Ao chegar em 12, nada avisava que o Plano tinha destravado.
3. Se achasse o Plano sozinha, gerava a meta e NÃO conseguia travá-la: o
   único caller de `approvePlanoVooV2Action` vivia numa página que expulsa
   quem não é PRIVILEGED_ROLES — embora a action já fosse client-first.
4. Se perguntasse ao chat sobre o próprio caixa, com 12 meses fechados, a
   IA era instruída a "NÃO interpretar dados financeiros".

O (4) tinha três camadas, uma dentro da outra. O contador `analyses_completed`
é ZERO em todo projeto real — os únicos escritores (`/financeiro/concluir`,
`/financeiro/publicar`) não têm caller no front. Com zero, o primeiro `if` da
máquina de fases vencia sempre e a fase ficava presa em `onboarding`, cujo
guardrail proíbe leitura financeira. O gate de tools (`>= 30`) era só o
sintoma visível.

D10 — o modelo de 30 análises sai do sistema:
- a FASE passa a vir da cobertura da janela de 12 meses, não da contagem
- os 6 gates inline viram `isModoConsultor()`, que já existia e já estava certo
- some o "X/30 análises" que chegava na cara da cliente, e o guardrail deixa
  de proibir falar do número: o limite certo é não PROJETAR sobre série curta
- `analyses_completed` e `module_progress` saem do dossiê — eram escritos e
  nunca lidos, e custavam uma ida ao banco por compile

D3 — a escada de publicação é aposentada (8 caminhos):
o mês fecha por `apurar -> gate -> preview -> concluir`, e quem grava o snapshot
é o POST /dre/fechar-mes. A escada era caminho paralelo e morto.
- `get_dre_readiness` parava em `module_analyses.publish_status`, sempre
  'rascunho' — a tool de IA dizia "sistema bloqueado" para quem tem 32 meses
- `vw_company_context` carregava `30 AS analyses_total` literal em SQL
- a tela da DRE disparava um RPC por load para alimentar prop nunca renderizado

A cobrança (passos 5 e 9 da jornada): `Readiness` carrega `mesesFaltantes`, e a
Apuração ganha a faixa que nomeia os meses e leva até cada um. Ao fechar o 12º,
a mesma faixa vira o sinal de que o Plano destravou. Trocar a assinatura em vez
de somar campo opcional foi decisão: opcional apagaria a cobrança em silêncio.

E6-A + D11 (passos 10 e 11): `TravarMetaCard` no overview, sem gate de papel —
`travarMetaAction` checa vínculo e qualidade no caminho de ESCRITA. Ajustar a
receita cria VERSÃO NOVA, para o que o motor propôs continuar registrado ao
lado do que ela escolheu. `ajustarReceitaDaMeta` é pura e roda igual no client
(preview) e no server (gravação).

Correção à D11: só o RESULTADO segue a receita, e só na fase `aproximacao`.
Margem e endividamento são metas de NÍVEL por lei do modelo-meta-pacing §4 —
a tela diz o que não se move em vez de fingir.

Guarda nova: teste de paridade registro × manifesto de tools. Deletar uma tool
sem tirar o anúncio do prompt faz a IA chamar ferramenta inexistente em runtime,
no meio da conversa da cliente — foi o que quase aconteceu aqui.

Duas migrations aplicadas no remoto e conferidas no banco vivo (a view voltou
com 17 colunas). SNAPSHOT.md marcado como OBSOLETO: o dump é anterior a elas.

Build limpo · 1906 testes verdes · zero lint novo.
Falta o check ao vivo — toda a prova aqui é estática.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…ei do backlog

O relatório do pente fino respondeu "o que está quebrado". Este doc responde
"em que ordem consertar", e a ordem mudou por causa de duas decisões do Lucas.

D1 — autonomia total desde o dia 1: "tudo o que o sistema faz deve ser feito
100% pelo cliente; ter ou não consultor não diferencia. Se ele não quiser
fazer, contrata a gente — mas se quiser seguir sozinho, o sistema funciona."

Isso INVERTE o §9 do relatório, que pôs "baratear a entrada" em P4. Sob
autonomia total a cliente faz ingestão, categorização e apuração na PRIMEIRA
sessão: se fechar 32 meses são 32 wizards, ela desiste antes de ver o primeiro
número. Consertar a Curva ABC de uma DRE que ela nunca vai gerar não a segura.
Três achados sobem de P4 para P2 — e o pior deles é o E3-D: refechar histórico
exige service key, e sob D1 não existe quem rode.

A jornada canônica (§0) são 14 passos, do OFX ao chat. Ela é a lei: o que não
a serve, sai. O E7-D (cadência trimestral) saiu por isso — deixou de ser
decisão pendente.

Dois achados NOVOS, fora dos 52, encontrados ao conferir a jornada no código:
- o sistema TRANCA os 12 meses mas não os COBRA: a lista de meses faltantes
  era descartada na chamada de `consultorPronto`
- a meta NÃO é editável: as 7 actions do Plano de Voo não movem o número. A E6
  parou em "ela não abre a porta" e nunca testou o que teria depois dela

E o que já estava certo e eu ia mexer sem precisar: `janelaPorMesDeEntrada` é a
conta exata do Lucas (entrada ago/2026 => jul/2026…ago/2025), a âncora na
ENTRADA evita a janela deslizar e re-trancar o Plano, e o endividamento pelo
gate já está ligado.

Inclui o eixo B, que o relatório não viu: como a cliente COMPRA e PAGA. Não há
dependência de pagamento, tabela de assinatura nem auto-cadastro — a oferta
"Sistema" avulso da proposta hoje não tem máquina.

§3.3 registra o que já foi executado (commit anterior) e o que espera check ao
vivo.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…dilhas do caminho

Handoff canônico da sessão que executou o Bloco 0 quase inteiro. Lidera com a
retomada, como é o padrão da casa: **pega no E3-B**, que é o último item que
ainda quebra a corrente — e o único que decide se o check ao vivo pode usar uma
empresa real do ICP (quem desconta duplicata não fecha um mês hoje).

§5 traz tudo que a implementação precisa sem reabrir o relatório: a conta
(`face = líquido / (1 − taxa×prazo/30)`), os 3 invariantes de regressão, os
arquivos a tocar, e a armadilha — o caminho "cliente informa a taxa" JÁ existe
no motor e está morto; religá-lo como está erra em silêncio e PARA MENOS.

§4 registra as 6 armadilhas que valem mais que os consertos, todas encontradas
nesta sessão:
- a suíte passou 1887 testes com erro de sintaxe (nenhum teste importava os 2
  arquivos; quem pegou foi o tsc)
- `.next/dev/types/` NÃO se regenera no build — o build lê e falha citando rota
  deletada; `rm -rf .next` depois de deletar rota
- campo obrigatório (não opcional) fez o compilador achar 5 construtores de
  `Readiness` que o grep não achou
- classe Tailwind inexistente passa no lint (`text-alert-red` não existe)
- o hook de pre-commit leva ~3 min; dê 10 ao commit

§7 deixa o estado do banco explícito: as 2 migrations estão aplicadas e
conferidas no banco vivo (a view voltou com 17 colunas), mas `db/snapshot/` está
🔴 OBSOLETO e o `ESTRUTURA.md` NÃO foi regerado de propósito — ele é dump ×
código, e regerar sobre dump velho descreveria a view antiga.

CLAUDE.md passa a apontar o plano de correção como a lei do trabalho, com o
aviso de que a ORDEM do relatório do pente fino foi substituída pela D1.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…s novo

`lerMeta` ordenava por `version desc` sem filtro de status. Como
`saveRevisedVersionAction` grava a edicao do plano como `status='revisado'`
com `version = max+1`, a primeira edicao feita DEPOIS de a dona travar a meta
roubava a referencia — e nada na tela dizia que o numero havia mudado de dono.

Quatro consumidores liam por ali: Cockpit, Capital de Giro, Simulador e o
motor de Iniciativas. Todos herdam o conserto sem mudanca propria.

Pior que errar: quando o vigente e um documento v2, o parse falha e a meta
some — mesmo sintoma de "nao ha plano aprovado", indistinguivel na tela.

O predicado de "vigente" virou `queryPlanoVigente`, num lugar so. Escrever a
query duas vezes foi como o filtro de status se perdeu na primeira; agora quem
precisar de outra coluna passa por la em vez de remontar a query.

Os testes travam o FILTRO, nao o resultado — um fake que devolvesse so a linha
certa passaria com o bug de volta. Provado por mutacao: repondo `version desc`,
o teste falha.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
O extrato credita o LIQUIDO; a face esta no bordero, que a maior parte do ICP
nao tem em maos na hora de fechar. Ate aqui um campo de formulario trancava a
apuracao — e com ela as 7 perguntas do produto — para todo cliente que desconta
duplicata. Era o `antValido` do gate exigindo a face.

A face deixa de ser obrigatoria e vira um switch DO MES: "tenho o bordero"
(face por operacao) x "sei a taxa que pago" (o sistema deriva a face do
liquido). Pede % AO MES, como o banco cota; o prazo faz a conversao.

    t_periodo = taxa_a.m. x prazo_dias / 30
    face      = liquido / (1 - t_periodo)

100k a 2% a.m. por 45 dias -> face 103.092,78, desagio 3.092,78, ~27,6% a.a.

A ARMADILHA, e por que ela ficou impossivel em vez de documentada:

O caminho "cliente informa a taxa" ja existia (Nivel 1, `nivelGeral`) e estava
morto. Religa-lo como estava erra em silencio e PARA MENOS: sem face,
`valorBrutoDe` cai no `valor_bruto_real`, que e o liquido do extrato, e o
forward faz `desagio = liquido x taxa` — base errada, liquido resolvido que nao
bate com o extrato. A taxa incide sobre a FACE, dai a divisao por (1 - t).

Por isso `resolverAntecipacaoPorTaxaDeclarada` e funcao IRMA, nao um modo da
antiga: o caminho da taxa nunca chama `valorBrutoDe`. Reintroduzir o bug exige
reescrever a funcao, nao esquecer um `if`. Provado por mutacao: repondo o
forward, dois testes caem.

Os invariantes travados em teste:

  1. o liquido nunca e recalculado — e o credito do extrato, ao centavo;
  2. cada linha deriva a propria face pelo proprio credito (por isso o toggle
     agregado x detalhado sai da tela no modo taxa — nao muda um centavo);
  3. tudo sai com `fonte: "estimada_taxa"`, e a tela e obrigada a rotular;
  4. face - desagio = liquido ao centavo — o efeito na DRE e exatamente o
     dinheiro que entrou na conta. E o que torna uma face ESTIMADA segura de
     publicar em regime de caixa.

O arredondamento fecha pelo desagio, nao pela face, para que a identidade valha.
Taxa/prazo impossiveis (t >= 1) nao estimam nada: bloqueiam o gate, em vez de
gravar um numero que ninguem consegue auditar depois.

Rastreado ate o snapshot fechado antes de confiar: a RPC
`calculate_monthly_dre_v3` le `resultado->>'total_desagio'`/`'total_bruto'`, o
`dre-canonico` le `det.ant.resultado` e o impeditivo do gate testa
`resultado IS NOT NULL` — todos agnosticos ao modo. Nada recalcula a partir de
`dados`, que era onde o desagio poderia sumir calado.

Migration 20260820030000 (aplicada e conferida no banco vivo): o CHECK de
`dre_detalhamento.modo` ganha `'por_taxa'`. E modo proprio de proposito — quem
ler 'agregado' depois tem o direito de assumir que a face veio do dono.

Falta o check ao vivo: a tela nao foi vista rodando.

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

vercel Bot commented Aug 20, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
atr-os Ready Ready Preview Aug 20, 2026 10:42pm

@supabase

supabase Bot commented Aug 20, 2026

Copy link
Copy Markdown

Updates to Preview Branch (feat/partes-relacionadas-e-regra-token) ↗︎

Deployments Status Updated
Database Thu, 20 Aug 2026 22:41:12 UTC
Services Thu, 20 Aug 2026 22:41:12 UTC
APIs Thu, 20 Aug 2026 22:41:12 UTC

Tasks are run on every commit but only new migration files are pushed.
Close and reopen this PR if you want to apply changes from existing seed or migration files.

Tasks Status Updated
Configurations Thu, 20 Aug 2026 22:41:13 UTC
Migrations Thu, 20 Aug 2026 22:41:14 UTC
Seeding Thu, 20 Aug 2026 22:41:15 UTC
Edge Functions Thu, 20 Aug 2026 22:41:15 UTC

View logs for this Workflow Run ↗︎.
Learn more about Supabase for Git ↗︎.

O gate `gerar-estrutura.ts --check` falhava na CI. Provado por worktree que a
defasagem NAO veio dos commits do E3-B/E5-B: em `2849b23` — ultimo commit da
sessao da A1, antes deles — o gate ja falhava. A A1 deletou 8 caminhos de codigo
(escada de publicacao, tools de IA) sem regerar o mapa, e ninguem viu porque
esta branch ainda nao tinha PR: a CI nunca rodou nela.

O diff e so contagem de usos, e conta a historia das delecoes:
`module_analyses` 30->19, `get_dre_readiness` 3->1, `project_modules` 22->19.
`plano_voo_snapshots` sobe porque o predicado de "vigente" virou funcao
compartilhada (E5-B).

A ressalva da sessao passada — "nao regerar, o dump esta velho" — era razoavel
mas nao se confirmou: nada derivado do dump mudou (113 tabelas, 13 views, 73
funcoes, identicos). O arquivo ja descrevia o dump de 18/ago e continua
descrevendo; regerar so atualizou o que vem do CODIGO. As partes derivadas do
dump seguem velhas ate o re-dump — inclusive `vw_company_context`, que no banco
vivo ja perdeu as 2 colunas do placar.

Segue aberto (pre-existente, nao desta leva): 4 `.from()` para tabela
inexistente — budget_items, budgets, pulse_checks, system_notifications.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@BarryBits
BarryBits merged commit 986785a into main Aug 20, 2026
4 checks passed
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