Pular para conteúdo

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:

  1. cria backup do diretório;
  2. aplica o schema;
  3. importa em transação;
  4. registra origem/checksum/ID;
  5. valida o banco;
  6. 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 --apply sobre 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_snapshots depende de consulta de dedupe sem UNIQUE composto;
  • 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.