"""Extend the sole audit matrix; preserve every previous source/criterion/state.""" import collections import csv import hashlib import json from pathlib import Path ROOT = Path(__file__).resolve().parents[1] DOC = ROOT / 'docs/investmoz/v2/auditoria-portal' PRIVATE = ROOT / 'storage/app/private/investmoz-v2/portal-audit' csv.field_size_limit(5000000) matrix = DOC / 'matriz-requisitos.csv' rows = list(csv.DictReader(matrix.open(encoding='utf-8-sig'))) headers = list(rows[0]) protected = ['ID', 'Pedido/trecho', 'Fonte/ocorrências', 'Estado', 'Critério de aceitação', 'Versões divergentes', 'IDs das linhas de origem', 'Quantidade de linhas de origem', 'Critérios e versões por origem (JSON)'] before = {r['ID']: {k: r[k] for k in protected} for r in rows if not r['ID'].startswith('AUD-LEG-')} tag = '[Legado QA 10/10/2026] ' evidence = 'legado-piloto-qa.md; legacy-content-http-qa.json: recursos6/5 públicos, hashes verificados, pesquisa, login/MFA21, criação/continuação, logout/nova sessão, recuperação, replay único e isolamento. Interface não observada.' items = [ ('01', 'Reconciliar páginas, imagens, anexos, categorias e URLs do legado com biblioteca/importações e auditoria', '92 URLs recolhidos,50 versões de artigos e2.826 referências inventariados;30.170 entradas de sitemap de outro domínio preservadas. Dois recursos únicos importados/repetição sem duplicação.', 'Cobertura histórica/paginação/destinos externos e restantes versões por reconciliar. Imagem histórica404 identificada.', 'Cada conteúdo/versão tem identidade, origem, autoria/datas conhecidas, hash, duplicados/conflitos e destino; inventário cobre as fontes acessíveis, sem omissão das não importadas.'), ('02', 'Adaptar cores, tipografia, imagens e padrões reais do legado aos componentes existentes, com acessibilidade e móvel', 'CSS/chunkwelcome e fotografia reais recuperados; Noto Sans/Serif locais, paleta vermelho/verde/fundo claro, cartões e foco/labels preparados no buildQA.', 'Render móvel/desktop, teclado e contraste efetivo no navegador pendentes.', 'Comparar com referências reais e demonstrar leitura/ações em desktop/móvel/teclado; sem imagens quebradas ou controlo inacessível.'), ('03', 'Piloto página de agronegócio → artigo → localizações/oportunidades → iniciar/continuar ideia no dossier', 'Rotas públicas e fontes da biblioteca; uma oportunidade publicada no setor; links da carteira/mapas; criação e continuação com referência ao artigo no payload cifrado e evento existentes.', 'Percurso visual completo pendente; seis referências territoriais não delimitadas/associadas por inferência.', 'Percorrer as ações na interface, guardar, recuperar noutra sessão, continuar e voltar ao conteúdo; dados/contextos separados e próxima ação existente.'), ('04', 'Consulta pública sem login; autenticação apenas para guardar/acompanhar dados pessoais', 'Artigo, imagem, biblioteca e pesquisa respondem sem login; início/associação ao dossier requer sessão verificada. Outra conta403, contexto privado ausente em leitura pública/alheia.', 'Verificação visual dos pontos de autenticação e sessão pendente; C37 permanece aberto.', 'Leitor anónimo consulta tudo o que foi autorizado; apenas gravação privada pede autenticação; nenhum dado do dossier vaza para anónimos/outras contas.'), ('05', 'Fonte única associada a setores, territórios e etapas com fundamento; artigo não vira oportunidade concreta', 'Documento único6 e imagem5; três links com fundamento: tema, thumbnail e apoio editorialS01. Relações de oportunidades pelo setor já cadastrado; zero oportunidade ou geometria criada pelo artigo.', 'Correspondências territoriais concretas e ampliação por etapa/setor ainda pendentes; não inventar ligações.', 'Mesma fonte/versionamento reutilizados; relações têm citação/âmbito; cadastros reais e artigos distintos; ausência de relação é apresentada explicitamente.'), ('06', 'Preservar contexto ao entrar/regressar ao percurso, URLs existentes e correspondências de migração', 'Mantido /agronegocio; /explorar/agronegocio novo. Referência comID/hash/versão/ator/data e retorno autorizado ao mesmo dossier; replay sem duplicação; cinco fases/19etapas conservadas.', 'Mapa final de migração de outros URLs e demonstração visual de retorno pendentes.', 'URL antigo recupera a mesma fonte; login/retorno preservam a escolha; nova sessão recupera; nenhuma mistura entre projetos nem conversão de roteiro legado.'), ('07', 'Verificar imagens, anexos, links, pesquisa, permissões e duplicações, registando aceitação visual separadamente', 'Dois originais com ClamD real/hash; links dos consumidores200; pesquisahit/miss; permissões; repetição do importador sem novas linhas e associação idempotente.', 'Visual pendente; anexos/referências de restantes conteúdos e erro404 do legado por resolver individualmente.', 'Cada consumidor apresenta ficheiro/link/resultado íntegro; importação/pesquisa/recuperação/permissões verificadas e resultados visuais registados por ação.') ] rows = [r for r in rows if not r['ID'].startswith('AUD-LEG-')] for suffix, request, implementation, gap, acceptance in items: ident = 'AUD-LEG-' + suffix origin = 'USR-LEG-20261010-' + suffix row = {k: '' for k in headers} row.update({'ID': ident, 'Pedido/trecho': request, 'Fonte/ocorrências': 'Pedido direto do responsável,10/10/2026, integração do legado emQA §' + str(int(suffix)) + '; https://investemoz.tempo.co.mz/; legado-piloto-qa.md', 'Módulo': 'biblioteca / conteúdo público / percurso do investidor', 'Rotas': '/explorar/agronegocio; /agronegocio; /documentos/6; /investimentos/a-partir-de/agronegocio; /investimentos/{id}/conteudos', 'Implementação encontrada': implementation, 'Disponibilidade ativa': 'Não disponibilizado; guarda QA/flagfalse. Nenhuma escrita ou divulgação ativa autorizada.', 'Evidência funcional': evidence, 'Estado': 'parcial', 'Lacuna': gap, 'Dependência': 'Biblioteca/documentos existentes; Balcão+190000 para referência ao dossier; navegador para aceitação visual. Perfis/SLA são decisões independentes.', 'Critério de aceitação': acceptance, 'Prioridade': 'P1', 'Versões divergentes': 'Legado post171985 modificado2026-04-20; categoria Oportunidades preservada como metadado, entidade guia. Metadados/sitemapInfopublica e área de investimento distintos; não remapear por nome.', 'IDs das linhas de origem': origin, 'Quantidade de linhas de origem': '1', 'Critérios e versões por origem (JSON)': json.dumps([{'id': origin, 'pedido': request, 'fontes': ['Pedido direto10/10/2026 §' + str(int(suffix))], 'aceitacao': acceptance, 'estado': 'parcial', 'variantes': 'IntegraçãoprogressivaQA, sem substituição de pendências anteriores', 'modulo': 'legado/biblioteca/percurso'}], ensure_ascii=False), 'Interface': 'React preparado e buildQA; dados Inertia recuperados. Navegador indisponível; roteiro manual em legado-piloto-qa.md. Aceitação visual pendente.', 'API': 'GET públicos e escritas autenticadas no servidor18097, alvo real55441 confirmado; publicação separada do importador.', 'Gravação': 'Dois recursos na biblioteca existente e referências no payload cifrado do dossier; sem nova tabela/cadastro paralelo.', 'Recuperação': 'Logout/nova sessão FortifyMFA21: fonte/versão/hashes recuperados; outra conta negada e projeto independente sem referência.', 'Resultado apresentado': 'Dados retornam ao artigo/dossier/consumidores; render/cliques ainda não observados.', 'Consolidação': 'Novo pedido verificável incorporado à matriz existente; não duplica nem encerra requisitos históricos.'}) rows.append(row) for row in rows: if row['ID'] in ['AUD-LIB-01', 'AUD-LIB-02', 'AUD-DOC-01']: for key, value in {'Evidência funcional': evidence, 'Lacuna': 'Legado:50 versões observadas e sitemap30.170 entradas inventariados; cobertura histórica não encerrada. Visual/C37 pendentes.'}.items(): if tag not in row[key]: row[key] += ' | ' + tag + value if row['ID'] in ['AUD-OP-04', 'AUD-PROFILE-28']: modular = '[Experiência modular QA 10/10/2026] ' if modular not in row['Evidência funcional']: row['Evidência funcional'] += ' | ' + modular + 'QA42–48:sete modalidades dos quatro módulos percorrem rascunho→submissão→correção→revisão/gates→aprovação→divulgação restrita e nova sessãoMFA. QA49 entidade semexercícios com declaração/projeções identificadas. Recibos opportunity-modular-http-qa.json e opportunity-history-http-qa.json; C67 projeção de resumo corrigida. Extensões explicitamenteQA; legados36–39 preservados. Visual, materialidade, Response normativo e aprovação funcional/compromissosSLA pendentes; confirmação do legado não aprova esses pontos.' assert before == {r['ID']: {k: r[k] for k in protected} for r in rows if not r['ID'].startswith('AUD-LEG-')} assert len({r['ID'] for r in rows}) == len(rows) with matrix.open('w', encoding='utf-8-sig', newline='') as f: w = csv.DictWriter(f, fieldnames=headers); w.writeheader(); w.writerows(rows) counts = {'requirements': len(rows), 'matrix_rows': len(rows), 'source_occurrences': sum(int(r['Quantidade de linhas de origem']) for r in rows), 'states': dict(collections.Counter(r['Estado'] for r in rows))} (PRIVATE / 'legacy-audit-update-qa.json').write_text(json.dumps(counts, ensure_ascii=False, indent=2), encoding='utf-8') marker = 'qa-legacy-20261010' def prepend(name, content): import re path = DOC / name text = path.read_text(encoding='utf-8-sig') text = re.sub(r'.*?\s*', '', text, flags=re.S) first, _, rest = text.partition('\n') replacements = {'exclusivamenteQA': 'exclusivamente em QA', 'um sitemap de outro domínio': 'um sitemap de outro domínio', 'Perfismodulares/SLA': 'Perfis modulares/SLA', 'Legado:50': 'Legado: 50', 'ClamD real nos dois': 'ClamD real nos dois', 'imagemhistórica404': 'imagem histórica 404', 'I0–I3/C37': 'I0–I3/C37', 'comPromotorQA21': 'com Promotor QA21', 'LEG07': 'LEG07', 'porperfil': 'por perfil', 'links não recolhidos antesde': 'links não recolhidos antes de', 'imagemlegada': 'imagem legada', 'original recuperado comfundamentoou': 'original recuperado com fundamento ou', 'artigosconsumidores': 'artigos consumidores', 'visual/materialidade/Response/aprovaçãoformal': 'visual/materialidade/Response/aprovação formal', '50 exemplares': '50 exemplares', 'perfilmodular': 'perfil modular', 'folgaQA': 'folga QA', '173lacunasgeo': '173 lacunas geo', '(Mandie/Guroambíguo)': '(Mandie/Guro ambíguo)', '030000→040000→190000conservado': '030000 → 040000 → 190000 conservado', 'recursoslegado/cartografia/divulgação': 'recursos do legado/cartografia/divulgação', 'revisãocandidata/config/bundle': 'revisão candidata/config/bundle', 'backup/restauroda baseativa correspondente': 'backup/restauro da base ativa correspondente', 'rasculhosprivados': 'rascunhos privados', 'hash/idjáexistirem': 'hash/ID já existirem', 'conflitosaguardamrevisão': 'conflitos aguardam revisão', 'Cartografiaéoutrolote, nãoautopublicado': 'Cartografia é outro lote, sem publicação automática', 'ocultarflag/bundlecompatível': 'ocultar a flag/usar bundle compatível', 'originais, versões, fontes dosdossiers': 'originais, versões, fontes dos dossiers', 'baseativa+candidata': 'base ativa + candidata', 'Decisõesperfis/SLA,C37,Promotor21visual,A/Bcontinuamabertas': 'Decisões de perfis/SLA, C37, Promotor21 visual e A/B continuam abertas', 'ClamD real/hash': 'ClamD real/hash', 'doexemplar doartigoJSONno importadorexistente': 'do exemplar do artigo JSON no importador existente', 'MIMEapplication/json': 'MIME application/json', 'uploadJSONguardadoQA': 'upload JSON guardado em QA', 'originalpreservado': 'original preservado', 'hashverificado': 'hash verificado', 'publicaçãoautenticada': 'publicação autenticada', 'Doisrecursos; repetição semduplicação': 'Dois recursos; repetição sem duplicação', 'imagem original; observado404': 'imagem original; observado 404', 'Identificado noinventário': 'Identificado no inventário', 'PNGdoagronegócio': 'PNG do agronegócio', 'emartigosconsumidores dessaimagem': 'em artigos consumidores dessa imagem', 'summary`de500carateres': 'summary` de 500 carateres', 'a499+reticência': 'a 499 + reticência', 'depoisQA42–48divulgadas/recupe­radas noutraMFA. Visualpendente': 'depois QA42–48 divulgadas/recuperadas noutra sessão MFA. Visual pendente', 'conforme autorizaçãoexistente; gestorQA14executou': 'conforme autorização existente; gestor QA14 executou', 'C37nãofoiretestadonemencerrado': 'C37 não foi retestado nem encerrado'} for source, replacement in replacements.items(): content = content.replace(source, replacement) path.write_text(first + '\n\n\n' + content + '\n\n\n' + rest.lstrip(), encoding='utf-8') summary = f'''## Frente atual — legado agronegócio, exclusivamenteQA Piloto: **http://127.0.0.1:18097/explorar/agronegocio → /agronegocio → /investimentos/a-partir-de/agronegocio**. Artigo6 e imagem5 reutilizam biblioteca; leitura/pesquisa/imagem públicas, gravação privada autenticada. HTTP/persistência demonstrados: criação e continuação, logout/novaMFA21, recuperação, replay, isolamento e retorno com contexto. ClamD real nos dois originais. Uma oportunidade publicada no setor permanece independente; nenhuma oportunidade/geo inferida do artigo. Inventário: **92URLs**, **50 versões de artigos**, **2.826 referências**, **30.170 entradas anunciadas num sitemap de outro domínio**, sem afirmar recolha integral. Dois recursos importados; repetição sem duplicação. Fonte/autoria/datas/versão/hash e diferenças preservadas. C68:imagemhistórica404. Cobertura documental histórica permanece aberta. Mesma matriz: **{counts['requirements']} requisitos únicos = {counts['matrix_rows']} linhas lógicas; {counts['source_occurrences']} ocorrências de origem**. Estados: ''' + '; '.join(f'{n} {state}' for state, n in counts['states'].items()) + '''. Sete pedidos novosLEG01–07 incorporados, todos parciais. Nenhum critério anterior foi encerrado por esta frente. Referências visuais reais: CSS/chunkwelcome, fotografia original, Noto Sans/Serif locais no buildQA, paleta/cartões adaptados. **Navegador integrado indisponível; aceitação visual pendente**, com roteiro executável. C37/QA18, Promotor21 visual e A/B continuam abertos. Perfismodulares/SLA têm decisões independentes; a confirmação do legado não os aprova. Relatório e critérios: [legado-piloto-qa.md](legado-piloto-qa.md); inventários `legado-inventario.csv`, `legado-conteudos-reconciliados.csv`, `legado-referencias.csv`, `legado-sitemap-anunciado.csv`. Recibos privados sanitizados por percurso. Ativo não alterado; candidata ainda não fixada. Blocos anteriores são evidências históricas datadas, não conclusão global atual. Merece distinção o ensaio modularQA42–49 já demonstrado porHTTP, comvisual/materialidade/Response/aprovaçãoformal pendentes.''' prepend('relatorio-executivo.md', summary) prepend('backlog.md', '''## Próximo lote — expansão do legado sem substituirI0–I3/C37 | Prioridade / requisito | Próxima ação concreta | Critério de fecho | | --- | --- | --- | | P1 / AUD-LEG-02/03/04/06/07 | Roteiro visual do piloto comPromotorQA21: leitura anónima, pesquisa, fonte, criação/continuação, nova sessão, retorno e móvel/teclado. | Ações observadas por perfil/consumidor; mesmosIDs/dados, recuperação/negações e resultado visível. C37 continua independente. | | P1 / AUD-LEG-01/05; LIB01/02; DOC01 | Reconciliar os50 exemplares observados, paginação/URLs externos e sitemap misto; importar apenas próximo lote com hash/versão/scan e divulgação separada. | Inventário/deduplicação/citações e destinos explícitos; sem omitir nãoimportados nem publicar automaticamente. | | P1 / AUD-LEG-03/05; GEO | Conferir seis nomes/âmbitos de corredores com cadastros/publicações existentes. | Relação territorial inequívoca com citação/IDs; geometria disponível distinguida de uso visual. Nada de inferir parcelas ou coberturas pelo nome. | | P1 / AUD-LEG-07 / C68 | Rever imagemlegada GOVERNADOR-in.gif404 e outroslinks não recolhidos antesde expandir esses artigos. | Original recuperado comfundamentoou ausência identificada; não declarar ficheiro inexistente como válido. | | P1 / OP04 / PROFILE28 | Continuar Response integral/materialidade e cerimónia funcional; quatro módulosQA42–48 e nohistóricoQA49 têm provaHTTP, não fecho visual/formal. | Critérios especializados por categoria; revisões afetadas, decisões/external/publicação distintas. Confirmação legado não aprova perfis/SLA. | Países/origem, estatísticas,173lacunasgeo (Mandie/Guroambíguo), documentos por consumidor, recuperação e acessos/workflow restantes permanecem no backlog anterior. Próximo trabalho executável não depende de designar pessoas reais ou aprovar compromissosSLA. Plano global030000→040000→190000conservado; recursoslegado/cartografia/divulgação separados. Ver `legado-piloto-qa.md`.''') prepend('plano-ativacao.md', '''## Complemento legado — preparaçãoQA, sem autorização ativa Preservar **030000→040000→190000**, verificar ledger/schema/dependências no alvo antesde aplicar; não repetir instalações. Leitura editorial usa a biblioteca existente; associação ao dossier exige Balcão+190000. Perfismodulares não são pré-condição da leitura pública. Ordem deste complemento: demonstração visualQA/correções→revisãocandidata/config/bundle→backup/restauroda baseativa correspondente→upload/importação autenticados como rasculhosprivados→hash/duplicados/ClamD/metadados→associações fundamentadas→divulgação explícita por recurso→disponibilização pública/percurso. O CLI importador sóaplicaQA; a guarda atual do piloto impede ativo, mesmo comflagtrue. Habilitação ativa requer revisão/autorização própria. Contagem prevista: **1artigo+1imagem**, **3associações**, **0oportunidades ou geometrias criadaspeloartigo**, **0contas/dossiersQAtransferidos**; noopssehash/idjáexistirem, conflitosaguardamrevisão. Cartografiaéoutrolote, nãoautopublicado. Reversão:ocultarflag/bundlecompatível e retirar divulgação auditada; preservar originais, versões, fontes dosdossiers e histórico. BackupQA não substitui prova da baseativa+candidata. Decisõesperfis/SLA,C37,Promotor21visual,A/Bcontinuamabertas.''') prepend('casos-reproducao.md', '''## Casos adicionais preservados — fontes e consumidor novo - **C67 / OP04:** publicar a chamada de concessãoQA42 após aprovação. Esperado: projeção restrita persistida e legível. Observado inicial:HTTP503; rótulo de modalidade adicionado à síntese excedia coluna `summary`de500carateres. Causa no novo adaptador, não no middleware. Correção limita projeção a499+reticência e preserva texto canónico completo. Tentativa falhada arquivada; depoisQA42–48divulgadas/recupe­radas noutraMFA. Visualpendente. - **C68 / LEG07:** GET público legado `/images/ANO-2017/JUNHO/21/GOVERNADOR-in.gif`. Esperado: imagem original; observado404. Identificado noinventário; não afeta PNGdoagronegócio. Falha permanece emartigosconsumidores dessaimagem; não inventar substituição ou autenticidade. - **C69 / LIB02 / LEG01:** prévia doexemplar doartigoJSONno importadorexistente. Esperado: cadastro privado, scan e adaptação segura; observado MIMEapplication/json não suportado. Lista doimportador reconciliada e uploadJSONguardadoQA; originalpreservado, apresentação sanitizada, hashverificado, publicaçãoautenticada separada. Doisrecursos; repetição semduplicação. Não alargar divulgação ativa. Superutilizador27 recebeu403 em gestão documental, conforme autorizaçãoexistente; gestorQA14executou divulgação. Esse ensaio negativo comprova segregação e não é uma falha a contornar. C37nãofoiretestadonemencerrado.''') proposal = DOC / 'propostas-perfis-modulares-sla.md' text = proposal.read_text(encoding='utf-8') text = text.replace('a decisão QA seguinte tem precedência nos pontos identificados.', 'o ensaio técnico QA seguinte identifica as recomendações adotadas experimentalmente, sem decisão formal de ativação.') text = text.replace('## Recomendações aceites para execução exclusivamente em QA — 10/10/2026', '## Recomendações ensaiadas tecnicamente em QA — decisões próprias pendentes') text = text.replace('Esta decisão permite implementação e ensaios sintéticos; não declara os módulos completos nem aprova ativação.', 'A implementação e os ensaios sintéticos decorrem da autorização QA existente. As recomendações não são compromissos operacionais aprovados; a confirmação posterior sobre o legado refere-se somente ao legado e não decide perfis/SLA.') proposal.write_text(text, encoding='utf-8') sources = DOC / 'inventario-fontes.csv' source_rows = list(csv.DictReader(sources.open(encoding='utf-8-sig'))) for path in [proposal, DOC / 'legado-piloto-qa.md', DOC / 'legado-inventario.csv', DOC / 'legado-conteudos-reconciliados.csv', DOC / 'legado-referencias.csv', DOC / 'legado-sitemap-anunciado.csv']: data = path.read_bytes(); sha = hashlib.sha256(data).hexdigest() record = next((r for r in source_rows if r['Exemplar/caminho'].replace('\\', '/').endswith('/' + path.name)), None) if record is None: record = {k: '' for k in source_rows[0]}; source_rows.append(record) record['Exemplar/caminho'] = str(path) elif record['SHA256'] and record['SHA256'] != sha: record['Edição/versão identificada'] += ' | exemplar anterior SHA256=' + record['SHA256'] record.update({'Bytes': str(len(data)), 'SHA256': sha, 'Tipo': 'reconciliação auditoriaQA', 'Nota de leitura': 'Legado inventariado/pilotoQA; cobertura histórica e decisões separadas explicitamente preservadas.', 'Disponibilidade': 'Relatório/inventário para consulta; não habilita importação/publicação ativa', 'Uso em cálculo': 'Nenhum cálculo ativado', 'Situação jurídica/documental': 'Documento de programação/reconciliação, não fonte oficial', 'Edição/versão identificada': record['Edição/versão identificada'] + ' | atualizaçãoQA10/10/2026'}) with sources.open('w', encoding='utf-8-sig', newline='') as f: w = csv.DictWriter(f, fieldnames=list(source_rows[0])); w.writeheader(); w.writerows(source_rows) print(json.dumps(counts, ensure_ascii=True))