Files
Investidor-V2/tools/portal-audit-sector-result-prepare.py
T
nala 20971d523f
tests / ci (push) Canceled after 0s
oportunidades 6
2026-10-10 21:17:46 +02:00

270 lines
33 KiB
Python
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""Prepare targeted edits and reconcile existing reports from completed QA receipts, no database access."""
import collections
import csv
import hashlib
import json
import re
from datetime import datetime, timezone
from pathlib import Path
csv.field_size_limit(16000000)
ROOT = Path(__file__).resolve().parent.parent
DOC = ROOT / 'docs/investmoz/v2/auditoria-portal'
PRIVATE = ROOT / 'storage/app/private/investmoz-v2/portal-audit'
OUT = PRIVATE / 'legacy-sectors-finalize-20261010'
OUT.mkdir(exist_ok=True)
def read(name):
return json.loads((PRIVATE / name).read_text(encoding='utf-8'))
http = read('legacy-sectors-http-qa.json')
browser = read('legacy-sectors-browser-qa.json')
persistence = read('legacy-sectors-persistence-qa.json')
imported = read('legacy-sectors-apply-qa.json')
replay = read('legacy-sectors-replay-qa.json')
backup = read('profile-installation-backup-qa.json')
copy = json.loads((PRIVATE.parent / 'portal-qa-recovery/20261010_154445/damaged-copy-proof.json').read_text())
origin = read('legacy-financial-origin-20261010/proof.json')
assert http['result'] == browser['result'] == 'passed'
assert backup['restore_verified'] and backup['files_restore_verified'] and backup['target'] == imported['target']
assert copy['all_names_sizes_hashes_equal'] and len(persistence['cases']) == 8
assert datetime.fromisoformat(backup['verified_at']) >= datetime.fromisoformat(browser['completed_at'].replace('Z', '+00:00'))
assert origin['html_http_status'] == origin['inertia_http_status'] == 200 and origin['initial_origin_content_length'] == origin['inertia_origin_content_length'] == 0
assert imported['before']['opportunities'] == imported['after']['opportunities'] == 49
assert replay['before'] == replay['after'] and len(imported['resources']) == 22
stamp = datetime.now(timezone.utc).isoformat()
tag = '[Setores QA após C71 10/10/2026] '
rows = list(csv.reader((DOC / 'matriz-requisitos.csv').open(encoding='utf-8-sig', newline='')))
headers = rows[0]
canonical = {r[0]: dict(zip(headers, r)) for r in rows[1:]}
assert len(canonical) == len(rows) - 1 == 3393
backup_tables = len(backup['tables'])
backup_files = len(backup.get('files', []))
assert backup_tables == 295 and backup_files == 103
evidence = ('22 originais importados privadamente após prévia renovada, hashes recuperados,22 scans ClamD reais e replay sem novos documentos/recursos/auditoria/scans. '
'Divulgação autenticada separada:16 artigos com texto+5 imagens públicos; Mercado Financeiro privado, vazio confirmado na origem HTML e Inertia200. '
'Chromium desktop:16 artigos, pesquisa/imagens, Energia e Mineração distintas, C72 título preservado; QA21 criou4 dossiers, retorno, logout/novaMFA/F5/replay; QA23 negado403 e anónimo sem contexto. '
'Persistência READ ONLY:8 dossiers HTTP/Chromium, uma referência/evento cada,22 hashes únicos,0 relações geográficas automáticas. '
'legacy-sectors-{apply,replay,http,browser,persistence}-qa.json; legacy-financial-origin-20261010/proof.json. C37/QA18,A/B,móvel/acessibilidade/cobertura integral não encerrados.')
ops_evidence = (f'C71 corrigido somenteQA: novo cluster íntegro em diretório estável, mesma porta55441/base;295 tabelas/34.902 linhas byte-a-byte e31sequências preservadas. '
f'Schema/owners/ACL/triggers comparados semanticamente;2 constraints varchar/text e slots de coluna normalizados, sem afirmar DDL byte-a-byte. '
f'Cluster danificado original e cópia retidos:{copy["files"]} ficheiros, todos nomes/bytes/hashes iguais. '
f'Backup/restauro posterior ao lote:{backup["verified_at"]},295tabelas/103ficheiros,restauro em{backup["restored_target"]["database"]}:55441. '
'APP_KEY/MFA/conta18 preservados. Históricos QA preservados fisicamente, não todos restaurados no novo runtime. Provas portal-qa-recovery/20261010_154445 e profile-installation-backup-qa.json. Candidata global não fixada.')
updates = {}
for requirement in ['AUD-OPS-01'] + [f'AUD-LEG-{i:02d}' for i in range(1, 8)]:
old = canonical[requirement]
update = {}
update['Evidência funcional'] = old['Evidência funcional'] + ' | ' + tag + (ops_evidence if requirement == 'AUD-OPS-01' else evidence)
update['Consolidação'] = old['Consolidação'] + ' | ' + tag + 'Mesmo ID/referências/versões. Subcritérios deste lote demonstrados emQA; decisões e aceitações independentes mantidas.'
variants = json.loads(old['Critérios e versões por origem (JSON)'])
for variant in variants:
variant['incremento_setores_qa_20261010'] = {'atualizado_em': stamp, 'ambito': 'QA18097;base55441isolada',
'evidencia': ops_evidence if requirement == 'AUD-OPS-01' else evidence,
'estado_requisito_atual': 'parcial' if requirement == 'AUD-OPS-01' else old['Estado'],
'exclusoes': ['ativação', 'C37/QA18', 'A/B', 'móvel', 'acessibilidade', 'cobertura integral']}
update['Critérios e versões por origem (JSON)'] = json.dumps(variants, ensure_ascii=False, separators=(',', ':'))
if requirement == 'AUD-OPS-01':
update['Estado'] = 'parcial'
update['Lacuna'] = ('C71 técnico resolvido emQA; falta aceitação global C37/QA18,A/B,demais consumidores e revisão candidata. '
'Futura ativação exige snapshot/backup/restauro próprios do ativo e ações autenticadas;CLI--actor é apenas atribuiçãoQA. '
'Não repetir030/040/09-190naQA. | Histórico da falha e lacunas anteriores: ' + old['Lacuna'])
update['Critério de aceitação'] = ('Revisão/destino atual, backup/restauro integral correspondente, guarda/compatibilidade/reversão e ativação autenticada única; '
'QA atual295tabelas/103ficheiros demonstrada. Não substitui prova ativa. | Critério de versão anterior preservado: ' + old['Critério de aceitação'])
update['Gravação'] = old['Gravação'] + ' | ' + tag + 'Recuperação em destino vazio isolado; comparações e backup/restauro verificados,0escritasativas.'
update['Recuperação'] = old['Recuperação'] + ' | ' + tag + ops_evidence
update['API'] = old['API'] + ' | ' + tag + 'QA18097/login200 após retirada da manutenção; alvo real55441confirmado.'
update['Resultado apresentado'] = old['Resultado apresentado'] + ' | ' + tag + 'Manutenção retirada; percursos setoriais recuperados. Aceitação QA18 permanece independente.'
else:
for field in ['Interface', 'API', 'Gravação', 'Recuperação', 'Resultado apresentado']:
update[field] = old[field] + ' | ' + tag + evidence
pending = {
'AUD-LEG-01': '18/50 versões observadas cadastradas:17comtexto consultáveis e1vazia privada;32versões ainda não importadas, sitemap/paginação/cobertura histórica eC68pendentes.',
'AUD-LEG-02': 'Identidade aplicada e desktop dos novos setores observado; acessibilidade sistemática/móvel e restantes conteúdos pendentes. Editor Assunto opcional C74 sem ensaio visual próprio.',
'AUD-LEG-03': 'Quatro percursos setoriais com retorno e nova sessão demonstrados; relações territoriais concretas, ferramenta/Balcão/acompanhamento deA/B e demais perfis continuam independentes.',
'AUD-LEG-04': 'Consulta pública/proteção privada dos setores demonstradas. Disponibilização ativa,C37/QA18,A/B e aceitações manuais adicionais permanecem separadas.',
'AUD-LEG-05': 'Mesma biblioteca/versão/imagem reutilizada. Turismo editorial sem geometrias inferidas; relações territoriais e por etapa ainda exigem fundamento concreto.',
'AUD-LEG-06': '17caminhos comtexto preservados incluindo agronegócio e4novos retornos; restante mapaURL/aliases/acervo ainda incompleto. C37/QA18 eA/B não encerrados.',
'AUD-LEG-07': '22originais/5imagens/hashes/scans/replay e16novos artigos públicos testados;32versões restantes, anexos/links externos/C68,móvel/acessibilidade e perfis adicionais por testar.'}
update['Lacuna'] = pending[requirement] + ' | Histórico anterior: ' + old['Lacuna']
updates[requirement] = update
canonical[requirement].update(update)
packet = {'captured_at': stamp, 'files': []}
def table(name):
raw = (DOC / name).read_bytes()
return list(csv.reader(raw.decode('utf-8-sig').splitlines(keepends=True))), hashlib.sha256(raw).hexdigest()
def add(name, data, digest, patches, allowed, appended=None, source_hash_updates=None):
packet['files'].append({'filename': name, 'input_sha256': digest, 'rows': data, 'patches': patches,
'allowed_columns': allowed, 'append': appended or [], 'source_hash_updates': source_hash_updates or []})
data, digest = table('matriz-requisitos.csv')
add('matriz-requisitos.csv', data, digest, [{'row': i, 'values': updates[r[0]]} for i, r in enumerate(data) if i and r[0] in updates],
list(set(k for value in updates.values() for k in value)))
states = collections.Counter(r['Estado'] for r in canonical.values())
assert sum(states.values()) == 3393 and states['concluído e demonstrado'] == 7 and states['com falha'] == 0
data, digest = table('backlog-priorizado.csv')
bh = data[0]; patches = []; known = set()
for i, values in enumerate(data[1:], 1):
old = dict(zip(bh, values)); item = canonical[old['ID']]; known.add(old['ID'])
new = {'Prioridade': item['Prioridade'], 'Estado': item['Estado'], 'Pendência': item['Lacuna'],
'Dependência/pergunta': item['Dependência'], 'Aceitação': item['Critério de aceitação']}
if old['ID'] in updates:
new['Correção/componentes'] = 'Biblioteca/importador/LegacyContent/portal/legacy/percurso existentes; QA cluster/backup quandoOPS01; ver relatório atual.'
new['Ordem do próximo lote'] = 'C37/A-B e aceitações independentes; continuar expansão/documentos/países/cartografia sem reabrir fontes oficiais.'
changed = {k: v for k, v in new.items() if old[k] != v}
if changed: patches.append({'row': i, 'values': changed})
appended = []
for requirement, item in canonical.items():
if requirement not in known:
new = {'Prioridade': item['Prioridade'], 'ID': requirement, 'Estado': item['Estado'], 'Pendência': item['Lacuna'],
'Correção/componentes': item['Implementação encontrada'], 'Dependência/pergunta': item['Dependência'],
'Aceitação': item['Critério de aceitação'], 'Ordem do próximo lote': 'Critério individual; consultar backlog atual e matriz única'}
appended.append([new[h] for h in bh])
assert len(data) - 1 + len(appended) == 3393
add('backlog-priorizado.csv', data, digest, patches, [h for h in bh if h != 'ID'], appended)
manifest = json.loads((ROOT / 'storage/app/private/document-import-staging/legacy-setores-20261010/manifest.json').read_text(encoding='utf-8'))
resources = {r['external_id']: r for r in imported['resources']}
by_url = {r['source_url'].rstrip('/'): resources[r['external_id']] for r in manifest['documents']}
for filename in ['legado-inventario.csv', 'legado-conteudos-reconciliados.csv']:
data, digest = table(filename); h = data[0]; patches = []
for i, values in enumerate(data[1:], 1):
item = dict(zip(h, values)); row = None
if filename == 'legado-inventario.csv': row = by_url.get(item['URL'].rstrip('/'))
else:
row = next((r for r in resources.values() if r['external_id'].startswith('legacy-tempo-post-' + item['ID legado'] + '-')), None)
if row:
empty = row['external_id'].startswith('legacy-tempo-post-171994-')
status = (f'RecursoQA{row["id"]}; original/hash/ClamD/replay demonstrados. ' +
('Corpo vazio na origemHTML/Inertia200;privado,sem página publicada.' if empty else 'DivulgaçãoQAseparada;consumidorHTTP/desktop observado;não é oportunidade nem geometria.'))
changes = {'Situação': status}
if filename == 'legado-inventario.csv': changes['Destino proposto'] = f'biblioteca existente recursoQA{row["id"]};URLs/versão originais preservados'
else: changes['Recurso QA'] = str(row['id'])
patches.append({'row': i, 'values': changes})
add(filename, data, digest, patches, ['Situação', 'Destino proposto', 'Recurso QA'])
block = f'''<!-- qa-c71-sectors-result-20261010:begin -->
## Resultado atual — {stamp}
**QA18097 recuperada; nenhuma alteração na base ativa.** C71 técnico corrigido: faltavam26 ficheiros de catálogos partilhados, incluindo `global/2964`, necessários ao `pg_dump`. O agente/processo que os removeu continua desconhecido. O cluster danificado foi preservado e substituído por cluster novo em diretório estável, apenas no alvoQA55441. As295tabelas/34.902linhas e31sequências iniciais conferiram; schema/owners/ACL/triggers equivalentes após normalização explícita de2constraints varchar/text e slots de colunas removidas. A cópia física retida confere em{copy['files']}ficheiros por nomes, tamanhos ehashes; isso não afirma restauro lógico de todos os bancos históricosQA.
**Backup/restauro posterior ao lote:** {backup['verified_at']},295tabelas e103ficheiros privados conferidos em `{backup['restored_target']['database']}:55441`. APP_KEY, MFA e contas preservados. Não é prova de backup da base ativa nem fixa candidata global. QA/login voltou aHTTP200; manutenção arquivada. 2FAS Auth continua compatível com TOTP; a rejeição manualQA18 registada foi de palavra-passe antes do MFA, sem justificar troca de aplicação.
| Requisito / caso | Evidência deste lote | Lacuna concreta |
| --- | --- | --- |
| AUD-OPS-01 / C71 | Cluster saudável, dados/sequências/permissões preservados, backup/restauro renovado,0escritasativas. Estado global passa de com falha para parcial. | C37/QA18,A/B e outros consumidores; candidata e backup ativo/autorização própria. |
| AUD-LEG-01/05/07 |22ficheiros (17artigos+5imagens) importados privadamente, hashes recuperados,22scans ClamD reais; replay sem duplicações/eventos/scans novos. |32das50versões observadas continuam não importadas; sitemap misto/paginação/links externos/C68 e cobertura integral. |
| AUD-LEG-02/03/06 / C72 |16artigos novos comtexto públicos, título deInfraestruturas preservado. Chromium desktop: imagens/pesquisa/artigos→MFA21→4dossiers→retorno→logout→nova sessão→F5/replay. HTTP e persistência separados,8dossiers no total. | Móvel/acessibilidade/mais perfis e percursosA/B completos não encerrados. |
| AUD-LEG-04 |Leitura pública; guardar requer login. Anónimo/QA23 sem contexto privado; QA23recebe403 ao dossier alheio. Estado de consulta/proteçãoQA mantido concluído no âmbito demonstrado. | Disponibilização ativa e demais aceitações são independentes. |
| Mercado Financeiro |OrigemHTML e respostaInertiaHTTP200 contêm corpo vazio; exemplar22 no lote fica privado na biblioteca (recurso23). `/mercado-financeiro` e respetiva exploração404,sem página vazia apresentada como entrega. | Obter conteúdo real/versão comtexto; não usar Fintech nem inventar corpo. |
| C74 |Imagens semAssunto eram recusadas porPUT422. QA aceita tema vazio sem inferir setor; interface tornaAssunto opcional. Cinco metadados vazios guardados/recuperados; teste dedicado1/11. | Editor administrativo sem ensaio visual próprio; alteração protegida pela guardaQA. |
**Contagens canónicas:**3393requisitos/3393linhas,6845ocorrências de origem; {states['parcial']}parciais,145não implementados,5dependentes de decisão,1implementado mas não ativado,7concluídos e demonstrados,0com falha. Casos adicionais e aceitações abertas continuam visíveis dentro dos requisitos parciais;0com falha global não significa portal sem erros. BacklogCSV é projeção reconciliada dos mesmos3393IDs, incluindo39 anteriormente omitidos, não outra matriz.
**RotasQA:** `/explorar/energia-mineracao` → `/energia-mineracao`, `/energia`, `/mineracao`; `/explorar/turismo` → `/turismo` e11artigos territoriais; `/explorar/infraestruturas` → `/infraestruturas`. Agronegócio preservado. Energia/Mineração são fontes distintas; Turismo mantém texto editorial e0associações territoriais automáticas. Todos os dados de fonte/autoria/datas/hash/URLs/versões originais são conservados. Os artigos não criaram oportunidades (49antes/depois), geometrias ou perfis. Divulgação foi uma ação autenticada do gestorQA14 pelo mecanismo existente, separada da importaçãoCLIQA (atribuição `--actor`, não autenticação ativa); nenhum desafioMFA foi observado nessa conta. QA21/QA23 usaramMFA normalmente.
**Próximo trabalho executável:** restantes32versões/acervo eC68 por ficheiro; Response/§27/validações, PDFs/derivadas epaíses/origem/estatísticas por consumidor;173correspondências territoriais ainda abertas comMandie/Guroambíguo. Não acrescentar jornadas educativas para contornar essas pendências. Perfis modulares eSLA conservam decisões próprias. C37/QA18,A/B,móvel/acessibilidade permanecem abertos; este lote não demonstraCRUD/workflow de oportunidades nem os encerra.
**Ativação futura:** revisão/hashes/flags e aceitação global→leitura/backup/restauro do ativo correspondente→código/build compatíveis com flags desligadas→030000→040000→09/190000 somente se ausentes→importação privada de conteúdo/cartografia com prévia e scan→divulgação separada→disponibilização incremental. QA já tem as3instalações;não as repetir. Previsão desta extensão:22originais (reutilizar hashes existentes),21potencialmente divulgáveis e1exemplar vazio privado;0contas/dossiers/opportunities/geometrias transferidos. O CLIrecusa aplicação ativa: falta preparar/demonstrar uma ação autenticada emlote que preserve exatamente as identidades/versionamento existentes; upload unitário não foi provado equivalente. Cartografia:408candidatas/173pendentes no retratoQA anterior, não importadas por esta frente. `ready=true` continua preparação, não instalação/disponibilização.
**Reversão:** retirar divulgação em ações auditadas e ocultar consumidores/flag, preservando originais, versões, consentimentos/referências eeventos. Restauro técnico usa o novo dump íntegro e chave estável numa QA distinta,comparação antes de corte;não voltar ao cluster danificado. Nunca drop de tabelas utilizadas nem copiar fixturesQA para o ativo. Provas: `legacy-sectors-{{apply,replay,http,browser,persistence}}-qa.json`, `legacy-financial-origin-20261010/proof.json`, `portal-qa-recovery/20261010_154445/*-proof.json` e `profile-installation-backup-qa.json` (privadas,sem credenciais/cookies/códigos nas capturas).
**Versões históricas preservadas abaixo:** frases sobreC71aberto,22ficheiros apenas preparados ou rotas apenas deagronegócio descrevem os incrementos anteriores e são substituídas, para o estado atual, pelas provas desta secção.
<!-- qa-c71-sectors-result-20261010:end -->
'''
def legible(value):
value = re.sub(r'\b(as|As|de|e|os|em|por)(?=\d)', r'\1 ', value)
value = re.sub(r'(\d+)(tabelas|linhas|ficheiros|sequências|constraints|instalações|artigos|imagens|itens|originais|versões|referências|eventos|scans|dossiers|requisitos|ocorrências|parciais|correspondências|nomes|exemplar|contas|pendentes|candidatas|não|dependentes|implementado|concluídos|com|das|novos|potencialmente)', r'\1 \2', value)
for old, new in {'comtexto': 'com texto', 'semt​exto': 'sem texto', 'sem página': 'sem página', 'original/hash/ClamD': 'original / hash / ClamD',
'semcontorno': 'sem contorno', 'semcontas': 'sem contas', 'apósguarda': 'após guarda', 'porficheiro': 'por ficheiro',
'preservedos': 'preservados', 'privadas,sem': 'privadas, sem', 'ativos,sem': 'ativos, sem',
'21divulgados': '21 divulgados', '1artigo': '1 artigo', 'comQA21': 'com QA21',
'QA23negado403': 'QA23 negado 403', 'das50': 'das 50', 'eC68imagem404': 'e C68 (imagem 404)',
'19etapas': '19 etapas', '3393IDs': '3393 IDs', '39estavam': '39 estavam',
':22': ': 22', ':3393': ': 3393', ',6845': ', 6845', ';3235': '; 3235', ',145': ', 145',
',5 dependentes': ', 5 dependentes', ',1 implementado': ', 1 implementado', ',7 concluídos': ', 7 concluídos',
',0 com falha': ', 0 com falha', 'CLI--actor': 'CLI `--actor`',
'sequência030→040→09/190': 'sequência 030000 → 040000 → 09/190000'}.items():
value = value.replace(old, new)
return value
block = legible(block)
backlog_block = f'''<!-- qa-c71-sectors-result-20261010:begin -->
## Prioridades atuais após C71 — {stamp}
**QA recuperada e lote setorial demonstrado; base ativa fora do âmbito.** C71 técnico corrigido, com backup/restauro final de295tabelas/103ficheiros. AUD-OPS-01 volta a parcial, porque aceitação global e ativação continuam pendentes. Biblioteca:22novos originais importados/scaneados;21divulgados por ação separada e1artigo vazio privado. Chromium desktop comQA21:4dossiers criados, retorno, logout/novaMFA/F5/replay; QA23negado403. Ver relatório executivo e provas individuais.
| Prioridade / requisito | Falta e próxima ação | Critério concreto |
| --- | --- | --- |
| P0 / AUD-UI-01,C37/QA18 | Incorporar tentativa manual sanitizada; primeira rejeição atual foi de palavra-passe, antes do MFA. 2FAS continua compatível; sem nova aplicação ou reset de segredo. | MFA→`/investimentos`→F5→logout→novo login na sessão problemática; não usar QA21 como substituto. |
| P1 / AUD-LEG-01/06/07 |32das50versões ainda não importadas, sitemap/paginação/acervo eC68imagem404. Reconciliar próximo lote por identidade/hash/citação. | Prévia, armazenamento privado, scan real, replay/recuperação e divulgação separada; não inventar corpo de Mercado Financeiro. |
| P1 / AUD-LEG-02/03/07 | Móvel/acessibilidade/mais perfis e editor administrativo C74 sem ensaio visual próprio; restantes ligações/consumidores. | Prova de cada ação visível e permissão; preservar confirmação manual limitada do piloto e diferenças entre versões. |
| P1 / percurso,I0–I3,A/B | Cenários completos com ferramenta→resultado→Balcão→acompanhamento, e ações do dashboard/workflow continuam independentes. | Cinco fases/19etapas, nova sessão, preparação paralela, isolamento/permissões, retorno ao dossier. |
| P1 / países e cartografia | Completar países/origem/filtros/estatísticas por formulário e173correspondências territoriais; Mandie/Guro permanece ambíguo. | UI→API→gravação→recuperação→resultado por consumidor; território≠geometria≠mapa. |
| P1 / documentos,Response/§27 | Completar condições semânticas, PDFs/exportações/derivadas por consumidor. | Scan próprio, originais/hash preservados, permissões e resultados recuperáveis. |
| P1/P2 / decisões e ativação | Perfis modulares/SLA, calendário/nomeações reais e candidata permanecem separados. Preparar ação autenticada de importação em lote preservando identidade:CLI--actor sóQA. | Decisão concreta por regra; backup/restauro da base ativa da candidata e sequência030→040→09/190 sem repetições. |
**Mesma matriz:**3393requisitos/linhas,6845ocorrências;3235parciais,145não implementados,5dependentes de decisão,1implementado mas não ativado,7concluídos e demonstrados,0com falha global. O backlogCSV agora projeta todos os3393IDs;39estavam omitidos. Casos/aceitações dentro dos parciais continuam abertos. As secções abaixo são histórico anterior à recuperação/importação, não o estado atual.
<!-- qa-c71-sectors-result-20261010:end -->
'''
activation_block = f'''<!-- qa-c71-sectors-result-20261010:begin -->
## Preparação atual após recuperação e lote setorial — {stamp}
C71 corrigido emQA: comparação integral e backup/restauro renovado {backup['verified_at']} ({backup_tables}tabelas/{backup_files}ficheiros), em `{backup['restored_target']['database']}:55441`. Esta prova não é backup do ativo nem fixa candidata. C37/QA18,A/B,móvel/acessibilidade/outros consumidores e decisões próprias permanecem pendentes.
1. Concluir as aceitações da revisão; fixar código/manifesto/configuração e hashes. A guarda atual do legado continua restrita aQA; código preparado não é habilitação ativa.
2. Conferir o alvo ativo por leitura e provar backup/restauro próprio dessa revisão, incluindo ficheiros e chave estável. O último retrato ativo anterior continua histórico.
3. Disponibilizar código/build compatíveis com flags desligadas; preservarURLs,IDs,roteiros/consentimentos. Não copiar contas ou dossiers sintéticos.
4. Prévia e aplicação autenticadas030000→040000→**2026_10_09_190000_prepare_investor_path**, apenas se ausentes e coerentes. QA já tem as3: não repetir. O Balcão07/190 não substitui a extensão09/190; dependências/efeitos e bootstrap separado estão na tabela histórica abaixo.
5. Conteúdo: prévia/hash/identidade/versão, armazenamento privado eClamD. **22candidatos do lote (17artigos+5imagens)**;17artigos comtexto no total incluindo piloto anterior,1exemplar vazio privado. Reutilizarbytes/identidades já existentes; qualquer conflito fica pendente. A CLI com--actor é apenas ensaioQA, não instalação autenticada ativa. Ainda falta preparar/demonstrar um ponto autenticado de importação emlote com as mesmas identidades; upload unitário não foi demonstrado equivalente.
6. Cartografia emlote distinto, por pais e correspondências inequívocas; retratoQA408candidatas/173pendentes, Mandie/Guroambíguo.0geometrias importadas nesta frente; medir novamente no alvo. Publicação das camadas separada.
7. Divulgar os conteúdos autorizados em ação própria: previsão21recursos deste lote; Mercado Financeiro permanece privado/vazio e nunca apresentado como página concluída. QA demonstrou divulgação autenticada do gestor14, sem presumir MFA não observado. Aprovação/consulta/cálculo/decisão externa são estados independentes.
8. Disponibilizar consumidores/percurso incrementalmente após provas;0fixturesQA transferidos. `ready=true` não instala, não publica e não fecha requisitos.
**Reversão:** retirar divulgação e ocultar consumidores/flags com auditoria, mantendo originais, versões, fontes dos dossiers eeventos. Preferir reconciliação/restauro numaQA distinta comdump íntegro/chave preservada antes de corte; não reativar o cluster danificado nem eliminar tabelas utilizadas. Conservados o original danificado e a cópia física de{copy['files']}ficheiros (nomes/tamanhos/hashes iguais), sem afirmar restauro lógico de todos os históricos no novo runtime. Plano não executado na base ativa.
As secções abaixo preservam a evolução anterior; referências aC71aberto ou lote apenas preparado foram superadas pelas provas atuais, sem apagar dependências independentes.
<!-- qa-c71-sectors-result-20261010:end -->
'''
reports = {'relatorio-executivo.md': block, 'legado-piloto-qa.md': block, 'backlog.md': legible(backlog_block), 'plano-ativacao.md': legible(activation_block)}
for filename, current_block in reports.items():
path = DOC / filename
original = path.read_text(encoding='utf-8')
archive = OUT / (filename + '.before')
if not archive.exists(): archive.write_text(original, encoding='utf-8')
original = re.sub(r'<!-- qa-c71-sectors-result-20261010:begin -->.*?<!-- qa-c71-sectors-result-20261010:end -->\s*', '', original, flags=re.S)
title, body = original.split('\n', 1)
path.write_text(title + '\n\n' + current_block + '\n' + body.lstrip(), encoding='utf-8')
case_path = DOC / 'casos-reproducao.md'
case = case_path.read_text(encoding='utf-8')
case_block = f'''<!-- qa-c71-sector-cases-20261010:begin -->
## Estado dos casos C71–C76 após o lote — {stamp}
**C71 / AUD-OPS-01:** causa/operação/reprodução históricas preservadas abaixo. CorrigidoQA: recuperação emcluster novo, comparação295tabelas/34.902linhas/31sequências e schema/permissões compatíveis; cópia física{copy['files']}ficheiros com hashes iguais. Prévia22itens renovada antes de aplicação; backup/restauro final295/103verificado. Processo que removeu26catálogos continua não identificado; nenhum remendo de catálogo ou rotação de chave. Requisito inteiro parcial pelas demais dependências/ativação. A manutenção foi retirada; GET/login200no18097. Ver provas no relatório atual.
**C72 / AUD-LEG-02/07:** título deInfraestruturas desaparecia pelo sanitizador excluirh1/filhos; h1→h2 preserva conteúdo e limpeza. Agora exemplar importado/scaneado/consultável e título observado no Chromium, captura `infra-original-heading`; original/hash intacto. CorrigidoQA, não ativo.
**C73 / AUD-UI-01/C37:** a manutenção técnica é independente das3rejeições de palavra-passe15:38–15:39UTC; estas ocorreram antes do MFA. QA18secret/chave/credencial reconfirmados15:57:57porREADONLY. 2FAS Auth já suportaTOTP; não requer nova aplicação/remoção da entrada antiga. C37continua pendente no navegador do responsável, sem novo ensaio MFA18 deste lote.
**C74 / AUD-LEG-07, consumidor documental:** reproduzir comimagem do manifesto semtema: login gestorQA14→PUT `/management/portal-services/resources/24` comAssuntovazio→HTTP422campo`topic`. Esperado: guardar metadados originais autorizados seminventarassociação. Causa: importador admite vazio, validaçãoAPI/frontend exigiamvalor. CorreçãoQA: tema nullable sobguarda realQA, vazio normalizado para stringcompatível; interfaceAssuntoopcional. RepetiçãoPUTs das5imagens+recuperaçãoSQL/download passaram; originais/hashes/scans/ausência de relações preservados. Teste dedicado1/11asserções; visualdoeditor ainda pendente. DivulgaçãoPOSTseparada, semcontorno da autorização.
**C75 / AUD-LEG-02/03, regressão detectada antes da divulgação:** GET `/explorar/agronegocio` no testeLegacyContentTest retornava500 na nova parametrização, `Undefined property stdClass::$title`. Causa: projeçãoSQL selecionava documento mas omitia título do recurso usado nas leituras relacionadas. Corrigido incluindo`r.title`; teste4/81eHTTP/Chromium passaram. Não atribuir ao middleware. Nenhuma ocorrência ativa alegada.
**C76 / AUD-LEG-03/06, regressão detectada antes da divulgação:** POST `/investimentos` comartigo válido retornava422. Esperado: validar ID da biblioteca e guardar fonte numdossier próprio. Causa: `value(d.external_id)`sobrebuilder comprojeção pré-existente lia primeiro campoUUID; não encontrava a identidade configurada. Correção: leitura explícita`first()?->external_id`, preservando controles público/hash/scan/contexto. Testes4/81; HTTP4dossiers/Chromium4dossiers eREADONLY8referências únicas. CorrigidoQA, nenhuma escrita ativa.
Falhas do harness conservadas separadamente: primeira leitura usou indevidamente rota pública de download para exemplar privado404 (a rota protegida correta é `/management/documents/{{UUID}}/download`); recompilação durante sessãoInertia produziu409/reload esperado; primeiras esperasnetworkidle/imagem ainda não decodificada falharam sob concorrência. Corrigida ordenação/espera no teste, sem alterar autenticação/aplicação para fabricar sucesso. Histórico de tentativasprivadas preservado; resultado final sófoi declarado após percursos completos.
<!-- qa-c71-sector-cases-20261010:end -->
'''
case = re.sub(r'<!-- qa-c71-sector-cases-20261010:begin -->.*?<!-- qa-c71-sector-cases-20261010:end -->\s*', '', case, flags=re.S)
title, body = case.split('\n', 1)
case_path.write_text(title + '\n\n' + legible(case_block) + '\n' + body.lstrip(), encoding='utf-8')
data, digest = table('inventario-fontes.csv'); h = data[0]; patches = []; source_updates = []
for i, values in enumerate(data[1:], 1):
item = dict(zip(h, values)); exemplar = Path(item['Exemplar/caminho'])
if exemplar.parent == DOC and exemplar.name in ['legado-piloto-qa.md', 'legado-inventario.csv', 'legado-conteudos-reconciliados.csv']:
patches.append({'row': i, 'values': {'Nota de leitura': 'C71 recuperado e 22 originais QA importados/scaneados; 21 divulgados separadamente; 16 novos artigos e 4 dossiers demonstrados. Históricos/restrições preservados. | Nota anterior: ' + item['Nota de leitura'],
'Edição/versão identificada': item['Edição/versão identificada'] + ' | loteQAsetores' + stamp + '; exemplar anteriorSHA256=' + item['SHA256']}})
source_updates.append({'row': i})
add('inventario-fontes.csv', data, digest, patches, ['Nota de leitura', 'Edição/versão identificada', 'SHA256', 'Bytes'], source_hash_updates=source_updates)
(OUT / 'csv-edits.json').write_text(json.dumps(packet, ensure_ascii=False), encoding='utf-8')
(OUT / 'counts.json').write_text(json.dumps({'requirements': len(canonical), 'rows': len(rows) - 1, 'source_occurrences': 6845,
'states': dict(states), 'canonical_backlog_missing_rows_added': len(appended), 'active_writes': 0}, ensure_ascii=False, indent=2), encoding='utf-8')
print(json.dumps({'prepared_csv_tables': 5, 'matrix_rows': len(updates), 'backlog_missing_rows': len(appended), 'states': dict(states), 'database_access': False}, ensure_ascii=False))