From 5b345c559840e421bd11736b854a12bd987f73a2 Mon Sep 17 00:00:00 2001 From: Leonardo Date: Wed, 20 May 2026 16:22:25 -0300 Subject: [PATCH] relatorios: analise senior do modulo agenda pos C1-C13 Relatorio standalone HTML (com print CSS otimizado pra PDF export): - 10 paginas estruturadas - Sumario executivo + metricas + pontos fortes - 10 codes smells / dividas tecnicas detalhadas - 8 issues de UX - 7 riscos arquiteturais - 15 recomendacoes priorizadas (P0-P3) com esforco e impacto - Roadmap proposto em 3 horizontes - Apendices: 14 bugs do dia, pendencias, commits, status dos cenarios Visao senior eng: arquitetura solida em conceito, divida tecnica em execucao. Top 5 achados: 1. 3 hotspots >2.8k LOC cada (AgendaEventDialog 6k, MelissaLayout 4.3k) 2. Logica de status change triplicada (Melissa/Rail/Clinica) 3. billing_contracts.updated_at gotcha 4. Snapshot stale popover (mitigado mas estrutural) 5. Audit trail acumulando ruido Recomendacao chave: extrair status change orchestrator pra composable shared ANTES da replicacao Rail/Clinica. Senao replica os mesmos 14 bugs vezes 2. Para PDF: abrir relatorios/RELATORIO-AGENDA-2026-05-20.html no browser e Ctrl+P -> Salvar como PDF. Co-Authored-By: Claude Opus 4.7 (1M context) --- relatorios/RELATORIO-AGENDA-2026-05-20.html | 1445 +++++++++++++++++++ 1 file changed, 1445 insertions(+) create mode 100644 relatorios/RELATORIO-AGENDA-2026-05-20.html diff --git a/relatorios/RELATORIO-AGENDA-2026-05-20.html b/relatorios/RELATORIO-AGENDA-2026-05-20.html new file mode 100644 index 0000000..a910bb9 --- /dev/null +++ b/relatorios/RELATORIO-AGENDA-2026-05-20.html @@ -0,0 +1,1445 @@ + + + + +Relatório de Análise — Módulo Agenda · 2026-05-20 + + + + + + + + + + +
+

+ Análise Sênior · Módulo Agenda + Estado pós-bateria de testes C1–C13 · 2026-05-20 · Sistema AgênciaPSI +

+ +
+
+
Files
+
142
+
Arquivos no escopo agenda
+
+
+
LOC
+
15.6k
+
Linhas em hotspots-chave
+
+
+
Bugs/dia
+
14
+
Corrigidos em 20/05
+
+
+
Commits/dia
+
15
+
Working tree clean
+
+
+ +

0Sumário Executivo

+ +

+ O módulo de agenda do AgênciaPSI atravessou uma rodada estrutural de validação + via 13 cenários do documento vivo src/docs/agenda-compromisso-financeiro-cenarios.html. + A bateria expôs 14 bugs reais em apenas um dia — todos corrigidos — + e revelou um padrão recorrente: regressões silenciosas em fluxos + paralelos, particularmente em interações com billing_contracts e + financial_records. +

+ +

+ A engenharia do módulo é funcionalmente robusta — os fluxos + críticos (status change, pacote saldo, pacote upfront, multa, antecipação, + edit imutável) operam corretamente após os fixes. Mas a base de código carrega + sintomas que devem ser endereçados antes da replicação para Rail e Clínica, + sob risco de duplicação de dívida. +

+ +
+ Verdict resumido: arquitetura sólida em conceito, dívida + técnica acumulada em execução. Refatoração de 3 hotspots e adoção de tipagem + forte resolveriam ≥60% dos bugs futuros antes de virarem produção. A + replicação Rail/Clínica é o ponto natural de inflexão para consolidar. +
+ +

Top 5 achados

+
    +
  1. + 3 hotspots ultrapassam 2,8k LOC cada — + AgendaEventDialog.vue (6.070), MelissaLayout.vue (4.331), + AgendaTerapeutaPage.vue (3.567). Refator sustentado é inviável neste estado. + P0 +
  2. +
  3. + Lógica de status change triplicada entre Melissa, Rail e + Clínica. Bugs corrigidos hoje em Melissa ainda persistem nas outras duas implementações. + P0 +
  4. +
  5. + Gotcha billing_contracts.updated_at — + coluna inexistente. UPDATEs falhavam silenciosamente em + Promise.allSettled. Patch tipo "trigger SQL" + eliminaria a categoria inteira. + P1 +
  6. +
  7. + Snapshot stale em popovereventoSelecionado.value + é cópia, não referência reativa. Padrão repetido em outros componentes do + Melissa. Ergonomia ruim para mutações otimistas. + P1 +
  8. +
  9. + Audit trail acumulando ruído — ciclos + antecipar/revogar/realizar geram records cancelled que poluem + a view /financeiro sem filtro. Falta UI dedicada de auditoria. + P2 +
  10. +
+ + +
+ + + + +
+

1Estado atual

+ +

1.1 Distribuição de complexidade (hotspots)

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
ArquivoLOCTipoObservação
AgendaEventDialog.vue6.070Componente Vue monolítico≥7 responsabilidades distintas: cadastro, recorrência, billing, multa, pacote, lock, preview
MelissaLayout.vue4.331Layout containerContainer + ~30 handlers + 8 dialogs montados; deveria ser shell
AgendaTerapeutaPage.vue3.567Página Rail (legacy)Implementação paralela com lógica duplicada
useMelissaAgenda.js2.863Composable monolíticoBulk-load, normalize, handlers, status change, decisions, applies — tudo aqui
AgendaClinicaPage.vue2.811Página Clínica (legacy)3ª implementação paralela; receberá os mesmos bugs
MelissaEventoPanel.vue1.010PopoverAceitável, mas concentra lógica de derivação (paymentLabel, contractInfo, ações)
useAgendaEventLifecycle.js685Composable de lifecycleCarregamento, edit, delete; saudável em tamanho
AgendaStatusChangeConfirmDialog.vue671Dialog dedicadoRecém-ampliado com bloco reverse (Fase 5); ainda dentro do aceitável
+ +

+ Total nos hotspots acima: ~22k LOC. Em escala de manutenção, + qualquer arquivo >1.000 LOC é candidato a refator; >2.500 LOC é débito + crítico — toda navegação demanda Ctrl+F. +

+ +

1.2 Estrutura de pastas (recap)

+
src/
+├── features/agenda/
+│   ├── components/        (15 .vue — DialogV2, Toolbar, Calendar, etc)
+│   ├── composables/       (20 .js — lifecycle, events, services, etc)
+│   └── pages/             (5 .vue — Terapeuta, Clínica, Recorrencias, etc)
+├── layout/melissa/
+│   ├── composables/       (useMelissaAgenda 2.863 LOC)
+│   ├── MelissaLayout.vue  (shell + handlers)
+│   ├── MelissaAgenda.vue  (FullCalendar wrapper)
+│   ├── MelissaEventoPanel (popover)
+│   └── ...
+└── components/agenda/     (AgendaEventoFinanceiroPanel, etc)
+ +

1.3 Cobertura de testes

+

+ src/features/agenda/composables/__tests__/ tem 7 specs Vitest + cobrindo composables principais (events, lifecycle, composer, picker, recurrence). + Não há testes E2E. Toda validação de fluxo financeiro + (status change, pacote, antecipar) depende de bateria manual contra o documento + vivo HTML. +

+ +
+ Métrica de risco: 14 bugs encontrados num único dia de + teste manual num único persona (Melissa). Extrapolando para Rail/Clínica + sem refator antes, esperar 25–35 bugs equivalentes na replicação. +
+
+ + + + +
+

2Pontos fortes

+ +

Análise reconhece méritos antes de criticar. O que está bem feito:

+ +

2.1 Cobertura de cenários documentada

+

+ O arquivo src/docs/agenda-compromisso-financeiro-cenarios.html + é uma boa prática rara: doc vivo que descreve cada cenário + com mockup interativo + critério de teste. Funciona como spec executável + informal. O addendum de C10 que adicionamos hoje preservou o histórico + de divergências. + Manter +

+ +

2.2 Audit trail em financial_records.notes

+

+ Todas as transições de cancelamento, reversão e revogação registram entrada + cronológica em notes com formato [YYYY-MM-DD] motivo. + Permite reconstruir o histórico financeiro de qualquer sessão. + Estrutura simples mas efetiva — não criou tabela de auditoria paralela. + Manter +

+ +

2.3 Reverse transition trava (recém-implementada)

+

+ O dialog AgendaStatusChangeConfirmDialog agora cobre 4 contextos + distintos: realizada (avulsa/pacote), faltou/cancelado (com multa+saldo), + realizada com paid pré-existente (antecipação), e reversão para agendado + (com cancelar records + devolver saldo). É o ponto único de decisão + para mudanças com impacto financeiro. + Manter +

+ +

2.4 Bubble @cobranca-atualizada

+

+ Sub-componentes (panel financeiro, dialog) emitem evento que sobe até + MelissaLayout e dispara M.refetch(). Padrão limpo + para propagar updates de pagamento sem acoplar pais e filhos via store global. + Manter — replicar em Rail/Clínica +

+ +

2.5 Lock em sessão encerrada

+

+ Sessões em cancelado/faltou agora bloqueiam + "Editar sessão" + transições de status (exceto "Agendada" como caminho + explícito de recuperação) + label e badge cientes do estado. Evita + operações financeiras inadvertidas em sessões que não aconteceram. + Manter +

+ +

2.6 Schema com constraints adequadas

+

+ financial_exceptions_charge_chk restringe valores possíveis + de charge_mode. financial_records.status_check + enumera estados válidos. Constraints de check ao invés de validação + apenas em código — defesa em profundidade. + Manter +

+ +

2.7 Quick-create inline

+

+ Em vez de navegar para outro form quando falta uma dep (serviço, convênio, + paciente), abre quick-create por cima. Preserva contexto do usuário. + Padrão consistente. + Manter — padrão de design +

+ +

2.8 Convenção de variantes visuais

+

+ Cores semânticas consistentes — --ok (verde realizada), + --warn (amarelo falta), --danger (vermelho cancelar), + --info (azul agendada recém-adicionada). Padrão se mantém + entre popover, dialog e calendário. + Manter +

+
+ + + + +
+

3Dívidas técnicas / code smells

+ +

3.1 Monólitos Vue (≥2.500 LOC)

+

+ AgendaEventDialog.vue com 6k LOC concentra: cadastro de evento, + recorrência, billing config, multas, pacote saldo/upfront, lock de cobrança + emitida, preview da série, edição de ocorrência única vs todos. Sete + responsabilidades distintas em um único componente — viola SRP. +

+

+ Sintomas operacionais já observados: edição perigosa (qualquer alteração + precisa varrer todo o arquivo), regressões frequentes em flows não-relacionados, + teste unitário não consegue isolar um caso sem mockar 80% do contexto. +

+

+ Quebra natural: +

+
    +
  • AgendaEventDialogShell — orquestrador, gerencia steps e props
  • +
  • AgendaEventDialogStepBasic — paciente, data, hora, modalidade
  • +
  • AgendaEventDialogStepRecurrence — recorrência + preview de série
  • +
  • AgendaEventDialogStepBilling — service/insurance/billing type/lock
  • +
  • AgendaEventDialogStepConfirm — preview final + diff antes/depois
  • +
+ +

3.2 Triplicação de lógica (Melissa / Rail / Clínica)

+
+ Maior risco arquitetural identificado. Os bugs corrigidos + hoje em useMelissaAgenda._applyStatusDecisions (cobrança dupla, + updated_at gotcha, link universal) não foram propagados + para AgendaTerapeutaPage nem AgendaClinicaPage. +
+

+ O caminho que vai dar errado: replicar manualmente os fixes para + cada um dos 3 contextos. Multiplica esforço por 3 e introduz drift. +

+

+ Caminho correto: extrair em useAgendaStatusChangeOrchestrator.js + consumido pelos três (Melissa via composable, Rail/Clínica via importação direta). + Tornar _applyStatusDecisions, _loadStatusChangeContext, + _needsConfirmDialog e _applyStatusUpdateOnly em + funções puras com deps por injeção. +

+ +

3.3 Promise.allSettled escondendo falhas

+

+ Padrão recorrente: tasks.push(...); await Promise.allSettled(tasks). + Quando uma das tasks falha, vira {status:'rejected'} e o toast + warn genérico aparece — fácil de ignorar. +

+

+ Caso real (corrigido hoje): UPDATE em billing_contracts com + campo updated_at inexistente. Falha silenciosa por semanas. + Foi descoberto manualmente quando o saldo não incrementava em teste E2E manual. +

+

+ Política sugerida: +

+
    +
  • Tasks com efeitos colaterais financeiros usam await sequencial + + try/catch + toast/console específico por falha
  • +
  • Promise.allSettled apenas para operações realmente + independentes (ex: pré-buscas de UI auxiliar)
  • +
  • Logger estruturado com tags por domínio + ([agenda/status], [agenda/billing]) ao invés de + console.warn espalhado
  • +
+ +

3.4 normalizeForMelissa whitelist silenciosa

+

+ A função normalizeForMelissa(r) retorna um objeto com campos + explícitos. Campos no r original que não estão no return + somem silenciosamente. Bug do dia: owner_id + não estava no return → após o watch sync introduzido, virava null → + INSERT em financial_records violava NOT NULL. +

+

+ Memória existente (project_pickdbfields_whitelist) documenta + bug análogo no pickDbFields. Padrão repetido. +

+

+ Mitigação imediata: documentar e auditar manualmente todos os campos + necessários. Mitigação estrutural: TypeScript ou JSDoc tipado + no return — o linter sinalizaria campos faltantes antes do runtime. +

+ +

3.5 Snapshot stale em refs reativas

+

+ eventoSelecionado.value = ev copia a referência. Quando o + eventos computed recalcula com novo objeto, a ref guardada não + acompanha. Padrão repetido em outros lugares do Melissa (já há memória + documentando). +

+

+ Mitigação aplicada hoje: watch em M.eventos com lookup por + id + recurrence_id/date (cobre transição virtual→materializada). +

+

+ Mitigação estrutural: trocar o pattern por const selectedId = + ref(null) + const selectedEvent = computed(() => + eventos.value.find(e => e.id === selectedId.value)). Reatividade + nativa, zero watches, zero race conditions. +

+
+ + + + +
+

3 · Dívidas técnicas (continuação)

+ +

3.6 Race conditions em refetch()

+

+ _reloadRange dispara queries paralelas (eventos, virtuais, + bloqueios, payment state, propagação cross-week). Operações UI sequenciais + rápidas (antecipar→revogar→antecipar) podem ler ctx antes do refetch anterior + propagar, levando a decrements/increments com base em valores stale. +

+

+ Mitigação aplicada hoje no bloco reverseRestoreSaldo: refetch + explícito do billing_contracts.sessions_used direto do DB + imediatamente antes do UPDATE. Padrão "lê fresh, escreve + consciente". +

+

+ Mitigação estrutural: mover lógica de increment/decrement de saldo para + RPC server-side com lock pessimista (SELECT ... FOR UPDATE). + Elimina race condition no nível de banco. +

+ +

3.7 RPCs sem idempotência forte

+

+ create_financial_record_for_session foi corrigido para ignorar + cancelled em idempotência (commit c23d0a5, já + documentado em memória). Mas o handler do antecipar fazia UPDATE manual + em record cancelled após chamar a RPC, reativando-o como paid. + Audit trail polluído. +

+

+ Princípio: RPCs financeiras nunca devem reusar records cancelados. + Toda operação que cria cobrança gera record novo. Cancellation é terminal. +

+ +

3.8 Pattern updated_at inconsistente entre tabelas

+

+ Algumas tabelas têm trigger set_updated_at automático + (ex: financial_exceptions), outras não + (billing_contracts). Código cliente assume incorretamente + que todas têm. Foi origem direta do bug do updated_at. +

+

+ Tabela atual: +

+ + + + + + + + + + + +
Tabelatem updated_at?tem trigger?
financial_recordspresume-se sim
financial_exceptionstrg_financial_exceptions_updated_at
agenda_eventospresume-se sim
recurrence_rulespresume-se sim
billing_contracts
+

+ Recomendação: adicionar updated_at + trigger em + billing_contracts via migration. Tornar consistente em todas + as tabelas de domínio operacional. +

+ +

3.9 RLS pode mascarar erros

+

+ UPDATEs em RLS-protected tables retornam silenciosamente 0 rows quando + a policy filtra. Sem RETURNING explícito o cliente não detecta. + No fluxo de status change atual, vários UPDATEs assumem que rodaram. +

+

+ Mitigação: adicionar .select() ou .select('id').single() + em UPDATEs sensíveis; falhar explicitamente se 0 rows afetadas. +

+ +

3.10 Estado distribuído sem fonte da verdade

+

+ Mesma informação (e.g. sessions_used) lida de múltiplas fontes: + ctx.billingContract.sessions_used (snapshot do load), + ev.contract.sessionsUsed (via ruleContractMap), DB fresh, + cached em variáveis locais. Pontos de divergência são pontos de bug. +

+

+ Princípio: single source of truth por operação. + Antes de UPDATE crítico, fresh-fetch + uso somente da leitura recente. + Em UIs reativas, derivar de um único store/computed. +

+
+ + + + +
+

4Issues de UX

+ +

4.1 Sobreposição semântica: Usar / Antecipar / Realizada

+

+ Em pacote saldo, três botões diferentes podem operar sobre a mesma sessão: +

+
    +
  • Usar (popover saldo card) → materializa + realizada + cria record + decrementa
  • +
  • Antecipar pagamento (popover financeiro) → materializa + cria record paid SEM decrementar
  • +
  • Realizada (popover status group) → dialog forward com checkbox "Gerar cobrança"
  • +
+

+ Cada um cobre um caso de uso ligeiramente diferente, mas o overlap confunde. + Durante o teste do dia, usuário ficou perdido entre os três caminhos. +

+

+ Proposta: unified action menu. Um único botão "Marcar sessão" + abre menu contextual: +

+
┌─ Marcar sessão como… ─────────────┐
+│ ✓ Realizada                       │
+│   ├─ Já recebi (PIX/dinheiro/...) │
+│   └─ Cobrar depois (saldo/avulsa) │
+│ ⚠ Faltou (+ multa/saldo)          │
+│ ✕ Cancelar (+ regra)              │
+├───────────────────────────────────┤
+│ 💰 Antecipar pagamento (futura)   │
+└───────────────────────────────────┘
+ +

4.2 Recovery único via "Agendada" — pouco descobrível

+

+ Sessão em cancelado tem 4 botões disabled (Realizada/Falta/ + Reagendar/Cancelar) com tooltip "use Agendada para reativar". O + aprendizado depende do hover no tooltip — primeiro encontro pode ser frustrante. +

+

+ Proposta: badge inline ao lado dos botões disabled, ou banner amarelo no topo + do popover: "Sessão encerrada. Clique Agendada para reabrir e + permitir novas ações." +

+ +

4.3 Múltiplos records cancelled poluindo audit

+

+ Ciclos antecipar/revogar/realizar geram records cancelled empilhados. + Em /financeiro o filtro padrão .neq('status','cancelled') + esconde do user normal, mas em queries de debug ou em telas + administrativas a poluição aparece. +

+

+ Propostas: +

+
    +
  • UI de "histórico" da sessão (drawer lateral) listando todos os + records, agrupando cancelled como timeline colapsada
  • +
  • Soft-archive: após N dias com sessão em terminal state, mover records + cancelled para tabela de audit (financial_records_archive)
  • +
+ +

4.4 Dialog "Como cobrar?" / "Já recebi?" — refatorado, mas inspecionar mais flows

+

+ Hoje foi refatorado o bloco "Cobrança no pacote saldo" para ter + sub-question explícita "A sessão já foi paga?". O mesmo padrão deve + ser auditado em outros lugares onde aparece "método de pagamento" com + opções mistas: +

+
    +
  • Dialog Antecipar pagamento — ainda usa + paymentMethodOptionsCobranca com "Já recebi — PIX". Padrão + novo deveria propagar para consistência.
  • +
  • Dialog Bloqueio, Dialog Service Quick — mesma checagem.
  • +
+ +

4.5 "A cobrar R$ X" enganoso para pacote saldo

+

+ Fix aplicado hoje: label muda para "Aguardando uso do pacote" (saldo) ou + "Coberta pelo pacote (upfront)". Verificar consistência em: +

+
    +
  • Card no FullCalendar — badge $ suprimido em pacote-tied state=none ✓
  • +
  • Dialog AgendaEventoFinanceiroPanel — verificar se exibe label correta
  • +
  • /financeiro lista — pacote saldo session sem record não deve aparecer + como "a cobrar"
  • +
+ +

4.6 Feedback de erro fraco em handlers async

+

+ Toasts genéricos "Falha ao processar mudança de status" não ajudam usuário. + Quando RLS bloqueia ou constraint quebra, mensagem deveria ser específica: +

+
    +
  • "Cobrança paga não pode ser editada — use estorno em /financeiro"
  • +
  • "Saldo do pacote insuficiente — disponíveis: 0 de 4"
  • +
  • "Sessão tem cobrança vinculada — revogue antes de cancelar"
  • +
+ +

4.7 Botão "Antecipar pagamento" → "Revogar pagamento" alternando

+

+ Padrão implementado hoje (alterna baseado em isAntecipacaoAtiva). + Pode ser confuso para usuário que esquece do que clicou — botão "muda + sozinho". Considerar: +

+
    +
  • Manter "Antecipar pagamento" sempre visível; quando há pagamento + ativo, exibir banner inline acima: "Pagamento antecipado: R$ X via + PIX em 20/05. "
  • +
  • Mais info, menos ambiguidade — mostra estado e ação no mesmo bloco.
  • +
+ +

4.8 Reverse dialog: "manter cobrança ativa" potencialmente perigoso

+

+ No dialog reverse, radio "Manter cobrança ativa" deixa user reativar sessão + com record pending órfão. É uma escolha legítima mas raramente intencional. + Considerar adicionar warning quando seleciona "manter". +

+
+ + + + +
+

5Riscos arquiteturais

+ +

5.1 Replicação Rail/Clínica sem refator = duplicação de dívida

+
+ Replicar os fixes do dia em AgendaTerapeutaPage e + AgendaClinicaPage mantém as 3 implementações divergentes. + Primeira sprint após replicação vai gastar tempo descobrindo "por que + funciona em Melissa mas não em Clínica" (ou vice-versa). +
+

+ Sequência recomendada antes da replicação: +

+
    +
  1. Extrair status change orchestrator para composable shared
  2. +
  3. Migrar Melissa para usar o composable shared
  4. +
  5. Testar (a bateria já existe — 13 cenários)
  6. +
  7. Migrar Rail/Clínica para o composable shared (1 commit por persona)
  8. +
+ +

5.2 Estado distribuído sem migration testada

+

+ Várias decisões de domínio são implícitas: +

+
    +
  • Sessão paid + status=cancelado → estado válido ou inválido?
  • +
  • Sessão sem billing_contract_id mas com record paid em pacote saldo → + órfã ou intencional?
  • +
  • Contract com status='completed' mas sessions_used < total — + possível após sessions_used decrementar via reverse
  • +
+

+ Não há documentação formal das invariantes do domínio. Consequência: cada + bug exige reconstrução mental do que deveria ser válido. +

+

+ Recomendação: documento de invariantes do domínio em + Obsidian/Brain/wiki/agenda-invariantes.md + (futuramente) + constraints SQL ou triggers checando os mais críticos no banco. +

+ +

5.3 Multi-tenant: tenant_id manual em todo INSERT

+

+ Cada INSERT em financial_records, agenda_eventos, + billing_contracts requer tenant_id explícito. + Esquecer = constraint violation, ou pior, escrever no tenant errado. +

+

+ Mitigação parcial: RLS bloqueia leitura cross-tenant. Mas writes ainda + dependem do código cliente. Pattern de "service layer" com tenant injetado + centralmente reduziria a área de risco. +

+ +

5.4 Edge functions desacopladas da migration story

+

+ Edge functions (asaas-webhook, evolution-whatsapp, etc) operam direto no DB + e podem deixar state inconsistente com o que o frontend esperar. + Não foi escopo deste relatório, mas merece auditoria separada. +

+ +

5.5 Ausência de feature flags

+

+ Cada fix do dia foi merged direto pra main e deploy implícito. Não há + feature flag para experimentar "novo dialog reverse" em produção sem + expor para todos. Em sistema com clientes pagantes, é fragil. +

+ +

5.6 Backfills manuais via psql não rastreados

+

+ Durante o teste, vários UPDATE billing_contracts SET sessions_used=X + foram aplicados manualmente para limpar estado. Em produção isso é + impraticável e arriscado. Falta: +

+
    +
  • Migration ferramenta para "operações de correção de dados" + com versionamento e rollback
  • +
  • Audit log de operações administrativas sobre dados
  • +
+ +

5.7 Indicador de saldo derivado vs persistido

+

+ billing_contracts.sessions_used é INTEIRO incrementado/ + decrementado manualmente. Verdade "real" é o COUNT de records associados. + Risco: drift entre os dois ao longo do tempo. +

+

+ Alternativa: tornar sessions_used uma VIEW computed via + COUNT(*) FROM financial_records WHERE billing_contract_id=X AND + status IN ('paid','pending'). Performance pode requerer materialização + ou índice; mas elimina classe inteira de bugs de sync. +

+
+ + + + +
+

6Recomendações priorizadas

+ +

+ Priorização: P0 = antes da próxima replicação (Rail/Clínica). + P1 = sprint atual ou próximo. P2 = quando + couber, valor compounding. P3 = ideal mas não bloqueante. +

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
#RecomendaçãoPrioEsforçoImpacto
R1 + Extrair status change orchestrator para composable shared + useAgendaStatusChangeOrchestrator.js. Funções puras (loadContext, + needsDialog, applyDecisions, applyStatusUpdate). Melissa migra primeiro, + depois Rail/Clínica importam. + P04–6hAlto · elimina trio
R2 + Migration: adicionar updated_at + trigger em + billing_contracts. Elimina o gotcha que custou + horas hoje. Padroniza com outras tabelas. + P030minAlto · elimina classe
R3 + Auditar Promise.allSettled em fluxos financeiros. + Trocar por awaits sequenciais com try/catch dedicado. Adicionar + logger estruturado ([agenda/billing/saldo]). + P02–3hAlto · visibilidade
R4 + Documentar invariantes do domínio em + Obsidian/Brain/wiki/agenda-invariantes.md. Estado válido + de status × records × contract. Referência consultável. + P01–2hMédio-alto · onboarding
R5 + Refatorar AgendaEventDialog.vue em 5 + sub-componentes (Shell, StepBasic, StepRecurrence, StepBilling, + StepConfirm). Manter API atual; migration interna. + P12–3 diasAlto · manutenção
R6 + Trocar eventoSelecionado snapshot por + selectedId + computed. Elimina snapshot stale. + Replicar em outros refs do Melissa. + P11–2hMédio · estabilidade
R7 + Auditoria visual da sessão — drawer com timeline + de records (pending/paid/cancelled), com cancelled colapsados por + default. Reduz poluição em /financeiro. + P11 diaMédio · UX
R8 + RPCs server-side para increment/decrement de saldo + com SELECT FOR UPDATE. Elimina race condition em + fluxos rápidos. + P13–4hMédio-alto · correctness
R9 + Mensagens de erro específicas em handlers de + status/billing. Catalogar erros típicos (RLS, constraint, + state inválido). + P12hMédio · UX
R10 + TypeScript ou JSDoc tipado em + normalizeForMelissa, pickDbFields e funções + de transformação de dados. Lint flagga campos faltantes. + P22–4h por áreaAlto longo prazo
R11 + Testes E2E (Playwright) dos 13 cenários. Substitui + bateria manual. CI roda em cada PR. + P21 semanaAlto · confiança
R12 + Unified action menu "Marcar sessão" substituindo + os 3 botões sobrepostos (Usar/Antecipar/Realizada). + P21 diaMédio · clarity
R13 + Feature flags via tenant_settings ou + biblioteca específica. Permite rollout gradual de mudanças no dialog. + P31 diaBaixo curto, alto longo
R14 + sessions_used como VIEW computed + em vez de coluna incrementada. Elimina drift de sincronia. + Avaliar performance. + P31–2 diasAlto correctness, médio risco
R15 + Ferramenta de "data fix" versionada para correções + administrativas em produção. Audit log obrigatório. + P32–3 diasAlto operacional
+ +
+ Sequência sugerida: R2 (migration updated_at) → R1 (extrair + orchestrator) → R4 (doc invariantes) → R3 (audit Promise.allSettled) → + replicar Rail/Clínica. Tudo P0 entrega antes da próxima replicação reduz + drasticamente o risco de regressão. +
+
+ + + + +
+

7Roadmap proposto

+ +

Sprint imediato (1–2 semanas) — Consolidação P0

+
    +
  1. + Dia 1 · R2 (migration updated_at + + trigger billing_contracts) · 30 min +
  2. +
  3. + Dia 1–2 · R3 (audit Promise.allSettled) · 2–3h +
      +
    • Grep Promise.allSettled em todo src/
    • +
    • Reescrever blocos financeiros para awaits sequenciais
    • +
    • Logger por domínio
    • +
    +
  4. +
  5. + Dia 2–3 · R4 (doc invariantes) · 1–2h +
  6. +
  7. + Dia 3–5 · R1 (extrair orchestrator) · 4–6h +
  8. +
  9. + Dia 5–6 · Replicar fixes pra Rail (AgendaTerapeutaPage) + via composable shared · 4–6h +
  10. +
  11. + Dia 6–7 · Replicar fixes pra Clínica (AgendaClinicaPage) + · 4–6h +
  12. +
  13. + Dia 8 · Bateria de teste manual nos 3 personas · 1 dia +
  14. +
+ +

Sprint seguinte (2–3 semanas) — Refator P1

+
    +
  1. R5 — Refator AgendaEventDialog em 5 sub-componentes (2–3 dias)
  2. +
  3. R6 — Snapshot stale fix global (2h)
  4. +
  5. R7 — Drawer de audit history (1 dia)
  6. +
  7. R8 — RPCs increment/decrement com lock (3–4h)
  8. +
  9. R9 — Mensagens de erro catalogadas (2h)
  10. +
  11. Iteração de UX C12 (consolidar Usar/Antecipar/Realizada) · 1 dia
  12. +
+ +

Próximo trimestre — Maturação P2

+
    +
  1. R10 — JSDoc tipado em normalizers (incremental por arquivo)
  2. +
  3. R11 — Testes E2E Playwright dos 13 cenários (1 semana)
  4. +
  5. R12 — Unified action menu (refator UX)
  6. +
  7. Auditoria de Edge Functions vs frontend (sprint dedicado)
  8. +
+ +

Backlog longo prazo P3

+
    +
  • R13 — Feature flags infrastructure
  • +
  • R14 — sessions_used como VIEW
  • +
  • R15 — Data-fix tooling
  • +
  • Migração TypeScript completa (decisão de produto)
  • +
+ +

Considerações finais

+ +

+ O módulo de agenda é o coração operacional do AgênciaPSI — + é por onde o terapeuta vive o dia. Investimentos em estabilidade aqui têm + retorno desproporcional: cada bug evitado é uma sessão real do paciente + que não vira incidente. +

+ +

+ A bateria de testes do dia provou que a metodologia funciona: + 13 cenários documentados + bateria manual + iteração rápida descobriu mais + bugs em 1 dia do que toda a auditoria anterior. Manter esse loop e + automatizá-lo (R11) é o investimento de maior alavancagem. +

+ +

+ O risco maior não é o que já encontramos — esses estão + consertados. É o que ainda não foi exercitado em Rail e Clínica. A janela + para consolidar antes de replicar é agora. +

+ +

+ A boa notícia: a base é sólida. Os bugs encontrados foram de execução, + não de design. As decisões arquiteturais maiores (SimplePractice-style + invariantes, status change centralizado, audit trail em notes, separação + avulsa/pacote) provaram-se corretas em uso real. O trabalho + é polir, não reescrever. +

+ +
+ Relatório gerado em 2026-05-20 · Análise pós-bateria C1–C13 · Sistema AgênciaPSI v5.0 · +
+ Baseado em: 15 commits do dia, 14 bugs corrigidos, 9 memórias de sessão, + ~22k LOC inspecionadas nos hotspots. +
+
+ + + + +
+

8Apêndices

+ +

A · Bugs corrigidos em 2026-05-20

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
#BugSeveridadeCommit
1Cobrança dupla em multa: _applyStatusDecisions inseria + multa mas deixava original pendingCríticod6423da
2'fixed' vs 'fixed_fee' off-by-key em + calcChargeAmountMédio (dormente)d6423da
3Label "Como cobrar?" com options "Já recebi" misturadas — usuário + marcou paid achando que era cobrarAlto UX079509e
4billing_contracts.updated_at inexistente causando + UPDATE silently falhar em Promise.allSettledCrítico16dfa02
5Reverse transitions deixavam multa órfã (faltou→agendado)Alto5684297
6consumeSaldo não amarrava billing_contract_id + ao eventoAlto3f3f2ac
7Badge $ amber em sessão canceladaMédio753182c
8paymentLabel usava ev.price em vez de + paymentAmount para pendingMédio753182c
9"Gerar fatura" disponível em sessão encerradaAlto753182c
10_reloadRange não destruturado em + _buildHandlers(deps)Médio753182c
11Faltou+multa-sem-consume não amarrava + billing_contract_idMédio5965b05
12Re-antecipar reutilizava record cancelled (audit trail confuso)Altob5e00a7
13Popover snapshot stale após materialização virtual→realAltob5e00a7 + f83315b
14normalizeForMelissa não expunha owner_id + → INSERT null violationCrítico7d2a405
+ +

B · Pendências mapeadas em memória (pós-C13)

+
    +
  • project_c12_antecipar_iterar — iterar UX da antecipação (deferido pelo user)
  • +
  • project_melissa_popover_snapshot — refator pra computed (mitigado mas não estrutural)
  • +
  • project_billing_contracts_no_updated_at — gotcha documentado, fix em R2
  • +
  • project_pendencia_doc_ajuda — doc completa para área de ajuda pós Fase 9
  • +
  • Cleanup: sessão Otto Rank 5364f631 com record pending leftover
  • +
+ +

C · Commits do dia (20/05)

+
4da0bc2 HANDOFF + log: C12 deferred · testando C13
+f83315b agenda: popover watch acompanha transicao virtual->materializada
+7d2a405 agenda: normalizeForMelissa expoe owner_id/tenant_id/contract_id
+b5e00a7 agenda: popover sincroniza com eventos + antecipar nao reusa cancelled
+272c804 agenda: revogar antecipacao de pagamento
+00c4168 agenda: C12 prep — detectar paid pre-existente em pacote saldo realizada
+9ead3fd HANDOFF + log: C11 fechado · 4/4 sub-testes OK · proximo C12
+5965b05 agenda: link universal pacote + refresh saldo no reverse
+45984e8 agenda: label + badge cientes de pacote em sessoes state=none
+3f3f2ac agenda: consumeSaldo agora amarra billing_contract_id no evento
+5684297 agenda: reverse transition trava (Agendada apos artefatos)
+16dfa02 agenda pacote saldo: fix root cause + sequential awaits
+079509e agenda: dialog pacote saldo realizada — 2 sub-questions claras
+7dc7dce wiki: log session C10 fechado completo
+1e74a11 HANDOFF: C10 fechado · 5/5 sub-testes OK · proximo C11
+753182c agenda: C10 pos-test fixes + lock sessao encerrada + addendum doc
+3caf579 agenda popover: botao Agendada + fixes pos-C10/B
+d6423da agenda: pre-C10 fix _applyStatusDecisions cancela pendingRecord
+ +

D · Status dos cenários documentados

+ + + + + + + + + + + + + +
#CenárioPersona testadaStatus
C1–C9Bloqueio, avulsas, recorrência, pacote, per_sessionMelissaOK (anteriormente)
C10Status change avulsa (5 sub-testes)MelissaOK
C11Status change pacote saldo (4 sub-testes)MelissaOK
C12Antecipar pagamentoMelissaDB OK · UX a iterar
C13Edit cobrada (imutabilidade)MelissaEm teste agora
Rail (AgendaTerapeutaPage)RailPendente — após refator
Clínica (AgendaClinicaPage)ClínicaPendente — após refator
+ +

E · Referências internas

+
    +
  • src/docs/agenda-compromisso-financeiro-cenarios.html — doc vivo dos 13 cenários (com addendum C10)
  • +
  • HANDOFF.md — estado de continuidade da sessão
  • +
  • Obsidian/Brain/log.md — log cronológico
  • +
  • Obsidian/Brain/wiki/agenda-compromisso-fluxo.md — fluxo conceitual
  • +
  • ~/.claude/projects/.../memory/MEMORY.md — índice de memórias persistentes
  • +
+ +
+ Fim do relatório · 10 páginas · Geração local sem dependências externas · + Para PDF, abrir no browser e Ctrl+P → Salvar como PDF. +
+
+ + +