from pathlib import Path from datetime import datetime, timezone from collections import Counter import hashlib, json, csv, zipfile R=Path(__file__).resolve().parent.parent;P=R/'storage/app/private/investmoz-v2/portal-audit' O=R/'outputs/investmoz-workflow-execution-preparation-20261011' C=R/'outputs/investmoz-workflow-consolidated-20261011' load=lambda p:json.loads(p.read_text(encoding='utf-8-sig')) sha=lambda p:hashlib.sha256(p.read_bytes()).hexdigest() candidate=load(P/'consolidated-candidate-latest.json');manifest=load(C/'manifesto.json') old_final=load(C/'verificacao-final.json') for name,expected in old_final['delivery_files'].items():assert sha(C/name)==expected,name assert sha(C/'manifesto.json')==candidate['manifest_sha256'] assert sha(C/candidate['zip'])==candidate['zip_sha256'] with zipfile.ZipFile(C/candidate['zip']) as z: assert len(z.namelist())==181 for name,expected in manifest['payload'].items():assert hashlib.sha256(z.read(name)).hexdigest()==expected,name for name,expected in manifest['base_hashes'].items(): path=R/name;assert (sha(path) if path.is_file() else None)==expected,name for name,expected in manifest['unchanged_dependencies'].items():assert sha(R/name)==expected,name prior=load(P/'consolidated-preservation-before.json') # The earlier preservation baseline retains fixed98/delta117/service plans and monitored active runtime files. preservation={} for category,folder in [('fixed_candidate','investmoz-workflow-upgrade-20261011'),('delta_reference','investmoz-service-followup-20261011'),('service_plan','investmoz-workflow-service-plan-20261011')]: entries=prior[category] for name,expected in entries.items():assert sha(R/'outputs'/folder/name)==expected,name preservation[category]={'files':len(entries),'changed':0} active=load(P/'service-followup-preservation-before.json')['active_hashes'] for name,expected in active.items():assert sha(R/name)==expected,name preservation['active_monitored_files']={'files':len(active),'changed':0} backup=load(P/'workflow-execution-target-backup.json');compat=load(P/'workflow-execution-preflight-qa.json') assert backup['result']==compat['result']=='passed' assert backup['runtime']['organization_country_enabled'] for flag in ['opportunity_dashboard_access_enabled','profile_access_enabled','opportunity_specialized_drafts_enabled','opportunity_modular_profiles_qa_enabled','legacy_content_qa_enabled','investor_path_enabled']: assert not backup['runtime'][flag],flag group=Counter(name.split('/')[0] for name in backup['active_files_manifest']) for filename in ['relatorio-preparacao.md','procedimento-unico.md']: path=O/filename;text=path.read_text(encoding='utf-8') replacements={ ',867':', 867', '**:faltam':'**: faltam', 'PHP,XML':'PHP, XML', 'upload20M':'upload 20M', 'POST24M':'POST 24M', 'memória256M':'memória 256M', 'execução180s':'execução 180 s', 'permanece128M':'permanece 128M', 'lease210s':'lease 210 s', 'Preservar050000':'Preservar 050000', 'confirmou030000':'confirmou 030000', 'PHP/scanner,4':'PHP/scanner, 4', 'assinaturas,2':'assinaturas, 2', 'COUNTRY02 ON,050000':'COUNTRY02 ON, 050000', 'do plano:`':'do plano: `', 'Prévia409':'Prévia 409', 'de050000':'de 050000', 'ClamD),RunSimulation':'ClamD), RunSimulation', 'local),CompareMneBusiness':'local), CompareMneBusiness', 'OFF;030000':'OFF; 030000', 'duplicações,7':'duplicações, 7', 'PORTAL_PROFILE_ACCESS_ENABLED,PORTAL':'PORTAL_PROFILE_ACCESS_ENABLED, PORTAL', 'Scanner 3310 atualizado, tarefas/ACLs':'A atualização do scanner 3310, tarefas/ACLs', 'Composição: '+str(dict(group))+'.': f'Composição: {group["private"]} originais privados + {group["runtime"]} ficheiros de runtime/configuração/bundle ativo + {group["active-code"]} de código ativo + {group["tls"]} TLS + {group["apache"]} Apache + {group["services"]} de serviços.' } for before,after in replacements.items():text=text.replace(before,after) path.write_text(text,encoding='utf-8') tag='' block=f'''{tag} ## Preparação técnica e habilitação operacional separadas [Relatório da preparação](../../../../outputs/investmoz-workflow-execution-preparation-20261011/relatorio-preparacao.md), [proposta única de execução](../../../../outputs/investmoz-workflow-execution-preparation-20261011/procedimento-unico.md), [backup do destino](../../../../outputs/investmoz-workflow-execution-preparation-20261011/backup-destino-verificado.json) e [designações operacionais](../../../../outputs/investmoz-workflow-execution-preparation-20261011/designacoes-operacionais.md). Candidata **41caea0f900f**, código **117d2d889701**, ZIP/manifesto imutáveis; 98a8e0b70892 e o plano histórico B1/B2 preservados. O responsável confirmou que ID 2/nala@tempo.co.mz e ID 13/onala@me.com são **duas pessoas diferentes**. Identificação e independência dos titulares resolvidas; nenhum bootstrap/grant ativo executado. Backup/restauro do **destino real investemoz:5432** renovado **{backup['verified_at']}**, restaurado somente no novo clone55441: **288 tabelas/31 sequências/867 ficheiros**,4 originais/0 ausentes/2 MFA legíveis, linhas/seq/bytes iguais. Mais68 ficheiros de serviços face aos799, com scanner/config/assinaturas/PHP/XML e metadados de986 ACLs. DACLs/tarefas recuperadas como ficheiros/metadados, não aplicadas no Windows ativo. Compatibilidade e reversão vazia030000→040000 passaram no novo clone:288→291→295 tabelas,5 colunas; todas as linhas/colunas/seq e050000 preservadas. Não é instalação autenticada no destino. **Uma autorização técnica proposta:**código/bundle+B1/B2+030000→040000 autenticadas+bootstrap cruzado dos dois titulares. Manter COUNTRY02 ON e os fluxos operacionais OFF. Readiness preparado apenas em armazenamento privado de recuperação, ainda não disponibilizado aos instaladores do destino. Fila ativa lida sem consumo/notificações externas; nova leitura imediatamente antes do worker, parar perante NotifyInterestReceived ou classe desconhecida. Intervenção no destino continua por autorizar. **Habilitação operacional separada:**faltam designações de Promotores,Validadores e Publicadores, com conta existente,organização/representação legítima,tipos e âmbito. Isso condiciona a abertura dos fluxos, não a preparação/instalação técnica. As provas de LocalService,bootstrap,coedição,categorias,A/B e C37 válidas foram reaproveitadas. Nenhuma nova prova de dispositivos reais/acessibilidade integral foi declarada. Restauração/partilha individual,190000,legado,ofertas/modulares/SLA permanecem fora; restauro completo do destino exige decisão própria. Mesma matriz:3401 requisitos/7739 ocorrências; três células de evidência atualizadas, nenhum ID/fonte/estado geral alterado. Os blocos seguintes preservam o histórico; as pendências de backup e de independência neles referidas ficam reconciliadas por este bloco atual. ''' for a,b in {'clone55441':'clone 55441', '**,4':'**, 4','Mais68':'Mais 68','aos799':'aos 799', 'de986':'de 986','vazia030000':'vazia 030000', ':288':': 288', ',5':', 5','e050000':'e 050000', '**:código':'**: código','**:faltam':'**: faltam', 'Promotores,Validadores':'Promotores, Validadores', 'existente,organização':'existente, organização', 'legítima,tipos':'legítima, tipos', 'LocalService,bootstrap,coedição,categorias,A/B':'LocalService, bootstrap, coedição, categorias, A/B', 'individual,190000,legado,ofertas':'individual, 190000, legado, ofertas', 'matriz:3401':'matriz: 3401', 'ficheiros**,4':'ficheiros**, 4'}.items():block=block.replace(a,b) for name in ['backlog.md','relatorio-executivo.md','plano-ativacao.md','workflow-criterios-saida-20261011.md']: path=R/'docs/investmoz/v2/auditoria-portal'/name;text=path.read_text(encoding='utf-8') assert tag not in text path.write_text(block+text,encoding='utf-8') csv.field_size_limit(16777216) old=load(P/'workflow-execution-matrix-baseline.json')['rows'] new=list(csv.reader((R/'docs/investmoz/v2/auditoria-portal/matriz-requisitos.csv').open(encoding='utf-8-sig',newline=''))) assert len(new)==len(old)==3402 and new[0]==old[0] diff=[] for a,b in zip(old,new): assert len(a)==len(b) for c,(x,y) in enumerate(zip(a,b)): if x!=y: assert new[0][c]=='Evidência funcional' and b[0] in ['AUD-OPS-01','AUD-PROFILE-15','AUD-PROFILE-31'] diff.append({'id':b[0],'column':new[0][c]}) assert len(diff)==3 attestation=load(O/'manifesto-preparacao.json') attestation['files']={p.name:sha(p) for p in sorted(O.iterdir()) if p.is_file() and p.name not in ['manifesto-preparacao.json','verificacao-final.json']} (O/'manifesto-preparacao.json').write_text(json.dumps(attestation,indent=2,ensure_ascii=False),encoding='utf-8') latest=load(P/'workflow-execution-preparation-latest.json');latest['preparation_manifest_sha256']=sha(O/'manifesto-preparacao.json') (P/'workflow-execution-preparation-latest.json').write_text(json.dumps(latest,indent=2),encoding='utf-8') result={'result':'passed','verified_at':datetime.now(timezone.utc).isoformat(),'candidate_revision':candidate['revision'], 'candidate_code_and_bundle_unchanged':True,'fixed_candidate_deliverables_preserved':len(old_final['delivery_files']), 'earlier_references_preserved':preservation,'destination_backup_physically_restored_only_in_QA':True, 'destination_tables':288,'destination_sequences':31,'restored_files':867,'originals':4,'missing':0,'MFA_readable':2, 'new_snapshot_compatibility_and_empty_transactional_reversal_passed':True, 'independence_confirmed_by_owner_for_existing_accounts':[2,13], 'matrix':{'requirements':3401,'source_occurrences':sum(int(dict(zip(new[0],r))['Quantidade de linhas de origem']) for r in new[1:]), 'changes':diff,'new_ids':0,'states_sources_versions_preserved':True}, 'single_technical_authorization_proposed_not_granted':True,'operational_designations_separate':True, 'COUNTRY02_enabled':True,'other_flows_disabled':True,'active_database_services_acl_or_flag_writes':0, 'preparation_manifest_sha256':sha(O/'manifesto-preparacao.json')} (O/'verificacao-final.json').write_text(json.dumps(result,indent=2,ensure_ascii=False),encoding='utf-8') print(json.dumps({k:result[k] for k in ['result','candidate_revision','destination_tables','destination_sequences','restored_files','independence_confirmed_by_owner_for_existing_accounts','active_database_services_acl_or_flag_writes']}))