# Atlas - Especificacao de entrega de raspagem

> Documento gerado pelo Atlas em 2026-08-04T02:00:41.362Z.
> Entregue este arquivo ao desenvolvedor responsavel pela raspagem.

---

# Visao geral

_O que e o Atlas e o que se espera da sua entrega._

O Atlas e um banco de dados central. Raspagens de fontes muito diferentes -
noticias, produtos, perfis, documentos publicos - vivem aqui no **mesmo schema**,
para poderem ser consultadas por uma unica API.

Voce foi contratado para raspar **uma** fonte. Voce nao precisa mexer no Atlas:
voce entrega um arquivo no formato descrito nesta documentacao e o
administrador faz a importacao.

## O que voce entrega

1. Um arquivo `.zip` com `manifest.json` + `entities.ndjson`, ou um `.ndjson` puro.
2. Todos os arquivos binarios (imagens, PDFs, videos) ja enviados para o
   IDrive E2, na pasta do projeto, com as URLs finais dentro do dump.

## O que voce NAO entrega

- Dump SQL, banco de dados, CSV ou JSON em array unico.
- Arquivos anexados por email, Drive, WeTransfer ou junto do zip.
- Codigo do crawler (a menos que combinado a parte).

## Como a entrega e avaliada

Ha duas camadas, e elas sao independentes:

- **Validacao automatica.** O Atlas roda uma esteira de 8 etapas no seu dump.
  E objetiva: passa ou nao passa. Se nao passar, voce recebe um relatorio com a
  linha, o campo e o que corrigir, e reenvia. Nao ha limite de tentativas.
- **Criterios de aceite.** Sao o combinado da entrega (cobertura, frequencia,
  qualidade do conteudo). Nao sao verificados por codigo - sao avaliados pelo
  administrador. Estao listados na secao "Criterios de aceite".

Passar na validacao automatica e condicao necessaria, nao suficiente.

---

# Como entregar, passo a passo

_Do primeiro contato ao dump aprovado._

## 1. Combine o slug do projeto

O **slug** identifica a sua entrega no Atlas e e tambem o nome da pasta no
IDrive E2. Formato kebab-case: `wikipedia-pt`, `mercado-livre-eletronicos`,
`instagram-perfis-br`.

Cada entrega e ligada pelo administrador a zero, uma ou varias **origens**
(Instagram, Wikipedia, ...). Isso permite que entregas de desenvolvedores
diferentes sobre a mesma plataforma fiquem agrupadas. Voce nao precisa fazer
nada quanto a isso - so o slug e da sua conta.

Confirme o slug com o administrador **antes de comecar**. Trocar depois
significa mover todos os arquivos no storage.

## 2. Peca as credenciais do IDrive E2

O administrador te envia `accessKeyId`, `secretAccessKey`, endpoint e o nome do
bucket. Voce so tem permissao dentro da pasta do seu projeto.

## 3. Rasque e normalize

Para cada registro da fonte, monte um objeto no formato da secao "Referencia
tecnica do contrato". Guarde o payload original em `raw` - ele e o que permite
reprocessar sem raspar de novo.

## 4. Suba os arquivos ANTES de gerar o dump

Baixe cada imagem/PDF/video, envie para
`<bucket>/<slug-do-projeto>/...` e use a URL final do IDrive E2 no campo
`files[].url`.

Ordem importa: se voce gerar o dump antes de subir os arquivos, a etapa de
storage reprova.

## 5. Gere o entities.ndjson

Uma entidade por linha, JSON completo, sem identacao e sem virgula no fim:

```js
const fs = require('node:fs');
const out = fs.createWriteStream('entities.ndjson', { encoding: 'utf8' });
for (const entity of entities) {
  out.write(JSON.stringify(entity) + '\n');
}
out.end();
```

## 6. Gere o manifest.json e compacte

`entityCount` precisa bater exatamente com o numero de linhas - a divergencia e
o sinal mais comum de exportacao interrompida, e o Atlas reprova por isso.

Coloque os dois arquivos na **raiz** do zip, nao dentro de uma subpasta.

## 7. Envie e acompanhe

O administrador sobe o arquivo no Atlas e acompanha a esteira ao vivo. Se
reprovar, ele te encaminha o relatorio: cada problema traz a linha, o campo e a
instrucao de correcao. Ajuste e reenvie.

## 8. Aprovacao

Passando na validacao, o administrador informa titulo, descricao e categoria e
grava no banco. A partir dai os dados ficam disponiveis na API de consulta.

## Dica: entregue uma amostra primeiro

Antes de rodar a raspagem completa, gere um dump com ~100 registros e peca ao
administrador para importar. Descobrir um erro de formato em 100 linhas custa
minutos; descobrir em 3 milhoes custa a raspagem inteira.

---

# Arquivos e IDrive E2

_Onde os binarios ficam e como as URLs precisam ser._

## Regra unica

**Nenhum binario fica na VPS do Atlas e nenhum binario fica na fonte original.**
Tudo vai para o IDrive E2, dentro da pasta do projeto.

```
<bucket>/
├── wikipedia-pt/
│   ├── images/
│   └── documents/
└── mercado-livre/
    └── images/
```

## O que o Atlas confere

Para cada URL em `files[].url`:

1. O host e o endpoint do IDrive E2 configurado.
2. O bucket e o bucket oficial.
3. O primeiro segmento do caminho e o slug do projeto.
4. O objeto **existe de fato** (requisicao HEAD no storage).

Falhou qualquer uma, a importacao reprova. URL apontando para
`https://upload.wikimedia.org/...` reprova mesmo que a imagem exista la.

## Organizacao dentro da pasta

Livre, desde que tudo esteja sob o slug do projeto. Uma estrutura que funciona
bem para volumes grandes:

```
wikipedia-pt/images/3b/8f/3b8f1c9a2e....jpg
```

Usar os dois primeiros pares de caracteres do hash como subpastas evita
diretorios com milhoes de objetos, o que deixa a listagem lenta.

## Nome dos objetos

Prefira o **hash do conteudo** como nome do arquivo, em vez do nome original:

- evita colisao entre arquivos de nomes iguais em paginas diferentes;
- reenviar o mesmo arquivo sobrescreve com conteudo identico, sem duplicar;
- casa com o campo `files[].hash`.

Evite espacos, acentos e caracteres especiais nas chaves.

## Content-Type

Envie com o `Content-Type` correto (`image/jpeg`, `application/pdf`, ...).
Content-Type errado gera aviso, nao erro - mas atrapalha quem consome a API.

## Dedup de arquivos

O mesmo arquivo referenciado por varias entidades deve ter **a mesma URL** nas
varias linhas. O Atlas grava uma unica linha em `files` e liga todas as
entidades a ela. Nao suba a mesma imagem com nomes diferentes.

---

# Qualidade dos dados

_Normalizacao, hash e o que separa uma entrega boa de uma ruim._

## raw e sagrado

`raw` guarda o payload **original** da fonte. Nao normalize, nao limpe, nao
remova campos que parecem inuteis. Ele e o seguro contra erro de normalizacao:
se algo estiver errado, o Atlas reprocessa a partir do `raw` em vez de mandar
raspar de novo.

- API que devolve JSON: guarde o JSON da resposta.
- HTML: guarde os campos extraidos e, quando fizer sentido, o HTML do bloco
  principal. HTML da pagina inteira, com menu e anuncio, so incha o banco.

## metadata e derivado

`metadata` guarda o que voce calculou: contagem de palavras, preco normalizado
em centavos, coordenadas, categoria da fonte. Campo livre.

## externalId precisa ser estavel

O mesmo registro raspado hoje e daqui a um mes precisa ter o mesmo
`externalId`. Use o id da fonte quando houver.

Nao use: posicao na listagem, timestamp da raspagem, contador incremental,
UUID gerado na hora. Todos quebram na proxima execucao.

Se a fonte reaproveita ids entre secoes, prefixe: `noticias:12345`,
`produtos:12345`.

## hash precisa refletir o conteudo

SHA-256 do conteudo normalizado. Estavel (mesmo conteudo, mesmo hash) e
sensivel (conteudo diferente, hash diferente). Nao use o HTML cru: anuncio e
timestamp mudam a cada visita e o hash muda junto sem o conteudo ter mudado.

## Datas

Sempre ISO 8601, de preferencia UTC: `2024-01-31T12:00:00Z`.

Data relativa na fonte ("ha 3 horas") precisa ser convertida para absoluta.
Sem data confiavel, deixe `publishedAt` ausente - preencher com a data da
raspagem e pior que nao preencher, porque cria um dado falso.

## Texto

- Sem HTML em `title` e `description` - texto limpo.
- Sem entidades HTML nao decodificadas (`&amp;`, `&#39;`).
- Sem espacos duplicados nem quebras de linha sobrando.
- Nao trunque `description` a menos que combinado.

## Tags

Termos livres, minusculos, sem acento quando possivel. O Atlas normaliza para
slug na ingestao. Evite tags de uma letra ou puramente numericas.

## Erros comuns que reprovam

| Erro | Consequencia |
| --- | --- |
| JSON identado no ndjson | Toda linha invalida |
| Array `[...]` em vez de uma linha por objeto | Dump inteiro invalido |
| `raw` como string com JSON dentro | Erro de tipo em toda linha |
| `size` como string (`"184320"`) | Erro de tipo |
| `country` minusculo (`"br"`) | Erro de formato |
| URL de imagem apontando para a fonte | Reprova na etapa de storage |
| `entityCount` desatualizado | Reprova por contagem divergente |

---

# Checklist de entrega

_Confira antes de enviar._

Confira item a item antes de mandar o dump. Cada um destes ja reprovou alguma
entrega.

## Formato

- [ ] O arquivo e `.zip` (com `manifest.json` + `entities.ndjson` na raiz) ou `.ndjson` puro.
- [ ] Cada linha do ndjson e um objeto JSON completo, em uma unica linha.
- [ ] Nao ha virgula no fim das linhas nem array externo.
- [ ] O arquivo esta em UTF-8.
- [ ] `entityCount` do manifesto e igual ao numero de linhas do ndjson.
- [ ] `project` do manifesto e igual ao slug combinado.

## Campos

- [ ] Toda linha tem `externalId`, `type`, `raw` e `hash`.
- [ ] `hash` tem 64 caracteres hexadecimais minusculos.
- [ ] `externalId` e unico dentro do dump.
- [ ] `raw` nao esta vazio em nenhuma linha.
- [ ] Datas em ISO 8601.
- [ ] `language` em ISO 639-1 minusculo, `country` em ISO 3166-1 alpha-2 maiusculo.
- [ ] Nao ha campos extras no nivel raiz (tudo extra esta em `raw` ou `metadata`).

## Arquivos

- [ ] Todos os binarios ja estao no IDrive E2.
- [ ] Toda `files[].url` aponta para o bucket oficial, dentro da pasta do projeto.
- [ ] Nenhuma URL aponta para a fonte original.
- [ ] `size` e o tamanho real em bytes, como numero.
- [ ] O mesmo arquivo usado por varias entidades tem a mesma URL.

## Antes de enviar

- [ ] Testei uma amostra de ~100 registros e ela foi importada com sucesso.
- [ ] Abri 5 registros aleatorios e conferi que o conteudo bate com a pagina da fonte.
- [ ] Reli os criterios de aceite.

---

# Criterios de aceite

Estes criterios sao o combinado da entrega. Eles **nao sao verificados
automaticamente pelo sistema** - a validacao tecnica e a da secao de
referencia. A aceitacao final da entrega depende deles.

## Arquivos

- **[AC-20] (MUST)** Todos os binarios no IDrive E2
  Nenhuma URL de arquivo aponta para a fonte original ou para outro storage. Tudo dentro de <bucket>/<slug-do-projeto>/.
- **[AC-21] (MUST)** Arquivos integros
  Os objetos no storage abrem corretamente e nao estao truncados. size no dump bate com o tamanho real.
- **[AC-22] (SHOULD)** Sem duplicatas no storage
  O mesmo binario nao foi enviado com varias chaves diferentes. Nome do objeto baseado no hash do conteudo.

## Cobertura

- **[AC-01] (MUST)** Cobertura minima acordada
  A raspagem cobre o escopo combinado (secoes, periodo, faixa de paginas). Divergencias precisam ser comunicadas antes da entrega, nao depois.
- **[AC-02] (MUST)** Sem lacunas silenciosas
  Paginas que falharam na raspagem sao reportadas com o motivo. Entregar 80% do escopo sem avisar conta como entrega incompleta.
- **[AC-03] (SHOULD)** Amostra aprovada antes do volume total
  Uma amostra de ~100 registros foi importada com sucesso antes de rodar a raspagem completa.

## Conduta

- **[AC-40] (MUST)** Respeito a fonte
  A raspagem respeita rate limit razoavel e nao derruba nem degrada o site de origem. Sem burlar autenticacao ou paywall.
- **[AC-41] (MUST)** Sem dados pessoais fora do escopo
  Nenhum dado pessoal sensivel e coletado alem do que foi expressamente acordado e do que a fonte publica abertamente.

## Entrega

- **[AC-30] (MUST)** Dump passa na validacao automatica
  A esteira de 8 etapas do Atlas conclui sem erros. Avisos sao aceitaveis quando justificados.
- **[AC-31] (MUST)** Manifesto completo e correto
  manifest.json preenchido, com entityCount batendo com o numero de linhas e project igual ao slug combinado.
- **[AC-32] (SHOULD)** Relatorio de execucao
  Junto do dump vem uma nota curta: quando rodou, quanto tempo levou, quantas paginas falharam e quais limitacoes conhecidas ficaram.
- **[AC-33] (MAY)** Codigo do crawler versionado
  Quando contratado como parte do escopo, o codigo e entregue em repositorio com README de execucao.

## Qualidade

- **[AC-10] (MUST)** Conteudo fiel a fonte
  Titulo, descricao e demais campos correspondem ao que esta na pagina de origem. Sem texto de menu, rodape, anuncio ou "aceite os cookies" vazando para os campos.
- **[AC-11] (MUST)** externalId estavel entre execucoes
  Rodar a raspagem duas vezes produz o mesmo externalId para o mesmo registro. Nada de indice de listagem, timestamp ou UUID gerado na hora.
- **[AC-12] (MUST)** raw preservado
  O campo raw contem o payload original da fonte, sem normalizacao. Sem raw util, a entrega nao pode ser reprocessada e perde metade do valor.
- **[AC-13] (SHOULD)** Datas absolutas
  Datas relativas da fonte ("ha 3 horas") foram convertidas para ISO 8601 absoluto. Sem data confiavel, o campo fica ausente em vez de receber a data da raspagem.
- **[AC-14] (SHOULD)** Texto limpo
  title e description sem tags HTML, sem entidades nao decodificadas e sem espacos/quebras sobrando.

---

## Referencia tecnica do contrato (v1.0)

> Esta secao e gerada automaticamente a partir do validador. O que esta aqui e
> exatamente o que o Atlas verifica na ingestao.

### Formato de entrega

Voce entrega **um** dos dois formatos:

**A) `.zip` (recomendado)** - com os dois arquivos na **raiz** do zip:

```
dump-wikipedia-pt.zip
├── manifest.json
└── entities.ndjson
```

**B) `.ndjson` puro** - apenas o arquivo de entidades. Sem manifesto, a etapa
de conferencia do manifesto e pulada e o slug do projeto e o informado na tela
de upload.

`entities.ndjson` e NDJSON: **um objeto JSON completo por linha**, sem virgula
no fim, sem array externo, sem identacao. UTF-8. `\n` ou `\r\n` funcionam.

### manifest.json

| Campo | Tipo | Obrigatorio | Regra |
| --- | --- | --- | --- |
| `contractVersion` | string | **Sim** | Atualmente `"1.0"`. |
| `project` | slug kebab-case | **Sim** | Igual a pasta do projeto no bucket e ao slug informado no upload. |
| `collectionType` | enum | **Sim** | Como os dados foram coletados: WEBSITE, API, RSS, PDF, SOCIAL, DATASET, OTHER |
| `baseUrl` | URL absoluta | Nao | Dominio raiz que foi raspado. |
| `entityCount` | inteiro >= 0 | **Sim** | Total de linhas de `entities.ndjson`. E conferido. |
| `generatedAt` | ISO 8601 | **Sim** | Quando a raspagem rodou. |
| `generatedBy` | string | Nao | Nome ou email de quem gerou. |
| `toolVersion` | string | Nao | Versao do crawler. |
| `notes` | string | Nao | Observacoes para o administrador. |

Exemplo:

```json
{
  "contractVersion": "1.0",
  "project": "wikipedia-pt",
  "collectionType": "WEBSITE",
  "baseUrl": "https://pt.wikipedia.org",
  "entityCount": 128400,
  "generatedAt": "2026-08-01T03:12:00Z",
  "generatedBy": "dev@exemplo.com",
  "toolVersion": "wiki-crawler 1.4.2"
}
```

### Entidade (uma linha do entities.ndjson)

| Campo | Tipo | Obrigatorio | Regra |
| --- | --- | --- | --- |
| `externalId` | string (1-512) | Sim | Id estavel do registro na fonte. Unico dentro do projeto. |
| `type` | enum EntityType | Sim | Um dos valores da tabela EntityType abaixo. |
| `title` | string (max 2000) | Nao | Titulo principal do registro. |
| `description` | string (max 20000) | Nao | Resumo ou corpo curto. |
| `url` | URL absoluta | Nao | Endereco canonico do registro NA FONTE (nao e URL de arquivo). |
| `author` | string (max 512) | Nao | Autor, vendedor, perfil ou responsavel. |
| `language` | ISO 639-1 | Nao | `pt`, `en`, `pt-BR`. |
| `country` | ISO 3166-1 alpha-2 | Nao | `BR`, `US` (sempre maiusculo). |
| `publishedAt` | ISO 8601 | Nao | Preferir UTC: `2024-01-31T12:00:00Z`. |
| `raw` | objeto JSON | **Sim** | Payload original da fonte, sem normalizar. Nao pode ser `{}`. |
| `metadata` | objeto JSON | Nao | Campos normalizados/derivados. |
| `hash` | SHA-256 hex (64) | **Sim** | Hash do conteudo normalizado. Base da deduplicacao. |
| `tags` | string[] (max 200) | Nao | Termos livres. Sao normalizados para slug na ingestao. |
| `files` | objeto[] (max 500) | Nao | Arquivos ja hospedados no IDrive E2. |

Qualquer campo fora desta lista no nivel raiz gera um **aviso** e e ignorado na
gravacao. Informacao extra deve ir dentro de `raw` ou `metadata`.

Exemplo de linha (quebrada aqui so para leitura; no arquivo fica em uma linha):

```json
{
  "externalId": "pt-wiki:12345",
  "type": "ARTICLE",
  "title": "Historia do Brasil",
  "description": "Artigo sobre a historia do Brasil...",
  "url": "https://pt.wikipedia.org/wiki/Historia_do_Brasil",
  "author": "Colaboradores da Wikipedia",
  "language": "pt",
  "country": "BR",
  "publishedAt": "2024-03-11T09:20:00Z",
  "hash": "3b8f1c...64 caracteres hex...9a2e",
  "tags": ["historia", "brasil"],
  "files": [
    {
      "url": "https://SEU-ENDPOINT.idrivee2-XX.com/atlas-scraping/wikipedia-pt/images/3b8f1c9a2e.jpg",
      "type": "IMAGE",
      "mimeType": "image/jpeg",
      "size": 184320,
      "width": 1200,
      "height": 800,
      "role": "cover"
    }
  ],
  "raw": { "pageid": 12345, "revid": 67890, "html": "<div>...</div>" },
  "metadata": { "wordCount": 4210, "sections": 12 }
}
```

### Arquivo (item de `files`)

| Campo | Tipo | Obrigatorio | Regra |
| --- | --- | --- | --- |
| `url` | URL absoluta | **Sim** | Precisa apontar para `https://SEU-ENDPOINT.idrivee2-XX.com/atlas-scraping/<slug-do-projeto>/...` |
| `type` | enum FileType | **Sim** | IMAGE, VIDEO, DOCUMENT, AUDIO, OTHER |
| `mimeType` | string | **Sim** | `image/jpeg`, `application/pdf`, ... |
| `size` | inteiro >= 0 | **Sim** | Tamanho em bytes do objeto no storage. |
| `width` | inteiro > 0 | Nao | Apenas para imagens/videos. |
| `height` | inteiro > 0 | Nao | Apenas para imagens/videos. |
| `hash` | SHA-256 hex (64) | Nao | Do binario. Permite deduplicar arquivos entre fontes. |
| `role` | string (max 64) | Nao | `cover`, `gallery`, `attachment`, ... |

### Regra de armazenamento (bloqueante)

Todo arquivo referenciado precisa **ja estar** no IDrive E2, no bucket
`atlas-scraping`, dentro da pasta do projeto:

```
atlas-scraping/
└── <slug-do-projeto>/
    ├── images/
    ├── documents/
    └── ...
```

O Atlas confere host, bucket, pasta do projeto e a existencia real do objeto
(HEAD). URLs apontando para o site de origem, para outro bucket ou para a raiz
do bucket **reprovam a importacao**. Nada pode ficar na VPS.

### Como calcular o `hash`

SHA-256 hexadecimal minusculo do conteudo **normalizado** - nao do HTML cru,
que muda a cada visita por causa de anuncios e timestamps. Uma receita que
funciona bem:

```js
const crypto = require('node:crypto');

function contentHash(entity) {
  const canonical = JSON.stringify({
    externalId: entity.externalId,
    type: entity.type,
    title: entity.title ?? null,
    description: entity.description ?? null,
    url: entity.url ?? null,
    publishedAt: entity.publishedAt ?? null,
  });
  return crypto.createHash('sha256').update(canonical, 'utf8').digest('hex');
}
```

O importante e ser **estavel** (mesmo conteudo, mesmo hash em execucoes
diferentes) e **sensivel** (conteudo diferente, hash diferente).

### Enums

**EntityType:** `ARTICLE`, `PRODUCT`, `POST`, `PROFILE`, `DOCUMENT`, `VIDEO`, `IMAGE`, `AUDIO`, `PLACE`, `EVENT`, `ORGANIZATION`, `PERSON`, `RECORD`, `OTHER`

**Tipo de coleta (`collectionType`):** `WEBSITE`, `API`, `RSS`, `PDF`, `SOCIAL`, `DATASET`, `OTHER`

**FileType:** `IMAGE`, `VIDEO`, `DOCUMENT`, `AUDIO`, `OTHER`

Nenhum valor serve? Use `OTHER` e detalhe o tipo real em `metadata`.

### A esteira de validacao

1. **Recebimento do arquivo** - Recebe o dump, calcula o checksum e confere o tamanho.
2. **Leitura e descompactacao** - Detecta o formato (.ndjson ou .zip) e prepara a leitura em streaming.
3. **Manifesto do projeto** - Le o manifest.json e confere o projeto, a versao do contrato e a contagem declarada.
4. **Validacao de schema (linha a linha)** - Valida cada linha contra o contrato Atlas: campos, tipos, enums e formatos.
5. **Deduplicacao por hash** - Procura hashes e externalIds repetidos dentro do proprio dump.
6. **Conferencia dos arquivos no IDrive E2** - Confere se toda URL de arquivo existe no bucket do IDrive E2, dentro da pasta do projeto.
7. **Resumo da importacao** - Consolida os numeros e aguarda voce informar titulo, descricao e categoria.
8. **Gravacao no banco** - Grava as entidades, arquivos e tags no banco em lotes transacionais.

Reprovou em qualquer etapa, nada e gravado. O relatorio aponta a linha, o campo
e o que fazer para corrigir.

### Catalogo de erros

| Codigo | Severidade | O que aconteceu | Como corrigir |
| --- | --- | --- | --- |
| `UPLOAD_EMPTY` | ERROR | O arquivo enviado esta vazio. | Gere o dump novamente e confira o tamanho antes de subir. Um dump valido tem no minimo o manifest.json e uma linha em entities.ndjson. |
| `UPLOAD_TOO_LARGE` | ERROR | O arquivo excede o tamanho maximo aceito. | Divida a raspagem em varios dumps menores (por faixa de data ou por secao do site) e envie um de cada vez. Cada envio vira uma importacao separada da mesma fonte. |
| `UPLOAD_UNSUPPORTED_FORMAT` | ERROR | Formato de arquivo nao suportado. | Envie um arquivo .ndjson (uma entidade por linha) ou um .zip contendo manifest.json e entities.ndjson. Dumps .sql, .csv e .json em array unico nao sao aceitos. |
| `UNPACK_CORRUPTED` | ERROR | Nao foi possivel abrir o arquivo compactado. | O .zip chegou corrompido ou incompleto. Recompacte a pasta do dump e reenvie. Se o upload for grande, prefira uma conexao estavel. |
| `UNPACK_MISSING_ENTITIES` | ERROR | O .zip nao contem entities.ndjson. | Coloque o arquivo entities.ndjson na raiz do .zip (nao dentro de uma subpasta). A estrutura esperada e: manifest.json + entities.ndjson na raiz. |
| `MANIFEST_MISSING` | ERROR | manifest.json nao encontrado no .zip. | Adicione um manifest.json na raiz do .zip declarando contractVersion, project, sourceType e entityCount. Veja o modelo na documentacao. |
| `MANIFEST_INVALID_JSON` | ERROR | manifest.json nao e um JSON valido. | Abra o manifest.json e corrija o JSON (virgula sobrando, aspas simples, comentario). Valide em um linter de JSON antes de recompactar. |
| `MANIFEST_SCHEMA` | ERROR | manifest.json nao segue o contrato. | Corrija os campos apontados abaixo no manifest.json. Todos os campos obrigatorios estao listados na documentacao do contrato. |
| `MANIFEST_VERSION_UNSUPPORTED` | ERROR | Versao de contrato nao suportada. | Atualize o campo contractVersion do manifest.json para uma versao aceita e reexporte o dump usando o schema dessa versao. |
| `MANIFEST_PROJECT_MISMATCH` | ERROR | O projeto declarado no manifesto e diferente do informado no upload. | O campo `project` do manifest.json precisa ser exatamente igual ao slug do projeto escolhido no upload, pois ele e o nome da pasta no IDrive E2. |
| `MANIFEST_COUNT_MISMATCH` | ERROR | A contagem declarada no manifesto nao bate com o numero de linhas. | Atualize entityCount no manifest.json para o total real de linhas de entities.ndjson, ou reexporte o dump. Divergencia normalmente indica exportacao interrompida. |
| `LINE_INVALID_JSON` | ERROR | Linha nao e um JSON valido. | Cada linha do entities.ndjson precisa ser um objeto JSON completo em UMA unica linha. Nao use JSON identado/multilinha e nao coloque virgula no fim da linha. |
| `LINE_NOT_OBJECT` | ERROR | Linha nao e um objeto JSON. | Cada linha precisa ser um objeto `{ ... }`. Arrays, strings e numeros soltos nao sao aceitos. |
| `FIELD_REQUIRED` | ERROR | Campo obrigatorio ausente. | Preencha o campo indicado. Se a fonte realmente nao fornece esse dado, use o campo opcional equivalente ou registre o valor bruto em `raw`. |
| `FIELD_TYPE` | ERROR | Campo com tipo errado. | Ajuste o tipo do campo conforme o contrato (ex.: `size` e numero, nao string; `raw` e objeto, nao string com JSON dentro). |
| `FIELD_ENUM` | ERROR | Valor fora da lista permitida. | Use um dos valores aceitos listados na documentacao. Se nenhum servir, use OTHER e detalhe o tipo real em `metadata`. |
| `FIELD_FORMAT` | ERROR | Campo com formato invalido. | Corrija o formato: datas em ISO 8601 UTC (2024-01-31T12:00:00Z), URLs absolutas com http/https, idioma em ISO 639-1 (pt), pais em ISO 3166-1 alpha-2 (BR). |
| `FIELD_UNKNOWN` | WARNING | Campo desconhecido no nivel raiz. | Remova o campo do nivel raiz. Qualquer informacao extra da fonte deve ir dentro de `raw` (bruto) ou `metadata` (normalizado). |
| `RAW_EMPTY` | ERROR | O campo `raw` esta vazio. | Preencha `raw` com o payload original da fonte (JSON da API ou objeto extraido do HTML). Ele e o que permite reprocessar sem raspar de novo. |
| `DUPLICATE_HASH` | ERROR | Hash repetido dentro do proprio dump. | Duas ou mais linhas tem o mesmo `hash`. Remova as duplicatas antes de exportar, ou revise como o hash e calculado (ele deve ser SHA-256 do conteudo normalizado). |
| `DUPLICATE_EXTERNAL_ID` | ERROR | externalId repetido dentro do proprio dump. | O `externalId` precisa ser unico dentro da fonte. Se a fonte reaproveita ids entre secoes, prefixe (ex.: "noticias:12345"). |
| `FILE_URL_WRONG_HOST` | ERROR | URL de arquivo aponta para fora do IDrive E2. | Nenhum arquivo pode ficar hospedado na fonte original nem em outro storage. Baixe o arquivo, suba para o IDrive E2 e coloque a URL do E2 no dump. |
| `FILE_URL_WRONG_BUCKET` | ERROR | URL de arquivo aponta para outro bucket. | Suba o arquivo para o bucket oficial informado nas credenciais que voce recebeu e atualize a URL no dump. |
| `FILE_URL_WRONG_PREFIX` | ERROR | Arquivo fora da pasta do projeto. | Todo arquivo precisa estar em <bucket>/<slug-do-projeto>/... . Mova os objetos para a pasta do projeto no IDrive E2 e atualize as URLs. |
| `FILE_NOT_FOUND` | ERROR | Arquivo nao existe no IDrive E2. | A URL esta no formato certo mas o objeto nao foi encontrado. Confirme que o upload terminou e que o caminho (incluindo maiusculas/minusculas) e exatamente o mesmo do dump. |
| `FILE_SIZE_MISMATCH` | WARNING | Tamanho declarado diferente do arquivo no storage. | Atualize o campo `size` com o tamanho real em bytes do objeto no IDrive E2, ou reenvie o arquivo que ficou truncado. |
| `FILE_MIME_MISMATCH` | WARNING | Content-Type do objeto diferente do declarado. | Faca o upload para o IDrive E2 definindo o Content-Type correto, ou corrija o campo `mimeType` no dump. |
| `STORAGE_UNAVAILABLE` | ERROR | Nao foi possivel consultar o IDrive E2. | Problema de credencial ou de rede do lado do Atlas, nao do seu dump. Avise o administrador e tente reenviar depois. |
| `COMMIT_CONFLICT` | ERROR | Conflito ao gravar: registro ja existe nesta fonte. | Ja existe uma entidade com o mesmo externalId ou hash nesta fonte. Reenvie apenas os registros novos, ou peca ao administrador para reimportar em modo de atualizacao. |
| `COMMIT_FAILED` | ERROR | Falha ao gravar o lote no banco. | Erro do lado do Atlas durante a gravacao. A importacao foi revertida; nenhum dado parcial ficou no banco. Tente novamente e avise o administrador se persistir. |