# Dossiê de paridade profunda — DB diff: Contábil + Multiliquidação + Segurança

Auditoria READ-ONLY, zero inferência. Confronto de ESQUEMA (tabela.coluna) + comportamento entre o
Legado PIX CRK/Corner (SQL Server vivo restaurado + .NET decompilado + scripts) e a cabine PIX
Monetarie (Elixir `pix/backend` + Vue `pix/frontend`), com foco nos 4 bancos da pista:
`CRK_SPICONTABIL`, `crk_multtiliquidacao`, `CRK_MULTILIQINTEGCONTA`, `crksecurityadminBKP` — mais o
banco `crk_spi` (onde vive a alçada de money-path do PIX legado).

Convenção de unidade: legado em REAIS decimais (`numeric/decimal(18,2)`); nossa cabine em
base_units/centavos (`accounting_event.amount :integer`) ou `:decimal` (alçada/fee). Toda comparação
de valor exige conversão explícita.

Método: (1) enumeração viva do schema legado por `INFORMATION_SCHEMA` via sqlcmd + contagem de linhas;
(2) enumeração do schema nosso lendo os módulos Ecto; (3) diff tabela/coluna e comportamento; (4)
classificação por GAP (ausente / divergencia / parcial) ou COBERTO. Sem prova, verdict INCONCLUSIVO.

---

## 0. Sumário executivo

| Domínio | Legado (bancos) | Nosso (schema) | Veredito de paridade |
|---|---|---|---|
| Contábil | `CRK_SPICONTABIL` (6 tabelas, data-driven por CdMsg+tag, 2D+2C+CC+hist, bucket "NÃO CONTABILIZADO") | `monetarie_settlement` (chart_of_accounts, journal_entries, accounting_events, cost_centers) — mapa hardcoded por direção, 1D+1C | **DIVERGÊNCIA estrutural**; contábil autoritativo do PIX está no Core |
| Multiliquidação | `crk_multtiliquidacao` (48 tabelas, 492.076 operações) + `CRK_MULTILIQINTEGCONTA` (de-para) | **inexistente** na cabine (não há modelo de lote/operação de negócio/bloqueio-de-conta/janela) | **AUSENTE** (maior lacuna) |
| Segurança | `crksecurityadminBKP` (RBAC: usuários/grupos/funcionalidades/MFA/senhas/certificados) + `crk_spi` (alçada de money-path) | `monetarie_auth` (users/groups/features/group_features/api_clients/audit_logs) + `monetarie_spi` alçada (parameters/registrations/vistos/held_messages) | **PARCIAL** — RBAC coberto no essencial; alçada existe mas com escopo/regras reduzidos e flag OFF |

Achados-chave (corrigem premissas de mapas anteriores):
1. **A cabine PIX TEM engine de alçada** (`monetarie_spi.alcada_parameters/registrations/vistos` +
   `alcada_held_messages`), com maker/checker, N vistos e dedup de voto. O mapa 09 dizia que "não
   temos" — está superado. Porém o escopo é SÓ envio manual (Construir Mensagem/reprocessos), com
   flag `ALCADA_PIPELINE_ENABLED` default OFF e fail-open. O legado aplica a TODA operação de IDA
   com valor.
2. **Não há modelo de acumulado diário persistido por cliente/conta** equivalente ao legado
   `SpiRegLimitesOperacao` (que tem coluna computada `ValorDisponivel`); nosso acumulado é calculado
   on-the-fly de `messages+payments`.
3. **RBAC no nível de MENSAGEM (CdMsg) do legado não tem par** — `spb_tb_cad_mens_func_grupo`
   (46.493 linhas: grupo→funcionalidade→cd_msg) vs nosso `group_features` (só verbos CRUD por tela).
4. **Multiliquidação (lote, agendamento por janela, bloqueio de conta, participante controlado,
   sanction screening, de-para) é o maior buraco** — nenhuma tabela nossa espelha.

---

## 1. CONTÁBIL — `CRK_SPICONTABIL` x `monetarie_settlement`

### 1.1 Schema vivo do legado (colunas confirmadas via INFORMATION_SCHEMA)

Contagem viva: `EventoContabil=5, EventoMensagem=5, EventoMensagemTag=5, PlanoConta=16, HistPadrao=8,
CentroCusto=5` (backup com config de laboratório — ver ressalva §1.5).

| Tabela.coluna (legado) | Tipo | Papel |
|---|---|---|
| `EventoContabil.IdEvento / IdInstFinanc / CdEvento / IdHist / DsEvento / FlAtivo` | int/varchar/bit | evento contábil por IF |
| `EventoContabil.IdContaDebito1 / IdContaDebito2 / IdContaCredito1 / IdContaCredito2` | int (D2/C2 nullable) | **até 2 contas de débito + 2 de crédito** |
| `EventoContabil.IdCentroCustoDebito1/2 / IdCentroCustoCredito1/2` | int nullable | **centro de custo POR PERNA** |
| `EventoMensagem.IdEventoMsg / CdMsg / IdEvento` | int/char(8) | **qual CdMsg dispara qual evento** |
| `EventoMensagemTag.IdEventoMsgTag / NmTag / VlTag / IdEventoMsg` | int/varchar(50)/varchar(250) | **condição por TAG/campo da msg** (peso = nº de condições) |
| `PlanoConta.IdConta / IdInstFinanc / CdConta(14) / DsConta(30) / FlAtivo / FlCentroCusto` | int/varchar/bit | plano de contas COSIF por IF; flag "aceita CC" |
| `HistPadrao.IdHist / CdHist(int) / DsHist(80) / FlAtivo` | int/varchar | **histórico padrão do lançamento** |
| `CentroCusto.IdCentroCusto / IdInstFinanc / CdCentoCusto(16) / DsCentroCusto(30) / FlAtivo` | int/varchar/bit | centro de custo por IF |

Comportamento (prova decompilada): motor `MonitorContabilUseCase.ObtemLancamentos`
(`SPI.Core.Contabil.Application.decompiled.cs:715-805`) lê operações `IdStatus==9` (Efetivada) do dia,
casa cada uma ao `EventoContabil` mais específico via `EventoMensagem(CdMsg)+EventoMensagemTag(NmTag,VlTag)`
com desempate por `Peso desc` (`:756,845-880`), soma D e C, e emite `Lancamento` com até 4 pernas +
histórico. Operação sem evento cai no bucket **`idEvento=0 "NÃO CONTABILIZADO"`** (`:789-803`).
Exportação posicional por EVENTO (`LayoutCRK`/`LayoutCRKDinamico`, `:910-1040`), excluindo o bucket
não-contabilizado (`:889`). Tarifa NÃO vira lançamento COSIF (`ParametroTarifa` é billing por contagem
de mensagens; grep contábil = 0).

### 1.2 Schema nosso (`monetarie_settlement`)

| Tabela.coluna (nosso) | Tipo | Prova |
|---|---|---|
| `chart_of_accounts.cosif_code / name / account_type / parent_code / level / active` | string/enum/int/bool | `accounting/chart_of_accounts.ex:13-22` |
| `journal_entries.date / amount(:integer) / description / reference / source_type / source_id / status / created_by / debit_account_id / credit_account_id` | 1 D + 1 C (FK) | `accounting/journal_entry.ex:14-28` |
| `accounting_events.type / event_type / description / amount(:integer) / debit_account(string) / credit_account(string) / date / user / status / reference` | 1 D + 1 C (string) | `accounting/accounting_event.ex:14-27` |
| `cost_centers.code / name / responsible / status / budget / spent` | orçamento standalone | `accounting/cost_center.ex:13-19` |
| `fee_tables / fee_calculations / fee_reports` | motor de tarifa (não-COSIF) | `fees/fee_schedule.ex:16`, `fees/fee_calculation.ex:34`, `fees/fee_report.ex:37` |

Comportamento (prova): único gatilho automático = evento NATS `monetarie.spi.transaction.settled`
consumido pelo `SettlementObligationWorker` (`settlement_obligation_worker.ex:48`); `emit_accounting`
resolve o par de contas por `settlement_cosif(event.direction)` — 2 casos hardcoded INBOUND/OUTBOUND
(`:177-179,392-406`), par fixo reserva↔payable (2 COSIF semeados, `cosif_seeder.ex`). `journal_entry`
é 1D+1C; best-effort (falha → `status="error"` + telemetry `[:pix,:accounting,:failure]`,
`accounting.ex:193-251`). Devolução (`return.settled`) NÃO é consumida pelo worker de contabilidade.
Export `GET /export/circular-4010` agrega por CONTA COSIF (`accounting_controller.ex:87-128`),
`SALDO_ANTERIOR` sempre 0, sem histórico/CC/seq. Contábil autoritativo do PIX vive no Core
(`core/.../cosif/pix_reconciliation.ex`, buckets `:missing_journal`/`:journal_pending`).

### 1.3 Diff de tabela/coluna — legado SEM PAR

| Legado | Nosso par? | Veredito |
|---|---|---|
| `EventoContabil` (2D+2C, 4 CC por perna, IdHist) | `journal_entries`/`accounting_events` (1D+1C, sem CC, sem hist) | **DIVERGÊNCIA** — sem multi-perna, sem CC por perna, sem histórico |
| `EventoMensagem` (CdMsg→evento) | ✗ nenhum | **AUSENTE** — mapa por direção, não por CdMsg |
| `EventoMensagemTag` (NmTag/VlTag) | ✗ nenhum | **AUSENTE** — sem condição por campo/tag |
| `HistPadrao` (CdHist/DsHist catálogo) | `journal.description` (texto livre) | **DIVERGÊNCIA** — sem catálogo de histórico |
| `PlanoConta.FlCentroCusto` | `chart_of_accounts` (sem flag CC) | **PARCIAL** — plano existe, sem vínculo CC |
| `CentroCusto` (por IF, vinculado às pernas) | `cost_centers` (orçamento standalone, sem `cost_center_id` em journal) | **DIVERGÊNCIA** — CC não vincula ao lançamento |

Colunas de conceito sem par: `IdContaDebito2/IdContaCredito2` (2ª perna), `IdCentroCustoDebito1/2 /
Credito1/2` (CC por perna), `EventoMensagemTag.NmTag/VlTag` (condição por tag), `HistPadrao.CdHist`
(código de histórico), `PlanoConta.IdInstFinanc/FlCentroCusto` (multi-IF + flag CC).

Detalhe completo do domínio contábil (motor, exportação, bucket, mitigação no Core) já está em
`deepaudit/contabil-cosif.md` (g1..g9, c1..c5). Este dossiê consolida a visão de schema.

### 1.4 Fee (nosso `monetarie_settlement.fee_*` x legado `ParametroTarifa`)
Nosso motor de tarifa é MAIS rico (faixa por valor, flat+percentual, min/max, por ISPB, tiers,
vigência) — `fee_schedule.ex:16-31`. O legado tem `ParametroTarifa` (billing por contagem, APIX),
fora dos 4 bancos-alvo. Em NENHUM dos dois a tarifa vira lançamento COSIF dentro do PIX — equivalência,
não regressão.

### 1.5 Ressalva de honestidade
O backup do legado tinha a contabilidade CONFIGURADA SÓ COM DADOS DE TESTE (`EventoMensagem` só
CAMT.052/PACS.008, um único `EventoContabil` ativo "teste magno", plano/hist/CC de laboratório, nenhum
evento de devolução). A superioridade do legado aqui é de CAPACIDADE (modelo configurável), não de
operação observada.

---

## 2. MULTILIQUIDAÇÃO — `crk_multtiliquidacao` + `CRK_MULTILIQINTEGCONTA` x nosso (AUSENTE)

Este é o maior gap. O legado tem um subsistema completo de PIX-out de negócio orientado a lote,
agendamento por janela, débito diferido, QR composto (saque/troco), bloqueio de conta (MED) e
integração com provedores de conta corrente de terceiros. A cabine Monetarie NÃO tem modelo
equivalente: nosso money-path é transação-a-transação (`monetarie_spi.messages`/`payments`), sem a
entidade de negócio `Operacao`, sem lote, sem bloqueio de saldo em conta na própria cabine.

### 2.1 Tabelas do legado (48) e par nosso

Contagem viva relevante: `TB_OPERACAO=492.076`, `TB_ARQUIVO_PAGTO=179`,
`TB_ARQUIVO_PAGTO_DETALHE=115`, `TB_INICPAGTO=324`, `TB_SANCTIONSCREENING=1.026`,
`TB_SOLICITACAO_PAGAMENTO=29`, `TB_PARTICIPANTECONTROLADO=5`, `TB_RECORRENCIA=3`;
`TB_BLOQUEIO=0`, `TB_SOLICITACAO_BLOQUEIO=0`, `TB_JANELA_RETENTATIVA=0`, `TB_AGENDAMENTO_RECORRENTE=0`
(config de laboratório: bloqueio/janela/recorrência nunca populados nesta instalação).

| Tabela legado | Papel (colunas-chave provadas) | Par nosso | Veredito |
|---|---|---|---|
| `TB_OPERACAO` (492k) | unidade de negócio: `IdOperacao, VlOperacao, VlDocumento, DtPagto, IdFimAFim(E2E), TxChave, tpQrCode, tpFinalidade(IPAY/OTHR/GSCB), tpPrioridade, FlDevolucao, IdOperacaoOriginal, JsMotivoDevolucao, IdTipoSituacaoAtual(36+ estados), NrSpbControlador, NrDoctosGeradoContaJson` | `messages`/`payments` (transação, sem modelo de operação de negócio nem estados 1..99) | **DIVERGÊNCIA/AUSENTE** — sem entidade `Operacao` |
| `TB_HISTSITUACAOOPERACAO` | trilha de transição por operação | `messages` status_id + audit | **PARCIAL** |
| `TB_ARQUIVO_PAGTO` | cabeçalho de lote: `NR_CPFCNPJ, NR_CONTA, NR_TIPO, DT/HR_CRIACAO, NR_SEQUENCIAL, ID_SITUACAO, NR/VL_PAGAMENTOS[_QRCODE]` | ✗ | **AUSENTE** — sem importação de lote PIX-out |
| `TB_ARQUIVO_PAGTO_DETALHE` | linha do lote (pagador/recebedor/valor/chave/`TX_URL_EMV`/`JS_TIPOVALOR`/`ID_OPERACAO`) | ✗ | **AUSENTE** |
| `TB_ARQUIVO_PAGTO_ERRO` | erro por arquivo/detalhe (`NR_TIPO 1/2, DS_ERRO`) | ✗ | **AUSENTE** |
| `TB_BLOQUEIO` | bloqueio de saldo em conta: `VL_BLOQUEIO, TX_CHAVEBLOQUEIO, DT_BLOQUEIO/DESBLOQUEIO` | ✗ (temos infração DICT, não bloqueio de saldo na cabine) | **AUSENTE** — MED por bloqueio de conta |
| `TB_SOLICITACAO_BLOQUEIO` | solicitação: `ID_INFRACAO_BACEN(char36), ID_INFRACAO_DICT, NR_SPB/AGENCIA/CONTA, VL_SOLICITACAO, ID_SITUACAO_BLOQUEIO(Total/Parcial/Desbloq)` | ✗ | **AUSENTE** |
| `TB_JANELA_RETENTATIVA` | janela de agendamento/retentativa por ISPB: `NR_SPB, TP_PAGAMENTO, DH_INICIO/FIM_AGENDAMENTO, DH_INICIO/FIM_RETENTATIVA, NR_TENTATIVAS, NR_MINUTOS_INTERVALO` | ✗ (Core tem ScheduledTed/ScheduledPix, sem modelo de janela por ISPB) | **AUSENTE** |
| `TB_HISTORICO_RETENTATIVA` | `ID_OPERACAO, NR_TENTATIVA, DH_TENTATIVA, TP_TENTATIVA` | ✗ | **AUSENTE** |
| `TB_PARTICIPANTECONTROLADO` | contas de terceiros controladas: `NR_SPB, NR_SPBDIRETO, TP_SISTEMACONTA(Agibank/Crefisa/Sinqia/Topazio/Cobis/BNP), TE_SENHASISTEMA{CONTA,SPI,DICT} (cifrado), TP_HEADERSISTEMACONTA` | ✗ (nosso débito é no ledger próprio Core/TB) | **AUSENTE / N/A por desenho** |
| `TB_INICPAGTO` | QR/EMV cadastrado: `IdTipoInicPagto(1-4), accessToken, TxPadraoEMV, TxChave, VlDocumento` | `monetarie_settlement.qr_codes` (`qr_codes.ex`) | **PARCIAL** — QR estático/dinâmico existe |
| `TB_INICPAGTODINAMICORECEBEDOR` | CobV/dinâmico + **saque/troco**: `IdPayLoad/IdRevisaoPayLoad, DhExpiracao, DtVencimento, VlSaque/NrSpbSaque, VlTroco/NrSpbTroco, IcPermiteAlteracao*` | CobV (parcial); **saque/troco AUSENTE** | **PARCIAL** |
| `TB_DESCONTODATAFIXA` / `TB_INFORMACAO_ADICIONAL` | desconto por data / campos extras do QR | parcial (CobV desconto) | **PARCIAL** |
| `TB_AGENDAMENTO_RECORRENTE` | `TP_FREQUENCIA, NR_DIAMES, LS_DATACUSTOMIZADA, NR_REPETICOES, NR_TOTAL_PARCELAS` | `recurrences` (parcial) | **PARCIAL** |
| `TB_RECORRENCIA` | PIX Automático: `ID_RECORRENCIA, ID_PERIODICIDADE, DT_INICIAL/FINAL, VL_RECORRENCIA/MINIMO/MAXIMO, NM/NR_CPFCNPJ_{RECEBEDOR,PAGADOR,DEVEDOR}, NR_CONTRATO, TP_JORNADA` | `spi_service recurrences.ex` (`mandate_id, frequency, next_execution, max_executions`) | **PARCIAL** — recorrência BACEN existe, sem o modelo de negócio |
| `TB_SOLICITACAO_PAGAMENTO` | jornada 3/4 (PSP recebedor solicita) | ✗ | **AUSENTE** |
| `TB_SANCTIONSCREENING` / `_ERRO` (1.026) | export AML/triagem de sanções: `ID_GERACAO, NM_ARQUIVO, TP_GERACAO, ID_SITUACAO` | ✗ | **AUSENTE** |
| `TB_CONFIRMACAO_RECEBIDA` / `TB_CONTROLE_CREDITO_RECEBIDO` | idempotência do crédito recebido (`ID_SPI`, `ID_FIMAFIM`) | E2E dedup (`Shared.E2ECache`) | **COBERTO (equivalência funcional)** |
| `TB_RETORNO_LEGADO` | retorno IBM MQ CAMPS/C&M (`TX_RETORNO`, parse `TxRef`) | ✗ | **N/A por desenho** (nosso retorno é evento NATS do Core) |
| `TB_ARQUIVO_APIX001` | relatório regulatório APIX001 | `settlement_service/apix.ex` | **COBERTO** |
| `TB_FERIADO` | feriados | Core/shared feriados | **PARCIAL/COBERTO** |
| `TB_TIPOSITUACAO / TB_TIPOOPERACAO / TB_CLASSIFICACAO_SITUACAO / TB_SITUACAO_*` | catálogos de estado | `monetarie_spi_ref` status codes | **PARCIAL** (nossa máquina é 1..10 do BACEN, não 1..99 de negócio) |
| `TB_JWKSETINTERNO / TB_JWKSETEXTERNO` | chaves JWS do QR | `qr_codes` JWS (chaves) | **PARCIAL/COBERTO** |
| `TB_PARAMETRO / TB_PARAMETRO_WORKER / TB_GRUPO_PARAMETRO / TB_ERRO_CUSTOMIZADO` | parâmetros/erros | `system_config` + ref | **PARCIAL** |

### 2.2 `CRK_MULTILIQINTEGCONTA` — de-para SFN↔legado de conta

| Tabela.coluna | Papel | Par nosso |
|---|---|---|
| `TB_DEPARA_AGENCIA.NR_SPB / NR_AGENCIASFN / NR_AGENCIALEGADO` | tradução de agência | ✗ **AUSENTE / N/A** (ledger próprio, sem legado de conta externo) |
| `TB_DEPARA_DOMINIO.CD_DOMINIO / NM_DOMINIO_PIX / NM_DOMINIO_LEGADO / NR_SPB / CD_TIPO / CD_PRODUTO` | tradução de domínio PIX↔legado | ✗ **AUSENTE / N/A** |
| `TB_DEPARA_IF.NR_SPB / NR_BANCO / ID_EMPRESALEGADO` | tradução IF↔empresa legado | ✗ **AUSENTE / N/A** |

Todo o `CRK_MULTILIQINTEGCONTA` é ponte para o sistema de conta corrente LEGADO (Agibank/Crefisa/etc);
no desenho Monetarie o débito/crédito é no ledger próprio (Core/TigerBeetle), então NÃO tem par por
decisão de arquitetura (não é regressão, é modelo diferente).

Detalhe completo do subsistema (parser 01/02/03/99, motor `TratarPix`, estados, workers) está em
`06-multiliquidacao.md`.

---

## 3. SEGURANÇA — `crksecurityadminBKP` + `crk_spi`(alçada) x `monetarie_auth` + `monetarie_spi`(alçada)

### 3.1 RBAC / autenticação — `crksecurityadminBKP` x `monetarie_auth`

Contagem viva: `spb_tb_cad_usuarios=229, spb_tb_cad_grupo_usuarios=34, spb_tb_cad_funcionalidades=534,
spb_tb_cad_mens_func_grupo=46.493, spb_tb_cad_sistemas=5, spb_tb_cad_usuarios_mfa=1,
TB_CERTIFICADO_ENVIO=2, TB_CERTIFICADO_AUTORIZADO=2, TB_ORIGEM_AUTORIZADA=0, TB_DESTINO_AUTORIZADO=0`.
(`crksecurityadminBKP` é o AdmWeb compartilhado PIX/SPB; o modelo é RBAC por bitmap `map_transacoes`
`varbinary` por sistema.)

| Legado (crksecurityadminBKP) | Colunas-chave | Nosso par (`monetarie_auth`) | Veredito |
|---|---|---|---|
| `spb_tb_cad_usuarios` | `id_usuario, ds_login, ds_nome_usuario, fl_ativo, ds_email, map_grupos(varbinary), map_transacoes(varbinary), dt_bloqueio_inicio/fim, nr_logins, ds_login_hash, ds_lang, emp_default/fil_default, tmp_expiracao_token, id_faixa_horario` | `users(username, email, full_name, password_hash, is_blocked, is_active, must_change_password, login_attempts, password_expires, institution_id, branch_id, keycloak_id, mfa_*)` (`auth/user.ex:23-42`) | **COBERTO (divergente na forma)** — perms relacionais (não bitmap); ver 3 gaps abaixo |
| `spb_tb_cad_usuarios_mfa` | `ds_Sec_Sec (segredo TOTP), ds_Sec_ad, ds_mfa_mod/seen, nr_logins` | MFA inline em `users`: `mfa_secret(:binary), mfa_enabled, mfa_verified_at, mfa_backup_codes` (`auth/user.ex:37-40`) + `Shared.Auth.MFA` | **COBERTO** |
| `spb_tb_cad_grupo_usuarios` | `id_grupo_usuario, ds_grupo_usuario, sg_grupo_usuario, map_transacoes(varbinary), fl_interno, id_sistema` | `groups(code, name, is_system, is_active, metadata)` + many_to_many institutions (`auth/group.ex:15-23`) | **COBERTO** |
| `spb_tb_cad_funcionalidades` (534) | `id_funcionalidade, ds/abrev/sigla_funcionalidade, nm_pagina_associada, id_modulo, id_funcionalidade_pai, **fl_acesso_mensagem, ds_tipo_msg**, ds_url, lbl_menu/botao, posi_menu, js_rota, fl_web` | `features(code, name, feature_type, route_path, component, icon, display_order, requires_permission)` + `modules` (`auth/feature.ex:14-27`, `auth/module.ex:13-22`) | **PARCIAL** — falta `fl_acesso_mensagem`/`ds_tipo_msg` (dimensão de mensagem) |
| `spb_tb_cad_funcionalidades_sistema` | `id_sistema, id_funcionalidade` | feature→module (não por sistema) | **PARCIAL** |
| `spb_tb_cad_mens_func_grupo` (46.493) | `id_grupo_usuario, id_funcionalidade, **cd_msg**` — **RBAC no nível de MENSAGEM** | `group_features(can_view/create/update/delete/approve/configure/export)` (`auth/group_feature.ex:15-26`) — só verbos por TELA, **sem `cd_msg`** | **AUSENTE** — quem pode operar qual CdMsg |
| `spb_tb_cad_senhas` | `id_usuario, dt_cad_senha, ds_senha, dt_exp_senha` — **HISTÓRICO de senha** | ✗ (só `users.password_hash` único + `password_expires`) | **AUSENTE** — sem tabela de histórico |
| `spb_tb_cad_restricoes_senhas` | `id_restr_senha, ds_restr_senha` — política de senha | política via `system_config` JSON `security_password_policy(minLength, historyCount:5, ...)` (`security_settings_controller.ex:35-42`) | **PARCIAL** — política existe mas `historyCount` não é executável sem tabela de histórico |
| `spb_tb_cad_usuarios_tokens` | tokens de sessão (stateful) | JWT stateless + `Shared.Auth.TokenBlacklist` (Redis) | **DIVERGÊNCIA (desenho)** — sessão stateless |
| `spb_tb_cad_usuarios_ad` | integração Active Directory | `users.keycloak_id` (Keycloak/SSO) | **DIVERGÊNCIA (desenho)** |
| `spb_tb_cad_usuarios_siteminder` | SSO SiteMinder | SSO tokens (`iss: monetarie-sso`) | **DIVERGÊNCIA (desenho)** |
| `spb_tb_log_atividade / _mfa / _email / _rpt` (4 tabelas) | trilha por tipo (login/MFA/email/relatório) | `audit_logs` (tabela única: `action, resource_type, resource_id, ip_address, user_agent, request_data, response_status`) (`auth/audit_log.ex:28-39`) | **PARCIAL** — auditoria única, não segmentada |
| `TB_CERTIFICADO_ENVIO` (pfx de assinatura, base64+senha) | mTLS de saída | `crypto/private_key.ex` + `crypto/certificate.ex` (+ Secrets Manager) | **COBERTO (divergente na forma)** |
| `TB_CERTIFICADO_AUTORIZADO` / `TB_CERTIFICADO_ARQUIVO` | trust de origem (mTLS entrada) | truststore ICP-Brasil `icp_brasil_ca_chain.pem` + `verify_peer` | **COBERTO (divergente na forma)** |
| `TB_ORIGEM_AUTORIZADA` / `TB_DESTINO_AUTORIZADO` (0/0 no backup) | whitelist host origem/destino | Security Group / rede + mTLS | **DIVERGÊNCIA (desenho)** |
| (sem par no legado) | — | `api_clients(client_id, client_secret_hash, scopes, status)` — OAuth client_credentials da Partner API (`auth/api_client.ex:17-27`) | **NOSSO A MAIS** (Partner API não existia no legado) |

Nota: `crksecurityadminBKP` também contém as tabelas `spb_tb_cad_alcadas_*` e `spb_tb_cnt_*` — essas são
a alçada e a contabilidade do **AdmWeb/SPB** (não do PIX). A alçada de money-path do PIX legado vive em
`crk_spi` (§3.2), e a contabilidade do PIX legado em `CRK_SPICONTABIL` (§1). Fora do escopo PIX, mas
registrado para não confundir a origem.

### 3.2 ALÇADA (aprovação de money-path) — `crk_spi.Spi*Alcada*` x `monetarie_spi.alcada_*`

Contagem viva legado: `SpiCadParamAlcada=2, SpiRegAlcada=0, SpiRegVistoAlcada=0,
SpiRegLimitesOperacao=0` (2 parâmetros configurados, nenhum registro de ordem — laboratório).

| Legado (`crk_spi`) | Colunas-chave | Nosso par (`monetarie_spi`) | Veredito |
|---|---|---|---|
| `SpiCadParamAlcada` | `IdAlcada, IdInstFinanc, NmAlcada, IdPeriodoVerificacao(Diariamente/DiasUteis/DiasNaoUteis), NrQtdeVistos, **UsuariosAlcada/GruposAlcada/IdSystemsAlcada** (escopo), **UsuariosAprovadores/GruposAprovadores** (whitelist), VlrMinimo/VlrMaximo, **VlrDiario** (acumulado), HrInicioVerificacao/HrFimVerificacao, JsonMsgs, FlAtiva, **FlEstendeLimites**` | `alcada_parameters(name, description, min_amount, max_amount, accumulated_amount, accumulated_period, required_vistos, start_hour, end_hour, message_types[], is_active, priority)` (`schemas/alcada/parameter.ex:13-25`) | **PARCIAL** — sem escopo usuário/grupo/sistema, sem whitelist de aprovadores, sem período/EstendeLimites |
| `SpiRegAlcada` | `IdRegistroAlcada, IdAlcada, IdOperacao, DtHrEntrada, DtHrFinal, IdStatusAlcada(Pendente/Rejeitada/Aprovada), NrVistos, DtBase, ValorLimite` | `alcada_registrations(operation_id, operation_type, amount, status(pending/approved/rejected/expired), required_vistos, current_vistos, created_by, resolved_at, parameter_id)` (`schemas/alcada/registration.ex:12-26`) | **COBERTO** |
| `SpiRegVistoAlcada` | `IdRegistroAlcada, IdUsuario, DtHrVisto, IdStatusVisto, DsVisto (justificativa)` | `alcada_vistos(user_id, user_name, status(approved/rejected), notes, signed_at)` + unique `[registration_id, user_id]` (`schemas/alcada/visto.ex:10-30`) | **COBERTO** |
| `SpiRegLimitesOperacao` | acumulado diário PERSISTIDO por `DtBase+Cliente(CPF/CNPJ)+ISPB+Agencia+Conta+IdAlcada`, coluna computada `ValorDisponivel = ValorLimite + TotalCredito - TotalDebito` | ✗ (acumulado calculado on-the-fly de `messages+payments` em `SendGate.accumulated_today/2`, `send_gate.ex:229-249`) | **AUSENTE** — sem ledger de limite por cliente/conta |
| `SpiRegAlcadasOperacao` | vínculo N:N registro↔alçada aplicável | ✗ (1 registro → 1 parameter_id) | **PARCIAL** |
| (sem par no legado) | — | `alcada_held_messages(message_type, xml_content, builder_params, status, created_by, approved_by, ...)` (`schemas/alcada/held_message.ex:19-35`) — mensagem retida do builder manual | **NOSSO A MAIS** (retenção de mensagem construída) |

**Diferença de ESCOPO e regras (comportamento, com prova):**

- **Escopo de aplicação**: legado aplica alçada a TODA operação de IDA com valor ANTES de enviar ao
  BACEN (`ValidacaoAlcadaUseCase.ValidaAlcada`, `SPI.Core.Worker.FilaEntrada.Application.decompiled.cs:72`).
  Nosso `SendGate` aplica SÓ ao envio MANUAL/admin (Construir Mensagem + reprocessos humanos);
  origens de sistema/automação são ISENTAS por construção (`@system_sources`, `send_gate.ex:81-83`) e a
  flag `ALCADA_PIPELINE_ENABLED` é **default OFF** (`send_gate.ex:113-120`) com **fail-open** em erro
  de lookup (`:161-170`). O fluxo automático do Core (IB/Partner) tem a alçada no Core (Onda 1), não na
  cabine. → **DIVERGÊNCIA de cobertura (alto)**.
- **Whitelist de aprovadores**: legado só deixa `UsuariosAprovadores/GruposAprovadores` votar
  (`ManAlcada`, `SPI.Core.Angular.Infrastructure.decompiled.cs:3476`). Nosso `add_visto` deixa QUALQUER
  usuário (exceto o criador) votar (`alcadas.ex:123 cannot_self_approve`; sem checagem de aprovador). →
  **AUSENTE (médio)**.
- **Maker/checker + N vistos + 1-rejeição-terminal + dedup de voto**: AMBOS têm.
  Nosso: `created_by == user_id -> :cannot_self_approve` (`alcadas.ex:123`), `new_count >=
  required_vistos -> approved` (`alcadas.ex:154-158`), `visto rejected -> reject_registration`
  (`alcadas.ex:137-138`), unique `[registration_id, user_id]` (`visto.ex:30`). → **COBERTO**.
- **Regras por campo da mensagem** (`PropriedadeFiltro` com operadores In/NotIn/>=/<= e desempate pela
  alçada MAIS específica — `OrderByDescending Regras.Count`): legado tem; nosso `SendGate.evaluate`
  casa por `message_type` + faixa + janela, ordena por `priority asc` e pega o PRIMEIRO
  (`send_gate.ex:185-203`). Sem filtro por campo, sem desempate por especificidade. → **AUSENTE (médio)**.
- **Acumulado com data-base + período + EstendeLimites/overnight/feriado**: legado computa `DtBase`
  por `ObtemDataBase` respeitando janela/feriado; nosso acumulado é bucket dia-UTC simples de
  `messages+payments` (`send_gate.ex:226-249`), sem período/feriado/overnight. → **PARCIAL (médio)**.
- **`NrQtdeVistos==0` auto-rejeita** quando estoura: legado tem; nosso não modela vistos=0 (changeset
  exige `required_vistos > 0`, `parameter.ex:38`). → **DIVERGÊNCIA (baixo)**.
- **Alerta por e-mail aos aprovadores** (`WorkerEnvioAlertaAlcada`): legado tem; nosso não tem worker
  de alerta de alçada dedicado (INCONCLUSIVO sobre notificação; não encontrado). → **PARCIAL (baixo)**.

---

## 4. Tabela consolidada — LEGADO SEM PAR (o que a pista pediu)

### Contábil (`CRK_SPICONTABIL`)
- `EventoMensagem` (CdMsg→evento) — **AUSENTE**
- `EventoMensagemTag` (condição por tag/campo) — **AUSENTE**
- `EventoContabil.IdContaDebito2/IdContaCredito2` (2ª perna) — **AUSENTE**
- `EventoContabil.IdCentroCusto{Debito,Credito}1/2` (CC por perna) — **AUSENTE**
- `HistPadrao.CdHist` (catálogo de histórico) — **AUSENTE**
- `PlanoConta.FlCentroCusto` / `IdInstFinanc` — **PARCIAL**

### Multiliquidação (`crk_multtiliquidacao` + `CRK_MULTILIQINTEGCONTA`)
- `TB_OPERACAO` (operação de negócio + 36+ estados) — **AUSENTE/DIVERGÊNCIA**
- `TB_ARQUIVO_PAGTO` / `TB_ARQUIVO_PAGTO_DETALHE` / `TB_ARQUIVO_PAGTO_ERRO` (lote) — **AUSENTE**
- `TB_BLOQUEIO` / `TB_SOLICITACAO_BLOQUEIO` (bloqueio de saldo em conta, MED) — **AUSENTE**
- `TB_JANELA_RETENTATIVA` / `TB_HISTORICO_RETENTATIVA` (janela por ISPB + débito diferido) — **AUSENTE**
- `TB_PARTICIPANTECONTROLADO` (conta de terceiro + credencial de provedor CC) — **AUSENTE/N-A**
- `TB_SANCTIONSCREENING` / `_ERRO` (AML) — **AUSENTE**
- `TB_SOLICITACAO_PAGAMENTO` (jornada 3/4) — **AUSENTE**
- `TB_INICPAGTODINAMICORECEBEDOR.VlSaque/VlTroco` (QR composto saque/troco) — **AUSENTE (parcial no QR)**
- `TB_DEPARA_AGENCIA/DOMINIO/IF` (de-para SFN↔legado de conta) — **AUSENTE/N-A**

### Segurança (`crksecurityadminBKP` + `crk_spi`)
- `spb_tb_cad_mens_func_grupo.cd_msg` (RBAC no nível de mensagem) — **AUSENTE**
- `spb_tb_cad_senhas` (histórico de senha) — **AUSENTE**
- `spb_tb_cad_usuarios_ad` / `spb_tb_cad_usuarios_siteminder` — **DIVERGÊNCIA (desenho)**
- `spb_tb_cad_usuarios_tokens` (sessão stateful) — **DIVERGÊNCIA (desenho)**
- `spb_tb_cad_funcionalidades.fl_acesso_mensagem/ds_tipo_msg` — **AUSENTE**
- `TB_ORIGEM_AUTORIZADA` / `TB_DESTINO_AUTORIZADO` (whitelist host) — **DIVERGÊNCIA (desenho)**
- `crk_spi.SpiCadParamAlcada.UsuariosAprovadores/GruposAprovadores` (whitelist aprovador) — **AUSENTE**
- `crk_spi.SpiCadParamAlcada.UsuariosAlcada/GruposAlcada/IdSystemsAlcada` (escopo) — **AUSENTE**
- `crk_spi.SpiRegLimitesOperacao` (acumulado persistido por cliente/conta) — **AUSENTE**

---

## 5. COBERTOS relevantes (confiança)

- Alçada existe na cabine com maker/checker, N vistos, 1-rejeição-terminal e dedup de voto
  (`schemas/alcada/*`, `alcadas.ex:123-158`, `visto.ex:30`) — o mapa 09 estava desatualizado.
- Contábil só de operação LIQUIDADA em ambos (legado `IdStatus==9`, nosso `transaction.settled`).
- Detecção de falha de materialização de evento é VISÍVEL na cabine (`status="error"` + telemetry +
  alerta), e a cobertura "NÃO CONTABILIZADO" existe no Core (`pix_reconciliation` `:missing_journal`).
- Idempotência de crédito recebido: legado `TB_CONFIRMACAO_RECEBIDA`/`TB_CONTROLE_CREDITO_RECEBIDO` ≈
  nosso `Shared.E2ECache` + dedup por E2E.
- RBAC essencial coberto: users/groups/features/group_features com verbos CRUD + `RequirePermission`
  fail-closed; MFA (TOTP + backup codes) inline; certificados via crypto schemas + Secrets Manager.
- APIX001 regulatório coberto (`settlement_service/apix.ex`).
- Fee: nosso motor é mais rico que o `ParametroTarifa` legado; nenhum dos dois lança tarifa em COSIF.

---

## 6. Ressalvas
- O backup do legado está com CONFIG DE LABORATÓRIO no contábil e em bloqueio/janela/recorrência
  (tabelas zeradas). A comparação é de CAPACIDADE/schema, não de operação observada.
- `crksecurityadminBKP` é o AdmWeb compartilhado PIX/SPB; as tabelas `spb_tb_cad_alcadas_*` e
  `spb_tb_cnt_*` são do SPB/AdmWeb, não do PIX (a alçada/contábil de PIX estão em `crk_spi`/
  `CRK_SPICONTABIL`). Registrado para evitar dupla-contagem.
- O de-para de conta (`CRK_MULTILIQINTEGCONTA`) e o `TB_RETORNO_LEGADO` (IBM MQ CAMPS) são
  dependências de sistemas de conta externos do legado; no desenho Monetarie o débito é no ledger
  próprio (Core/TB), então NÃO ter par é decisão de arquitetura, não regressão.
