# Handoff para o Codex — continuidade do monorepo Monetarie (2026-07-22)

Este documento e AUTO-CONTIDO. Foi escrito para um agente novo (Codex) que tem acesso ao repositorio e as credenciais AWS, mas NAO tem nenhuma memoria das sessoes anteriores. Nao dependa de nenhum sistema de memoria externo, nem de `[[links]]`, nem do Serena. Tudo que voce precisa esta aqui ou nos caminhos de arquivo citados.

Regra de ouro para voce, Codex: **verifique o estado vivo antes de agir e nao confie cegamente nos numeros deste documento** (eles foram confirmados ao vivo em 2026-07-22 fim do dia, mas o ambiente muda). O dono valoriza prova empirica acima de tudo e ja teve prejuizo com afirmacoes sem verificacao. Quando este handoff disser "provado vivo", ainda assim reconfira se for tomar decisao de dinheiro ou de deploy.

---

## 0. As regras inegociaveis do dono (leia primeiro, valem sempre)

1. **BACEN e a verdade.** Se algo diverge do BACEN, o defeito e NOSSO. Ache o defeito e PROVE empiricamente. Zero inferencia, nada inventado, nada de suposicao.
2. **Em producao NAO existe teste.** Nao "teste" em PRD. Validacao definitiva em PRD e a operacao real do dono, acompanhada.
3. **Validacao de tela SO com screenshot** (regra 11). Nunca declarar tela "pronta/validada" com fixture do caminho feliz. So sobre dado real.
4. **Nunca dizer "o dado nao existe" sem abrir a cabine.** A cabine PIX e a cabine SPB sao a fonte de verdade de pagador/recebedor. Inferencia publicada ao cliente estraga a imagem do dono. (Eu, Claude, errei exatamente nisso hoje. Ver secao 12.)
5. **Push e deploy PRD SO com OK explicito do dono, a cada vez.** Autorizacao nao e transitiva. "Pode ajustar" = trabalho + commit local, nunca push.
6. **NUNCA deployar ou reiniciar pix/spb enquanto o dono opera money-path ao vivo.** Isso troca a lideranca do ICOM e PERDE mensagem do BACEN. Deploy pix/spb so combinado/autorizado.
7. **Nao deployar as jornadas de onboarding/cadastro (frontend) a PRD ate a validacao visual tela a tela COM o dono.**
8. **Segredos so no AWS Secrets Manager.** Nunca em .md, .env, Terraform vars, log ou PDF.
9. **Docs em pt-br, SEM travessao** (o caractere longo de IA). Frase direta.
10. **Terraform HML: nunca `apply`.** Drift nunca vira problema, nunca vira regressao.
11. **Monetarie e o CLIENTE; Vulci e a desenvolvedora.** Documentos ao cliente sao assinados "Equipe tecnica Vulci" e NAO expoem defeito interno (um 4xx e "recusa de protecao", nunca um bug).
12. **Nao alterar nada de infra** (bastion, rotas, TGW, VPC, security groups da Lerian) nem incluir infra em commit.

---

## 1. Contexto minimo do negocio

- Instituicao: MONETARIE SOCIEDADE DE CREDITO DIRETO S.A. (SCD). CNPJ 46.026.562/0001-05. ISPB 46026562. COMPE 526. SISBACEN 00018. Dominio `monetarie.com.br`, zona privada `monetarie.internal`.
- Produtos REAIS (fonte unica, confirmada pelo dono): conta digital de pagamento gratuita, PIX, TED, extrato/comprovantes, transferencias, limites, seguranca 2FA, Credito Consignado, Internet Banking web, e API de integracao (SO para PJ). NAO oferece: cartao/maquininha, PIX ilimitado, investimentos, pagamento de boleto/contas, poupanca propria, seguros/cambio/previdencia, app mobile, cooperativa/cooperado/capital social.
- Repositorio: monorepo em `/Users/luizpenha/monetarie`. Modulos principais:
  - `core/backend` — Elixir/Phoenix, o Core bancario. App OTP: `monetarie`. Repo Ecto: `Monetarie.Repo` (config base em `Monetarie.Infra.Repo.Base`). Ledger TigerBeetle. NATS JetStream. Aurora Postgres.
  - `core/apps/{banking,admin,merchant}` — 3 SPAs Vue 3 + PrimeVue (IB, coreadmin, portal merchant).
  - `pix/` — cabine PIX (SPI/DICT/ICOM). App: `bin/monetarie_pix`.
  - `spb/` — cabine SPB (STR/LDL, TED). App: `bin/bacen_gateway`.
  - `sta/` — cabine STA (SISBAJUD, CCS, CADOC). Frontend Vue em `sta/frontend`.
- Referencia de arquitetura a espelhar (read-only): `/Users/luizpenha/coreproviders` (produto "AVIV"/Fluxiq). Foi a base do modelo de auth PJ/merchant (Bloco A abaixo).

---

## 2. Ambiente e credenciais (exato)

- AWS: conta `990933657879`, regiao `sa-east-1`, profile OBRIGATORIO `vulcimonetarie`. O profile `default` aponta para OUTRA conta; nunca use.
- Wrapper seguro (cole no shell antes de usar aws):
  ```bash
  awsmon() { env -u AWS_ACCESS_KEY_ID -u AWS_SECRET_ACCESS_KEY -u AWS_SESSION_TOKEN AWS_PROFILE=vulcimonetarie AWS_REGION=sa-east-1 AWS_EC2_METADATA_DISABLED=true aws "$@"; }
  ```
- ECR registry: `990933657879.dkr.ecr.sa-east-1.amazonaws.com`. Plataforma de build obrigatoria: `linux/arm64`.
- Clusters ECS: `monetarie-greenfield-homolog` (HML) e `monetarie-greenfield-prod` (PRD).
- VPN: os hosts `*.monetarie.internal` (ALB interno, `/health`, telas) so respondem com a VPN HML ativa. O control-plane da AWS (`ecs describe-services`, `logs`, `secretsmanager`) NAO precisa de VPN. Se `describe-services` funciona mas o host `.internal` da timeout, o problema e VPN, nao a AWS.
- rpc dentro dos conteineres vivos (para consultar/consertar dado sem redeploy):
  - Core: `bin/monetarie rpc "<expr Elixir>"`
  - PIX: `bin/monetarie_pix rpc "<expr>"`
  - SPB: `bin/bacen_gateway rpc "<expr>"`
  - Executar via `aws ecs execute-command` (ECS Exec) num task vivo, ou via `aws ecs run-task` com command override.
- Cabines (endpoints internos, so com VPN): coreadmin `http://coreadmin-h.monetarie.internal`, pix-admin `http://pixadmin-h.monetarie.internal`, spb-admin `http://spbadmin-h.monetarie.internal`. Login admin: `admin@monetarie.com` (senha inicial em Secrets Manager `monetarie/{env}/admin/{core,pix,spb}/initial_password`).

---

## 3. Estado do git AGORA

- Branch: `main`. `origin/main = 563694df82060c8b875fe29a97fb5f7f0f0683ef`. **Local == origin, 0 ahead / 0 behind. Tudo pushado.**
- As duas frentes de hoje (detalhadas na secao 6) estao INTERCALADAS na mesma historia linear; nao ha branch aberta nem merge pendente.
- `git log --oneline -30` mostra os commits de hoje. Os principais estao listados na secao 6 de cada frente.

---

## 4. Revisoes ECS VIVAS (confirmadas ao vivo 2026-07-22, reconfirme antes de agir)

Comando para reconfirmar:
```bash
for env in homolog prod; do
  awsmon ecs describe-services --cluster "monetarie-greenfield-$env" \
    --services core-api spb-api pix-api pix-admin-ui core-banking-ui core-merchant-ui core-admin-ui \
    --query 'services[].{svc:serviceName,td:taskDefinition,run:runningCount,des:desiredCount,st:deployments[0].rolloutState}' --output text
done
```

Estado confirmado (todos COMPLETED, running==desired):

| servico | HML | PRD | observacao |
|---|---|---|---|
| core-api | 207 | **99** | PRD:99 contem TUDO (Bloco A + Bloco B backend + COMPE + STR core + extrato) |
| spb-api | 72 | 41 | frente STR |
| pix-api | 202 (3 tasks) | 73 (3 tasks) | cap6 desired=3; frente STR (`tedlimits`) |
| pix-admin-ui | 55 | 29 | frente STR (card ANS) |
| core-banking-ui | 49 | 14 | HML:49 tem a jornada de onboarding + autocomplete COMPE, SEGURADO de PRD |
| core-merchant-ui | 28 | 10 | login email-only VIVO nos 2 |
| core-admin-ui | 37 | 30 | cadastro manual PF do coreadmin: conferir se so HML |

Ponto critico: **PRD banking-ui:14 esta muito atras de HML:49** de proposito. A jornada de onboarding (frontend) NAO esta em PRD, aguardando validacao visual com o dono.

---

## 5. Receita de deploy / migration / rollback (exata, testada hoje)

### 5.1 Build + push da imagem (HML)
```bash
TAG=homolog-<sha7>-<slug>-$(date +%Y%m%d)
REG=990933657879.dkr.ecr.sa-east-1.amazonaws.com
awsmon ecr get-login-password | docker login --username AWS --password-stdin "$REG"
docker buildx build --platform linux/arm64 -t "$REG/monetarie/core-api:$TAG" --push core/backend
```
Para UIs: build em `core` com o Dockerfile do app; UIs que mostram selo de ambiente (banking, merchant) EXIGEM `--build-arg VITE_APP_ENV=production` para PRD (senao o selo sai "Homologacao"). Por isso essas UIs NAO podem ser retag por digest de HML para PRD.

### 5.2 Retag HML -> PRD (para backend, com guard de digest)
```bash
PTAG=prod-<sha7>-<slug>-$(date +%Y%m%d)
docker buildx imagetools create -t "$REG/monetarie/core-api:$PTAG" "$REG/monetarie/core-api:$TAG"
# CONFERIR digest MATCH entre homolog e prod antes de swap (imagetools inspect).
```
GOTCHA: NAO use `ecr batch-get-image`+`put-image` para retag: isso MUDA o digest do indice OCI. Use `imagetools create`.

### 5.3 Registrar task-def nova e trocar o servico
Pegue a task-def atual, troque so a imagem, registre, e `update-service`. Se o ECS drenar a task velha e NAO colocar a nova (aconteceu hoje em core-api PRD, apesar de capacidade sobrar), force:
```bash
awsmon ecs update-service --cluster monetarie-greenfield-prod --service core-api \
  --task-definition monetarie-core-api-prod:<nova> --force-new-deployment
```
GOTCHA PRD: core-api PRD roda em 1 EC2 com `minimumHealthyPercent:0`, entao ha uma janela curta de `running=0` durante o swap (self-heal, nao e outage travado). Nao confunda com falha.

### 5.4 Migration (rodar a migration REAL da imagem nova ANTES do swap)
Preferir `run-task` com a imagem nova e command override, para o codigo real da migration rodar:
```bash
awsmon ecs run-task --cluster monetarie-greenfield-homolog \
  --task-definition monetarie-core-api-homolog:<nova> --launch-type EC2 \
  --overrides '{"containerOverrides":[{"name":"core-api","command":["bin/monetarie","eval","Monetarie.Release.migrate()"]}]}'
```
Em PRD ha teto de memoria no EC2: use a task-def leve `monetarie-core-migrate-prod` (768MB). NUNCA use `bin/monetarie eval` no servico rodando em :4000 (sobe outro BEAM e conflita). Para consulta/reparo use `rpc`, nao `eval`.

### 5.5 Rollback
```bash
awsmon ecs update-service --cluster monetarie-greenfield-prod --service core-api \
  --task-definition monetarie-core-api-prod:<revisao-antiga>
```

---

## 6. As 4 frentes de 22/07 (o que mudou, arquivos, provas, o que falta)

### 6.A — Auth PJ / merchant subconta por e-mail / API estruturalmente PJ-only (deployado nos 2, provado vivo)

Objetivo do dono: IB loga por CPF+CNPJ; Merchant loga SO por e-mail; conta PF NAO pode ter NENHUM acesso de API (API e so PJ); invasor nao pode logar no merchant com cpf/cnpj (enforced no backend, nao so UI). Merchant = subconta obrigatoriamente vinculada a CNPJ/PJ. Modelo espelhado do AVIV (`/Users/luizpenha/coreproviders`).

Arquivos chave (Core):
- Migration `core/backend/priv/repo/migrations/20260722100000_create_merchants_and_merchant_users.exs` — cria `merchants` (unique `user_id` = a ponte para `accounts`; unique `document` com CHECK do CNPJ alfanumerico `^[0-9A-Z]{12}[0-9]{2}$`), `merchant_users`, `users.login_email` (indice unico parcial), `api_keys.merchant_ref`, `collaborator_invites.merchant_id`. **`accounts` NAO foi tocada** (money-path).
- Schemas `core/backend/lib/monetarie/schemas/merchants/{merchant,merchant_user}.ex`.
- `core/backend/lib/monetarie/util/document_credential.ex` — normaliza credencial para CPF/CNPJ canonico. CORRIGE um defeito real: o login antigo fazia `String.replace(doc, ~r/\D/, "")`, entao `"<cpf>@evil.com"` AUTENTICAVA (RED devolveu 200).
- `core/backend/lib/monetarie/util/capabilities.ex` — catalogo de capabilities fail-closed.
- `core/backend/lib/monetarie_web/controllers/merchant/auth_controller.ex` — login merchant SO por e-mail (`POST /api/merchant/auth/login`).
- `core/backend/lib/monetarie_web/plugs/merchant_scope.ex` — deriva o merchant do TOKEN; substitui o antigo `ApiKeyController.authorized?/2` que so comparava id e deixava PF criar chave.
- `core/backend/lib/monetarie_web/controllers/v2/api_key_controller.ex` — gate PJ-only via MerchantScope.

Prova viva registrada (HML): merchant e-mail 200; merchant CPF 401 / CNPJ 401; IB e-mail 401; IB CNPJ 200; PF cria chave 403 e lista 403 (era 201/200); PJ cria 201. Prova viva PRD (sem criar nada): merchant CPF/CNPJ 401, IB e-mail 401, tabelas 2/2, api_keys=0.

Decisoes: SEM backfill dos 856 PJ existentes (PJ sem merchant recebe 403 ate o admin cadastrar). Onboarding parou de pedir senha (a credencial nasce na aprovacao/criacao da subconta; `POST /api/auth/register` agora nasce com hash aleatorio e ignora a senha enviada, com teste provando que nao autentica).

Fixtures vivas em HML (para voce testar sem criar nada): merchant `7fb2770b-2a7c-43e7-ae6f-e9168ceca2a4` (EMPRESA PROVA BLOCO A), operador `operador.prova.8226@monetarie.com` / `ProvaViva@2026`; merchant real ALDEIA MIRIM `55b4a472-2174-4be4-a32b-503e6fefa07e`.

Pendencias A: convite/primeiro acesso do operador merchant NAO implementado (espera o SES sair do sandbox); a tela merchant `/register` nao envia o campo `cpf` que o backend exige (responderia 400, achado para o Bloco B); a contracao (drop de `api_keys.merchant_id`) so depois de tudo rodado.

### 6.B — Jornada de onboarding/cadastro (Nextcode PF/PJ) + design system (frontend SEGURADO de PRD)

Objetivo: PF e PJ obrigatoriamente pela Nextcode (KYC). Corrigir estilo/tema (o "botao roxo" do PrimeVue = design system nao aplicado), contraste, tratamento de erro (`[object Object]`), e o fluxo PF -> Nextcode.

Fatos e arquivos:
- Nextcode: a key antiga de HML era INVALIDA (401). O dono forneceu key nova `6a5fd5ad...` que autentica 200, gravada em Secrets Manager `monetarie/{homolog,prod}/kyc/nextcode/api_key` E no `kyc_provider_configs`. Defeito real corrigido: `build_payload` nunca mandava os dados da pessoa; o journey usa `deliveryTypes:["email"]`, entao a Nextcode recusava 422; fix `attach_applicant` injeta email/name/phone. Prova viva: proposta PJ criada com link real `https://onboarding-api.nxcd.app/proposals/.../loa`. Arquivo: `core/backend/lib/monetarie/use_cases/onboarding/nextcode/proposals.ex`.
- Gatilho antecipado: PF apos o OTP, PJ apos o SMS (nao mais so no passo 8). Guard `journey_linked?` evita duplicata.
- PF sem captura manual: DocumentView/SelfieView/ReviewView REMOVIDAS do router PF (os `.vue` continuam no repo). Fluxo PF final = intro, Personal, Address, Occupation, Email, Phone, Nextcode, Pending, aprovacao admin. Guarda https-only (link http ou vazio nunca vira redirect). Arquivos: `core/apps/banking/src/router/index.ts`, `core/apps/banking/src/views/onboarding/{PhoneView,EmailView}.vue`.
- Design system: `OnboardingShell` + `BusinessOnboardingShell` envolvem as 20 telas com o shell do login. Contraste WCAG corrigido. `[object Object]` corrigido em `core/apps/banking/src/lib/apiError.ts` (causa: `new Error(body.error)` com objeto). PrimeVue pt-BR em `core/apps/banking/src/main.ts` e `core/apps/merchant/src/plugins/primevue.ts` (o "No results found" era default do PrimeVue).
- TED banco por COMPE (backend em PRD core-api:99): novo `core/backend/lib/monetarie/use_cases/banking/institution_directory_importer.ex` le o `ParticipantesSTRport.csv` do BACEN (494 instituicoes, 348 com COMPE), idempotente, ABORTA se vier <200 linhas. Migration `20260722180000`. A tabela `institution_directory` estava VAZIA em PRD (autocomplete de banco nunca funcionou em prod); a tabela `banks` tem COMPE ERRADO (001=Banco Central etc.), NAO usar. Prova viva nos 2: 237=BRADESCO, 341=ITAU, 001=BCO DO BRASIL, 260=NU PAGAMENTOS. O FRONTEND do autocomplete esta em banking-ui:49 (HML), segurado de PRD junto com a shell.
- Cadastro manual PF do coreadmin: `Admin.NextcodeJourneyController` nao repassava email ao create_proposal (toda jornada do coreadmin morria 422); corrigido + telefone opcional na `NextcodeJourneyForm` (rota `/onboarding/new`).

Validacao visual FEITA (screenshot): telas PF (personal/address/occupation/email/phone/pending) e /register PJ. Pendencias B: validacao visual das telas PJ 2 a 9; prova viva do fluxo PF ponta a ponta pelo navegador (OTP real por SMS nao chega ao ambiente de teste); busca de banco por apelido ("Nubank") ainda vazia (nome BACEN e "NU PAGAMENTOS - IP"). **NAO deployar banking-ui a PRD ate a validacao visual com o dono.**

### 6.C — Extrato pagador/recebedor + OTP e-mail (deployado em PRD core-api:99, provado vivo)

O extrato do IB e do Merchant (endpoint `GET /api/v2/accounts/:id/transactions`) mostrava "-" no nome da contraparte e duplicava PIX/TED.

Arquivos:
- `core/backend/lib/monetarie_web/controllers/v2/account_controller.ex` — cadeia de resolucao do nome da contraparte (`entry_counterparty_name` e `payment_counterparty`): (1) nome na TRANSACAO espelhada via `Receipts.Payload.party_from_metadata` (mesma fonte do comprovante do IB); (2) nome no metadata do lancamento; (3) INSTITUICAO pelo ISPB via `InstitutionDirectory.name_by_ispb` (payer_ispb no credito, recipient_ispb no debito); (4) so entao nil. Dedup por num_ctrl_str + end_to_end_id + transaction_id (antes so num_ctrl_str, nulo no PIX, entao PIX duplicava; TED referencia por transaction_id). Acervo AutBank: `autbank_complement_name` extrai a contraparte do texto livre `metadata.complement` de forma conservadora (nil se ambiguo); `entry_display_description` renomeia "TARIFA..." do AutBank para "Tarifa de emissao de TED".
- `core/backend/lib/monetarie/services/spb_integration/cabin_party_lookup.ex` — resolve o nome pela cabine SPB: `GET /api/messages/:id` devolve `message.xml_content`; `NomCliCredtd` (favorecido) e `NomCliDebtd` (remetente). Cacheia o token (persistent_term TTL 2min) porque a cabine SPB limita `/api/auth/login` a 5/min por IP.
- `core/backend/lib/monetarie/release/pix_party_backfill.ex` — Release que GRAVA o nome no metadata da transacao UMA vez (o extrato le local, PROIBIDO bater na cabine por linha). PIX pela cabine PIX (E2E). TED enviada pela cabine SPB (NomCliCredtd). TED recebida por `spb_inbound_credits` (coluna e `num_ctrl_str`, NAO control_number) com fallback NomCliDebtd. Rodar:
  ```
  bin/monetarie rpc "Monetarie.Release.PixPartyBackfill.run(:dry_run) |> IO.inspect()"
  bin/monetarie rpc "Monetarie.Release.PixPartyBackfill.run(:apply, limit: 500) |> IO.inspect()"
  ```
  Aplicado em PRD: 13 PIX + 5 TED. Provado: contas 982/977/768/986 com sem_nome=0; conta 059 so a tarifa fica sem contraparte (correto).
- OTP de onboarding por e-mail: `core/backend/lib/monetarie/use_cases/onboarding/otp_email.ex` — entrega pelo `Monetarie.Mailer` via SES (`EMAIL_TRANSPORT=ses` ligado nos 2, `EMAIL_FROM=nao-responda@monetarie.com`), logo real (`priv/email_assets/monetarie-logo.png`) por CID, acentos pt-br. Antes publicava num topico NATS `notification.email` SEM consumidor (nunca entregava). Provado: SES devolveu `{:ok, message_id}`. GOTCHA: SES em SANDBOX (so entrega a enderecos verificados ate a AWS liberar producao; dominio monetarie.com ja verificado).

### 6.D — STR teto 15k + limites zerados + variante *E + HSM keep-warm + card ANS (frente da outra sessao, deployada nos 2)

- CANONICO (lado BACEN, nao e nosso codigo): o STR rejeita sincronamente TED de cliente (STR0008) >= R$ 15.000,00 na participacao da Monetarie, com `CodErro="EGEN1102"` no atributo do `<VlrLanc>`. Nao e saldo nem formato. Se TED grande virar requisito, tratar no cadastral com BACEN/RTM. Hoje o trilho de valores grandes e PIX (SPI nunca teve teto). NAO ha muro geral: SME0001 R$ 1,3M e LPI0001 150k-200k manuais foram ACEITOS; a recusa e ESPECIFICA de STR0008.
- Politica de limites (decisao do dono, chamado "limites zerados"): R$ 0,00/vazio nas telas = NAO CONFIGURADO = ILIMITADO. Defaults de sistema viraram o sentinela 1_000_000_000_000 subcent; limitar e SEMPRE configuracao. Unico default que resta: piso NOTURNO Res. 142 (R$ 1.000).
- 6 bugs corrigidos (TDD): (1) SPB variante *E cega (o parser nao lia `CodErro` em ATRIBUTO): `ErrorVariantParser` + `BacenErrorCatalog` (CSV 5.318 codigos compile-time) + persistencia do motivo real. (2) Funil TED sem motor de limites: agora `LimitCheck.verify(:ted)`, ZERO hardcode (o guard de 15k foi REMOVIDO por ordem do dono). (3) Espelho envenenado por rejeicao (a raiz do "saldo insuficiente"): o trigger `update_daily_summary_on_insert` somava TODO insert e a rejeicao devolvia so o hold TB, entao cada rejeitado ficava como debito eterno em `account_daily_summaries`; fix: HoldRelease estorna amount+fee do resumo na MESMA tx PG, exactly-once. (4) Conta com zero a esquerda no debito manual (`debtor_account_not_found`): fix no `AccountResolver.find_account_by_number`. (5) PIX cold-path: `HsmKeepWarm` (GET /v1/health 20s) mata o sign frio (1438ms -> 46ms). (6) Card ANS do pix-admin rotulava rejeicao como "Aceite (ACCC)"; agora "Rejeicao (RJCT)".
- Reparo PRD: estorno dos debitos-fantasma nas summaries das 3 contas (2423, 2627, 2630); prova `safe_balance==pg==tb`. Fila "Em analise manual" do SPB PRD NEGADA e ZERADA (5 ops) por ordem do dono, com evento MANUAL_REV_DENIED auditavel.
- Pendencias D: responder o chamado do Gabriel (respostas prontas); as telas de limites do coreadmin ainda mostram R$ 0,00 cru (exibir o efetivo e follow-up de UI); probe de fronteira STR0008 (15.000,00 vs 14.999,99) falta fundear a conta de teste 982; gatear a automacao STR0013/STR0014 das 08:00 por dia util (rodou no domingo e voltou EGEN0300); flip opcional ECHO_PROBE_ENABLED para aquecer o post_ms frio do ICOM.

---

## 7. Como rodar os testes

- Core backend (Elixir): `cd core/backend && mix test <caminho>`. Dinheiro = teste de integracao com TigerBeetle real. GOTCHA: um tunel SSM na porta 15432 pode sombrear o Postgres de teste; rode `lsof -i :15432` antes de suspeitar do codigo.
- Frontends (Vitest): `pnpm --filter @monetarie/banking test`, `--filter @monetarie/merchant-portal`, `--filter @monetarie/admin`. Gates registrados hoje: banking 249/249, admin 439/439, merchant 27/27, backend onboarding 65/0, API externa 132/0.
- Convencao de unidade monetaria (CRITICO, ja causou bug 100x): DB/ledger/TigerBeetle = base_units (subcentavos); rotas `v2`/`partner`/`external` = CENTAVOS; rotas `v1`/admin = valor com escala /10000. Consulte `Monetarie.Util.MoneyUnit` ANTES de tocar em qualquer valor. Nunca converta na duvida.

---

## 8. Pendencias priorizadas para voce, Codex (acionaveis, com arquivo)

Alta (destravam cliente):
1. **Validacao visual COM o dono das telas de onboarding PJ 2 a 9 e do fluxo PF ponta a ponta**, com screenshot de cada. So depois disso deployar `core-banking-ui` a PRD (HML:49 -> PRD). Rota: `core/apps/banking/src/views/onboarding/*` e `core/apps/banking/src/views/business-onboarding/*`.
2. **Corrigir o `/register` do merchant**: nao envia o campo `cpf` que o backend exige (responde 400). Arquivo: `core/apps/merchant/src/views/auth/RegisterView.vue` + `core/apps/merchant/src/http/api.ts`.
3. **Responder o chamado do Gabriel** (respostas ja levantadas na frente STR) e **exibir o limite EFETIVO no coreadmin** (hoje mostra R$ 0,00 cru mesmo com a politica "0 = ilimitado"). Telas de limites do coreadmin.

Media:
4. Convite/primeiro acesso do operador merchant (Bloco A), quando o SES sair do sandbox. Envolve gerar senha temporaria + e-mail branded (reuse o `OtpEmail` como referencia de branding).
5. Busca de banco por apelido/marca no autocomplete de TED (tabela de apelidos; hoje "Nubank" nao acha "NU PAGAMENTOS - IP"). `institution_directory_importer.ex`.

Cadastral/externo (dependem do dono ou de terceiros):
6. Cadastro do certificado CQRC no BACEN via STA (QR dinamico; x5c ja no ar; PEMs no Desktop do dono; exige usuario STA "Sisbacen SCERTQRC").
7. SES sair do sandbox (pedido de producao na AWS) para OTP/onboarding e-mail chegar a qualquer destinatario.
8. Teto de R$ 15k do STR: cadastral com BACEN/RTM se TED grande virar requisito.

Baixa/higiene:
9. Contracao do Bloco A: drop de `api_keys.merchant_id` depois de tudo rodado.
10. Gatear STR0013/STR0014 das 08:00 por dia util (evitar EGEN0300 no fim de semana).
11. pix-api PRD: Postgrex disconnects periodicos (2-6/h, pre-existente, Task segurando conexao >15s).

---

## 9. Gotchas que vao te morder (aprendidos na pratica)

- Retag ECR: use `docker buildx imagetools create`, NUNCA `batch-get-image`+`put-image` (muda o digest).
- UIs com selo de ambiente (banking/merchant): PRECISAM de build separado com `--build-arg VITE_APP_ENV=production`; nao da para retag por digest.
- core-api PRD: 1 EC2, `minimumHealthyPercent:0`, janela de `running=0` no swap; se o ECS nao recolocar a task nova, `--force-new-deployment`.
- Migration PRD: teto de memoria no EC2; use a task-def leve `monetarie-core-migrate-prod` (768MB).
- Cabine SPB: `/api/auth/login` limita 5/min por IP; cacheie o token.
- Coluna correta e `spb_inbound_credits.num_ctrl_str` (NAO `control_number`).
- rpc por app: `bin/monetarie` (core), `bin/monetarie_pix`, `bin/bacen_gateway`. Use `rpc`, nunca `eval` no servico vivo.
- Timeline SPB na UI vem de `operation_events` (a `message_state_history` fica vazia).
- zsh come `$r:h` como modificador; use `${r}` em scripts.
- SES em sandbox: so entrega a enderecos verificados ate a AWS liberar producao.
- Tunel SSM na porta 15432 pode sombrear o Postgres de teste do Core (`lsof -i :15432`).
- VPN HML intermitente engole `*.internal` e AWS; se `google.com` passa mas `.internal` nao, e a VPN.

---

## 10. Erros que EU (Claude) cometi nesta jornada, para voce NAO repetir

Registro honesto, a pedido do dono, para proteger a continuidade:

1. **Afirmei "o dado nao existe no acervo" (pagador/recebedor) sem abrir a cabine SPB.** Estava ERRADO: os nomes estavam no XML da cabine (STR0008 tem NomCliCredtd/NomCliDebtd; creditos interbancarios STR0006/0007 identificam o pagador pelo ISPB). O dono cobrou com razao. Licao: SEMPRE abrir a cabine (`GET /api/messages/:id` -> `xml_content`) e o diretorio de ISPB antes de dizer que algo nao existe. Publicar inferencia ao cliente estraga a imagem do dono.
2. **Deploy de core-api PRD que deixou o servico com 0 task rodando** (ECS drenou a antiga e nao colocou a nova). Resolvi com `--force-new-deployment`, mas houve janela em PRD. Licao: em core-api PRD, ja entrar com `--force-new-deployment` e vigiar `runningCount` durante o swap.
3. **Erro de nome de coluna** (`control_number` em vez de `num_ctrl_str`) que fez a query falhar em silencio e ESCONDER 121 nomes de TED recebida. Licao: validar o schema real da tabela (`\d spb_inbound_credits`) antes de assumir o nome da coluna.
4. **Dedup do extrato incompleto** (so por num_ctrl_str, nulo no PIX) causou duplicacao visivel ao cliente. Licao: dedup de extrato precisa cobrir todas as chaves de correlacao (num_ctrl_str, end_to_end_id, transaction_id).

O dono esta frustrado e quer continuar parte do trabalho com voce. Trate cada afirmacao deste handoff como hipotese a reconfirmar, priorize prova viva, e nao declare nada "pronto" sem screenshot/execucao real.

---

## 11. Documentos irmaos (contexto adicional no repo)

- Handoff consolidado das 2 frentes (visao de sessao): `docs/handoff/2026-07-22-sessao-unica-blocos-ab-extrato-str-limites-handoff.md`.
- Handoff de origem desta frente (madrugada, com o design do Bloco A e a analise do AVIV): `docs/handoff/2026-07-22-blocos-auth-pj-e-qa-onboarding-handoff.md`.
- Design aprovado do Bloco A: `docs/plans/2026-07-22-auth-pj-merchant-api-design.md` (+ `-plan.md`).
- Instrucoes do repo (topologia, infra, regras): `/Users/luizpenha/monetarie/CLAUDE.md` (raiz) e `core/CLAUDE.md`.
