Files
Investidor-V2/tools/portal-consolidated-finalize.py
nala 287e991486
tests / ci (push) Waiting to run
oportunidades 7
2026-10-13 11:40:29 +02:00

122 lines
12 KiB
Python
Raw Permalink 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.
from pathlib import Path
import json,hashlib,csv,shutil,zipfile
from datetime import datetime,timezone
from collections import Counter
R=Path(__file__).resolve().parent.parent;P=R/'storage/app/private/investmoz-v2/portal-audit'
O=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()
manifest=load(O/'manifesto.json');revision=manifest['revision'][:12]
preview=load(P/'consolidated-preview-qa.json');holder=load(P/'consolidated-holder-compatibility-qa.json')
assert preview['result']=='passed' and holder['result']=='passed'
assert len(preview['screenshots'])==1
shots=O/'capturas-qa';shots.mkdir(exist_ok=True)
shot=R/preview['screenshots'][0];assert shot.is_file()
shutil.copyfile(shot,shots/shot.name)
public_preview={k:preview[k] for k in ['result','started_at','base','target','browser','checks','active_writes']}
public_preview['screenshot']='capturas-qa/'+shot.name
public_preview['test_harness_correction']='First attempted legacy manager14 after initialized pair received403; corrected authorized holder15, no application change. Historical failed attempt retained privately.'
(O/'preview-qa.json').write_text(json.dumps(public_preview,indent=2,ensure_ascii=False),encoding='utf-8')
(O/'compatibilidade-titulares-qa.json').write_text(json.dumps(holder,indent=2,ensure_ascii=False),encoding='utf-8')
# Improve authored prose only, without changing scope, hashes, paths or service proposal.
spacing={' +159':' + 159','a030000':'a 030000','somente030000':'somente 030000',
'somente040000':'somente 040000','somente030':'somente 030', 'pedido15→confirmação16':'pedido 15 → confirmação 16',
'PDF20MiB':'PDF de 20 MiB','Fila4→3→2→1→0':'Fila 4 → 3 → 2 → 1 → 0','repetição0→0':'repetição 0 → 0',
'CLI256M':'CLI 256M','pico148MiB':'pico de 148 MiB','scanner13310/ClamAV1.5.4':'scanner 13310 / ClamAV 1.5.4',
'assinaturas11/10':'assinaturas de 11/10','às08:26UTC':'às 08:26 UTC','DBQA':'DB QA','destino3310':'destino 3310',
'real180s/recuperação210s':'real de 180 s / recuperação de 210 s','nos191':'nos 191',
'aumento288→295':'aumento 288 → 295','tabelas030000+040000':'tabelas 030000 + 040000',
'contém181':'contém 181','fixtures.191':'fixtures. 191','ID2':'ID 2','ID13':'ID 13','IDs3/18':'IDs 3/18',
'legado e190000':'legado e 190000','atuais3310':'atuais 3310','provasLocalService':'provas LocalService',
'reconciliação191':'reconciliação dos 191 ficheiros','tabelaB3':'tabela B3',
'→030000→040000':' → 030000 → 040000',
'→ flags':' → flags','escopo': 'âmbito','SHA-256:':'SHA-256:',
'Dest​ino':'Destino','050000 não se repete':'050000 não se repete',
'COUNTRY02 permaneceON':'COUNTRY02 permanece ON','code/bundle':'código/bundle',
'destino real**':'destino real**','flagsOFF':'flags OFF','COUNTRY02 ON':'COUNTRY02 ON',
'fila default partilhada':'fila default partilhada','upload20M':'upload 20M','POST24M':'POST 24M',
'memória256M':'memória 256M','execução180s':'execução 180 s','cadência60s':'cadência 60 s',
'externo180s':'externo 180 s','lease210s':'lease 210 s','prazo15min':'prazo 15 min',
'em030000':'em 030000','em040000':'em 040000','050000/190000':'050000/190000',
'somente030000/040000':'somente 030000/040000','somente030000':'somente 030000',
'190000 fica':'190000 fica','notificações enfileiradas':'notificações enfileiradas',
'OFF.050000':'OFF. 050000','revisão/ZIP':'revisão/ZIP',
'|288/31 |799 |':'| 288 / 31 | 799 |','|295/31 |188 |':'| 295 / 31 | 188 |','|295/31 |191 |':'| 295 / 31 | 191 |',
'Destino,15:11:50UTC':'Destino, 15:11:50 UTC','anterior,15:11:55UTC':'anterior, 15:11:55 UTC',
'delta,16:29:27UTC':'delta, 16:29:27 UTC','QA renovada,':'QA renovada, ',
'+476':' + 476','+312':' + 312','+2TLS':' + 2 TLS','+5Apache':' + 5 Apache',
'+1env':' + 1 env','+17 código':' + 17 código','+158bundle':' + 158 bundle',
'+22 código':' + 22 código','+159bundle':' + 159 bundle',',9 envelopes':', 9 envelopes',
'investment_project,real_estate,land_rights,business_sale,business_partnership,franchise':'investment_project, real_estate, land_rights, business_sale, business_partnership, franchise'}
for name in ['relatorio-consolidacao.md','procedimento-unico.md','designacoes-b3.md']:
p=O/name;text=p.read_text(encoding='utf-8')
for a,b in spacing.items():text=text.replace(a,b)
p.write_text(text,encoding='utf-8')
report=O/'relatorio-consolidacao.md'
report.write_text(report.read_text(encoding='utf-8')+'''\n## Verificações afetadas da consolidação\n\nChromium HTTPS/MFA normal confirmou a prévia040000 com o novo fingerprint/readiness e a dependência030000 com fingerprint/readiness correspondente ao restauro renovado. Interface de instalação renderizada pelo bundle consolidado. Não houve repetição da instalação: a recusa403 ao gestor legado que não integra a dupla QA foi verificada antes da leitura permitida ao titular15. As contas reais clonadas2/13 passaram validateBootstrapHolder, incluindo leitura do envelope MFA normal; zero alterações às contas. [Prévia QA](preview-qa.json), [compatibilidade dos titulares](compatibilidade-titulares-qa.json).\n''',encoding='utf-8')
block=f'''<!-- consolidated-workflow-qa-20261011 -->
## Candidata consolidada {revision} — QA, sem ativação
[Relatório e bloqueios](../../../../outputs/investmoz-workflow-consolidated-20261011/relatorio-consolidacao.md), [procedimento único](../../../../outputs/investmoz-workflow-consolidated-20261011/procedimento-unico.md) e [manifesto](../../../../outputs/investmoz-workflow-consolidated-20261011/manifesto.json).
Delta 117d2d889701 integrado: 22 ficheiros de código + 159 assets (181 no ZIP), referência 98a8e0b70892 preservada. Compilação de produção do delta verificada por bytes; nenhuma alteração de código adicional. Prévia Chromium/MFA 040000 com fingerprint novo e dependência030000 corresponde ao restauro renovado; gestor legado fora da dupla QA recebe403, titular autorizado recebe200. Instalações/funcionalidades válidas não repetidas.
LocalService S-1-5-19 demonstrada no clone55441: configuração lida, ClamD13310, quatro scans PDF20MiB/trêsWebP, fila4→0 e repetição0→0, zero recursos novos/falhas e hashes/cifra recuperados; escrita só nas áreas QA autorizadas, negada em código/vendor/config/.env e leitura alheia negada. Tarefa própria removida. Limite180s/lease210s anteriores preservados como prova distinta de utilizador interativo. Não é instalação/prova do destino3310.
191 ficheiros reconciliados:22código+159bundle+1env+9documentais, incluindo os4originais do destino com hashes iguais e5ficheiros sintéticos. Backup799 do destino tem âmbito maior; os8documentais exclusivos da QA188 continuam no backup anterior. RestauroQA renovado16:49:42UTC:295tabelas/31seq/191ficheiros/9MFA; renovar o backup/restauro do destino real e associar à revisão antes da intervenção.
B3: responsável confirmou nala@tempo.co.mz (**ID2**) e onala@me.com (**ID13**); email/MFA/gestão legada verificados READ ONLY e compatibilidade do envelope validada no clone. IDs3/18 iniciais reconciliados. Designação registada, sem conceder privilégios. Ainda faltam autorização/cerimónia cruzada e contas/organização/tipos/âmbito operacionais. B1/B2: serviços3310, supervisão/limites e ACLs efetivas do destino por autorizar/verificar; RX adicional não neutraliza escrita herdada. Notificações OFF e nova leitura da default imediatamente antes de iniciar worker; parar perante classe desconhecida ou notificação enfileirada.
Mesma matriz3401requisitos/7739ocorrências, três evidências complementadas, zero IDs/estados gerais novos. COUNTRY02 ativo e fechos A/B/C37 preservados. Restauração/partilha individual/190000/legado/modulares/ofertas/SLA permanecem fora. Os blocos seguintes mantêm o histórico.
<!-- /consolidated-workflow-qa-20261011 -->
'''
for a,b in {**spacing,'dependência030000':'dependência 030000','recebe403':'recebe 403','recebe200':'recebe 200',
'clone55441':'clone 55441','ClamD13310':'ClamD 13310','fila4→0':'fila 4 → 0','quatro scans PDF20MiB/trêsWebP':'quatro scans PDF de 20 MiB / três WebP',
'Limite180s/lease210s':'Limite 180 s / lease 210 s','191 ficheiros reconciliados:22código+159bundle+1env+9documentais':'191 ficheiros reconciliados: 22 código + 159 bundle + 1 env + 9 documentais',
'os4originais':'os 4 originais','e5ficheiros':'e 5 ficheiros','Backup799':'Backup 799','os8documentais':'os 8 documentais',
'QA188':'QA 188','RestauroQA renovado16:49:42UTC:295tabelas/31seq/191ficheiros/9MFA':'Restauro QA renovado 16:49:42 UTC: 295 tabelas / 31 sequências / 191 ficheiros / 9 MFA',
'IDs3/18':'IDs 3/18','serviços3310':'serviços 3310','matriz3401requisitos/7739ocorrências':'matriz: 3401 requisitos / 7739 ocorrências'}.items():block=block.replace(a,b)
for name in ['backlog.md','relatorio-executivo.md','plano-ativacao.md','workflow-criterios-saida-20261011.md']:
p=R/'docs/investmoz/v2/auditoria-portal'/name;old=p.read_text(encoding='utf-8')
assert '<!-- consolidated-workflow-qa-20261011 -->' not in old
p.write_text(block+old,encoding='utf-8')
before=load(P/'consolidated-preservation-before.json');checks={}
for kind,base in [('fixed_candidate',R/'outputs/investmoz-workflow-upgrade-20261011'),('delta_reference',R/'outputs/investmoz-service-followup-20261011'),('service_plan',R/'outputs/investmoz-workflow-service-plan-20261011')]:
changed=[key for key,value in before[kind].items() if not (base/key).is_file() or sha(base/key)!=value]
assert not changed,(kind,changed);checks[kind]={'files':len(before[kind]),'changed':0}
active=load(P/'service-followup-preservation-before.json')['active_hashes']
assert all(sha(R/key)==value for key,value in active.items())
checks['active_monitored_files']={'files':len(active),'changed':0}
csv.field_size_limit(16777216)
old=list(csv.reader((P/'consolidated-matrix-before.csv').open(encoding='utf-8-sig',newline='')))
new=list(csv.reader((R/'docs/investmoz/v2/auditoria-portal/matriz-requisitos.csv').open(encoding='utf-8-sig',newline='')))
assert len(old)==len(new)==3402 and len({r[0] for r in new[1:]})==3401
differences=[]
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 old[0][c]=='Evidência funcional' and b[0] in ['AUD-OPS-01','AUD-PROFILE-15','AUD-PROFILE-31']
differences.append({'id':b[0],'column':old[0][c]})
assert len(differences)==3
for key,value in manifest['service_proposal'].items():assert sha(O/key)==value
assert sha(O/manifest['zip'])==manifest['zip_sha256']
with zipfile.ZipFile(O/manifest['zip']) as z:
assert len(z.namelist())==181
for key,value in manifest['payload'].items(): assert hashlib.sha256(z.read(key)).hexdigest()==value
for key,value in manifest['unchanged_dependencies'].items(): assert sha(R/key)==value, key
backup=load(P/'consolidated-backup-restoration-qa.json')
assert backup['code_manifest']=={key:value for key,value in manifest['payload'].items() if not key.startswith('public/build/')}
assert backup['production_manifest_sha256']==manifest['production_manifest_sha256']
result={'result':'passed','verified_at':datetime.now(timezone.utc).isoformat(),'candidate':manifest['revision'],
'code_revision':manifest['code_revision'],'zip_sha256':manifest['zip_sha256'], 'manifest_sha256':sha(O/'manifesto.json'),
'preservation':checks,'matrix':{'requirements':3401,'source_occurrences':sum(int(dict(zip(new[0],row))['Quantidade de linhas de origem']) for row in new[1:]),'changes':differences,'new_ids':0,'states_sources_versions_preserved':True},
'QA_restore_bound_to_exact_candidate_code_and_production_bundle':True,
'changed_preview_and_holder_compatibility_passed':True,'LocalService_QA_passed':True,
'destination_account_ids_designated':[2,13], 'active_writes':0,'activation_ready':False,'activation_authorized':False,
'delivery_files':{p.relative_to(O).as_posix():sha(p) for p in sorted(O.rglob('*')) if p.is_file()}}
(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','manifest_sha256','LocalService_QA_passed','active_writes','activation_ready']}))