Saltar para o conteúdo
DataVault
Voltar às respostas para RFP

Documentação do DataVault

DataVault — Capacidades por critério de avaliação

Documento público. Organiza o que o DataVault faz pelos critérios com que os analistas avaliam ferramentas de qualidade de dados (profiling, monitorização, parsing e normalização, matching, limpeza, enriquecimento, gestão de metadados, orquestração e experiência para utilizadores de negócio). É a fonte técnica da página /datavault/rfp do site: cada secção diz o que existe, onde está no código e que testes o provam — e, quando é parcial, o que falta.

Segurança e conformidade estão em SEGURANCA.md. A cobertura por dimensão de qualidade (DAMA, ISO 8000, ISO/IEC 25012) e por área DAMA-DMBOK está em COBERTURA_QUALIDADE.md.

Profiling

Descoberta automática e perfil de cada tabela em todas as fontes ligadas: contagens, nulos, distintos, mínimo/máximo/média/desvio, cardinalidade, distribuições e amostras limitadas.

  • Mais de 20 tipos de fonte: warehouses (BigQuery, Snowflake, Databricks, Fabric, Azure Synapse), bases de dados operacionais (PostgreSQL, MySQL, Oracle Database, SQL Server e Azure SQL — é nestes dois que correm os ERPs Primavera, PHC e Sage), MongoDB (schema inferido por amostra), aplicações de negócio (SAP S/4HANA e SAP Gateway por OData, Salesforce, Dynamics 365 e outros serviços OData v2 e v4), ficheiros, folhas Google (cada separador é um dataset), armazenamento de objetos (GCS, S3, Azure Blob e ADLS Gen2), SFTP, streaming (Kafka, Pub/Sub) e fontes documentais — lista e setup no guia de conectores.
  • Profiling incremental: em tabelas grandes com coluna temporal, o re-scan lê só a janela nova e combina-a com o perfil anterior (com um perfil completo periódico).
  • Documentos (SharePoint, Google Drive) têm um perfil próprio de qualidade documental.

Testes: backend/tests/test_incremental_profiling.py, backend/tests/test_profile_distributions.py.

Monitorização

  • Monitores automáticos por omissão em cada tabela da conexão (até 500 por conexão em cada scan), sem configuração e a partir dos metadados de cada fonte (nas bases de dados, só do catálogo): atualidade (aprende a cadência de atualização), volume (z-score modificado sobre o histórico) e mudanças de schema, conforme o que cada fonte expõe (método).

  • Deteção de drift e anomalias: colunas adicionadas/removidas, mudança de tipo, picos de nulos, quedas de volume, drift de distribuição por coluna (quantis e categorias já perfilados, método), anomalias sazonais e por segmento.

  • Metodologia publicada: fórmulas, pesos, limiares e limitações do score, das anomalias, dos duplicados, do trust score e da amostragem em METODOS.md, com uma secção «Como calculamos» na app.

  • Motor de regras com 16 tipos de regra: nulos (not_null), unicidade (unique), padrões (pattern), intervalos (range), atualidade (freshness), volume mínimo (row_count_min), validade por dígito de controlo (valid_identifier, registo de identificadores por país) — NIF/NIPC, NISS, NIB e Cartão de Cidadão; DNI, NIE e NIF de empresa de Espanha; CPF, CNPJ (também o alfanumérico) e PIS/PASEP do Brasil; AVS/AHV e UID da Suíça; NHS number e VAT GB do Reino Unido; IBAN com a estrutura e o comprimento do país (registo SWIFT e Angola); Luhn. Sem dígito de controlo publicado, só a estrutura: SSN, EIN e ITIN dos EUA, NINO e Companies House do Reino Unido, NIF e BI de Angola —, listas de referência (in_reference_set: as ISO incluídas ou as do registo de dados de referência do workspace, com versões, mapeamentos de códigos entre sistemas e uma verificação de coerência entre datasets), condições entre colunas da mesma linha numa linguagem de expressões fechada — «data_fim >= data_inicio», «total = soma(subtotal, iva)», «se pais = 'PT' então válido(nif, 'nif')» — (expression, com o dígito de controlo de NIF, NISS, CPF, CNPJ, DNI, NIE, NHS number, AVS/AHV e UID verificado na própria fonte), SQL à medida só de leitura — um SELECT das linhas violadoras, com zero ou até N linhas esperadas — (custom_sql), integridade referencial (referential: chaves órfãs face a outro dataset, por pushdown na mesma ligação ou anti-join em memória entre ligações, proposta pelo DETGEN para cada aresta do lineage), exatidão contra fontes autoritativas (accuracy, abaixo) e, nos streams, idade do evento (max_event_age), conformidade com o Schema Registry (schema_conformity), campos obrigatórios (required_fields) e mensagens ilegíveis (max_malformed_pct). As regras de expressão e de SQL correm na fonte, sobre a tabela inteira, e só nas fontes SQL; em ficheiros, APIs e streams ficam «não avaliáveis». O SQL do cliente passa por uma guarda — um só SELECT, sem escrita, só funções de uma lista e só tabelas da ligação — e corre numa transação sempre desfeita, com só-leitura e tempo máximo na sessão onde o motor os tem (DBA.md). Checks em Python à medida continuam fora. As regras chegam por packs por indústria (e-commerce, financeiro, saúde, SaaS), por sugestões a partir do perfil e do histórico de aceitação do workspace — só entram quando um administrador as aceita — ou pelo import de dbt (também os testes singulares, que viram regras de SQL à medida), Great Expectations, SodaCL e ODCS (o que converte).

  • Exatidão contra fontes autoritativas (regra accuracy, cartão #266). Um NIF com o dígito de controlo certo pode não existir: a regra pergunta a uma fonte se o valor existe. Número de IVA no VIES da Comissão Europeia (Estados-Membros e Irlanda do Norte), código postal CP7 e localidade na GeoAPI.pt (dados dos CTT), telefone no plano de numeração do país com a biblioteca phonenumbers (E.164, tipo e país, sem rede), email por sintaxe e registo MX do domínio (DNS sobre HTTPS, sem enviar nada à caixa) e países e moedas nas listas ISO publicadas, descarregadas com atualização periódica. É opt-in: um admin autoriza cada fornecedor em PUT /api/v1/workspace/accuracy e, sem autorização, nenhum valor sai e a regra fica «não avaliada». Os pedidos são só GET pelo cliente HTTP só-leitura dos conectores, com a guarda anti-SSRF; as respostas ficam em cache no worker, separadas por workspace, cada fornecedor tem um intervalo mínimo entre pedidos e cada scan tem um teto de consultas (DATAVAULT_ACCURACY_MAX_LOOKUPS_PER_SCAN). O que falha abre um issue de exatidão com exemplos mascarados. Os números de IVA do Reino Unido e da Suíça ficam por verificar: os serviços oficiais (HMRC e UID) exigem POST (backend/app/domain/quality/accuracy.py, backend/app/domain/scans/accuracy.py).

  • Regras portuguesas só com dados de forma portuguesa. O que é próprio de Portugal deixa de se aplicar a dados com outra forma (backend/app/domain/quality/pt_shape.py):

    • a lista de CP4 exige o CP7 em maioria na amostra (NNNN-NNN, também com espaço ou sem separador). Uma coluna que só guarde o CP4 («1000») deixa de receber essa regra, porque não se distingue de um código suíço. Um código postal japonês escrito sem hífen («1000001») continua a passar por português;
    • o NIF e o código postal do pack de e-commerce em colunas vat_number, tax_id, zip ou postal_code exigem a mesma forma na amostra do último scan. Um VAT britânico, um EIN americano ou um UID suíço não têm a forma do NIF;
    • na correspondência de telefones, um número sem indicativo casa com o mesmo número com indicativo estrangeiro («923 456 789» com «+244 923 456 789», backend/app/domain/quality/matching.py). Sem indicativos estrangeiros na chave, o +351 aplica-se aos 9 dígitos. Com o país dos dados PT (a omissão), o caso espelho também casa: um telemóvel português «912 345 678» liga-se a «+244 912 345 678» quando é esse o único número da chave com os mesmos 9 dígitos. Com outro país num dos lados (ver o país dos dados, abaixo), o número sem indicativo desse lado leva o indicativo do país (+34 em Espanha) e nunca o +351;
    • na deteção de dados pessoais, um telefone de outro país conta como telefone: com +, com o DDD brasileiro («(11) 91234-5678») ou, numa coluna com nome de telefone, só pelos dígitos. O nome conta quando tem uma palavra de telefone (telefone_alt, phone_number, tlm) ou quando é só celular, contato, contacto ou whatsapp; canal_contacto e is_mobile não contam. Datas, decimais, o CP7 e o CEP nunca contam como telefone. Deixa de passar por morada sem sair do PII (backend/app/domain/compliance/pii_map.py).

    Nas datas NN/NN/AAAA, a correção só lê o mês primeiro quando algum valor tem o 2.º campo acima de 12. Sem prova da ordem não há UPDATE, só o aviso.

  • País dos dados (#513). O workspace tem um país (ISO 3166-1 alfa-2, PT por omissão) e a conexão e o dataset podem sobrepô-lo. O scan usa o do nível mais baixo que o tenha (backend/app/domain/catalog/data_country_service.py). Não é a residência dos dados. Com um país que não seja PT (backend/app/domain/quality/data_country.py):

    • o tipo semântico deixa de dar phone_pt e postal_code_pt, e o DETGEN não propõe a lista de CP4 dos CTT nem o padrão do telefone português (backend/app/domain/quality/semantic_types.py, backend/app/domain/rules/generation.py);
    • os telefones passam a ser o tipo phone com a região do país, quando a maioria da amostra são números atribuídos no plano de numeração desse país. O plano vem do phonenumbers, sem rede (backend/app/domain/quality/phones.py, #516). Um valor com parte decimal nunca conta. Sem pista de telefone no nome da coluna, só contam os números escritos com «+», «00» ou separadores, e com os grupos de um telefone desse plano: o processo «2024/12345» não conta. Uma coluna de tipo numérico não é phone (backend/app/domain/quality/semantic_types.py);
    • a standardização não propõe o +351 nem o CP7, as moradas não se leem como portuguesas e não há enriquecimento por distrito e concelho (backend/app/domain/insights/standardization.py);
    • a standardização de telefones propõe o E.164 do país, por exemplo +34 num dataset espanhol. Um número escrito com «+» ou «00» só se converte com o indicativo do país. Os números de outro país ficam como estão, para revisão manual, e contam na tolerância da regra que protege a coluna. Em Portugal, o mesmo vale para o +351 (backend/app/domain/insights/phone_standardization.py);
    • na correspondência, cada telefone passa a E.164 no plano do país do seu lado. «923 456 789» de um dataset angolano casa com «+244 923 456 789» e não com «+351 923 456 789». Os números brasileiros com o 9.º dígito casam com «+55…», também com o 0 de tronco ou o código de operadora. No Reino Unido, na Suíça e nos EUA o formato nacional casa com o internacional;
    • NIF, NISS, CC e IBAN continuam a reconhecer-se, porque o dígito de controlo prova o identificador. O mapa de PII continua a apanhar um telefone numa coluna com nome de telefone, pelas amostras.

    Numa fonte SQL, uma tabela com uma coluna de país (pais, country…) com códigos ISO de dois países ou mais, um deles PT, recebe para a coluna de NIF ou NISS a proposta se pais = 'PT' então válido(nif, 'nif') quando a amostra mistura valores com e sem o dígito português (pelo menos 30 % com ele): o dígito de controlo só se pede às linhas portuguesas, na fonte.

  • SLO por dataset e incidentes por linhagem (um problema a montante agrupa os afetados a jusante).

  • Alertas in-app, email, Slack, Teams e webhook, com escalas de piquete e escalonamento. Cada canal tem um âmbito opcional (domínios, conexões, datasets, tipos de problema, monitores automáticos), para cada equipa receber só os seus, com pré-visualização dos últimos 30 dias.

Testes: backend/tests/test_auto_monitors.py, backend/tests/test_schema_changes.py, backend/tests/test_anomalies.py, backend/tests/test_quality_rules.py, backend/tests/test_slo.py, backend/tests/test_oncall_escalation.py, backend/tests/test_alert_routing.py.

Parsing e normalização

Tudo é proposta. O DataVault mede a amostra do perfil e gera o SQL no dialeto da conexão e o PySpark. Quem aplica é o cliente, e nada é escrito na fonte.

  • Tipos semânticos detetados automaticamente por coluna, a partir dos valores e do nome: email, URL, UUID, telefone, NIF, NISS, IBAN, Cartão de Cidadão, código postal, país, moeda, datas, percentagens, montantes, nomes e moradas (backend/app/domain/quality/semantic_types.py). Os tipos com formato fixo dão uma regra de padrão ou de dígito de controlo sugerida (backend/app/domain/rules/generation.py).

  • Standardização de formatos: telefones, códigos postais, NIF, IBAN e datas guardados em formatos mistos passam à forma canónica. O passo 1 conta as linhas por máscara e o passo 2 é um UPDATE proposto. Ao aceitar, o DataVault propõe a regra pattern que impede os formatos antigos de voltar (backend/app/domain/insights/standardization.py).

  • Decomposição de campos compostos por regras de locale (pt, es, en), sem serviços externos (backend/app/domain/quality/parsing.py):

    • moradas → rua, número, andar, código postal, localidade e país;
    • nomes completos → título, primeiro nome, apelidos (com «de/da/del/y») e último apelido, também na forma «Apelido, Nome»;
    • telefones → indicativo, número nacional, extensão e E.164;
    • NIF ou DNI/NIE/CIF junto com o nome → identificador validado pelo dígito de controlo e nome;
    • datas em texto livre (meses e dias da semana em português, espanhol e inglês) → data ISO;
    • montantes com moeda → valor decimal e código ISO da moeda.

    O locale e a ordem dia/mês leem-se dos próprios valores. Uma coluna com um tipo semântico atómico (NIF, IBAN, email, código postal, data ISO…) nunca é tratada como composta, e sem pista no nome um telefone só se reconhece com «+», «00» ou separadores (ATOMIC_SEMANTIC_KINDS em backend/app/domain/quality/parsing.py). Para cada coluna, o relatório dá a taxa de sucesso na amostra, a cobertura de cada componente e as máscaras dos valores que não se decompõem, nunca os valores. Num corpus de teste com formatos mistos, decompõem-se 30 de 30 moradas PT/ES, 30 de 30 nomes, 29 de 30 telefones e 30 de 30 datas.

  • Proposta de normalização (sugestão parse_standardize): um SELECT só de leitura com as colunas derivadas e a contagem das linhas reconhecidas, mais o PySpark equivalente. As colunas derivadas só se preenchem nas linhas que a contagem dá como reconhecidas e ficam a NULL nas outras. Nunca há UPDATE (backend/app/domain/insights/parsing_sql.py). Uma coluna que já tem standardização não recebe a decomposição. Como qualquer sugestão aceite, abre um pull request no repositório do cliente (limpeza e remediação).

  • Na app: o painel «Parsing» no detalhe do dataset mostra os componentes detetados, a cobertura e os formatos que falham. O botão «Propor normalização» mostra o SQL e o PySpark da sugestão. A API é GET /api/v1/datasets/{id}/parsing.

  • Deteção de variantes do mesmo valor numa coluna categórica (maiúsculas, espaços), com proposta de normalização em SQL e PySpark.

  • Contratos de dados ODCS com tipos e formatos, validáveis em CI (gate de qualidade em CI).

Limites: o SQL usa só funções que existem em PostgreSQL, BigQuery, Snowflake, Databricks e MySQL. SQL Server/Fabric e SQLite não têm REGEXP_REPLACE e recebem só o PySpark. A coluna data_iso sai em texto e só se preenche com dias que existem no calendário. No SQL, o NIF e o DNI/NIE reconhecem-se pela forma; o dígito de controlo só se valida no relatório da amostra. Nos telefones, os indicativos reconhecidos são os de uma lista fixa (Portugal, Espanha, Reino Unido, EUA, França, Alemanha, Itália, Suíça, Luxemburgo, Bélgica, Países Baixos, Irlanda, Brasil, Angola, Moçambique e Cabo Verde), e os outros não se decompõem.

Testes: backend/tests/test_parsing_composite.py (corpus e componentes), backend/tests/test_parsing_sql.py (SQL compilado em PostgreSQL e BigQuery, colunas derivadas a NULL fora das linhas reconhecidas, PySpark e API), backend/tests/test_postgres_only.py (o SQL executado em PostgreSQL no job backend-postgres), backend/tests/test_suggestions_standardization.py, backend/tests/test_suggestions.py, backend/tests/test_quality_rules.py.

Matching e deduplicação

Tudo é proposta. O DataVault pontua e agrupa os registos, a decisão de quem revê fica registada no DataVault e a consolidação sai em SQL e PySpark só de leitura. Nada é escrito na fonte.

  • Deteção de duplicados exatos (hash) e aproximados (semelhança textual com rapidfuzz) dentro de um dataset, agrupados em clusters que se reveem, fundem ou ignoram na UI, com proposta de consolidação em SQL (ROW_NUMBER de deduplicação) e PySpark.
  • Correspondência entre dois datasets, também de fontes diferentes (ex.: clientes no CRM e no ERP — resolução de entidades): chaves exatas, normalizadas (email, telefone, NIF, nome, código postal) ou aproximadas, com pares, linhas sem par, ambíguas e cobertura, na página «Correspondências» (backend/app/domain/quality/matching.py, método). Para cada par, o registo que ficaria (mais completo, depois mais recente, depois o lado A), com o motivo e um SELECT de diagnóstico.
  • Regras de correspondência configuráveis por campo, guardadas por par de datasets (backend/app/domain/quality/matching_rules.py): para cada campo, o comparador, o normalizador, o peso e a semelhança mínima. Os comparadores são exact, normalized, jaro_winkler, token (token_sort_ratio), numeric com tolerância e date com janela em dias (backend/app/domain/quality/matching_comparators.py). O blocking é por chave, por prefixo ou por um código fonético do português, e há dois limiares: correspondência (0,85 por omissão) e revisão (0,70). Na UI, as colunas de cada lado escolhem-se do schema dos dois datasets e os campos (até 12) e as regras de blocking (até 4) acrescentam-se e removem-se. Sem campos escolhidos, a regra nasce das chaves sugeridas.
  • Clusters revistos na UI: cada linha liga-se ao seu melhor candidato e as ligações formam clusters entre os dois datasets. Um cluster fica em «correspondência» quando a ligação mais fraca passa o limiar de correspondência, e em «revisão» quando fica entre os dois limiares. Na secção «Correspondências: regras e clusters» da vista «Entre datasets» de Duplicados, cada cluster mostra a semelhança de cada campo e marca-se como fundido ou ignorado. A decisão liga-se às linhas do cluster, e não à execução: vale nas execuções seguintes enquanto o cluster tiver as mesmas linhas, e fica no trilho de auditoria.
  • Regras de sobrevivência por campo (backend/app/domain/quality/survivorship.py): fonte prioritária, mais recente (pela coluna de atualização de cada dataset, que a sugestão deteta pelo nome; sem as duas, a regra é recusada), mais completo ou mais frequente, com os empates resolvidos pela ordem das fontes e depois pela das linhas. O registo consolidado é só proposto, e cada campo diz de que fonte e de que linha vem o valor e por que regra. Exporta-se num SELECT só de leitura ou em PySpark sem escrita, e pode seguir como sugestão que, aceite, abre um pull request no repositório do cliente (limpeza e remediação).
  • Métricas por conjunto de regras: pares candidatos e pontuados, clusters em cada faixa, taxa de correspondência, decisões de revisão (fundidos, ignorados, por rever) e a precisão observada nessas decisões.
  • Benchmark rotulado (sintético, português): com as regras sugeridas para CRM × ERP e os limiares por omissão, precisão 1,0 e recall 0,85 em 10 000 linhas, e precisão 1,0 e recall 0,83 em 2 000 (método).

As execuções das regras correm fora do scan, na fila do matching, sobre uma amostra determinística de até 50 000 linhas de cada dataset. Com colunas de dados pessoais nas regras, a execução pede confirmação, como na correspondência por chaves. Os clusters guardam os scores e um hash de cada linha. Os valores de exemplo ficam truncados, com os dados pessoais mascarados, e a retenção apaga-os.

Limites: o matching corre no DataVault sobre essa amostra, sem pushdown para a fonte. Cada conjunto de regras compara dois datasets. Os limiares não se afinam sozinhos a partir das decisões: a precisão observada fica à vista e quem gere as regras decide se os muda. Um bloco com mais de 200 linhas num dos lados não gera candidatos e fica contado. O código fonético é uma redução própria da ortografia portuguesa, e não um algoritmo publicado. No SQL exportado, o JOIN usa só os campos de igualdade e cada linha é um par A × B, com a mesma escolha que a app faz para esse par; os pares que dependem de campos de semelhança, e a escolha entre mais de dois registos de um cluster, estão nos clusters da app.

Por desenho, não somos um hub MDM: não mantemos um registo-mestre persistido nem escrevemos o golden record nas fontes (o que não fazemos).

Testes: backend/tests/test_duplicates.py, backend/tests/test_duplicate_constellation.py, backend/tests/test_matching.py, backend/tests/test_record_matching.py, backend/tests/test_matching_bench.py, backend/tests/test_matching_rules.py, backend/tests/test_matching_rules_api.py.

Limpeza e remediação

Por desenho: propomos, não escrevemos.

O DataVault propõe a correção — SQL e PySpark gerados, com poupança estimada e risco analisado (seguro vs breaking) — e o cliente aplica. Não pedimos credenciais de escrita e nunca escrevemos na fonte.

  • Remediação como pull request: a correção abre um PR no repositório GitHub/GitLab do cliente (modelos SQL/PySpark e testes dbt), para revisão pelo processo que já existe.
  • Fluxo governado: proposto → aceite → aplicado → verificado, com trilho de auditoria e verificação automática no scan seguinte.

Testes: backend/tests/test_git_remediation.py, backend/tests/test_remediation_lifecycle.py.

Enriquecimento

Tudo é proposta. O DataVault junta a cada valor de uma coluna o que uma fonte de referência diz dele, com a origem, e gera o SQL no dialeto da conexão e o PySpark. Quem aplica é o cliente, por pull request ou SQL depois de aprovar a sugestão, e nada é escrito na fonte.

  • Seis tipos de enriquecimento, escolhidos pelo tipo semântico da coluna no último perfil e, no código postal espanhol, pelo nome da coluna (backend/app/domain/quality/enrichment.py):
    • código postal português (CP7) → localidade, concelho e distrito, na GeoAPI.pt (dados dos CTT e do INE);
    • código postal espanhol → localidade e comunidade autónoma, no Nominatim público do OpenStreetMap, que recebe só o código postal;
    • NIF de empresa → nome e morada registados no VIES da Comissão Europeia. Só se consulta o NIF de pessoa coletiva (1.º dígito 5, 6 ou 9). O NIF de uma pessoa singular fica fora do âmbito e não sai;
    • morada → latitude e longitude, numa instância Nominatim do próprio cliente. As moradas não vão para o Nominatim público, cuja política de uso pede que não se lhe enviem dados pessoais;
    • país → código ISO 3166-1 alfa-2, e moeda → código ISO 4217, pelas listas ISO versionadas no DataVault, sem consulta externa.
  • Proveniência: cada valor proposto leva a fonte, a data da consulta e a confiança. Cada proposta dá a cobertura na amostra, ou seja, as linhas com valor proposto entre as linhas com valor na coluna. Um valor que a fonte não conhece, que passa o teto de consultas ou cuja fonte não respondeu fica sem proposta, com o motivo. No VIES, um campo publicado como --- conta como sem valor.
  • Proposta (sugestão enrichment, backend/app/domain/quality/enrichment_service.py): o passo 1 conta, só com leitura, as linhas a enriquecer, e pode ensaiar-se na fonte (ensaio de uma correção). O passo 2 é um UPDATE proposto numa coluna à escolha, que só preenche as linhas onde ela está vazia, ou, sem coluna escolhida, um SELECT que junta os campos propostos. A tabela de referência do SQL só leva valores que a fonte confirmou: códigos postais, NIF de empresas e códigos ISO. Na geocodificação, o SQL não leva moradas: o passo 2 junta uma tabela de coordenadas que o cliente carrega. Como qualquer sugestão aceite, pode seguir por pull request para o repositório do cliente (limpeza e remediação). Os scans não apagam uma proposta por aprovar: só uma nova proposta da mesma coluna e do mesmo tipo a atualiza. Depois de aplicada, a sugestão não passa sozinha a verificada, porque o scan não mede o enriquecimento: marca-se como verificada à mão, no cartão da sugestão.
  • Opt-in por fornecedor: nenhum valor sai antes de um administrador autorizar o fornecedor no workspace, em «Regras → Fornecedores de enriquecimento» ou em PUT /api/v1/workspace/enrichment. A autorização regista quem consentiu e quando. Sem autorização, a amostra nem se lê. É independente da autorização da regra accuracy. O que sai para cada fornecedor está em SEGURANCA.md.
  • Geocodificação só na instância do cliente (nominatim_self_hosted): para a autorizar, o administrador indica o URL da instância (nominatim_url) e confirma que as moradas podem ser dados pessoais (confirm_personal_data). Mudar o URL pede outra vez essa confirmação, e retirar a autorização apaga o URL. Sem instância configurada e autorizada, nenhuma morada sai. O URL passa pela guarda anti-SSRF ao gravar e em cada consulta. Uma instância atrás de autenticação não está suportada.
  • API: GET /api/v1/datasets/{id}/enrichment lista as colunas candidatas, com o tipo e o fornecedor de cada uma; POST /api/v1/datasets/{id}/enrichment/proposals lê uma amostra só dessa coluna, consulta o fornecedor e grava a sugestão (backend/app/api/v1/enrichment.py).

Limites: a proposta corre sobre uma amostra da coluna, e os valores que não aparecem nela ficam por propor. Cada proposta tem um teto de consultas externas, e o Nominatim público responde a 1 pedido por segundo. No código postal espanhol, o Nominatim público devolve um só resultado, por isso um código partilhado por várias localidades dá só uma delas. A GeoAPI.pt e o Nominatim público são serviços gratuitos sem nível de serviço contratado. Os valores do Nominatim público vêm do OpenStreetMap, com a licença ODbL, que pede atribuição a quem os publica. Fora da lista acima, o DataVault não acrescenta dados de terceiros aos registos: não há serviço postal pago, base comercial de empresas nem geocodificação fora da instância Nominatim do cliente.

Testes: backend/tests/test_enrichment.py.

Gestão de metadados

  • Catálogo automático de todas as fontes, com domínios/data products, dono e membros.

  • Linhagem: chaves estrangeiras, SQL das views (ao nível da coluna), manifest.json do dbt e eventos OpenLineage de terceiros — OpenLineage.

  • Publicação no catálogo do cliente: veredictos de qualidade no OpenMetadata e no DataHub, como assets «Data Quality Metric» no Collibra (Import API v2) e, no Microsoft Purview, o veredito, o score, a data do scan e o link para o DataVault em atributos da tabela no Data Map (Collibra e Purview validados com transporte simulado, validação em tenant do cliente pendente) — catálogo externo.

  • Classificação de sensibilidade por coluna (dados pessoais) e README por dataset gerado e aprovado por um humano.

  • Qualidade do catálogo e dos metadados (KPIs de governação), por workspace e por domínio: % de datasets com owner, descrição, classificação, domínio, contrato e regras; descrições desatualizadas (o schema mudou depois), owners inativos ou que saíram, datasets sem uso e sem owner e docs de colunas que já não existem. Cada problema traz a sugestão pelo canal que o resolve (README gerado, import do catálogo externo), com metas, tendência diária e export CSV (GET /catalog/metadata-quality, backend/app/domain/catalog/metadata_quality.py).

  • Glossário de negócio (plano Team ou superior): termos como «Receita líquida», com definição, sinónimos, domínio e owner, ligados aos datasets e às colunas que os implementam. Aprovar um termo ou uma ligação é de um admin ou do owner do domínio do termo, e fica na auditoria. O termo herda a qualidade das colunas aprovadas: o score dos datasets e os issues por resolver de cada coluna, com a explicação («está a 51.7 por causa de 2 issue(s)…»). O sugeridor propõe colunas pelo nome, pelos sinónimos, pelo tipo semântico e pela descrição dbt, sem chave de IA; com IA, a sugestão fica marcada como gerada por IA. Uma sugestão nunca nasce aprovada. Os termos aprovados entram no contrato ODCS (businessName e tags), no datavault.yaml, na pesquisa do catálogo, no assistente (search_glossary) e no MCP (glossary://terms) (backend/app/domain/catalog/glossary.py, backend/app/api/v1/glossary.py). Na app, a página Glossário pesquisa, cria, edita e aprova termos e revê as sugestões, e a tabela de colunas do dataset mostra os termos de cada coluna (aceite com borda sólida, proposta tracejada), com «+ termo» e o aviso das ligações por rever (frontend/src/pages/Glossario.tsx, frontend/src/components/DatasetTermChips.tsx).

  • Owner e steward por dataset: cada dataset pode ter owner e steward próprios. Sem owner próprio, herda o do primeiro domínio que tem owner. Esta regra é a mesma no detalhe, no dicionário, na pesquisa e nos KPIs de metadados (effective_owner em backend/app/domain/catalog/metadata.py). O steward não se herda. Muda o owner e o steward de um dataset quem pode escrever nesse dataset; o owner de um domínio só o muda um admin. O owner do dataset não dá permissões.

  • Tags por dataset e por coluna: o workspace escolhe uma política de tags. Em modo livre aceita qualquer tag; em modo controlado, só as da lista, e só um admin a muda. Editar as tags substitui as manuais e deixa as do catálogo externo. Mudar o owner, o steward ou as tags fica no trilho de auditoria (PATCH /datasets/{id}/owners, PUT /datasets/{id}/tags, PUT /catalog/tag-policy).

  • Dicionário de dados exportável por dataset, domínio ou workspace, em CSV, JSON, Markdown, OpenMetadata e DataHub. O dicionário leva descrições, tipos, sensibilidade, termos aprovados, tags, owner, steward, regras ativas e o SLA dos domínios. Os termos só saem nos planos com glossário. O ficheiro OpenMetadata traz um corpo de PUT /api/v1/tables (CreateTableRequest) por tabela e, à parte, o que esse pedido não aceita: o owner e o steward por email (o pedido exige o id do utilizador no OpenMetadata), os domínios, o SLA e as regras. As tags e os termos vão na classificação e no glossário DataVault, que têm de existir no OpenMetadata. O ficheiro DataHub traz MCPs UPSERT para o datahub ingest, com o owner como BUSINESS_OWNER e o steward como DATA_STEWARD. O FQN ou URN vem do mapeamento da sincronização do catálogo, se a conexão o tiver. É um ficheiro: o DataVault não envia descrições nem tags para o catálogo do cliente. No CSV, as células que começam por =, +, - ou @ saem neutralizadas. Cada pedido leva até 2 000 datasets; com mais, a resposta traz X-Dictionary-Truncated: true (GET /datasets/{id}/dictionary, GET /domains/{id}/dictionary, GET /catalog/dictionary, backend/app/domain/catalog/dictionary.py).

  • Histórico de metadados: cada alteração fica registada com o quê, quem, quando, o antes e o depois. Entram as mudanças de owner, steward e tags, a sensibilidade posta à mão, as ligações de termos a colunas criadas ou decididas pela API e as edições dos campos de um termo. O histórico devolve as 500 alterações mais recentes da janela, com X-History-Truncated: true quando há mais; para recuar, passa-se em before o id da mais antiga devolvida. O diff entre duas datas conta com todas as alterações da janela e dá, por campo, o valor no início e no fim (GET /datasets/{id}/metadata/history, GET /datasets/{id}/metadata/diff, GET /catalog/metadata-history, GET /catalog/metadata-diff). Entra também no export do workspace. O histórico começa com esta versão: os eventos anteriores de auditoria e de sensibilidade não foram convertidos.

  • Pesquisa transversal (GET /search?q=): datasets, colunas, termos do glossário, tags e owners ou stewards. No Postgres combina FTS com um LIKE escapado; no SQLite usa só o LIKE. As colunas casam pelo nome e pela descrição, também com palavras acentuadas. Os datasets dos domínios restritos a quem pesquisa não aparecem em nenhuma das espécies (backend/app/domain/catalog/metadata_search.py).

O que falta: o owner e as tags, o dicionário, o histórico e a pesquisa ainda não têm página na app e usam-se pela API. Ficam fora do histórico a criação de termos, as ligações sugeridas automaticamente, o que o datavault.yaml aplica e a classificação automática da sensibilidade. Com o catálogo externo ligado, contam-se os termos de glossário lidos no último import, mas esses termos não entram no glossário do DataVault. O import também não escreve tags do dataset.

Testes: backend/tests/test_column_lineage.py, backend/tests/test_dbt_manifest.py, backend/tests/test_openlineage_consume.py, backend/tests/test_catalog_sync.py, backend/tests/test_column_sensitivity.py, backend/tests/test_dataset_readme.py, backend/tests/test_metadata_quality.py, backend/tests/test_glossary.py, backend/tests/test_metadata_management.py, frontend/src/pages/Glossario.dom.test.tsx, frontend/src/components/DatasetTermChips.dom.test.tsx.

Elementos de dados críticos e dossiê regulatório

  • Elementos de dados críticos (CDE): um dataset ou uma coluna marcado para o BCBS 239, a Solvência II ou outra regulação, com referência («Princípio 3», «art. 19.º», a linha do relatório ao supervisor), owner e racional. Declaram-no um admin ou o owner de um domínio do dataset.
  • Dossiê regulatório, por regulação e requisito: princípios 3 a 6 do BCBS 239; art. 82.º da Diretiva 2009/138/CE e art. 19.º do Regulamento Delegado (UE) 2015/35, para a Solvência II. Para cada elemento traz os controlos de qualidade por dimensão exigida, o último resultado de cada regra, os problemas abertos, o cumprimento do SLA do domínio, a linhagem a um salto (ao nível da coluna), o contrato ODCS e as lacunas: sem regra, sem resultado nos últimos 30 dias, sem owner, sem SLA. O KPI «cobertura de CDEs» conta as dimensões exigidas com controlo e resultado.
  • Exporta-se em Markdown, CSV e PDF, e partilha-se pelo link de leitura dos relatórios. É determinístico, sem IA generativa e só com metadados. Nenhum estado afirma conformidade: o dossiê aponta lacunas e não certifica (GET /api/v1/cde/dossier, backend/app/domain/compliance/regulatory_dossier.py).

O que falta: a exatidão aparece como lacuna em todos os elementos, porque nenhum tipo de regra do DataVault conta hoje nessa dimensão (a integridade fica coberta com uma regra referential). É a proposta de exatidão verificada da cobertura de qualidade. Também falta a interface: o chip «CDE» no dataset, a página do dossiê e o KPI no painel do domínio. Por agora, o dossiê usa-se pela API e pelos relatórios.

Testes: backend/tests/test_regulatory_dossier.py, backend/tests/test_cde.py.

Orquestração e fluxo de trabalho

  • Orquestradores: webhook do dbt Cloud (scan no fim de cada job) e pacote Airflow com um gate de qualidade numa DAG — orquestradores.
  • Integradores: saúde dos pipelines Fivetran e Airbyte (só leitura, APIs oficiais) — sync falhado ou atrasado por dataset de destino, latência e reconciliação origem↔destino e o sync no diagnóstico de causa-raiz — integradores.
  • Ferramentas de BI: Power BI, Tableau e Looker (só leitura, APIs oficiais) — refreshes falhados ou atrasados como issues, dashboards na linhagem e no impacto das issues com o dono, e aviso de dashboards sem visualizações — ferramentas de BI.
  • CI/CD: gate de regras e contratos no pipeline, data-diff entre base e candidato — gate de qualidade em CI.
  • Agentes: servidor MCP para Claude e outros clientes MCP — servidor MCP.
  • Validação de registos no ponto de entrada: o integrador ou a pipeline mandam até 5 000 registos a POST /api/v1/datasets/{id}/validate antes de os escrever e recebem o veredito de cada um com as regras ativas do dataset (not_null, pattern, range, required_fields, valid_identifier com os identificadores do registo por país, in_reference_set com as listas ISO e as do workspace), o código do motivo de cada falha e o veredito do lote. As regras do dataset inteiro (unique, freshness…) vêm marcadas como não aplicáveis. Nada é guardado nem enviado a um LLM e a fonte não é lida: rejeitar ou pôr em quarentena fica do lado do cliente. Token dvk_* ou SDK (dv.validate.records) — validação de registos.
  • IDE: extensão VS Code com a qualidade inline nos ficheiros dbt/SQL — score, issues, contrato e PII por tabela, perfil por coluna no hover, alertas de política e data-diff do branch — extensão VS Code.
  • Configuração como código: export/import determinístico da configuração do workspace em YAML (datavault.yaml), para rever em code review e promover entre ambientes. O ficheiro leva as regras (rules), os domínios (domains), a política de dados (data_policy), a política de IA (llm_policy), os canais de alerta (channels), os relatórios agendados (report_schedules), o glossário de negócio com as ligações aprovadas (glossary) e, por conexão, a agenda de scans e a ativação dos monitores automáticos (scan_schedules, auto_monitors) e o ambiente da fonte (connection_environments) — sem segredos nem ids. O import mostra primeiro o plano (o diff do que muda) e aplica tudo ou nada. O cliente corre-o a partir do próprio CI com o SDK (datavault config plan no PR, datavault config apply no merge, sobre /api/v1/workspace/config) e um token «Configuração como código»; as mudanças às políticas de IA e de dados e ao ambiente das conexões ficam com um admin. O operador também o corre com backend/scripts/workspace_config.py, que corre junto do backend (no pipeline). Pela API, regras, domínios, canais e relatórios agendados gerem-se recurso a recurso em /api/v1/iac com o mesmo token — é o que usa o provider Terraform (integrations/terraform). Monitores definidos noutras ferramentas (ex.: montecarlo.yml) não se importam: as regras entram pelos formatos do import — dbt, Great Expectations, SodaCL e ODCS (o que converte) — e saem em dbt, SodaCL e GX: testes dbt, checks SodaCL ou uma suite do Great Expectations (GET /api/v1/datasets/{id}/rules/export?format=dbt|sodacl|gx; o workspace inteiro num só ficheiro SodaCL por GET /api/v1/rules/export). No SodaCL e no GX só sai o que o import lê de volta como a mesma regra; o que fica de fora (regras de streams, dígito de controlo, regras SQL fora do dbt, tolerâncias acima de zero) vem no ficheiro com o motivo, no cabeçalho do SodaCL e em meta.datavault.unsupported no GX (o que sai).
  • Fluxo de problemas com estados, responsáveis, escalas e agendamento de scans e relatórios.

Testes: backend/tests/test_orchestrators.py, backend/tests/test_data_diff.py, backend/tests/test_mcp_server.py, backend/tests/test_workspace_config_gitops.py, backend/tests/test_iac_api.py, backend/tests/test_ide.py, backend/tests/test_record_validation_api.py, integrations/vscode/test/.

Experiência para utilizadores de negócio

  • Relatórios por persona (engenharia, gestão, executivo) com impacto estimado em euros, em PDF, agendáveis e partilháveis por link de leitura.
  • Painel de governação para o CDO: a eficácia do programa num só sítio — cobertura (owner, documentação, classificação, contratos, regras), cycle time e MTTR dos críticos, cumprimento do SLO/SLA e dos prazos, adoção de sugestões e de regras «nunca mais», score por dimensão, incidentes críticos e uso —, cada KPI com meta por workspace ou organização, semáforo, tendência mensal e drill-down por domínio; export do scorecard executivo (CSV) e da evidência para o pack de procurement (Markdown) (GET /governance, backend/app/domain/governance/kpis.py).
  • Assistente em linguagem natural sobre os dados de qualidade do workspace.
  • Scan ao vivo: o primeiro scan mostra-se passo a passo; primeiro resultado em minutos.
  • Interface em português, inglês e espanhol; badge público de qualidade por dataset.

Testes: backend/tests/test_reports_v2.py, backend/tests/test_report_pdf.py, backend/tests/test_assistant_chat.py, backend/tests/test_public_badge.py, backend/tests/test_governance.py.