Nota (PT-BR): este documento é uma referência normativa de engenharia e permanece temporariamente em inglês para preservar precisão técnica. A visão geral em português está em Decision Engine. A tradução completa está no backlog de documentação.
Contrato de dados de decisioning¶
Status: normativo para novas implementações de inteligência e alocação de capital. Baseline: 2026-08-20. Escopo: fatos, qualidade, cenários, evidências e snapshots consumidos pelo motor determinístico. As decisões de arquitetura complementares estão em architecture-decisions.md; fontes e SLAs estão em source-matrix.md.
Este contrato implementa quatro regras inegociáveis:
- dinheiro e métricas de decisão são calculados em PostgreSQL, nunca por LLM nem pelo navegador;
nullsignifica desconhecido e nunca é convertido silenciosamente em zero;- conversão, refund/chargeback e rebill amadurecem em relógios independentes;
- uma recomendação é somente leitura até uma aprovação humana registrada.
Os termos DEVE, NÃO DEVE, DEVERIA e PODE são normativos.
1. Escopo e planos¶
O modelo é separado em seis planos. Uma tabela pode materializar mais de um plano apenas se preservar a imutabilidade e a proveniência de cada fato.
| Plano | Responsabilidade | Pode ser escrito por |
|---|---|---|
| Fatos | mídia, tracking, eventos de rede, ledger, saldos, FX e compromissos | adapters, webhooks, jobs e RPCs transacionais |
| Qualidade | completude, frescor, dedupe, reconciliação, atribuição e maturidade | SQL determinístico |
| Quant | unit economics, amostra, risco, capital e alocação | funções/views SQL versionadas |
| Decisão | regras, findings, recomendações, cooldown e aprovação humana | avaliador SQL/RPC e usuário autorizado |
| IA | explicação tipada limitada aos evidence IDs recebidos | gateway server-side |
| Aprendizado | outcomes de coortes maduras e taxonomias versionadas | jobs após gates de maturidade |
Dados externos entram como raw imutável, são normalizados e só depois podem gerar fatos canônicos. Raw, fato normalizado, evidência e decisão NÃO DEVEM ser colapsados em uma única linha mutável.
fonte -> raw imutável -> normalização versionada -> fato canônico
-> gates de qualidade -> evidence snapshot -> cálculo SQL
-> recomendação -> decisão humana -> ação operacional fora do motor
2. Identidade e granularidade¶
Todo objeto tenant-owned DEVE carregar workspace_id. Chaves estrangeiras
entre objetos tenant-owned DEVEM impedir referências entre workspaces. A
workspace ativa é escolhida explicitamente e validada no servidor; escolher a
primeira membership não satisfaz este contrato.
Campos comuns mínimos de um fato canônico:
| Campo | Tipo lógico | Regra |
|---|---|---|
id |
UUID | gerado no servidor, imutável |
workspace_id |
UUID | obrigatório e coberto por RLS forçada |
source_system |
enum/text | valor controlado da matriz de fontes |
source_account_id |
text | identificador externo da conta; null só com reason code |
source_record_id |
text | ID externo ou chave canônica reproduzível |
source_event_id |
text | ID do evento quando a fonte o fornece |
raw_record_id |
UUID | referência ao raw; obrigatório para integrações automáticas |
normalization_version |
text | versão imutável do parser/mapeamento |
event_at |
timestamptz |
relógio econômico ou operacional da fonte |
observed_at |
timestamptz |
instante em que a fonte representava o valor |
ingested_at |
timestamptz |
instante de recebimento pela Brandvelia |
dedupe_key |
text | chave determinística, com unique por workspace/fonte |
payload_hash |
text | SHA-256 de representação canônica sem secrets |
quality_status |
enum | VALID, PROVISIONAL, UNKNOWN ou INVALID |
quality_reasons |
text[] | códigos controlados, nunca apenas texto livre |
source_record_id não é substituído por nome de campanha, nome de oferta ou
qualquer outra dimensão mutável. Nomes são atributos; IDs são identidade.
3. Símbolos de capital¶
Todos os termos abaixo pertencem a uma única workspace_id, allocation_run_id,
as_of, horizon_end e allocation_currency. É proibido misturar moedas ou
instantes no mesmo cálculo.
| Símbolo | Campo canônico | Definição |
|---|---|---|
S0 |
settled_cash |
saldo de caixa liquidado, reconciliado e disponível no início lógico do allocation run (as_of); exclui accrued, pending, held, forecast e recebíveis |
Cp |
prior_commitments |
compromissos prévios ainda não liquidados que já são autorizados ou inevitáveis dentro do horizonte, sem duplicar saídas já refletidas em S0 |
L |
liquidity_floor |
reserva de liquidez exigida pela policy version: operação, refund/chargeback, impostos e piso mínimo de caixa, em componentes não sobrepostos |
Ca |
allocatable_capital |
capital alocável conservador depois de compromissos e reserva |
I |
requested_investment |
nova alocação solicitada pelo cenário/decisão; ao ser aprovada vira reserva ou compromisso e deixa de estar livre |
Fórmulas normativas:
Ca = greatest(0, S0 - Cp - L)
0 <= I <= Ca
retained_cash = Ca - I
Regras adicionais:
S0,Cp,L,CaeIDEVEM ter a mesma moeda.- Saldos em outra moeda não entram em
S0até estarem liquidados na moeda de alocação. Uma conversão futura ou uma cotação de mercado não é caixa em USD. - Comissões ClickBank accrued, return allowance, payout scheduled, valores held
e rebills esperados não entram em
S0. - Budget total ou diário é um teto operacional, não automaticamente
Cp. Somente a parte já aprovada, reservada ou incorrida e não paga é compromisso. - Gasto Meta já incorrido, ainda não debitado da conta financeira, entra em
Cp; o mesmo gasto não pode também reduzirS0. Lé produzido por componentes identificáveis e umapolicy_version; não é um percentual livre digitado no dashboard.Ca = 0é um resultado válido. Caixa retido é uma alocação válida.- Falta de
S0,CpouLproduzCa = null, nunca0.
O SQL equivalente DEVE preservar o desconhecido:
case
when s0 is null or cp is null or liquidity_floor is null then null
else greatest(0::numeric, s0 - cp - liquidity_floor)
end
4. Dinheiro, moeda e precisão¶
4.1 Tipo canônico¶
Na API e em snapshots JSON, dinheiro é um objeto e o decimal é string:
{
"amount": "1250.430000",
"currency": "USD"
}
No PostgreSQL:
- valores monetários novos:
numeric(20,6); - taxas e razões:
numeric(24,12); - moeda: código ISO 4217 em maiúsculas, inicialmente
USDeBRL; - TypeScript: decimal string na fronteira;
numbernão é contrato financeiro; NaN,Infinity, float SQL e soma de moedas diferentes são inválidos.
A escala adicional preserva micros de plataformas e rateios. Arredondamento só
ocorre na liquidação exigida pela fonte ou na apresentação; o modo e a escala
DEVEM ser explícitos (HALF_EVEN por padrão). O valor bruto recebido permanece
no raw.
4.2 Sinal e natureza¶
amount canônico é não negativo. O sentido é explícito:
direction = INFLOW | OUTFLOW
economic_type = REVENUE | REFUND | CHARGEBACK | FEE | AD_SPEND |
OPEX | PAYOUT | TRANSFER | FX | ADJUSTMENT
Uma transferência entre contas produz duas pernas ligadas por transfer_id e
não é receita nem despesa. Refund e chargeback apontam para a coorte/transação
original. Correções são novas entradas de ajuste; fatos financeiros importados
e reconciliados não são atualizados in-place.
4.3 Conversão cambial¶
Uma conversão DEVE registrar:
| Campo | Regra |
|---|---|
base_currency / quote_currency |
par explícito; por exemplo USD/BRL |
base_amount / quote_amount |
valores efetivamente trocados |
market_rate |
benchmark observado, opcional com reason code |
actual_rate |
quote_amount / base_amount, obrigatório para lucro realizado |
fx_fee e fx_fee_currency |
separados do principal |
provider, external_ref |
comprovante da operação |
quoted_at, executed_at, settled_at |
relógios distintos |
market_rate serve para benchmark e planejamento; nunca substitui
actual_rate no lucro realizado. Métricas em moeda gerencial DEVEM expor a
fonte e o timestamp da taxa. Não se soma USD e BRL antes da conversão por uma
regra versionada.
5. Relógios e janelas¶
Datas são fatos, não metadados intercambiáveis.
| Relógio | Significado |
|---|---|
event_at |
quando o clique, impressão, compra, refund, gasto ou compromisso ocorreu na fonte |
source_updated_at |
quando a fonte corrigiu a versão, se disponível |
observed_at |
instante ao qual um snapshot de saldo, insight ou cotação se refere |
ingested_at |
quando a Brandvelia persistiu o raw |
effective_at |
competência contábil/econômica |
settled_at |
quando houve liquidação financeira |
available_at |
quando o saldo ficou realmente utilizável |
reconciled_at |
quando o fato entrou em uma reconciliação válida |
as_of |
cutoff fechado do snapshot de decisão |
matured_at |
instante calculado para cada tipo de maturidade |
Todos os instantes são timestamptz em UTC. Também são preservados
source_timezone e source_business_date. Um dia Meta usa o timezone da conta
de anúncios; um evento ClickBank usa o timestamp da transação; relatórios
executivos podem depois projetar ambos em um calendário gerencial versionado.
Regras:
ingested_atvem do servidor e não pode ser enviado pelo cliente.as_of <= now()e fatos comingested_at > as_ofnão entram no snapshot.- atraso é
ingested_at - observed_at; frescor éas_of - observed_at. - timestamp ausente não recebe
now()como substituto. O valor ficanullcom reason code, excetoingested_at. - evento mais de 5 minutos no futuro em relação à ingestão é
PROVISIONALpor clock skew; acima de 24 horas éINVALIDaté correção. - todo range usa limite
[from, to). Datas inclusivas de uma API são normalizadas explicitamente pelo adapter.
6. Maturidades independentes¶
Cada coorte de aquisição mantém três estados separados. Não existe um campo
canônico is_mature.
maturity_kind = CONVERSION | REFUND | REBILL
maturity_status = MATURE | IMMATURE | UNKNOWN | NOT_APPLICABLE | INVALID
| Maturidade | Início do relógio | Torna-se MATURE quando |
|---|---|---|
| conversão | click.event_at/início da coorte |
encerrou a janela de conversão da policy, tracking está fresco e eventos foram reconciliados até o cutoff |
| refund | evento de sale/bill da coorte | encerrou a janela de refund e chargeback aplicável e a reconciliação cobre a janela |
| rebill | sale inicial ou ciclo anterior | os ciclos observáveis definidos na policy terminaram e seus eventos foram reconciliados |
Cada registro inclui cohort_key, kind, window_start, window_end,
matured_at, policy_version, quality_status e evidence_ids. Atraso da
fonte estende o cutoff de observação; não antecipa maturidade.
Uso por fase:
| Uso | Requisito mínimo |
|---|---|
PREFLIGHT |
cenários sourced; maturidades históricas podem ser UNKNOWN |
TEST |
tracking, caixa e compromisso VALID; economics podem ser PROVISIONAL com cap de risco |
VALIDATE |
maturidade de conversão MATURE; refund fica separado e exige reserva conservadora se ainda IMMATURE |
SCALE |
conversão e refund MATURE/VALID; rebill só entra no economics se também MATURE/VALID |
| aprendizado/calibração | somente outcomes cujas maturidades necessárias são MATURE/VALID |
Oferta sem recorrência tem rebill NOT_APPLICABLE, não MATURE. Oferta
recorrente sem histórico tem rebill UNKNOWN ou IMMATURE; expected rebill é
zero peso em caixa e não é convertido em fato realizado.
7. null, zero e reason codes¶
Todo campo nullable usado em decisioning DEVE possuir um campo irmão
<field>_null_reason. Códigos permitidos:
| Código | Uso |
|---|---|
NOT_OBSERVED |
a fonte existe, mas não publicou/observou o valor |
SOURCE_UNAVAILABLE |
integração ou credencial indisponível |
NOT_APPLICABLE |
o conceito não se aplica, como rebill não recorrente |
NOT_YET_MATURE |
janela ainda aberta |
NOT_RECONCILED |
observado, mas ainda não reconciliado |
ZERO_DENOMINATOR |
razão não definida por denominador zero |
LEGACY_MISSING |
legado não capturou o campo obrigatório |
REDACTED |
removido por privacidade/retenção |
Falha de parse, moeda inválida, conflito de dedupe ou timestamp impossível não
são reason codes de ausência: o registro vai para quarentena com gate
INVALID. Zero é aceito somente quando a fonte observou zero ou uma fórmula
válida resultou em zero.
Exemplos:
spend = "0.000000", reason = null # Meta confirmou zero
spend = null, reason = SOURCE_UNAVAILABLE # não houve coleta
roas = null, reason = ZERO_DENOMINATOR # spend válido igual a zero
rebill = null, reason = NOT_YET_MATURE # não inventar previsão
Agregações não fazem coalesce(value, 0) antes de verificar completude. A UI
exibe “desconhecido” e o reason code; não exibe $0.00.
8. Proveniência e evidência¶
8.1 Envelope de proveniência¶
Todo fato usado em uma decisão deve resolver para:
{
"source_system": "clickbank_ins",
"source_account_id": "account-alias",
"source_record_id": "receipt|SALE|item",
"source_event_id": null,
"raw_record_id": "00000000-0000-0000-0000-000000000001",
"normalization_version": "clickbank-ins/v1",
"formula_version": null,
"event_at": "2026-08-20T12:00:00Z",
"observed_at": "2026-08-20T12:00:00Z",
"ingested_at": "2026-08-20T12:00:04Z",
"payload_hash": "sha256:...",
"quality_status": "PROVISIONAL",
"quality_reasons": ["NOT_RECONCILED"]
}
Valores derivados trocam raw_record_id por input_evidence_ids e exigem
formula_id, formula_version, parâmetros e hash dos inputs. Um novo parser ou
uma nova fórmula cria nova versão; não reescreve a evidência antiga.
8.2 Evidence IDs¶
evidence_id é UUID opaco, global e imutável, gerado no servidor. O envelope de
evidência contém workspace_id, evidence_type, ponteiro para fato/snapshot,
content_hash, quality_status, as_of, created_at e, para derivadas,
input_evidence_ids e formula_version.
Tipos mínimos:
SOURCE_FACT | RECONCILIATION | DERIVED_METRIC | POLICY |
SCENARIO_ASSUMPTION | HUMAN_DECISION
IA recebe do servidor uma allowlist de evidence IDs e deve citar
evidence:<uuid> em toda afirmação factual. ID desconhecido, de outro workspace
ou fora do snapshot invalida a resposta. Evidências não contêm PII e não podem
apontar apenas para uma linha mutável “atual”.
9. Gates de qualidade¶
Os quatro gates têm esta semântica única em toda a plataforma:
| Gate | Semântica | Efeito |
|---|---|---|
VALID |
schema, proveniência, dedupe, frescor, reconciliação e maturidade exigidos pelo uso foram satisfeitos | pode alimentar a fase autorizada pela policy |
PROVISIONAL |
fato existe e é estruturalmente íntegro, mas está imaturo, dentro da tolerância de atraso, em reconciliação, manualmente sourced ou no shadow legado | pode aparecer e alimentar shadow/test conservador; não libera SCALE sozinho |
UNKNOWN |
não existe observação obrigatória ou não há fonte/credencial/cobertura | mantém valor null; bloqueia a decisão que depende dele |
INVALID |
observação contraditória ou insegura: schema, moeda, relógio, tenant, hash, dedupe ou reconciliação hard falhou | quarentena; bloqueia snapshot e alerta owner |
Ordem determinística para um input:
INVALID se qualquer hard check falhar
UNKNOWN se não houver fato requerido válido ou provisório
PROVISIONAL se existir fato íntegro, mas faltar soft freshness,
reconciliação ou maturidade requerida
VALID somente se todos os checks requeridos passarem
Composição de inputs obrigatórios usa a precedência
INVALID > UNKNOWN > PROVISIONAL > VALID. Inputs opcionais não promovem nem
rebaixam o gate agregado, mas continuam visíveis. quality_reasons lista todos
os motivos, não apenas o primeiro.
Cada fonte define soft_sla e hard_sla na matriz:
- dentro do soft SLA: elegível a
VALID; - após soft e até hard SLA: no máximo
PROVISIONAL; - após hard SLA:
INVALIDse uma linha stale está sendo apresentada como atual;UNKNOWNse nenhum fato atual é selecionado.
Reconciliação não altera o raw. Divergência cria finding e rebaixa o snapshot dependente. A tolerância monetária é declarada por fonte/moeda, nunca embutida em TypeScript.
10. Contratos por domínio¶
10.1 Tracking first-party¶
O contrato mínimo do clique é:
click_id, event_id, workspace_id, session_id, landing_page_id,
campaign_id, creative_id, offer_id, event_at, ingested_at,
referrer, device, country,
utm_source, utm_medium, utm_campaign, utm_content, utm_term,
gclid, fbclid, ttclid, external_click_id, consent_state,
source_system, dedupe_key, payload_hash
click_id é aleatório, não contém PII e atravessa landing, HopLink extclid,
ClickBank INS e atribuição. event_id deduplica retries; click_id não deve ser
reutilizado para eventos diferentes. UTMs são dimensões informativas, não
identidade canônica. Atribuição por nome fuzzy só pode ser PROVISIONAL.
10.2 ClickBank¶
O evento raw INS é persistido antes da normalização. O fato normalizado preserva
receipt, transaction_type, item, payout da afiliada, moeda, timestamps,
tracking codes, conta, vendor e ponteiros de atribuição. Dedupe segue a matriz de
fontes e a gravação raw -> conversion -> ledger -> audit/outbox deve ser
atômica ou retomável por estado, sem produzir duas entradas financeiras.
SALE/BILL, RFND e CGBK continuam ligados à mesma aquisição/coorte. O
QuickStats é controle agregado oficial, não substituto silencioso de transações
detalhadas. Divergência acima da tolerância rebaixa os períodos afetados.
Marketplace payout, Gravity, marketplace EPC, score e opinião de IA são sinais de pre-flight. Eles nunca se tornam prova de lucro próprio.
10.3 Meta Ads, piloto read-only¶
Meta é o primeiro canal oficial de mídia. O adapter só lê:
- catálogo mínimo de account/campaign/ad set/ad com IDs e status;
- insights diários por nível e objeto;
spend,impressions,clicks,outbound_clicks,landing_page_viewse actions normalizadas poraction_type, quando a API efetivamente retornar o campo;- moeda, timezone, attribution setting/window e versão da API.
Meta não é autoridade de vendas ClickBank. Actions de conversão da Meta
permanecem métricas de plataforma e não são somadas ao ledger de receita.
Nenhum endpoint de escrita, ads_management, mudança de budget, pausa ou
publicação pertence ao piloto.
10.4 Caixa, saldos e compromissos¶
Um balance snapshot registra por conta:
financial_account_id, provider, account_currency, as_of,
available_amount, pending_amount, held_amount, accrued_amount,
source_record_id, statement_id, reconciled_at, quality_status
Somente available_amount liquidado e reconciliado é candidato a S0.
pending, held e accrued são preservados e excluídos.
Um compromisso registra:
commitment_id, kind, counterparty, external_ref, amount, currency,
incurred_at, due_at, status, reserved_amount, settled_amount,
campaign_id, approval_id, source_system, evidence_ids
Estados: DRAFT, AUTHORIZED, INCURRED, PARTIALLY_SETTLED, SETTLED,
CANCELLED, DISPUTED. Apenas AUTHORIZED, INCURRED,
PARTIALLY_SETTLED e a parcela conservadora de DISPUTED entram em Cp.
DRAFT não entra; SETTLED já está em caixa/ledger; CANCELLED vale zero.
10.5 Cenários e snapshots¶
Um scenario set contém exatamente LOW, BASE e HIGH, cada um com os mesmos
campos econômicos. Cada input é um fato com evidence ID ou uma assumption
explícita com owner, rationale, unidade, source URL/document, observed_at e
expires_at. Aprovação não transforma assumption em fato.
Um decision snapshot é imutável e contém:
snapshot_id, workspace_id, as_of, horizon_end, allocation_currency,
source_watermarks, scenario_set_version, policy_version, formula_versions,
input_evidence_ids, input_hash, gate_status,
S0, Cp, L, Ca, requested_I, retained_cash,
conversion_maturity, refund_maturity, rebill_maturity
Reexecutar com os mesmos inputs e versões deve produzir o mesmo input_hash e
resultado.
11. Exemplo de allocation snapshot¶
{
"as_of": "2026-08-20T15:00:00Z",
"allocation_currency": "USD",
"S0": { "amount": "1000.000000", "currency": "USD" },
"Cp": { "amount": "150.000000", "currency": "USD" },
"L": { "amount": "300.000000", "currency": "USD" },
"Ca": { "amount": "550.000000", "currency": "USD" },
"I": { "amount": "200.000000", "currency": "USD" },
"retained_cash": { "amount": "350.000000", "currency": "USD" },
"maturity": {
"conversion": "MATURE",
"refund": "IMMATURE",
"rebill": "NOT_APPLICABLE"
},
"gate_status": "PROVISIONAL",
"quality_reasons": ["REFUND_NOT_YET_MATURE"],
"evidence_ids": [
"00000000-0000-0000-0000-000000000101",
"00000000-0000-0000-0000-000000000102"
]
}
O exemplo não autoriza SCALE: o caixa é conhecido, mas refund ainda é
provisional. A policy pode autorizar um TEST limitado e reservar risco em L.
12. Lacunas verificadas no estado atual¶
As lacunas abaixo são constatações do código em 2026-08-20, não autorização para compatibilidade silenciosa.
| Área | Estado atual | Gap para este contrato | Gate inicial |
|---|---|---|---|
| dinheiro | legado usa transactions.amount numeric(12,2)/TypeScript number; a migration 20260821130000_brandvelia_decision_truth_foundation.sql introduz ledger numeric(20,6) e direção |
falta migrar fronteiras para decimal string e tornar o novo ledger a autoridade | PROVISIONAL |
| relógios | transactions têm só tx_date; visits só clicked_at |
faltam event/observed/ingested/effective/settled/available separados | PROVISIONAL ou UNKNOWN |
| null | dashboards usam coalesce(..., 0) |
ausência pode aparecer como zero; faltam reason codes | INVALID para decisão de capital dependente |
| proveniência | metadata, external_ref e raw legados; a foundation adiciona sync runs e hashes em vários fatos |
faltam envelope uniforme, normalization/formula versions e ligação a evidence IDs | PROVISIONAL |
| ClickBank INS | raw, conversion, dedup e ledger existem | escrita é multi-etapa não atômica; dedupe e ledger não têm constraint composta tenant-aware; PII é copiada | PROVISIONAL até reconciliação |
| QuickStats | sync diário/manual compara totais USD | janela fixa curta, single-workspace no cron, tolerância em TypeScript e sem watermark formal | PROVISIONAL |
| tracking | click_id, UTMs e click IDs existem |
GET com side effect, sem event_id, consent, source clocks ou proveniência; atribuição pode usar nome fuzzy |
PROVISIONAL |
| Meta Ads | CSV genérico; a foundation cria accounts, mappings e performance facts read-only | adapter/API, credenciais, actions detalhadas, ingestão e reconciliação inexistem | UNKNOWN |
| caixa | contas legadas e schema de balance snapshots na foundation | não há ingestor, reconciliação, available_at nem fatos operacionais de saldo |
UNKNOWN |
| FX | colunas legadas e fx_rate_snapshots na foundation |
server action atual não envia FX do formulário; faltam ingestor, comprovante/settlement e reconciliação | UNKNOWN |
| compromissos | schema capital_commitments na foundation |
faltam serviço, approval evidence, dedupe externo, histórico de estado e cálculo de Cp sem dupla contagem |
UNKNOWN |
| capital | settings e reserve inputs na foundation | não há allocation run nem cálculo/versionamento de S0, Cp, L, Ca e I |
UNKNOWN |
| maturidade | foundation cria três *_matures_at, mas um único maturity_status e um score agregado |
faltam estados/gates independentes de conversion/refund/rebill | UNKNOWN |
| evidência | audit log genérico | não há evidence IDs, allowlist para IA, content hash ou snapshot imutável | UNKNOWN |
| cenários | experiments não são scenarios financeiros | não há LOW/BASE/HIGH versionado nem aprovação | UNKNOWN |
Até essas lacunas serem fechadas, os dashboards legados continuam informativos, mas não são autorização para escala ou movimentação de capital.
13. Critérios de aceite do contrato¶
Uma implementação está conforme somente se testes automatizados demonstrarem:
- replays de tracking, INS, Meta e imports não duplicam fatos nem ledger;
- falta de fonte produz
null + reason, não zero; - nenhuma soma cruza moedas sem FX evidence;
CaficanullseS0,CpouLestiver desconhecido;- pending/held/accrued/forecast/rebill esperado nunca entra em
S0; - mudar uma maturidade não altera as outras duas;
- fatos posteriores ao
as_ofnão entram no snapshot; - a mesma combinação de inputs/versions gera o mesmo hash e resultado;
- evidence ID de outro workspace ou fora da allowlist é rejeitado;
- gate agregado respeita
INVALID > UNKNOWN > PROVISIONAL > VALID; - Meta opera com
ads_reade testes impedem qualquer chamada de escrita; - recommendation não altera campanha, budget, dinheiro ou fase sem decisão humana registrada.