# Handoff — Blocos A (auth PJ/merchant/API) e B (QA jornada onboarding/cadastro) + pendências abertas

Data: 2026-07-22 (madrugada). Sessão longa que fechou a Fase 5 (API externa) + WAF + limpeza de produtos falsos, e DESCOBRIU dois blocos grandes de trabalho ainda não feitos (auth PJ/subconta e QA da jornada de cadastro). Este handoff é a fonte de verdade para retomar com calma, um bloco por vez, com design/validação antes de codar.

Regra que passou a valer nesta sessão (o dono cobrou): **PARAR de dizer "pronto/apto a clientes" enquanto os Blocos A e B existirem.** Só é "pronto" o que foi validado empiricamente (build+test+prova viva+screenshot). Nada de inferência, nada inventado, nada de suposição.

---

## 1. Estado canônico verificado empiricamente (2026-07-22)

### Git
- `origin/main = 05af3a73` (pushado). **HEAD local = 662b01ee** (8 commits locais NÃO pushados).
- Commits locais não pushados (mais novo → mais velho):
  - `662b01ee` fix(ib): contraste do onboarding (theme-aware) + logo dourado — **NÃO deployado**
  - `80a07a10` fix(merchant): título aba 'Portal do Cooperado' → 'Portal Merchant' — **NÃO deployado**
  - `c933ed93` fix(merchant): remove produtos falsos + valores crus + selo por build-arg — **DEPLOYADO** (HML:26/PRD:8)
  - `ec95e0a5` fix(ib): remove produtos falsos das telas + valores crus + bug 'Minha Poupança' — **DEPLOYADO** (HML:44/PRD:12)
  - `6b060f9f` fix(ib): título da aba por página — DEPLOYADO (dentro de ec95e0a5)
  - `049a1747` fix(ib): oculta Cartões — DEPLOYADO
  - `7d8cf026` fix(ib): cabeçalho dashboard 'payment' → 'Conta Pagamento' — DEPLOYADO
  - `060c94e9` fix(ib): login/onboarding só produtos reais + seletor PF/PJ — DEPLOYADO
- **Working tree NÃO commitado**: `core/apps/merchant/src/views/auth/LoginView.vue` (merchant email-only — NÃO commitar/deployar até o Bloco A do backend estar pronto, senão QUEBRA o login do merchant: hoje o backend é document-only, e-mail dá 401).

### Revisões ECS vivas (fonte da verdade)
- **HML**: core-api:192 (`homolog-fae71264-wafsync`), core-banking-ui:44 (`homolog-ec95e0a5-ibsweep`), core-merchant-ui:26 (`homolog-c933ed93-mersweep`). Também sta-api:16, sta-admin-ui:9, core-admin-ui:36/37 (Fase 5).
- **PRD**: core-api:85 (`prod-fae71264-wafsync`), core-banking-ui:12 (`prod-ec95e0a5-ibsweep`), core-merchant-ui:8 (`prod-c933ed93-mersweep`). sta-api:20, sta-admin-ui:11, core-admin-ui:30.
- Rollback dos UIs de prod: banking :11 e merchant :7 (ambos `prod-df12aba3-bankbox`, imagem VELHA sem as limpezas).
- **Imagens já buildadas no ECR mas NÃO deployadas** (do título do merchant): `homolog|prod-80a07a10-mertitle-20260721`. As de cor do onboarding (662b01ee) ainda NEM foram buildadas.

### GOTCHA de deploy PRD (confirmado nesta sessão)
- core-api PRD tem 1 só EC2 e `minimumHealthyPercent:0` → durante o swap há janela curta de `running=0` (self-heal, não é outage travado). Não confundir com falha. Migration PRD usa a task-def leve `monetarie-core-migrate-prod` (768MB) por causa do teto de memória do EC2.
- Retag HML→PRD por `docker buildx imagetools create` com guard de digest MATCH. **Exceção**: UIs que precisam de `VITE_APP_ENV=production` (selo de ambiente) NÃO podem ser retag por digest — precisam de BUILD separado com `--build-arg VITE_APP_ENV=production` (senão o selo sai "Homologação" em prod). Banking e merchant se enquadram nisso.

---

## 2. O que ESTÁ sólido e no ar (comprovado, não mexer sem motivo)

- **Fase 5 — API externa de cliente final `/api/external`** (espelho AVIV): 20 rotas, ApiKey + whitelist obrigatória + HMAC-SHA512 + rate limit + idempotência. Validada VIVA rota a rota (19/19 ensaios), incl. cash-in QR real e cash-out 202 lookup-to-pay. PDF de comprovação entregue. Gate de paridade doc==código VERDE. Detalhe: [[monetarie-fase5-api-externa-0721]].
- **WAF**: sync do IP set (`AwsWafReconciler` cron */5 + flag ON + IAM escopada) provado vivo (HML populou 127.0.0.1/32; PRD sync :ok com 0 clientes = correto). **WAF em modo BLOCK nos 2 ambientes** (allow prio-1 do IP set de clientes + 5 managed rules em block + rate 90k/60s per-IP), tráfego legítimo passa 200, rate rule bloqueou 0 legítimo. IP sets `monetarie-extapi-clients-{h,p}`, WebACLs `monetarie-public-{homolog,prod}`.
- **Limpeza de produtos falsos das telas de PRODUTO** (dashboard/PIX/extrato/etc.): Boleto/Investimento removidos dos filtros e telas do IB; Cartões, "Pagar Conta" (com boleto FALSO fabricado), Open Finance ocultados; /capital (cooperativa) ocultado; valores crus corrigidos; bug "Minha Poupança" corrigido; título da aba por página. **Deployado** (banking:12/merchant:8 em prod). Merchant prod login validado vivo (selo "Produção", copy honesta).

**Lista de produtos REAIS da Monetarie (SCD)** — confirmada pelo dono, usar como fonte única: conta digital de pagamento gratuita, PIX, TED, extrato/comprovantes, transferências, limites, segurança 2FA, Crédito Consignado, Internet Banking web, e API de integração (só PJ). **NÃO oferece**: cartão/maquininha, PIX ilimitado, investimentos, pagamento de boleto/contas, poupança (própria), seguros/câmbio/previdência (Open Insurance), app mobile, cooperativa/cooperado/capital social/assembleia.

---

## 3. BLOCO A — Arquitetura de auth PJ / merchant subconta / API só-PJ (BACKEND, precisa DESIGN antes de codar)

**Mandato do dono:** IB = login CPF+CNPJ; Merchant = login SÓ e-mail; **conta PF não pode ter NENHUM acesso de API — API é só PJ**; e **invasor não pode logar no merchant com cpf/cnpj (enforced no backend, não só UI)**. Merchant = subconta obrigatoriamente vinculada a CNPJ/PJ.

**Descoberta crítica (empírica, nossa base):** hoje o login v2 (`POST /api/auth/login` → `MonetarieWeb.V2.AuthController.login`, `core/backend/.../controllers/v2/auth_controller.ex:42-105`) é **document-only**: faz `String.replace(document, ~r/\D/, "")` e busca por `users.tax_id` (`:392-397`). **Login por e-mail NÃO existe no backend** (e-mail vira lixo no strip → 401). O merchant hoje loga por CPF/CNPJ. **Não há sinal de portal** (merchant vs IB): mesmo endpoint, mesmo corpo `{document,password}`, sem header/host. **O gate de criar api_key** (`POST /api/merchants/:merchant_id/api-keys` → `V2.ApiKeyController.create`, `:41-75`; `authorized?/2` `:190-194`) só checa posse (id/role), **ZERO checagem PF/PJ** → um PF cria api_key hoje. Fonte autoritativa PF/PJ = `users.user_type` ∈ {member_pf, member_pj, ...} (`schemas/relational/user.ex:22,63`).

**Modelo de REFERÊNCIA a replicar (AVIV / coreproviders, investigado a fundo):**
- AvivPay é **PJ-only por construção**. Três tabelas: `merchants` (o PJ: `document` CNPJ com `validate_length(is:14)` + unique, `entity_id` = a instituição, `access_mode ∈ {full,api_only,custom}`, `capability_ceiling`), `merchant_users` (junção `merchant_id`+`user_id`+`role ∈ {owner,admin,financial,operator,readonly}`+`status`+`capabilities`, unique `[merchant_id,user_id]`), `users` (email único + password_hash + tax_id da pessoa).
- **Login único** `POST /api/merchant/auth/login` (`coreproviders/.../merchant/auth/login_controller.ex:23-64`) aceita `credential||document||login` e ramifica por `classify_credential`: contém `@` → e-mail (`Repo.get_by(User, email:)` → `merchant_users` active → `Merchant`); 14 díg → CNPJ (`Repo.get_by(Merchant, document:)`); 11 díg → CPF legacy. Admin é portal SEPARADO (`/api/admin/auth/login`, e-mail-only, token `type:"admin"`).
- **API só-PJ é ESTRUTURAL, não um `if`**: `api_keys.merchant_id` NOT NULL + `account_id` NOT NULL + `validate_account_belongs_to_merchant`; criar via controller com `plug RequireCapability, "apikeys.manage"` + `merchant_id` do JWT (plug `MerchantFromToken`, 403 se ausente); a API externa `/api/external` só aceita ApiKey que resolve merchant. Como toda key nasce presa a um merchant (=CNPJ 14), a API fica PJ-only por construção.
- **Capabilities**: catálogo `Fluxiq.Util.Capabilities` (`@all`, preset `@api_only=[dashboard.view,apikeys.manage,webhooks.manage]`, `ceiling_for/2`, `effective_for/4` = user_caps ∩ ceiling, fail-closed). Backend EMBUTE `capabilities` no JWT E no corpo do login; frontend usa pra esconder menu/rota.
- **Flags de risco do AVIV** (decidir ao replicar): `RequireCapability` roda LOG-ONLY por default (`require_capability_enforce=false` → passa e loga); `hasCapability` no front é fail-open (vazio=libera tudo, "UX only"); ramo PF legacy (`do_legacy_login`) existe e precisa ser fechado deliberadamente se o merchant é PJ-only; `ip_whitelist` obrigatório na criação de api_key.

Arquivos AVIV chave (read-only, referência): `coreproviders/backend/lib/fluxiq_web/controllers/merchant/auth/login_controller.ex`; `.../routers/{merchant,admin,external}_router.ex`; `.../schemas/merchants/{merchant,merchant_user}.ex`; `.../schemas/accounts/{user,account}.ex`; `.../schemas/api_keys/api_key.ex`; `.../util/capabilities.ex`; `.../use_cases/onboarding/account_creator.ex` (upsert_merchant_from_application PF vs PJ); `.../plugs/{merchant_from_token,api_key_auth,require_capability}.ex`. Frontends: `coreproviders/frontends/{merchant,banking}/src/{views/LoginView.vue,stores/auth.ts,http/api.ts,layouts/AuthenticatedLayout.vue,router/index.ts}`.

**PRÓXIMO PASSO do Bloco A: escrever um DESIGN (docs/plans/) espelhando o AVIV** — decidir se criamos `merchants`+`merchant_users` (migração + backfill dos merchants PJ existentes) ou uma versão mínima; login por e-mail; sinal de portal (merchant manda `channel:"merchant"` e o backend exige e-mail nesse canal / rejeita cpf-cnpj); gate PF-no-API na `ApiKeyController` (create+update+delete+status+ip-whitelist) por `user_type=="member_pj"` + capability na resposta de login; frontends escondem API para PF. **Aprovar o design com o dono ANTES de tocar em auth.** Nada de improviso — auth é sensível.

---

## 4. BLOCO B — QA da jornada de cadastro/onboarding (FRONTEND, visual + funcional, tela a tela)

Estado real (o dono flagrou por screenshot): a jornada de cadastro NÃO está pronta para cliente.
- **`/register` (PJ, `business-onboarding/Step1CnpjView.vue`)**: SEM estilo/tema — botão **roxo do PrimeVue** (tema da marca não aplicado), campos sem card, layout cru. Precisa aplicar o design system.
- **`/onboarding/personal` (PF, `views/onboarding/*`)**: erro cru **"[object Object]"** no toast (tratamento de erro quebrado — mostra o objeto em vez da mensagem); e **o PF NÃO redireciona para a jornada da Nextcode** — cai no fluxo manual de 8 passos. (Contexto: a integração Nextcode foi code-complete mas `NEXTCODE_ENABLED` está OFF e a key de HML era inválida — ver pendências herdadas. O fluxo manual é o fallback e está bugado.)
- **`OnboardingView` (intro)**: contraste corrigido nesta sessão (commit `662b01ee`, tokens theme-aware + logo dourado) — **mas NÃO deployado** (segurado para o QA). Falta validar por screenshot.
- **Provável**: os demais passos (PF address/occupation/selfie/document; PJ email/2fa/sms/company/documents/partners/kyc) têm os mesmos problemas de estilo/tema/erro — precisam de varredura visual tela a tela.

**PRÓXIMO PASSO do Bloco B: QA visual completo tela a tela** — inventariar cada rota de onboarding/register (PF e PJ), corrigir estilo/tema (design system, matar botão roxo), contraste, tratamento de erro (`[object Object]` → mensagem real), e o fluxo PF→Nextcode. **Validar CADA tela com screenshot** antes de qualquer "pronto" e antes de deploy. Depende do Bloco A para saber o modelo final PF/PJ (ex.: PF vai pra Nextcode, PJ vai pro fluxo empresarial vinculado a CNPJ).

---

## 5. Correções de UI feitas MAS não deployadas (segurar até validar a jornada)
- `662b01ee` cor do onboarding (theme-aware) — commit local, não buildado/deployado.
- `80a07a10` título merchant "Portal Merchant" — commit local; imagens `*-80a07a10-mertitle` buildadas no ECR mas NÃO deployadas (merchant prod ainda tem "Portal do Cooperado" no título da aba).
- Merchant email-only (`LoginView.vue`) — SÓ working tree, NÃO commitado. **Não commitar/deployar sem o Bloco A** (quebraria login merchant: backend é document-only).

---

## 6. Pendências HERDADAS ainda registradas e SEM atuação (de MEMORY.md, antes desta sessão)
1. **QR dinâmico: cadastrar CQRC no BACEN via STA** (cadastral, não código; x5c+typ já no ar). HML nunca cadastrado; PRD com cert LEGADO. PEMs no Desktop; exige usuário STA "Sisbacen SCERTQRC"; revalidar no PIX Tester. [[monetarie-qr-cert-audit-tednumctrlif-0720]]
2. **TED viva pelo IB em HML** (PR#20 ponta a ponta no gate `:xsd_oficial`, NumCtrlIF=20). Código provado; falta o teste vivo.
3. **Validação VISUAL por screenshot (regra #11)** das telas deployadas do lote IB B/C+B4-B9 pelos hosts públicos ib-h/ib.monetarie.com.
4. **STA follow-ups**: token DEDICADO `STA_SERVICE_TOKEN` (exige +1 ARN na whitelist da execution role = IAM); job de snapshot do saldo da cabine em PRD não atualizava (CA-001/002); MetricsView ainda mock; aviso benigno "static manifest" no boot. [[monetarie-sta-v1-auth-failclosed-0721]]
5. **pix-api PRD**: Postgrex disconnects periódicos (2-6/h, pré-existente, Task segurando conexão >15s). [[monetarie-varredura-pos-wave1-0718]]
6. **Bloqueado em TERCEIROS**: SES sa-east-1 (sandbox ON, pedido de produção na AWS; `CCS_NOTIFICATION_EMAILS` sem definição; e-mail do Gabriel pendente de clique); conta de tesouraria p/ `SPI_REMUNERATION_SWEEP`; ensaio AMES-SIMBA (secret cloak_key + operador); infração/MED em HML exige transação LIQUIDADA via outra instituição. [[monetarie-deploy-pacote-degrau1-0719]]
7. **Vulci item 6 (tarifa SLB)**: único ainda em desenvolvimento (14/15 atendidos).
8. **Onboarding Nextcode**: key HML inválida (401 — aguarda o cliente/Nextcode confirmarem a key de homolog; config `kyc_provider_configs` pronta em HML esperando a troca). Jornada PF/PJ viva bloqueada nisso. `NEXTCODE_ENABLED` flip pendente. [[monetarie-fase5-api-externa-0721]] / [[monetarie-exposicao-publica-onboarding-0721]]
9. **WAF COUNT→BLOCK**: JÁ FEITO nesta sessão (BLOCK nos 2). O que resta é OBSERVAR métricas e ajustar limites se necessário. IP set de clientes isento via allow prio-1.
10. **Follow-ups doc-only da Fase 5** (baixa): `pix-cashout-e2e.md` cita "3 fontes" stale; controle "bloquear PIX-OUT p/ CNPJ" aspiracional; cabine sem `consult_qrcode` (EMV dinâmico vivo responde 422 até a cabine ganhar a action).
11. **Higiene (baixa)**: 4 memórias 06-30 citam PIX signing UID `lMcWo5` (STALE; autoritativo pós-07-03 = T011 `oe1ZQyCUK4CwYFfbRaiX`).
12. **Push dos 8 commits locais** (`060c94e9`..`662b01ee`) — aguarda OK do dono (a decisão foi segurar o merchant email-only e as journeys; decidir se pusha só os já-deployados ou tudo).

---

## 7. Regras vivas (inegociáveis)
- BACEN é verdade; achar NOSSO defeito e PROVAR empiricamente; zero inferência. **Em produção NÃO existe teste.** Validação de tela SÓ com screenshot (regra #11), nunca com fixture do caminho feliz.
- NUNCA deployar/reiniciar pix/spb com o dono operando money-path ao vivo (troca liderança ICOM e PERDE mensagem).
- Push e deploy PRD só com OK explícito do dono a cada vez (não é transitivo). Segredos só no Secrets Manager.
- Docs pt-br sem travessão de IA. Terraform HML: nunca apply, drift nunca vira problema.
- **Nesta sessão**: não deployar as jornadas de onboarding/cadastro até validação visual com o dono. Não chamar nada de "pronto/apto a clientes" sem prova.
- AWS: conta 990933657879, profile `vulcimonetarie`, sa-east-1. `awsmon() { env -u AWS_ACCESS_KEY_ID -u AWS_SESSION_TOKEN -u AWS_SECRET_ACCESS_KEY AWS_PROFILE=vulcimonetarie AWS_REGION=sa-east-1 AWS_EC2_METADATA_DISABLED=true aws "$@"; }`

---

## 8. Ordem recomendada de ataque
1. **Bloco A primeiro** (design escrito → aprovação do dono → implementação TDD → deploy HML→PRD → prova viva de login nos 2 portais + tentativa de api_key como PF = 403). Destrava merchant-email + PF-no-API de forma correta e permanente.
2. **Bloco B depois** (QA visual tela a tela, corrigindo estilo/tema/erro/fluxo, com screenshot de cada), já sabendo o modelo final PF/PJ do Bloco A.
3. Só então: deploy das journeys, push dos commits, certificação final e atualização das memórias.
</content>
