Shadow de Paridade do Conteúdo Institucional
Status: S1–S3 concluídos e certificados; S4 — Shadow em dados reais — é o próximo gate seguro em 08/09/2026.
Base do código SIGESC: main em 4ba3f502995433f7261ababc4fc7b7ebba3b003c — merge da PR #531 (S3).
Produção permanece: 2c203568d9575b47bd66345695692a88de3a7743 — F5 Cobertura Curricular v2. Até o encerramento de S3, nenhuma etapa Shadow foi promovida para produção e nenhum consumidor operacional foi cortado.
1. Problema
Após o cutover canônico de escrita do conteúdo docente, novas gravações pertencem a content_entries. A coleção learning_objects permanece como legado histórico/read-only.
Entretanto, existem consumidores institucionais que ainda calculam indicadores diretamente a partir de learning_objects. Se esses consumidores permanecerem assim, seus números tenderão a subcontar lançamentos novos mesmo que o Diário do professor esteja correto.
A solução não é trocar mecanicamente learning_objects por content_entries. Parte dos consumidores mede volume/tempo/carga de registros; outra parte mede Cobertura Curricular, cuja semântica já foi alterada pela F5 para usar Plano de Ensino publicado como denominador.
Portanto, o cutover deve separar dois domínios:
- Reporting de conteúdo: volume, aulas, atraso, distribuição temporal e carga registrada;
- Cobertura curricular: previsto versus trabalhado conforme Plano de Ensino Bimestral publicado.
Misturar os dois produziria uma nova dupla SSoT.
2. Fundamentos implementados
2.1 S1 — Read Model Institucional
A PR #529 criou backend/services/content_reporting_projection.py como fundação estritamente read-only.
O read model:
- compõe
content_entries+ histórico elegível delearning_objects; - exige
mantenedora_idexplícito; - ancora turmas por mantenedora e ano;
- aceita ano histórico como inteiro ou string;
- não infere cutover por presença de dados nem por
diary_settings.enabledisoladamente; - recebe escopos de corte explícitos;
- permite corte por componente ou class-wide, com precedência do específico;
- suprime legado posterior ao corte apenas quando o corte é conhecido;
- faz o canônico prevalecer em sobreposição semântica;
- rejeita tenant explicitamente divergente;
- falha fechado para escopos de cutover fora das turmas autorizadas;
- não executa escrita, migração ou backfill.
S1 foi integrada em main pelo merge da PR #529, SHA 9605fc8fd91d06686ef725466fe104b413db25c9, com CI pós-merge verde e sem deploy.
2.2 S2 — Resolver Canônico de Escopos de Cutover
A PR #530 criou backend/services/content_reporting_cutover_resolver.py.
O resolver:
- transforma vínculos válidos do Diário por Vínculo em
ContentReportingCutoverScope; - ancora mantenedora + ano por
classes; - exige
reference_dateexplícita no mesmo ano letivo; - considera somente vínculo não excluído e canonicamente habilitado para Conteúdo;
- reutiliza o contrato de
effective_diary_settings, sem interpretarenabled=trueisoladamente; - valida tenant, escola, turma e escopo DVD v1;
- respeita
valid_from/valid_untilinclusivos; - ignora vínculo desabilitado, excluído, futuro ou encerrado na data de referência;
- permite class-wide e componente específico coexistirem;
- falha fechado quando vínculos vigentes do mesmo escopo apresentam datas de corte divergentes;
- não infere corte por presença de
content_entries,learning_objectsou primeira ocorrência de dados; - permanece estritamente read-only.
S2 foi integrada em main pela PR #530, SHA fdbacaaebde0569038ed79b6d13a6c59f9ca704e. A PR fechou 15/15 workflows com sucesso e o push pós-merge fechou 9/9 workflows com sucesso. production permaneceu em 2c203568d9575b47bd66345695692a88de3a7743; não houve deploy.
2.3 S3 — Executor de Paridade
A PR #531 criou backend/services/content_reporting_parity.py e ampliou o read model S1 apenas com métricas internas das fontes, preservando sua semântica de projeção.
O executor S3:
- resolve cutovers por S2 e lê as fontes pela S1;
- não possui router ou endpoint e não altera respostas HTTP existentes;
- não importa nem monkey-patcha consumidores operacionais;
- compara a fonte legada com a projeção canônica por consumidor;
- exige
generated_atecode_shaexplícitos; - gera SHA-256 determinístico do mapa de cutover e do relatório;
- produz as classificações
MATCH,EXPECTED_CANONICAL_GAIN,EXPECTED_POST_CUTOVER_LEGACY_EXCLUSION,HISTORICAL_OVERLAP_SUPPRESSED,PROVENANCE_INCOMPARABLE,SCOPE_ERROR,UNEXPECTED_DIFFERENCEeERROR; - preserva a semântica atual do PMPI para cálculo de atraso (
created_at - date, apenas valores não negativos); - não fabrica paridade quando a proveniência temporal é incomparável;
- não grava relatório em Mongo nem executa escrita, migração ou backfill.
Consumidores técnicos reconhecidos por S3:
DIARY_DASHBOARD_CONTENT;MONTHLY_REPORT;PMPI_LESSONS;PMPI_WORKLOAD;PMPI_DELAY;ANALYTICS_CONTENT.
S3 foi integrada em main pela PR #531, SHA 4ba3f502995433f7261ababc4fc7b7ebba3b003c. A PR fechou 15/15 workflows com sucesso e o push pós-merge fechou 9/9 workflows com sucesso. production permaneceu em 2c203568d9575b47bd66345695692a88de3a7743; não houve deploy.
Nenhum dos fundamentos S1–S3 está ligado, até aqui, a PMPI, Analytics, Dashboard de Diários, relatório mensal ou Intervenções em produção.
3. Princípio central da Shadow de Paridade
A etapa shadow nunca altera a resposta entregue ao usuário.
Para cada consumidor elegível, o SIGESC deve calcular em paralelo:
resultado legado atualmente exibido
versus
resultado candidato pelo read model canônico
A comparação deve produzir relatório técnico, nunca substituir silenciosamente a resposta operacional.
O objetivo não é obrigar o novo número a ser igual ao legado em todos os casos. O objetivo é separar:
- divergência esperada por novos lançamentos canônicos;
- divergência por histórico/cutover;
- divergência por diferença de semântica;
- erro de escopo/tenant;
- erro real de implementação.
4. Inventário e rota de migração
| Consumidor | Métrica atual baseada em legado | Destino correto | Tipo de shadow |
|---|---|---|---|
| Dashboard de Diários — Conteúdo | quantidade de registros, aulas e distribuição mensal | Read Model Institucional | paridade numérica |
| PMPI — aulas lançadas | quantidade de registros numa janela | Read Model Institucional | paridade numérica |
| PMPI — atraso de lançamento | diferença entre created_at e data da aula |
Read Model Institucional | paridade numérica condicionada à proveniência temporal |
| PMPI — carga horária | soma de number_of_classes |
Read Model Institucional | paridade numérica |
| Analytics — indicadores derivados de conteúdo | agregações sobre registros/aulas | Read Model Institucional | paridade numérica por escopo |
| Relatório mensal | contagens de lançamentos por escola | Read Model Institucional | paridade numérica por escola |
| Detector de Intervenções Curriculares | adaptações cobertas por learning_objects |
Cobertura Curricular F5 | substituição semântica, não paridade percentual |
| Plano de ação / pendências de cobertura | adaptações ausentes no legado | Cobertura Curricular F5 | substituição semântica, não paridade percentual |
Fora desta migração
Não devem ser tratados como consumidores a cortar nesta fase:
- bridges históricos do DVD;
- leitores/PDFs que já compõem histórico de forma própria e autorizada;
- scripts forenses, migrações históricas e auditores;
- índices de banco relacionados ao legado;
- compatibilidade read-only necessária para dados anteriores ao cutover.
A simples ocorrência textual de learning_objects no repositório não significa que o uso é incorreto.
5. Shadow para Reporting de Conteúdo
5.1 Unidade de comparação
A comparação deve ser reproduzível por:
mantenedora + ano letivo + escola opcional + turma opcional + componente opcional + janela temporal
Todo relatório deve registrar o escopo efetivo e os cortes aplicados.
5.2 Métricas mínimas
Para cada escopo, calcular pelo menos:
legacy_record_count;projected_record_count;record_count_delta;legacy_number_of_classes_sum;projected_number_of_classes_sum;number_of_classes_delta;- distribuição mensal de registros;
- distribuição mensal de aulas (
number_of_classes); - atraso médio de lançamento, quando
created_atcomparável estiver disponível; - quantidade de registros canônicos;
- quantidade de registros legados mantidos;
- quantidade de legado suprimido pós-cutover;
- quantidade de duplicidades semânticas suprimidas;
- rejeições por tenant incompatível;
- quantidade e identidade dos escopos de cutover usados.
5.3 Classificações
Cada linha de comparação deve receber uma classificação explícita:
MATCH— valores equivalentes;EXPECTED_CANONICAL_GAIN— candidato possui registros novos canônicos que o legado não poderia conter;EXPECTED_POST_CUTOVER_LEGACY_EXCLUSION— legado posterior ao corte foi corretamente excluído;HISTORICAL_OVERLAP_SUPPRESSED— duplicidade legado/canônico foi suprimida;PROVENANCE_INCOMPARABLE— campo necessário para determinada métrica não possui proveniência comparável;SCOPE_ERROR— turma, tenant, ano ou corte inválido;UNEXPECTED_DIFFERENCE— divergência sem explicação prevista;ERROR— falha técnica no cálculo.
MATCH não deve ser considerado a única condição saudável. Depois do cutover de escrita, EXPECTED_CANONICAL_GAIN é uma divergência esperada e positiva.
5.4 Tolerâncias
- contagens de registros: tolerância
0; - soma de aulas: tolerância
0; - distribuição mensal: tolerância
0por mês; - atraso médio: tolerância máxima inicial de
0,01dia apenas para arredondamento; - percentuais derivados: comparar primeiro numerador e denominador; o percentual é consequência.
Não usar uma tolerância percentual ampla para esconder diferença estrutural.
6. Shadow para Cobertura e Intervenções
6.1 Regra de semântica
A cobertura antiga e a F5 não possuem o mesmo denominador.
O legado responde, de forma simplificada:
adaptações/habilidades referenciadas em learning_objects
-------------------------------------------------------
adaptações curriculares consideradas ativas
A F5 responde:
itens previstos do Plano de Ensino publicado que foram trabalhados
---------------------------------------------------------------
itens previstos do Plano de Ensino publicado para o escopo
Logo, não existe requisito de igualdade percentual entre as duas métricas.
6.2 Shadow correto para Intervenções
O shadow deve comparar decisões, não exigir igualdade de porcentagem:
- alerta legado ativo/inativo;
- cobertura F5 disponível/indisponível;
- estado F5: previsto trabalhado, previsto pendente, fora do plano, histórico não estruturado, período futuro, plano inexistente;
- alerta candidato segundo política de intervenção futura;
- motivo da divergência.
6.3 Regra sem Plano de Ensino publicado
Sem Plano de Ensino publicado:
- F5 não produz percentual;
- o shadow não converte isso em
0%; - nenhuma nova intervenção curricular deve ser criada por inferência de ausência de plano;
- o caso deve ser classificado como
PLAN_REQUIRED/COVERAGE_UNAVAILABLEaté decisão institucional específica.
6.4 Período futuro
Itens de período futuro não entram artificialmente como pendência de cobertura e não podem gerar alerta por atraso curricular.
7. Resolução dos escopos de cutover
A fundação da PR #529 aceita escopos explícitos e a PR #530 implementou o resolver read-only que deriva esses cortes do contrato canônico do Diário por Vínculo, respeitando:
- vínculo válido;
- tenant;
- turma;
- componente ou class-wide;
valid_from;- capacidade de conteúdo efetivamente habilitada pelo autorizador canônico;
- precedência de componente específico sobre class-wide na projeção;
- ausência de ambiguidades.
É proibido decidir cutover apenas porque existe content_entries, apenas porque diary_settings.enabled=true, ou pela data do primeiro registro encontrado.
8. Relatório Shadow
O executor deve ser read-only e produzir um artefato reproduzível com:
- timestamp;
- commit SHA do código;
- tenant;
- ano letivo;
- filtros;
- mapa/hash dos escopos de cutover;
- consumidor avaliado;
- métrica legada;
- métrica candidata;
- delta;
- classificação;
- contadores internos do read model;
- erros;
- resumo por escola, turma e componente quando aplicável.
S3 já implementa o núcleo reproduzível do relatório e seus hashes. S4 deve definir a forma operacional segura de executá-lo sobre dados reais e reter evidência de auditoria sem escrever em coleções operacionais.
Nenhum relatório shadow deve ser gravado em coleções operacionais nesta fase. O artefato pode ser JSON/CSV de CI/auditoria ou resposta técnica protegida, desde que permaneça read-only.
9. Gates para avançar
Gate S1 — Fundação
Concluído pela PR #529.
- serviço read-only;
- contrato de merge;
- fail-closed de tenant/escopo;
- CI verde;
- zero consumidor alterado.
Gate S2 — Resolver de cutover
Concluído pela PR #530.
- testes para vínculo por componente;
- testes class-wide;
- sobreposição específico × class-wide;
- vínculo inativo/encerrado;
- tenant divergente;
- ano histórico;
- ausência de cutover;
- zero escrita;
- 15/15 workflows do PR verdes;
- 9/9 workflows pós-merge em
mainverdes; - zero deploy.
Gate S3 — Executor de paridade
Concluído pela PR #531.
- comparação por consumidor;
- classificações determinísticas;
- relatório reproduzível;
- nenhuma alteração da resposta HTTP existente;
- nenhuma escrita em Mongo;
- testes S1 + S2 + S3 executados juntos;
- CI/regressões gerais verdes;
- 15/15 workflows do PR verdes;
- 9/9 workflows pós-merge em
mainverdes; - zero deploy.
Gate S4 — Shadow em dados reais
Próximo gate. Não iniciado na data desta revisão.
Executar por mantenedora/ano em modo read-only.
Antes de qualquer execução sobre produção, é obrigatório confirmar o mecanismo operacional autorizado para disponibilizar/executar S1–S3 contra a base real, sem criar atalho de deploy, acesso paralelo ou credencial ad hoc.
Para Reporting de Conteúdo, a condição para considerar cutover é:
UNEXPECTED_DIFFERENCE = 0nos escopos certificados;SCOPE_ERROR = 0;ERROR = 0;- toda divergência remanescente classificada e explicável;
- amostras críticas verificadas por turma/componente;
- nenhum cruzamento de tenant.
Para Cobertura/Intervenções, não usar match-rate percentual. A condição é:
- todos os casos de
plano_inexistentetratados sem fabricar percentual; - período futuro não gera falso atraso;
- divergências de decisão explicadas pela mudança de semântica;
- política de gatilho para a F5 explicitamente aprovada antes do cutover.
Gate S5 — Cutover por consumidor
Somente depois dos gates anteriores.
O cutover deve ser incremental, um consumidor ou grupo semanticamente homogêneo por PR, preservando rollback simples.
Ordem recomendada:
- Dashboard de Diários — Conteúdo;
- Relatório mensal;
- PMPI — aulas lançadas/carga/atraso;
- Analytics de conteúdo;
- Intervenções Curriculares, em PR própria e usando F5, não o read model de volume.
10. Regras de rollback
Como o read model é apenas leitura, o rollback de cada consumidor deve consistir em restaurar seu reader anterior enquanto a compatibilidade legada ainda existe.
Não remover learning_objects, não apagar histórico e não executar backfill destrutivo como parte do cutover de reporting.
A remoção futura de compatibilidade legada é uma decisão separada e depende de auditoria de completude histórica.
11. Próxima implementação segura
A próxima etapa é S4 — Shadow em dados reais, ainda sem cutover de consumidor.
O primeiro movimento de S4 deve ser um preflight operacional read-only para localizar e validar o mecanismo já autorizado de execução contra produção. Esse preflight deve responder, antes de qualquer promoção de código:
- se existe executor/auditor read-only já homologado capaz de usar o código de
maincontra a base real sem alterar a aplicação pública; - se S1–S3 precisam ser promovidos para o runtime de produção para que S4 seja tecnicamente possível;
- quais gates de release/proveniência precisam ser usados se essa promoção read-only for necessária;
- como o relatório S4 será gerado e retido sem escrita em coleções operacionais;
- como será garantido o isolamento por mantenedora/ano e a reprodutibilidade pelo SHA exato do código.
É proibido criar mecanismo paralelo de SSH, credencial ad hoc, endpoint público temporário ou deploy fora da governança normal apenas para executar S4.
Somente após esse preflight indicar um caminho canônico e os gates operacionais correspondentes estarem satisfeitos pode ocorrer a execução real do Shadow.
12. Relação com Avaliação e Evidência de Aprendizagem
Este trabalho não é a futura camada de Avaliação/Evidência.
Ele saneia a transição de leitura do domínio de conteúdo e evita que indicadores institucionais continuem presos à coleção legada enquanto o professor escreve no motor canônico.
A cadeia futura permanece:
Plano de Ensino → Registro Docente → Cobertura → Avaliação → Evidência → Intervenção.
Nenhuma estrutura de questão, habilidade avaliada, resposta do estudante, domínio ou evidência individual deve ser criada por inferência a partir deste blueprint.