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

162 lines
28 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
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.
"""Seal the bounded QA candidate; explicitly keep destination activation blocked."""
from pathlib import Path
from datetime import datetime,timezone
import json,hashlib,zipfile,shutil,csv,collections,xml.etree.ElementTree as ET
csv.field_size_limit(20000000)
root=Path(__file__).resolve().parent.parent;p=root/'storage/app/private/investmoz-v2/portal-audit';source=Path(json.loads((p/'portal-stability-latest.json').read_text())['source'])
read=lambda n:json.loads((p/n).read_text(encoding='utf8'))
sha=lambda x:hashlib.sha256(x.read_bytes()).hexdigest()
scope=read('functional-delivery-scope-code-20261011.json');code=scope['code_files'];build=read('functional-scope-production-build-20261011.json')
backup=read('functional-scope-backup-restoration-qa-20261011.json');target=read('functional-scope-target-backup-20261011.json');deps=read('functional-scope-dependencies-chromium-20261011.json')
assert scope['code_revision']==backup['code_revision']==target['candidate_code_revision']
assert build['manifest_sha256']==backup['build_manifest_sha256']==scope['production_manifest_sha256']
assert deps['profiles_preview']['code_hash']==backup['profile_fingerprint']
for rec in [backup,target]:
assert rec['result']=='passed'
for file,h in [('dump_path','dump_sha256'),('files_path','files_sha256')]:assert sha(Path(rec[file]))==rec[h]
names=['functional-document-alias-before-20261011.json','functional-document-alias-after-20261011.json','functional-document-boundary-chromium-20261011.json','functional-document-images-chromium-20261011.json','functional-delivery-scope-chromium-20261011.json','functional-scope-dependencies-chromium-20261011.json','functional-scope-persistence-qa-20261011.json','functional-scope-country-compatibility-qa-20261011.json','functional-scope-production-build-20261011.json','functional-scope-backup-restoration-qa-20261011.json','functional-scope-target-backup-20261011.json']
for n in names:assert read(n)['result']=='passed',n
names+=['functional-scope-target-readonly-20261011.json','functional-scope-destination-services-readonly-20261011.json','functional-scope-finalize-20261011/csv-authoring-proof.json']
previous=root/'outputs/investmoz-opportunity-workflow-consolidated-final-20261011';prior=json.loads((previous/'manifesto.json').read_text())
assert sha(previous/'manifesto.json')=='410bc74e6befde6e51a6e8afe8d55a699d600c1a857f1fc7d395baba30cd49ca'
assert sha(previous/'investmoz-workflow-8af63e2fe657.zip')=='feb7f4e8ae2608f08ef90d3a0acfbe71ba118480c967e784fa5037b645cc3403'
for n,h in prior['QA_evidence'].items():assert sha(p/n)==h,('Historical evidence changed',n)
for n,h in code.items():assert sha(source/n)==h
for n,h in build['assets'].items():assert sha(source/'public/build'/n)==h
for n,h in read('functional-active-files-before-20261011.json').items():assert sha(root/n)==h
for n,h in prior['unchanged_dependencies'].items():assert sha(root/n)==h and sha(source/n)==h
with (root/'docs/investmoz/v2/auditoria-portal/matriz-requisitos.csv').open(encoding='utf-8-sig',newline='')as f:rows=list(csv.DictReader(f))
counts=read('functional-scope-finalize-20261011/counts.json')
assert len(rows)==counts['requirements']==3401
assert dict(collections.Counter(r['Estado']for r in rows))==counts['states']
out=root/'outputs/investmoz-opportunity-workflow-scope-20261011';out.mkdir(exist_ok=True)
assert not(out/'manifesto.json').exists(),'Never overwrite a sealed candidate'
payload=out/'payload';payload.mkdir()
for n in code:
to=payload/n;to.parent.mkdir(parents=True,exist_ok=True);shutil.copy2(source/n,to)
for n in build['assets']:
to=payload/'public/build'/n;to.parent.mkdir(parents=True,exist_ok=True);shutil.copy2(source/'public/build'/n,to)
payload_hashes={f.relative_to(payload).as_posix():sha(f)for f in payload.rglob('*')if f.is_file()}
assert len(payload_hashes)==172
failure=ET.parse(source/'storage/functional-scope-regression-20261011.xml').getroot().find('testsuite')
repair=ET.parse(source/'storage/functional-scope-error-regression-20261011.xml').getroot().find('testsuite')
assert int(failure.attrib['tests'])==26 and int(failure.attrib['failures'])==1
assert int(repair.attrib['tests'])==1 and int(repair.attrib['failures'])==0 and int(repair.attrib['errors'])==0
quality={'existing_regression':'25 unaffected passes reused; one failure of semantic error preservation corrected and its test rerun successfully (50 assertions)','production_build':build['manifest_sha256'],'typescript_exit':0,'XML_before_fix_sha256':sha(source/'storage/functional-scope-regression-20261011.xml'),'XML_affected_after_fix_sha256':sha(source/'storage/functional-scope-error-regression-20261011.xml'),'acceptance_is_criterion_based':True}
core={'format':'investmoz-opportunity-workflow-scope-1','base_revision':prior['base_revision'],'previous_candidate':{'revision':prior['revision'],'manifest_sha256':sha(previous/'manifesto.json'),'archive_sha256':'feb7f4e8ae2608f08ef90d3a0acfbe71ba118480c967e784fa5037b645cc3403','preserved':True,'availability_blocked_by':'C126'},
'code_revision':scope['code_revision'],'payload':payload_hashes,'base_hashes':scope['active_base_hashes'],'unchanged_dependencies':prior['unchanged_dependencies'],'additional_changes':scope['changed_from_previous'],
'production_manifest_sha256':build['manifest_sha256'],'recompilation_required':True,'profile_fingerprint':backup['profile_fingerprint'],'resources_fingerprint':deps['resources_preview']['code_hash'],
'included_categories':prior['included_categories'],'included_cases':[30,31,32,33,34,35,54],'included_scope':'Existing base-category internal workflow, compatible Response, scoped group-private resources, materiality/conditions, filters and scoped accessibility; sale/lease completed vacant unencumbered preparation branch',
'unavailable':['Individual per-file recipients/sharing/NDA/revocation','Opportunity restoration pending R-ARCHIVE/R-DRAFT decision','Full normative Response/27 export','Real estate planned/under-construction/occupied/renovation/encumbered/urban-limit submission','Experimental modular categories/offers/SLA','Legacy integration and integrated journey/190000','Geometry imports/publication','Real devices/full accessibility and external legal decisions'],
'effects':{'code_files':14,'new_service_files':1,'assets':158,'new_routes':0,'new_configuration_files':0,'prospective_installations':['030000','040000'],'prospective_new_tables':7,'prospective_new_columns':5,'automatic_accounts':0,'automatic_organizations':0,'automatic_representations':0,'automatic_publications':0,'active_writes':0},
'privacy_policy':'Existing opportunity organization/type/role authorization; authenticated manager alone cannot bypass it. Classifications remain metadata, not individual grants. Publication of teaser does not publish files. Original/image derivatives require current authorization through every binary route.',
'QA_evidence':{n:sha(p/n)for n in names},'reused_evidence':prior['QA_evidence'],'quality':quality,
'QA_backup':{k:backup[k]for k in ['at','source','restored','tables','sequences','files','dump_sha256','files_sha256','MFA_envelopes_readable','code_revision','acervo']},
'destination_backup':{'at':target['verified_at'],'target':target['target'],'restored_only_in_QA':target['restored_target'],'tables':len(target['tables']),'sequences':len(target['sequences']),'files':target['file_count'],'originals':target['active_originals_backed_up'],'MFA_envelopes_readable':target['MFA_envelopes_readable'],'dump_sha256':target['dump_sha256'],'files_sha256':target['files_sha256'],'candidate_code_revision':target['candidate_code_revision'],'read_only_active':True,'installer_readiness':False},
'COUNTRY02_compatibility':read('functional-scope-country-compatibility-qa-20261011.json'),'destination_inventory':read('functional-scope-target-readonly-20261011.json'),
'activation_ready':False,'activation_authorized':False,'activation_blocks':['ClamD destination signatures_stale; fresh real scan needed','Destination supervised document/image worker/GD operation not demonstrated','Final upgrade rehearsal of restored active target and scoped installer readiness/preview proofs needed','Legitimate bootstrap holders and functional scopes not designated','Administrative map geometry absent; separate authorization/import required if that display is included'],
'restoration_decisions_pending':{'R-ARCHIVE':'Independent scoped Publisher with MFA/reason/audit -> needs_information nonpublic; explicit new submission/review/publication, no automatic republication','R-DRAFT':'Author or authorized organization coeditor -> private draft only never submitted/published; reason/audit and original ID/history preserved'},
'preserved':['COUNTRY02 active/050000','A/B and C37 closures','five phases/19 existing stage numbering','IDs/URLs/contracts/classifications/relations/originals/cipher/audit','active TLS/session/services/code/database'],'audit_counts':counts}
revision=hashlib.sha256(json.dumps(core,sort_keys=True,separators=(',',':'),ensure_ascii=False).encode()).hexdigest();manifest={**core,'revision':revision,'sealed_at':datetime.now(timezone.utc).isoformat()}
(out/'manifesto.json').write_text(json.dumps(manifest,ensure_ascii=False,indent=2)+'\n',encoding='utf8')
report=f'''# Gestão de Oportunidades — candidata delimitada {revision[:12]}
**Preparada e demonstrada nos critérios indicados em QA; disponibilização ativa bloqueada.** Código `{scope['code_revision']}`. Substitui para apreciação a referência `8af63e2fe657`, conservada intacta: C126 impede disponibilizá-la sem a correção documental. COUNTRY02 ativo, A/B e C37 mantêm os fechos. Nenhuma alteração no código, configuração, serviços ou base ativa.
## Funcionalidades incluídas e limites
| Categoria/consumidor | Incluído e evidência reutilizada | Limite concreto |
| --- | --- | --- |
| Projeto, 30/52 | Rascunho incompleto; correção, condição, revisão técnica material, aprovação24/publicação25 e recuperação. | Cenários/licenças dependem do caso; análise interna não certifica viabilidade. |
| Imobiliário, venda31/arrendamento54 | Concluído/desocupado/sem encargos; documentos próprios; submissão→correção→três revisões24→aprovação→publicação restrita25. Renda4500 MZN/mês preservada. | Outros negócios imobiliários, construção planeada/em curso, ocupação/renovação, encargos e limites urbanísticos não avançam neste pacote; podem guardar preparação. |
| Fundiário, 32 | Revisão territorial, bloqueio externo, aprovação/publicação restrita sintética explicitamente identificada e recuperação. | Não atribui terra/DUAT nem comprova conflitos/consulta reais.173 correspondências/Mandie–Guro mantidos fora. |
| Venda de negócio, 33 | Revisão societária, aprovação independente e teaser público. Documentos/imagens/derivadas privados. | Interesse em anúncio legado, modalidades financeiras/histórico/projeções e destinatários de due diligence não encerrados por esse caso. |
| Parceria, 34 | Governação, materialidade apenas nos gates afetados, correção/aprovação/publicação restrita; recurso já ensaiado reutilizado. | Variantes de contribuições/documentação e partilha individual externa pendentes. A ACL da sala de parceria não é ACL dos anexos da oportunidade. |
| Franquia, 35 | Registo societário/materialidade, licenças, aprovação/publicação restrita e recuperação. | Exclusividade, royalties, titularidade e territórios por variante ainda exigem provas próprias; não são modalidades certificadas por35. |
| Recursos15/33/54; lista `/minhas-oportunidades`; estatísticas `/management/interests` | País/hierarquia/mapa, filtros e estatísticas já ensaiados reutilizados; retirada/restauro de anexo conservam originais. C126 e revogação acrescentam proteção em todas as rotas binárias. | Estes resultados não encerram consumidores públicos restantes, todas as estatísticas, toda cartografia ou todo o portal. |
O Promotor continua a guardar rascunhos incompletos. A interface mostra «Âmbito desta entrega» e «Privacidade dos anexos»; apenas seis categorias base são oferecidas no contexto autorizado com módulos OFF. API422 explica a variante excluída sem apagar os erros semânticos originais. O rascunho55planeado recupera noutra sessão; o54 conserva4500/monthly. Taxonomia, contratos e valores antigos não foram convertidos. A administração legada preserva a política existente; não conta como cobertura do novo workflow por perfil.
**Indisponíveis nesta candidata:** partilha individual de anexos; restauração de oportunidade; exportação normativa§27 integral (Response compatível conserva `normative_response_complete=false`); concessão, financiamento, fornecimento, outros e ofertas; SLA; legado/percurso integrado190000; importação/divulgação cartográfica; atos externos; dispositivos físicos, leitores reais e acessibilidade integral.
## ACL documental: alcance e correção
| Documentos/perfil | Operações incluídas | Operações indisponíveis |
| --- | --- | --- |
| Contratos, títulos, licenças, avaliação, finanças, beneficiário efetivo e restantes anexos próprios da oportunidade; imagens originais e WebP derivados | Metadados e download pelos Promotores autorizados da organização; Validadores/Publicadores conforme âmbito/tipo; Auditor/Superutilizador com acesso previsto. Coedição/edição só no estado autorizado; revisões/publicação independentes. | Conceder ou revogar por ficheiro a um destinatário escolhido; restringir o ficheiro a alguns membros autorizados; NDA/partilha com comprador externo. |
| Gestão geral sem perfil funcional, outra organização sem âmbito, visitante | Recusa da entrega privada, inclusive pela rota geral UUID e URL assinado. Publicação do teaser não torna os binários públicos. | Nenhum acesso por ser gestor da biblioteca, por conhecer o UUID, por estar autenticado ou por classificar metadados como public/authenticated. |
| Perfil revogado | Sessão anterior invalidada; novo login não recupera a autorização revogada. | Não é possível retirar cópias já descarregadas. Revogação de perfil não é ACL por ficheiro. |
| Biblioteca e sala de parceria | Reutilizam os seus controlos próprios. O original aceite29 mantém bytes/hash e consulta autorizada. | Não converter a prova de revogação da sala ou de retirada global de anexo em prova de ACL individual da oportunidade. |
C126 era **bloqueante**: gestor17 recebia403 na rota própria54 e200/PDF na rota geral. `VerifiedDocumentBinary` agora resolve a ligação existente, verifica a autorização vigente e a situação do recurso/oportunidade, antes de entregar os bytes. Sem nova tabela ou sistema de permissões. Originais/derivadas dessa origem sem vínculo válido falham fechados. Ambas as rotas passam a403; três PDFs recuperados por21/24/25 com hashes; imagem33 e derivadas autorizadas recuperadas, gestor/visitante recusados. Conta sintética32 recebeu/revogou um grant através do serviço autenticado:200→401 na sessão anterior→403 após novo login, com auditoria; nenhum grant das contas anteriores foi retirado.
**Condição de inclusão documental:** estes fluxos internos usam a política de grupo acima. Um documento que exija restrição a uma pessoa/subconjunto não pode integrar este pacote: a interface avisa e a operação de partilha individual não é oferecida. A correção fecha o contorno C126 em QA; não declara implementada a ACL individual.
## Restauração — propostas separadas, ainda por decidir
| Decisão | Quem | Destino e condições |
| --- | --- | --- |
| R-ARCHIVE | Publicador autorizado por âmbito/tipo, independente do Promotor e da aprovação, MFA recente | Oportunidade arquivada→`needs_information`, **não pública**, com motivo/auditoria. Conservar publicação anterior como história; nova submissão/revisões/decisões/publicação explícitas. Nunca repor a publicação automaticamente. |
| R-DRAFT | Autor ou outro Promotor autorizado da organização, com representação/tipo válidos | Só rascunho eliminado logicamente e **nunca submetido/publicado**→`draft` privado. Motivo/auditoria, mesmoID/originais/histórico. Não concede restauro de arquivo publicado. |
Nenhuma destas políticas foi implementada ou tratada como aprovação implícita. O restauro de anexos já ensaiado é operação distinta e mantém a autorização existente.
## Dependências e preparação real do destino
**020000 já instalado →030000→040000.**030000 adiciona3tabelas e `documents.storage_encryption`;040000 adiciona4tabelas, `users.portal_access_revision` e3colunas de opportunities: **7tabelas/5colunas no total**. Nenhuma conta, organização, representação, atribuição, titular ou publicação automática.050000/COUNTRY02 já está instalado e ON: não reaplicar.190000 entra apenas quando o percurso integrado for disponibilizado; não é dependência DDL do workflow isolado.
As prévias autenticadas QA mostram ambas instaladas/coerentes, dependências verdadeiras e `ready=false`: instalação já demonstrada não é readiness do destino. Fingerprints atuais:030000`{deps['resources_preview']['code_hash']}`,040000`{backup['profile_fingerprint']}`. A prévia do alvo só será válida com a revisão, schema, recibos e serviços correspondentes; parar se divergir antes de aplicar.
Destino lido em14:11:18UTC: **288tabelas,13contas,0organizações/representações,1.990territórios,0geometrias**,020000/050000 presentes; demais flagsOFF. Backup do **destino real**, renovado em **{target['verified_at']}**, restaurado só em `{target['restored_target']['database']}:55441`: **288tabelas,31sequências,799ficheiros**, comparação de dados/sequências/hashes e2envelopesMFA. Inclui4originais,312ficheiros de código/configuração/locks,476itens de runtime/bundles (incluindo assets antigos retidos),2TLS e5Apache/vhosts. Não reiniciou Apache nem alterou a base ativa.
QAfuncional: restauro **{backup['at']}**,295tabelas/31sequências/288ficheiros, código final e bundle atual,17MFA recuperáveis; chave histórica preservada e controlo negativo19 permanece recusado. Diferença295/288: **as sete tabelas030000/040000**. O acervo29 e os três PDFs sintéticos54 estão no backupQA privado; não se transferem automaticamente ao destino nem entram no ZIP público.
**Ainda bloqueiam disponibilização:** ClamD3310 com assinaturas obsoletas (~53,8h, `signatures_stale`); execução supervisionada de scans/GD/derivadas/worker no destino; ensaio de atualização do clone restaurado do alvo e readiness por instalador; titulares/âmbitos funcionais legítimos não designados. Mapas administrativos exigem importação separadamente autorizada porque o destino tem0geometrias. Não inventar organizações/representações: fluxos organizacionais só com cadastro/representação legítimos; âmbito individual usa o fluxo aprovado, sem organização fictícia.
## Alterações, evidências e reversão
Face à8af:6ficheiros alterados, total14no delta; proteção comum C126, fingerprint inclui essa proteção, disponibilidade das categorias/variantes no servidor e avisos da interface. **Recompilação de produção executada**,158assets, manifesto`{build['manifest_sha256']}`. Verificação TypeScript passou. A regressão revelou a ocultação do erro de ocupação: corrigida a agregação dos erros e repetido com sucesso o teste afetado (50assertions);25passes não afetados reutilizados. O código não mudou HTTPS/cookie/APP_KEY/cifra/schema/dados ativos. A nova prévia COUNTRY02 muda o fingerprint comum, sem justificar reinstalar050000.
As provas sanitizadas novas estão ligadas por hash no manifesto; as anteriores são reutilizadas por critério. Sete casos aprovados/publicados mantêm versões/hashes; renda/periocidade54preservadas. Nova interface vista a390px; não extrapolar para dispositivos ou acessibilidade integral.
Contenção futura: desligar só as flags do workflow, **mantendo a correção de entrega de ficheiros**. Preservar COUNTRY02ON,HTTPS,chaves,tabelas,colunas,dados,originais/histórico. Não voltar ao serviço binário vulnerável nem executar down/restauro completo automaticamente. Restauro completo exige decisão própria e tratamento das escritas posteriores ao snapshot. Sem uso/instalação no ativo neste incremento, a contenção ainda não foi executada.
Mesma matriz:3.401requisitos/linhas,7.739ocorrências,10linhas existentes atualizadas por projeção; zeroIDsnovos. Estados gerais preservados:7concluídos,3.236parciais,152não implementados,1preparado não ativado e5decisões. O fecho deC126não fecha os critérios individuais/documentais/por modalidade restantes. **`activation_ready=false`; não se solicita ativação enquanto os bloqueios acima subsistirem.**
'''
# Normalize new report units only; exact IDs, hashes and history are retained.
import re
report=re.sub(r'(\d+)(tabelas|ficheiros|sequências|contas|territórios|geometrias|colunas|MFA|assets|originais|itens|requisitos|ocorrências|linhas|assertions|passes|parciais|decisões|TLS|Apache|IDs)(?=\b)',r'\1 \2',report)
report=report.replace('COUNTRY02ON','COUNTRY02 ON').replace('QAfuncional','QA funcional').replace('backupQA','backup QA').replace('perioci','periodici').replace('zeroIDsnovos','zero IDs novos')
(out/'relatorio.md').write_text(report,encoding='utf8')
procedure=f'''# Procedimento da candidata {revision[:12]} — preparação, não ativação
1. Fixar revisão/ZIP/14ficheiros/158assets e confirmar base ativa e COUNTRY02ON. Conservar a candidata anterior somente como referência impedida porC126. Nenhuma instalação ou flag é aplicada nesta fase.
2. Antes de qualquer futura disponibilização: resolver ClamD/worker/GD no destino; executar o ensaio de atualização/restauração no clone real do alvo e emitir recibos separados030000/040000 para código/fingerprint exatos. Backup alvo de{target['verified_at']}verificado; renovar se expirado, se o destino/revisão divergir ou depois de alterações relevantes. Não converter o backup em readiness por escrever booleans sem ensaio. O atual não autoriza aplicação.
3. Agrupar a designação humana: dois titulares legítimos confirmados com MFA, terceiro gestor existente para aprovação do bootstrap, âmbitos/perfis funcionais e substitutos aplicáveis. Não escolher pessoas, criar organizações ou atribuir representações para demonstrar. O calendário/SLA permanece separado. Pedir autorização somente quando o pacote e estes serviços/ensaios estiverem completos.
4. Após autorização própria: disponibilizar o código/bundle com as flags do workflowOFF, COUNTRY02ON. Se for necessário reinício para a revisão, usar o controlo normal do Laragon e conferir vhosts; este delta não altera TLS, PHP ou DLL. Prévia autenticada em`/management/opportunity-resources-install`: conferir target/hash/schema/backup/dependências. POST nessa rota com`hash,confirm,idempotency_key,reason`, mantendo a chave nas repetições. Aplicar **somente030000** se ausente/coerente. Já instalado: changed=false; não reinstalar. Parar antes de aplicar em qualquer divergência.
5. Só depois: prévia autenticada`/management/profile-install`,030000presente e íntegro. POST com os mesmos quatro campos e chave da operação040000; aplicar **somente040000**. Bootstrap é ação distinta`/management/profile-install/bootstrap`, titulares designados e aprovação independente, sem concessões automáticas.190000/050000 não integram a aplicação deste pacote.
6. Disponibilização funcional separada: habilitar só profile_access, opportunity_dashboard_access e opportunity_specialized_drafts conforme autorização. Manter modulares, legado e percurso integradoOFF; nenhuma importação/publicação automática. Concessões pelo fluxo existente com representação e tipo legítimos. Mostrar Minhas oportunidades/Nova oportunidade ou pedido de perfil conforme autorização.
7. Critérios de sucesso: dashboard→rascunho→guardar/recuperar→documentos/scan→submeter→correção→revisões independentes→aprovação→publicação explícita restrita/teaser→nova sessão. Hashes/renda/documentos/estados preservados; mesma tentativa não duplica dados. Gestor sem âmbito, organização alheia, visitante e grant revogado recusados em rotas próprias/genéricas/URLsassinados. Auditoria e COUNTRYO2(050000)intactos. Reutilizar A/B,C37 e critérios anteriores; repetir apenas o que a mudança ou o uso real afetar.
8. Contenção: desligar só flags deste workflow, **conservar a correção C126** e negar binários privados sem perfil vigente. Preservar COUNTRY02ON,HTTPS,cookie,chaves,DDL,dados/documentos/histórico. Nunca repor o serviço binário vulnerável. Falha DDL deve manter transação/ledger coerentes. Após uso, não executar down/drop ou restauro completo; eventual restauro exige decisão separada e reconciliação das escritas posteriores.
R-ARCHIVE e R-DRAFT continuam propostas separadas, sem implementação nesta entrega. Arquivo não é exclusão lógica do rascunho; nenhum restauro de oportunidade a torna pública automaticamente. A ausência de geometrias deve ser apresentada claramente; mostrar limites oficiais exige pacote/importação autorizados à parte.
'''
procedure=procedure.replace('COUNTRYO2','COUNTRY02').replace('COUNTRY02ON','COUNTRY02 ON')
procedure=re.sub(r'(\d+)(ficheiros|assets|tabelas|colunas)(?=\b)',r'\1 \2',procedure)
(out/'procedimento.md').write_text(procedure,encoding='utf8');(out/'evidencias-por-criterio.json').write_text(json.dumps({'new':core['QA_evidence'],'reused':core['reused_evidence'],'quality':quality},ensure_ascii=False,indent=2),encoding='utf8')
archive='investmoz-workflow-'+revision[:12]+'.zip'
with zipfile.ZipFile(out/archive,'w',zipfile.ZIP_DEFLATED)as z:
for file in sorted(payload.rglob('*')):
if file.is_file():z.write(file,file.relative_to(out).as_posix())
for n in ['manifesto.json','relatorio.md','procedimento.md','evidencias-por-criterio.json']:z.write(out/n,n)
with zipfile.ZipFile(out/archive)as z:
assert z.testzip()is None and len(z.namelist())==176
for n,h in payload_hashes.items():assert hashlib.sha256(z.read('payload/'+n)).hexdigest()==h
receipt={'revision':revision,'code_revision':scope['code_revision'],'archive':archive,'sha256':sha(out/archive),'manifest_sha256':sha(out/'manifesto.json'),'payload_files':172,'members':176,'activation_ready':False,'activation_authorized':False}
(out/'pacote.json').write_text(json.dumps(receipt,indent=2)+'\n',encoding='utf8')
note='\n<!-- workflow-scope-qa-20261011 -->\n## Candidata delimitada e proteção documental — 11/10/2026\n\nC126corrigido somenteemQA: rota geral não contorna a autorização da oportunidade;originais/derivadas/hash e revogação de perfil demonstrados. ACL individual continua indisponível e anunciada; restauração R-ARCHIVE/R-DRAFT é decisão separada. Seis categorias base, variantes imobiliárias excluídas guardáveis em preparação mas bloqueadas na submissão, com erros semânticos preservados.\n\nNova candidata **'+revision[:12]+'**, código **'+scope['code_revision'][:12]+'**;14ficheiros+158assets recompilados. [Relatório/âmbito/ACL/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ência8af63e2fe657permanece intacta mas está impedida para disponibilização porC126.\n\nBackup/restauro do DESTINO REAL renovado em'+target['verified_at']+':288tabelas/31sequências/799ficheiros/2MFA; somenteleituraativa e restauroemQA55441. Não é autorização/prontidão: ClamD destinoobsoleto,worker/GD,ensaio deatualização declone ereadiness/authprévia/titulareslegítimos permanecem dependências. QAfuncional295tabelas/31sequências/288ficheiros/17MFA. COUNTRY02ativo/A-B/C37preservados;190000separado. Mesma matriz3401requisitos/7739ocorrências;10IDsatualizados,0novos e estados preservados.\n'
for name in ['relatorio-executivo.md','backlog.md','workflow-criterios-saida-20261011.md','plano-ativacao.md']:
file=root/'docs/investmoz/v2/auditoria-portal'/name;text=file.read_text(encoding='utf8');assert '<!-- workflow-scope-qa-20261011 -->'not in text;file.write_text(note+'\n'+text,encoding='utf8')
(previous/'IMPEDIMENTO-C126.md').write_text('Referência selada preservada. C126 descoberto emQA impede disponibilizar este pacote. A correção e a delimitação posteriores estão em../investmoz-opportunity-workflow-scope-20261011/relatorio.md. Nenhuma alteração no ZIP/manifesto antigos.\n',encoding='utf8')
print(json.dumps(receipt))