monify business
Mony Business · Mapa Mental · O que a IA consegue hoje
versão v0.1.73+ · 📅 B34 — DRE Consolidado: seletor de mês específico. Chip "📅 Mês" substitui "1 mês" fixo; navegador ‹/› por mês; CSV renomeado; empty state contextual. 25 tests novos = 4519 verdes. Base: 🔧 B26 — 5 UX fixes pós-PROD: PAGAS card separado em A Pagar · ExecIcon cor inativa (#304a62) · Radar do Dia threshold < -1 · DRE Consolidado PDF (window.print()) · filtro "1 mês". 31 tests novos. Base: 📈 B24 — DRE Tendência Histórica. Toggle MENSAL/TENDÊNCIA no tab DRE; buildDRETrend + DRETrendChart SVG; H8 Mony. Lote B18: SociosModal CRUD com divisão percentual por sócio (modal, state, handlers, endpoint api/org/socios PUT). Lote B19: checkOrgViewerBlock() helper centralizado em api/_lib/org.ts + viewer bloqueado em txs/goals/socios/receivables (POST) + isViewerCollaborator prop em FluxoCaixaReal oculta botão "Paguei essa fatura" para viewers. 3697 testes verdes. Engine híbrida herdada do Monify pessoal. LGPD ✅
base fork pessoal: Lote 60.79 (Lançamentos: sort por date DESC + filtro chips por período): Bug reportado pelo user via screenshot PROD — lista de Lançamentos vinha ordenada por `createdAt` (último lançado primeiro), confuso quando lançava tx retroativa. Cenário real visível na screenshot: JD Joelma 02/06 lançada AGORA aparecia ACIMA de Smart TV 06/06. Fix: helper puro `sortTxsByDateDesc(txs)` ordena por `date` DESC, com `id` DESC como tiebreaker (preserva ordem de inserção entre tx de mesma data — última lançada primeiro DENTRO do mesmo dia). Bonus pedido pelo user na mesma screenshot: filtro por data dentro do mês via chips toggle "Mês inteiro · Hoje · Ontem · Últimos 7d" abaixo do tab de tipo (Todos/Entradas/Saídas), dentro do bloco "📅 Período". Helper puro `applyDateFilter(txs, range, today)` testável isolado, AND com filtros existentes de tipo + categoria. Default "all" = comportamento atual preservado. Visível só fora da aba Metas (filter !== "goals"). Sort aplicado APÓS todos os filtros, antes do agrupamento de cartões. Cada toggle reseta usersPage(0) anti bug de paginação stale. 24 tests novos = 3070 verdes (era 3046, +24). Backend INTACTO. Frontend: src/App.tsx. Base: 📣 Lote 60.78 (sobre.html — labels leigos nas 6 badges de auditoria): User notou que os labels técnicos das badges ("Security Headers", "HTTP Observatory", "Website Grader", "PageSpeed Mobile") não eram entendíveis pelo usuário leigo. Sugestão dele: frase amigável em destaque + nome técnico embaixo. Trocas (badge-label leigo · badge-source técnico): "Security Headers" → Servidor blindado · "Snyk (Security Headers)"; "HTTP Observatory" → Conexão segura · "MDN/Mozilla (HTTP Observatory)"; "Website Grader" → Site bem feito · "HubSpot (Website Grader)"; "PageSpeed Mobile" → Rápido no celular · "Google (PageSpeed)"; "Acessibilidade" → Acessível pra todos · "WebAIM (WAVE)"; "Sem malware" → Sem vírus nem golpe · "Sucuri Sitecheck". Estratégia preserva credibilidade do nome técnico pra quem reconhece + comunica claramente pro leigo. Meta description também atualizada com linguagem leiga. Backend INTACTO. Frontend: só public/sobre.html. Base: 🏅 Lote 60.77 (sobre.html — 6 badges de auditoria atualizados): Lote 60.76 conquistou PageSpeed mobile **100/100/100/100** (Desempenho + Acessibilidade + Boas Práticas + SEO TODOS perfeitos no Moto G Power emulado 4G lento). A seção "Pode confiar" do /sobre.html ficou desatualizada citando "PageSpeed 94+" da era pré-otimização. Atualizações: (1) PageSpeed 94+ → 100 + label vira "PageSpeed Mobile" pra ser explícito; (2) Headline "4 auditorias independentes" → "6 auditorias independentes"; (3) 2 badges novos com auditorias rodadas na sessão de manutenção — WAVE AIM 9.9 (Acessibilidade · WebAIM) + Sucuri ✓ (Sem malware · 9 blacklists limpas: Google Safe Browsing + McAfee + ESET + PhishTank + Yandex + Opera + Sucuri Labs + outras); (4) meta description atualizada citando as novas auditorias. Badges originais preservados: Snyk A+ Security Headers, MDN/Mozilla A+ HTTP Observatory, HubSpot 100 Website Grader. Grid CSS mantido (1fr 1fr): 6 badges viram 3 linhas × 2 colunas naturalmente. 12 tests novos = 3046 verdes (era 3034, +12). Backend INTACTO. Frontend: só public/sobre.html. Base: 🎯 Lote 60.76 (skeleton estático LCP — PageSpeed 92 → 95+ esperado): Lote 60.75 fez code-split do AdminPanel (bundle -17%) mas PageSpeed mobile continuou 92. Diagnóstico real do PageSpeed v1.2.149 revelou *"Detalhamento da LCP — Atraso na renderização do elemento: 2810ms"* (TTFB 0ms = Vercel Edge perfeito, problema é só timing JS). Causa raiz: <main id="root"> ficava VAZIO no HTML estático; browser esperava o React bootar (~2.8s no Moto G Power emulado) antes de pintar QUALQUER coisa. Fix cirúrgico: replicar visualmente o conteúdo da tela de login (logo "monify.app" + "COPILOTO COMPORTAMENTAL" + <h1>Evita o colapso financeiro</h1> + tagline completa) DENTRO de <main id="root"> como HTML estático. CSS do skeleton inline no <style> critical com classes .lcp-skel-* usando CSS vars (--bg/--tx/--lime) — themes dark/light continuam funcionando. Browser pinta o LCP element IMEDIATAMENTE no parse do HTML; quando React monta, createRoot().render() substitui sem flash perceptível (visual idêntico). Resultado esperado: LCP delay 2810ms → ~300ms, PageSpeed mobile 92 → 95-97. 17 tests novos = 3034 verdes (era 3017, +17). Backend INTACTO. Frontend: index.html. Base: ⚡ Lote 60.75 (code-split AdminPanel — PageSpeed mobile 92 → ~95+): user reportou que PageSpeed mobile caiu de 95+ pra 92 com warning "245KB de JS não usado" no bundle inicial. Diagnóstico do build: bundle único `index-*.js` de 1.2MB (308KB gzip), warning explícito do Vite sobre chunks >500KB. Causa: AdminPanel (2940 linhas, painel super-admin usado por <1% do tráfego) vinha junto no bundle inicial. Fix cirúrgico: extrair AdminPanel pra `src/AdminPanel.tsx` separado + `React.lazy(() => import("./AdminPanel"))` + `` na rota /admin. Constantes/helpers/types (BG/LIME/TX/ENV/ADMIN_VERSION/ADMIN_API_URLS/fmt/localDateKey/formatActiveTime/deriveAdminChart/applyUsersFilters/AdminUser/etc) ganharam `export` pra serem importadas pelo arquivo extraído. Resultado medido: bundle inicial caiu de 1228KB → 1023KB (-205KB raw, 308KB → 291KB gzip, -17KB gzip), AdminPanel virou chunk lazy de 117KB (23KB gzip) que só baixa em /admin. Bundle inicial -17% pra 99% dos usuários. Tests anti-regressão dos Lotes 58/60.18/60.73/60.74 atualizados pra ler App.tsx + AdminPanel.tsx concatenados. 22 tests novos = 3017 verdes (era 2995, +22). Backend INTACTO. Base: 🔎 Lote 60.74 (filtros admin combináveis + chip Pagante + LGPD privacy.html): 3 melhorias em 1 lote pra fechar observabilidade do painel admin. (1) Filtros combináveis (AND entre dimensões) por coluna no painel: chips toggle Plano (Free/Pro/Juntos/🎁 Trial) + chips toggle Auth (🔑 Senha/G Google) + dropdown Cadastro (Todos/Hoje/Últimos 7d/Últimos 30d), além do search Nome+Email existente. Botão "✕ Limpar" RED aparece só quando algum filtro ativo. Cada toggle reseta usersPage(0) anti bug de paginação stale. Helper puro applyUsersFilters(users, filters) + hasActiveUsersFilters exportados em escopo de módulo pra teste isolado. AND entre dimensões, OR dentro da mesma dimensão (multi-select). (2) Chip LIME "💳 PIX" / "💳 Cartão" ao lado do plano pra identificar conversão paga real (paymentMethod ∈ {pix, card} e NÃO trial) — distingue visualmente conversão paga de cortesia Trial (amber). (3) public/privacy.html atualizada: linha explícita sobre "método de autenticação utilizado (e-mail e senha, login com Google)" na seção 3.1 + parágrafo novo explicando que no login social a Monify recebe do provedor apenas nome + e-mail verificado, NUNCA a senha do usuário no provedor. ADMIN_VERSION 1.5.0 → 1.6.0. 39 tests novos em lote60-74 = 2995 verdes (era 2956, +39). Backend INTACTO. Frontend: src/App.tsx. Base: 🛂 Lote 60.73 (coluna Auth no painel admin): após Lote 60.72 recuperar 2 users Google invisíveis em PROD, user pediu visibilidade de qual método de autenticação cada user usou (Senha tradicional vs Login com Google) pra suporte operacional + observabilidade. Análise LGPD: authProviders é metadado operacional, NÃO PII sensível Art. 5 II — base legal Art. 7 V (execução de contrato). Admin já vê dados mais sensíveis (CPF redacted, partnerEmail). Backend: api/admin/users.ts parseia authProviders defensivamente (array OR string JSON via Upstash auto-parse), inferência retroativa ["password"] quando passwordHash existe sem campo (users pré-60.60). Retorna em PROD e DEV. Frontend: coluna nova "Auth" entre "Plano" e (DEV-only Pref), chip cinza "🔑 Senha" + chip BLUE "G Google" lado a lado quando há múltiplos providers. Tooltips explicam cada chip. Empty state "—" pra legado raro. ADMIN_VERSION 1.4.0 → 1.5.0. 19 tests novos = 2956 verdes (era 2937, +19). Base: 🔧 Lote 60.72 (backfill admin retroativo): user reportou em PROD que mesmo após Lote 60.71 (que corrigiu o zadd faltante pra cadastros NOVOS) um usuário cadastrado via Google continuava INVISÍVEIS no painel admin — porque ele tinha sido criado ANTES do deploy de v1.2.145 e nunca foi indexado em users:by_created. Solução: novo endpoint admin POST /api/admin/backfill-users-index (GET=dry-run) que varre Redis via SCAN cursor-based (não bloqueia Upstash), filtra chaves do shape ^user:[^:]+@[^:]+$ (rejeita subkeys :txs/:push/:paid_faturas/:categories), lê createdAt do hash e adiciona no zset com esse score (preserva ordem cronológica real). Idempotente via zscore — não sobrescreve quem já está indexado, pode rodar várias vezes. Fallback Date.now() com marcador "fallback-now" se createdAt ausente/inválido. Audit log backfill-users-index. 13 tests novos = 2937 verdes (era 2924, +13). Base: 🩹 Lote 60.71 (2 bugs CRÍTICOS PROD): Bug 1 — usuários cadastrados via Login com Google estavam INVISÍVEIS pro painel admin pq socialAuth.ts esquecia zadd("users:by_created", ...) que /api/signup já fazia (linha 105). Fix: espelha lógica do signup tradicional no helper Google quando isNewUser=true, não-bloqueante. Bug 2 — convite Juntos via login Google não vinculava automaticamente (cenário real Tiago→Ligia em PROD v1.2.144): user precisou vincular manualmente via /api/admin/force-link. Causa: race condition do useEffect retry de invite com React 19 batching de setStates. Fix: chamar tryAcceptPendingInvite(newProfile) EXPLICITAMENTE dentro do callback Google após hidratar profile + setLoggedIn, sem depender de timing de useEffect. Idem em handleReactivateAccount (cobertura completa pra cenário raro de reativação + invite).
último sync: 2026-07-02
main: HEAD · fork Business v0.1.73+ — engine híbrida herdada (localReply + Claude Haiku fallback) com domínio adaptado para PMEs/MEIs. Snap agrega dados do negócio, nunca PII. LLM Monitor ativo. B26: 5 UX fixes pós-PROD (PAGAS card / ExecIcon / Radar threshold / DRE PDF / 1 mês). B24: DRE Tendência Histórica — buildDRETrend + DRETrendChart SVG + toggle MENSAL/TENDÊNCIA + H8 Mony. B23: alertas fiscais (calcFiscalAlerts por regime · H7 Mony). 3900+ testes verdes.
⚡ MONIFY PULSE
localReply + LLM fallback
camada 1 local (zero custo)
+ Claude Haiku como rede de segurança
v0.1.73+ (Business) · 3900 testes · engine B2B 🏢 · LGPD ✅
CORE
Engine NLP híbrida
localReply rápido + LLM fallback
  • Camada 1 — localReply: 233+ handlers regex/keyword, ~7k linhas, <100ms, zero custo
  • Camada 2 — LLM fallback: Claude Haiku 4.5 via /api/pulse/llm-fallback quando bate em fallback genérico
  • Snapshot agregado: saldo, totais, % poupança, categoria topo, ctx (salário/aluguel/dívida). NUNCA nomes/datas individuais.
  • Cache 15min · rate limit 10/min + 100/dia por email
  • Fail-open: erro do LLM mantém fallback local — UX nunca trava
  • Self-aware · Anti-aposta · Anti-golpe · Life events (13) · EMO ampliado
MEMÓRIA
ChatContext + continuity
KV · user:<email>:chatctx
  • extractStatedFacts: salário, aluguel, financiamento, dívida, tom
  • extractReplyContext: 11 padrões de lastTopic
  • TTL 20min · persistido cross-session
  • Dispatcher trial-aware: planMeta (paymentMethod/proExpiresAt)
  • FOLLOWUP: "e os 10?", "bora atacar", "quero focar" via ctx
MONETIZAÇÃO
Conceder/cancelar Pro grátis
cortesia controlada · cron expiração
  • POST /grant-trial · valida conflitos · email Resend
  • POST /revoke-trial · cascade Juntos preservado
  • Modal sorteado! + Modal encerramento · polling 15s
  • Cron pix-expiry trata trial além de PIX
  • Admin UI: badge 🎁 Trial · Nd · botões grant/revoke
PROATIVO
Insights + Radar + Push
determinístico · ranqueado · ritual de retorno
  • 📈 Categoria alta brusca (≥40%)
  • 🌱 Sobra consistente (2 meses)
  • 🛡️ Reserva ausente (3 meses)
  • 🌅 Radar do Dia: 6 humores · brief matinal 1×/dia
  • Micro-reações: toast pós-lançamento (6 triggers)
  • 🔔 Push diário 8h BR + admin manual: VAPID · SW custom · auto-prompt
  • WOW: salário real · modo crise
QA · TELEMETRIA
Arena + Analytics + LLM Monitor
1399 testes · 16.166 sim · custo Anthropic em tempo real
  • Camada 1 — Corpus: 703 seeds × 38 transforms = 16.166 queries únicas
  • Camada 2 — Multi-turn Arena: 81 cenários × 177 turnos com ctx
  • Camada 3 — Follow-up Corpus: 25 triggers × 30 follow-ups = 750 combos
  • /api/pulse/event · question/outcome anonimizado · TTL 90d
  • Pulse Analytics admin: volume, %fallback, funnel, fallbacks tratados
  • 🆕 LLM Fallback Monitor (Lote 34): custo USD real, latência, cache hit, projeção mês vs orçamento, top queries por custo, ❌ falhas com classificação de erro
  • 🆕 pulse:llm:events:* · tokens reais Anthropic · classificação de erro · cap 50k/dia · TTL 90d
SEGURANÇA · LGPD
Hardening
defense-in-depth
  • Anti-golpe: pirâmide, "garantido"
  • Sessão única · polling sessionId · revoga em login novo
  • Cross-gateway defense: Stripe + MP idempotência
  • Sanitização inputs · whitelist KV · X-Admin-Token
  • Zero PII em logs · admin PROD redigido
PAGAMENTO
PIX QR inline + Webhook
/v1/payments direto · sem redirect
  • POST /api/pix/create · MP /v1/payments + payer.identification (CPF)
  • Modal CPF inline · máscara + validação 2 dígitos · LGPD redact admin
  • GET /api/pix/status · polling 3s · countdown 30min
  • Fallback Checkout Pro automático (TODAS falhas MP)
  • Webhook idempotente: SETNX 24h · cron expira PIX vencido
  • MERCADOPAGO_TEST_TOKEN pra test_user em DEV
CÁLCULO
Engine financeira
fórmulas determinísticas · caps anti-absurdo
  • Juros compostos P × (1 + i)^n · FV de annuity
  • FIRE 4%: patrimônio ≥ 25× gasto anual
  • Math composto: +50%/-50% não zera (= -25%)
  • Inflação · 50/30/20 · CDI · Tesouro Selic/IPCA+
  • 13 caps anti-absurdo: % cap 100%, dias cap 3650...