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

56 lines
5.9 KiB
Python

"""Verify the sealed candidate and the exact existing audit edits; improve current prose only."""
from pathlib import Path
from datetime import datetime,timezone
import json,hashlib,zipfile,csv,collections
csv.field_size_limit(20000000)
r=Path(__file__).resolve().parent.parent;p=r/'storage/app/private/investmoz-v2/portal-audit';d=r/'docs/investmoz/v2/auditoria-portal';o=r/'outputs/investmoz-opportunity-workflow-scope-20261011'
read=lambda x:json.loads(x.read_text(encoding='utf8'));sha=lambda x:hashlib.sha256(x.read_bytes()).hexdigest()
m=read(o/'manifesto.json');packet=read(o/'pacote.json');source=Path(read(p/'portal-stability-latest.json')['source'])
core={k:v for k,v in m.items()if k not in ['revision','sealed_at']}
assert hashlib.sha256(json.dumps(core,sort_keys=True,separators=(',',':'),ensure_ascii=False).encode()).hexdigest()==m['revision']==packet['revision']
assert sha(o/packet['archive'])==packet['sha256']and sha(o/'manifesto.json')==packet['manifest_sha256']
with zipfile.ZipFile(o/packet['archive'])as z:
assert z.testzip()is None and len(z.namelist())==176
for name,h in m['payload'].items():assert sha(o/'payload'/name)==sha(source/name)==h==hashlib.sha256(z.read('payload/'+name)).hexdigest()
for name in ['manifesto.json','relatorio.md','procedimento.md','evidencias-por-criterio.json']:assert z.read(name)==(o/name).read_bytes()
for name,h in m['QA_evidence'].items():assert sha(p/name)==h
for name,h in read(p/'functional-active-files-before-20261011.json').items():assert sha(r/name)==h
for name,h in m['unchanged_dependencies'].items():assert sha(r/name)==sha(source/name)==h
changes=read(p/'functional-scope-finalize-20261011/csv-edits.json');updated=set()
for entry in changes['files']:
with(d/entry['filename']).open(encoding='utf-8-sig',newline='')as f:rows=list(csv.reader(f))
original=entry['rows'];assert len(rows)==len(original)and rows[0]==original[0]
allowed={edit['row']:edit['values']for edit in entry['patches']};headers=rows[0]
for n,(a,b)in enumerate(zip(original[1:],rows[1:]),1):
assert len(a)==len(b)
for c,(old,new)in enumerate(zip(a,b)):
if old!=new:assert headers[c]in allowed.get(n,{})and new==allowed[n][headers[c]];updated.add(b[headers.index('ID')])
assert [a[headers.index('ID')]for a in original[1:]]==[a[headers.index('ID')]for a in rows[1:]]
if entry['filename']=='matriz-requisitos.csv':
states=dict(collections.Counter(a[headers.index('Estado')]for a in rows[1:]));assert states==m['audit_counts']['states']
assert len(updated)==10
prefix=f'''<!-- workflow-scope-qa-20261011 -->
## Candidata delimitada e proteção documental — 11/10/2026
C126 corrigido somente em QA: a rota geral exige a autorização atual da oportunidade. Originais, derivadas e revogação de perfil foram demonstrados; a ACL individual continua indisponível e explicada na interface. R-ARCHIVE e R-DRAFT são propostas separadas. As variantes imobiliárias excluídas continuam recuperáveis em preparação, com submissão recusada e erros semânticos preservados.
Nova candidata **{m['revision'][:12]}**, código **{m['code_revision'][:12]}**: 14 ficheiros de código e 158 assets recompilados. [Relatório com âmbito, ACL e decisões](../../../../outputs/investmoz-opportunity-workflow-scope-20261011/relatorio.md), [procedimento](../../../../outputs/investmoz-opportunity-workflow-scope-20261011/procedimento.md), [manifesto](../../../../outputs/investmoz-opportunity-workflow-scope-20261011/manifesto.json). A referência `8af63e2fe657` mantém os bytes e o histórico, mas C126 impede a sua disponibilização.
Backup do **destino real** renovado em {m['destination_backup']['at']}: 288 tabelas, 31 sequências, 799 ficheiros e dois envelopes MFA, com restauro apenas em QA/55441. QA funcional: 295 tabelas, 31 sequências, 288 ficheiros e 17 envelopes MFA. A diferença de sete tabelas corresponde a 030000/040000; o acervo privado QA não foi importado no ativo.
**Ainda não pronto para ativação:** ClamD do destino com assinaturas obsoletas; worker/GD e processamento documental por demonstrar no destino; ensaio de atualização do clone e recibos/prévias específicos; designação de titulares e âmbitos legítimos. O destino tem apenas duas contas com MFA e o bootstrap exige dois titulares e um terceiro gestor independente com MFA: essa preparação humana falta. Zero geometrias no destino; importação separada se forem incluídos os limites administrativos. Não reaplicar 050000. 030000 precede 040000; 190000 permanece separado.
COUNTRY02 ativo, A/B e C37 preservados; nenhuma alteração no ativo. Mesma matriz: 3.401 requisitos/linhas, 7.739 ocorrências, dez requisitos existentes atualizados e zero IDs novos; estados gerais preservados.
'''
for name in ['relatorio-executivo.md','backlog.md','workflow-criterios-saida-20261011.md','plano-ativacao.md']:
file=d/name;text=file.read_text(encoding='utf8');marker='<!-- workflow-fixed-candidate-20261011 -->';assert text.count('<!-- workflow-scope-qa-20261011 -->')==1
# The new section was prepended to historical contents; preserve every byte after it.
if marker in text:tail=text[text.index(marker):]
else:
start=text.index('Mesma matriz3401requisitos/7739ocorrências;10IDsatualizados,0novos e estados preservados.')
tail=text[start+len('Mesma matriz3401requisitos/7739ocorrências;10IDsatualizados,0novos e estados preservados.'):].lstrip('\n')
file.write_text(prefix+tail,encoding='utf8')
proof={'result':'passed','at':datetime.now(timezone.utc).isoformat(),'revision':m['revision'],'code_revision':m['code_revision'],'package_sha256':packet['sha256'],'source_bundle_payload_hashes_verified':True,'historical_evidence_preserved':True,'updated_existing_IDs':sorted(updated),'requirements':3401,'source_occurrences':7739,'states':states,'active_critical_files_preserved':True,'active_writes':0,'activation_ready':False}
(o/'verificacao-final.json').write_text(json.dumps(proof,ensure_ascii=False,indent=2)+'\n',encoding='utf8');print(json.dumps(proof,ensure_ascii=False))