Implementação de confiabilidade de dados, migrações e backups¶
Objetivo da entrega¶
Esta etapa consolida uma base local-first antes de ampliar automações do produto. O trabalho acrescenta integridade, histórico e recuperação sem reescrever todos os módulos e sem apagar os stores anteriores.
Escopo implementado:
- contrato comum de repositório;
- adapters JSON, JSONL e SQLite;
- schema SQLite versionado;
- importador idempotente dos stores legados;
- backup, export, restore e data health;
- snapshots imutáveis;
- vínculos do Tracker;
- metadados seguros de execuções de IA;
- API e painel de dados na rota
/privacy.
Decisões de arquitetura¶
Migração incremental¶
Os módulos existentes não foram convertidos em massa. JSON/JSONL continuam disponíveis e o SQLite assume as responsabilidades que exigem transação, FK ou imutabilidade. Isso reduz risco de regressão e permite comparar contagens durante a transição.
Banco local único¶
O padrão é <SOTUHIRE_DATA_DIR>/sotuhire.db, sem servidor. As conexões ativam FK, WAL e timeout de concorrência. O schema mantém parte dos objetos em JSON para evitar normalização excessiva.
Histórico sem mutação¶
Vaga, currículo, análise e edital recebem snapshots por hash. Triggers impedem UPDATE e DELETE; mudança de conteúdo cria nova versão.
Rollback por backup¶
O runner não implementa down. A recuperação oficial é validar e restaurar o backup. Os arquivos antigos continuam no disco e são uma segunda via de compatibilidade.
Componentes adicionados¶
| Área | Arquivos principais |
|---|---|
| conexão e schema | modules/storage/database.py, modules/storage/migrations/* |
| repositórios | modules/storage/repositories/* |
| importação legada | modules/storage/legacy_migration.py, scripts/migrate_local_data.py |
| backup/restore | modules/storage/backup.py, scripts/backup_data.py, scripts/restore_data.py |
| diagnóstico | modules/storage/health.py, scripts/check_data_health.py |
| snapshots | modules/storage/snapshots.py |
| candidaturas | modules/storage/applications.py |
| auditoria de IA | modules/storage/ai_runs.py |
| API | apps/api/routes/data.py, apps/api/services/data.py, apps/api/schemas/data.py |
| frontend | apps/web/src/routes/privacy.tsx e cliente/tipos da API |
Mapeamento dos stores legados¶
| Origem | Destinos possíveis |
|---|---|
profile/profiles.json |
profiles, profile_items |
memory/career-profile.json |
profiles como perfil legado |
memory/career-memory.jsonl |
memories |
sotuhire-history.json |
applications |
sotuhire-opportunities.json |
opportunities, job_snapshots quando há descrição |
sources/imports.json |
sources, captures, job_snapshots quando há texto |
public_exams/notices.json |
editais, cargos, requisitos e snapshots quando há texto bruto |
radar/radar.json |
wishlists, fontes, runs, resultados e notificações |
radar/schedules.json |
agendamentos, runs e notificações |
companion/captures.jsonl |
capturas e snapshots de vaga quando há texto |
companion/active-context.json |
snapshot do currículo quando há texto |
portfolio/project-analyses.jsonl |
projetos GitHub/portfólio |
Deduplicação durante a importação¶
O migrador combina somente identidades fortes ou conteúdo exato:
- URL canônica para oportunidades e capturas;
- conteúdo exato para memórias que não são evento/feedback;
- identidade de ProfileItem com referência forte ou fallback semântico;
- owner/repo ou URL para GitHub;
- fingerprints específicos para cada tipo de snapshot.
Quando há merge, o payload preserva:
merged_legacy_ids
legacy_source_paths
source_refs
deduplication_reason=strong_or_exact_identity_during_legacy_migration
Similaridade incerta não autoriza merge automático.
Rejeições e warnings esperados¶
- JSON com raiz escalar ou formato incompatível;
- linha JSONL inválida;
- candidatura sem anúncio original;
- memória sem referência de origem;
- data inválida;
- duplicata forte/exata;
- oportunidade, edital ou candidatura legada sem snapshot;
- schema ausente/divergente;
- FK ou integrity check inválido;
- trace de IA sem metadados mínimos.
Um item rejeitado permanece no arquivo de origem. O relatório de --apply pode terminar com success=false mesmo importando os itens válidos, pois rejeições não são ocultadas.
Fluxo de migração¶
python scripts/migrate_local_data.py --dry-run
python scripts/migrate_local_data.py --apply
python scripts/migrate_local_data.py --verify
python scripts/check_data_health.py
O --dry-run deve ser guardado e revisado antes de qualquer aplicação. --apply:
- cria backup do diretório;
- aplica o schema;
- importa em transação;
- registra origem/checksum/ID;
- valida o banco;
- mantém todos os arquivos anteriores.
Repetir a importação do mesmo checksum não duplica itens já registrados.
Integrações de produto¶
Tracker¶
O cartão mutável recebe IDs de snapshots e metadados de histórico. ApplicationRepository espelha o estado e acrescenta evento quando o status muda. O modo rápido continua possível; sem texto original, o snapshot guarda apenas o conteúdo disponível.
Editais¶
Salvar um edital revisável cria snapshot geral e por cargo. O conteúdo antigo permanece mesmo quando o estado editável é alterado.
Extensão¶
Capturas sanitizadas de vaga/edital produzem snapshots. A análise pode vincular o contexto de currículo e o resultado. Nenhum storage, cookie, token ou header autenticado entra no snapshot.
IA¶
AiRunStore registra provider, modelo, prompt, fallback, custo/latência opcional e evidências. Chaves e material de autorização são rejeitados.
API e interface¶
A API expõe health, criação/listagem/download de arquivos e restore. O restore HTTP é dry-run por padrão, aceita somente arquivos no diretório gerenciado e exige RESTAURAR para aplicar.
A rota /privacy apresenta:
- integridade e versão do schema;
- contagens e issues;
- criação de backup/export;
- download no modo API Real;
- validação antes do restore;
- confirmação textual e diálogo final.
O modo Demo simula o fluxo e não grava arquivos.
Validação automatizada¶
Comandos direcionados:
pytest tests/test_storage_repository_contract.py
pytest tests/test_storage_migrations.py
pytest tests/test_storage_snapshots.py
pytest tests/test_storage_backup_restore.py
pytest tests/test_legacy_data_migration.py
pytest tests/test_api_data_reliability.py
cd apps/web
npm run typecheck
npm run build
npx --no-install playwright test tests/e2e/data-reliability.spec.ts --project=chromium
Os testes de --apply e restore usam diretórios temporários e dados fictícios.
Estado operacional¶
- implementação da camada de storage;
- migrações versionadas e idempotentes em testes;
- dry-run read-only coberto por teste;
- backup/restore e checksum cobertos por teste;
- snapshots e FKs cobertos por teste;
- API e frontend cobertos por testes direcionados;
- revisar o dry-run do diretório de dados real;
- autorizar e executar
--applysobre dados reais, se desejado; - executar restore drill de um backup real em diretório isolado;
- registrar contagens e warnings reais na conferência da release.
Não há alegação de que a migração real tenha sido aplicada. Até a revisão humana do relatório, os stores legados continuam sendo a base compatível existente.
Limitações conhecidas¶
- estado mutável em JSON e vínculos SQLite não formam uma única transação;
analysis_snapshotsdepende de consulta de dedupe semUNIQUEcomposto;- restore é atômico por arquivo, não pelo conjunto inteiro;
- health check não repara dados;
- arquivos antigos sem conteúdo original continuam sem snapshot completo;
- nem todos os módulos usam o contrato
EntityRepository; - o filtro de segredo não abre o conteúdo do SQLite durante o backup.
Esses limites são explícitos para evitar falsa sensação de integridade e orientar as próximas migrações incrementais.