8.2 KiB
Proposta de migração Education
Actualização: o repositório já contém a migration de recepção interna
2026_09_26_100000_education_intake.php, ensaiada numa base temporária nesta revisão. Ver relatório actual. O inventário abaixo é histórico e não comprova o esquema nem as contagens da base actual da aplicação.
Estado: Proposta — Pendente de validação. Este ficheiro não é uma migration executável. Não criar SQL activo para B02/B03 antes da decisão de modelo/identidade e autorização. Diagnóstico local read-only em 26/09/2026: as seis tabelas da migration Education existem e têm zero registos. Isso não prova ausência de fontes externas.
Inventário e mapeamento
| Origem confirmada | Destino lógico | Transformação proposta | Rejeição/quarentena |
|---|---|---|---|
| skills.code/name | Skill canónica | Preservar UUID/código; sinónimos e versão em extensão | Código ambíguo, taxonomia desconhecida |
| occupations.code/name | Occupation | Preservar UUID; relacionar Skills por versão | Equivalência de nível sem prova |
| training_needs.quantity | Registo legado de necessidade | Manter o valor e a semântica original; só ligar a LabourDemand após revisão | Nunca assumir que quantity significa trabalhadores |
| training_needs.project_id/occupation_id/skill_id | Relações contextuais | Manter FKs; verificar consistência da cadeia | Órfão, projecto eliminado, fonte ausente |
| training_providers.organization_id/name/status | Provider canónico | Conservar candidate/approved/inactive | Nenhuma conversão approved → acreditada |
| courses.provider_id/code/title/status | Curso canónico | Manter UUID e vínculo; separar revisão curricular | Nenhuma conversão published → certificado |
| education_opportunities.course_id/datas/capacity | Oferta calendarizada existente | Preservar contrato actual | Não reutilizar como projecto de construção de centro |
| Documentos/fontes externos não inventariados | Área de recepção proposta | Fonte, proprietário, autorização, checksum, versão, unidade e data | Sem autorização não importar |
Vagas técnicas propostas
| Vaga | Conteúdo lógico aditivo | Pré-condição | Reconciliação | Reversão |
|---|---|---|---|---|
| M0 | Snapshot do catálogo, colunas, FKs, índices, triggers, consumidores e permissões | Inventário aprovado; backup base/documentos/configuração verificado | Contagens, hashes por conteúdo normalizado e manifesto | Nenhuma escrita |
| M1 | Extensões de fonte, validade e versão de taxonomia; relações ocupação–competência e curso–competência | Taxonomia/Shared confirmados | 100% UUID preservados, zero órfãos; decisão de unicidade | Desactivar leitores novos; conservar histórico |
| M2 | LabourDemand e relações contextuais, sem renomear training_needs | B02/B03 resolvidos | Quantidades/unidades/períodos iguais à fonte aprovada; rejeições explicadas | Parar escritores novos; não dropar dados |
| M3 | Capacidade por período/pool e snapshots SkillGap | Regras e fonte de disponibilidade aprovadas | Contagem de pools/alocações; zero dupla contagem | Reverter exposição e workers; snapshots preservados |
| M4 | Identidade de investimento educativo e vínculo a Opportunity | Decisão formal B02 | Cada oportunidade tem promotor, lacuna/versão e fonte; sem duplicação comercial | Despublicar e suspender encaminhamento |
| M5 | Projecções de leitura, contratos e eventos | Idempotência e mandatos testados | Reconciliar outbox/inbox e consumidor; zero estados oficiais promovidos | Feature flag desligada; drenar ou reter fila conforme plano |
Nomes físicos novos, índices, rotas e policies são propostas, não factos confirmados. Antes de converter para Laravel migrations: revisar catálogo real completo e tipos, validar envelope, constraints e impacto nas queries/consumidores. Não acrescentar estado oficial a enum editorial. Preferir extensões opcionais inicialmente; backfill separado e validado antes de NOT NULL. A idempotência da criação estrutural usa o ledger de migrations; não esconder divergência com IF NOT EXISTS indiscriminado.
Pseudocódigo do importador, não executável
exigir fonte autorizada + versão do mapeamento aprovada + feature flag de ensaio
para cada lote:
verificar checksum, unidades, datas, direitos e chaves externas
normalizar apenas segundo taxonomia aprovada
rejeitar ambíguos para quarentena com motivo e referência, sem PII em logs
numa transacção:
procurar por sistema_origem + id_externo + versão
se versão igual e checksum igual: no-op
se versão igual e conteúdo diferente: conflito, não sobrescrever
se versão menor: rejeitar regressão
se versão maior: preservar snapshot e aplicar decisão de precedência
nunca copiar acreditação/certificação como confirmada sem decisão externa verificada
escrever auditoria e recibo do lote
emitir relatório de aceites/rejeitados/conflitos e totais por unidade/período
aprovar reconciliação antes de activar leitores públicos
Ensaio, backup e aceitação
Usar ambiente PostgreSQL/PostGIS isolado, primeiro dados sintéticos e depois cópia anonimizada autorizada. Zero dados pessoais reais em fixtures. Comparar antes/depois: contagens, somas por unidade e moeda, chaves, hashes normalizados, versões, relações, geometrias válidas, classificações e estados. Relatório inclui rejeições; não forçar sucesso descartando linhas silenciosamente.
Backup consistente com manifestos de base/documentos/configuração; cifrado, acesso restrito, restauro verificado em ambiente separado. Critérios propostos: zero perda não autorizada, zero órfãos, zero duplicados de chaves, zero estados oficiais promovidos, totais reconciliados para cada lote aceite; divergências pendentes impedem activação do lote. Responsáveis e limites financeiros de reconciliação permanecem por aprovação.
Rollback de aplicação começa pela flag, exposição e produtores/consumidores; não remove tabelas. Restauro físico só em incidente aprovado e depois de avaliar escritas posteriores ao backup. Eventos já entregues requerem compensação/reconciliação, não fingir que restaurar a base os desenvia. Remoções futuras exigem inventário, período de transição, aprovação e restauro ensaiado, numa entrega distinta.