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

282 lines
49 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.
"""Reconcile this bounded increment into the original matrix, never another inventory."""
from pathlib import Path
from datetime import datetime, timezone
import collections, csv, hashlib, json, sys
csv.field_size_limit(50_000_000)
ROOT = Path(__file__).resolve().parent.parent
DOC = ROOT / 'docs/investmoz/v2/auditoria-portal'
PRIVATE = ROOT / 'storage/app/private/investmoz-v2/portal-audit'
PACKET = PRIVATE / 'revision-candidate-finalize-20261011'
PACKET.mkdir(exist_ok=True)
TAG = '[QA Response/materialidade/consumidores/candidata 11/10/2026] '
def sha(path):
return hashlib.sha256(path.read_bytes()).hexdigest()
def load(path):
data = path.read_bytes()
return json.loads(data.decode('utf-16' if data.startswith(b'\xff\xfe') else 'utf-8-sig'))
def rows(path):
return list(csv.DictReader(path.open(encoding='utf-8-sig', newline='')))
def write(path, data):
path.write_text(json.dumps(data, ensure_ascii=False, indent=2), encoding='utf-8')
remaining = {
'AUD-OP-03': 'Materialidade editorial/preço demonstrada no imóvel31: conserva pareceres e assinaturas originais, reabre somente property_review no preço, exige nova aprovação final independente e publicação restrita. Restam alterações materiais/condições por consumidor nas outras categorias, pareceres/atribuições/coedição/múltiplos papéis na interface, responsabilidade/calendário/SLA e regras funcionais modulares por decidir. Suspensão/recurso/republicação anteriores conservam as provas próprias; não as repetir nem fechar o workflow integral.',
'AUD-OP-04': 'Response recupera completion aplicável/erros atuais, metadados de raiz/recurso da auditoria existente, UUIDs/versões de itens canónicos e exclusões históricas; IDs/código/estados legados conservados e diferenças normativas explícitas. §27 integral NÃO encerrado: bigint raiz/ator/setor, referência não INV-MZ, criação histórica desconhecida, estados de revisões paralelas, condições com responsável/prazo/evidência e suspensão automática continuam distintos. Restam contratos/condições semânticas e consumidores já inventariados; quatro módulos/ofertas ainda experimentais, sem aprovação inferida da integração do legado.',
'AUD-COUNTRY-02': 'CAND-COUNTRY02-20261011 delimita21ficheiros, extensão050000 independente de030000/040000/190000,2colunas/0tabelas/0contas/0organizações. Mestre/cópia explícita/histórico/recuperação/permissões demonstrados em QA; instalação a frio e consumidor profile demonstrados no clone restaurado do ativo com os demais recursos ausentes. Ativo sem extensão/colunas e sem organizações. Faltam compilação de produção preparada em diretório separado, dados/representações reais autorizados e autorização de ativação separada; readiness não é instalação.',
'AUD-COUNTRY-04': 'Acrescentado percurso fundiário de suporte com paísMZ e origemPT separados e recuperados no pedido ligado. Não encerra todos os formulários públicos/legados: restam serviços/condições, origem desconhecida, cancelamento/arquivo e filtros combinados por consumidor já enumerado. Constituição da organização não é residência nem origem da solicitação.',
'AUD-COUNTRY-07': 'País do requerente e origem declarada com nomes distintos no formulário fundiário; rascunho incompleto com erro/foco conservado; gravação de apoio, nova sessão, recuperação pelo gestor e recusa ao estranho demonstradas. Restam demais categorias de apoio/requerente, seletores territoriais deste formulário por ramo, filtros combinados e operações documentais aplicáveis; não confundir país do requerente com constituição da entidade.',
'AUD-STATS-01': 'Assistência fundiária ligada a um único pedido; interface management/interests?origin=PT mostra interestsPT=1, deskPT=0 e businessesPT=0 em coleções distintas. RepetiçãoPOST409 conserva versão/referência e SQLREADONLY confirma uma única ligação. Restam outros serviços/consumidores, não informado, filtros combinados e indicadores setoriais; uma origem corrigida não encerra a estatística transversal.',
'AUD-GEO-01': 'Seleção provincial pública em /zonas usa opções preenchidas e parâmetroprovince_id real; prova anterior provincial preservada. Restam consumidores e hierarquias explicitamente inventariados, seleção/gravação/recuperação por formulário e cobertura cartográfica; não fechar por simples contagem da API.',
'AUD-GEO-02': 'Pesquisa/filtro provincial de zonas demonstrados. Restam distritos/postos/localidades nos consumidores já enumerados, armazenamento de seleções, geometria própria e leitura em mapas. Preservar408correspondências/173pendências e Mandie/Guro ambíguo; nenhuma importação neste lote.',
'AUD-GEO-03': 'Ficha pública Parque Industrial Balama: ação de mapa por teclado, resposta200 e estado explícito sem geometria própria autorizada,390px sem extravasamento após C107. GestãoQA17: seletor e limite administrativo por consumidor; editor não contém o mapa de geometrias próprias da ficha pública. GestorQA14 sem MFA recebe403 fundamentado; não reduzir segurança. Território cadastrado/limite administrativo não prova geometria da zona. Restam geometria própria oficialmente correspondente, mapas/seletores dos restantes consumidores, pública e privada por perfil; não inferir parcela/associação territorial.',
'AUD-LIB-02': 'Recurso público Agronegócio6 retirado temporariamente pelo mecanismo existenteQA: URL previamente200→404 na mesma sessão e noutra sessão; gestor mantém original200/hash; divulgação reposta e hash igual. Não houve reimportação/scan fictício/duplicação. Restam conflitos de identidade/versão/importações pendentes, PDFs/exportações/derivadas e retirada por outros consumidores/perfis. Mercado Financeiro permanece privado.',
'AUD-SCAN-01': 'ClamD real e derivadas com scans próprios anteriormente demonstrados conservados. OriginalJSON do recurso6 manteveclean e SHA=scannedSHA na retirada/reposição; não é nova análise nem prova de novas derivadas. Restam gerações/exports/derivadas por consumidores e formatos ainda enumerados; indisponibilidadeClamD restringe apenas operações afetadas.',
'AUD-UI-01': 'Fundiário: erros levam à etapa/campo, aria-invalid/describedby, resumo nomeado, países distintos; zonas: filtro, ação mapa teclado e apresentação390px; biblioteca: retirada/reposição e atualização de acesso. Axe sem violações nas rotas/capturas delimitadas, não equivale a acessibilidade integral. Restam leitor de ecrã, outras rotas/condições, documentos etiquetados,320px dos novos consumidores e dispositivos reais. A/B320/390px e C37QA18 conservam fecho.',
'AUD-OPS-01': 'Candidata utilizável delimitadaCOUNTRY02, sem exigir todos3386abertos; restauração correspondente de288tabelas/154ficheiros e ativos de runtime,0ausentes,2envelopesMFA originais recuperados sem exposição. Instalação050000/consumidor/reversão da flag comprovados sócloneQA. AtivoREADONLY/0escritas. Faltam compilaçãoprodução fora do ativo, dados/representações autorizados e aprovação separada de ativação. Outros recursos continuam030000→040000→09/190000 e com provas próprias atualizadas; importação e publicação cartográficas separadas.',
'AUD-DOC-01': 'Retrato datado liga C104–C107, documentos normativos, provas por consumidor e candidata delimitada à mesma matriz. Estados/versões históricas preservados, distinguindo experimentaçãoQA de aprovação modular/SLA e readiness de instalação. Restam alegações históricas sem prova individual; aceitação de fontes oficiais preservada.',
'OP-FIELD-opportunity-completion_percentage': 'Response calcula presença de requisitos aplicáveis/condições ativadas; UI e API do imóvel31 apresentam100/58campos aplicáveis, testes cobrem rascunho/condições/módulos ocultos. Não é score/validação/decisão externa. Restam demonstrações do indicador em todas as categorias/condições; preservar diferença entre Response e Create/Patch.',
'OP-FIELD-opportunity-validation_result': 'Response revalida conteúdo atual, recupera hash/submissão anterior separadamente e apresenta erros/rule/data sem gravar noGET. Recursos indisponíveis têm erro controlado; teste34asserções do adaptador mantém permissões/IDs/dados. Restam exemplos visuais de cada erro/ramo/categoria e correspondência normativa integral de estados; nenhuma aprovação externa inferida.',
'OP-FIELD-realEstate-price': 'Imóvel31 preço+25 gravado/recuperado/submetido/revisto/aprovado/publicado restritamente; apenas property_review reaberto, outros dois pareceres e assinaturas preservados. Restam variantes de condição indicativa/compromisso e demais restrições tipológicas; não atribuir esta prova aos contratos de ofertas ainda pendentes de decisão.',
}
def prepare():
receipts = ['revision-materiality-chromium-20261011.json', 'revision-land-chromium-20261011.json', 'revision-land-errors-chromium-20261011.json', 'revision-land-replay-chromium-20261011.json', 'revision-zones-chromium-20261011.json', 'revision-zones-map-chromium-20261011.json', 'revision-library-chromium-20261011.json', 'revision-persistence-qa-20261011.json']
for name in receipts:
proof = load(PRIVATE/name)
assert proof.get('result') == 'passed', name
assert proof.get('active_writes', 0) == 0, name
candidate = load(ROOT/'outputs/investmoz-country02-candidate-20261011/manifesto-candidata.json')
assert candidate['candidate_revision_fixed'] and not candidate['installation_active'] and not candidate['activation_ready']
selected_sources = ['app/Services/OpportunityReviewMateriality.php', 'app/Services/OpportunityCanonicalValue.php', 'app/Services/OpportunityCanonicalMetadata.php', 'app/Services/OpportunityCompleteness.php', 'app/Services/OpportunityCanonicalResponse.php', 'app/Services/OpportunityManagement.php', 'app/Services/OpportunityProfileWorkflow.php', 'app/Services/OpportunityCanonicalContract.php', 'app/Services/OpportunityCategoryFields.php', 'app/Services/OrganizationCountryInstallation.php', 'app/Services/OrganizationCountry.php', 'app/Services/PortalProfileAccess.php', 'app/Services/PortalProfileInstallation.php', 'resources/js/pages/management/opportunity-draft.tsx', 'resources/js/pages/management/opportunity-install.tsx', 'resources/js/pages/land-assistance/form.tsx', 'resources/js/components/interactive-investment-map.tsx', 'resources/js/components/territorial-source-evidence.tsx', 'config/portal.php', 'tools/opportunity-resources-qa.php', 'tests/Feature/OpportunityCanonicalWorkflowTest.php', 'tests/Feature/OrganizationCountryTest.php']
scope = {'at': datetime.now(timezone.utc).isoformat(), 'same_matrix': True, 'active_writes': 0, 'preserved': ['A/B integrated QA acceptance', 'C37 QA18 authentication', 'official source acceptance', 'sources/versions/IDs'], 'tested_sources': {p:sha(ROOT/p) for p in selected_sources}, 'receipts': {p:sha(PRIVATE/p) for p in receipts}, 'candidate': candidate, 'regression': load(PRIVATE/'revision-regression-final-20261011.log'), 'response_root_regression': load(PRIVATE/'revision-response-root-regression-20261011.log'), 'phpstan': load(PRIVATE/'revision-response-root-stan-20261011.log'), 'quality': load(PRIVATE/'revision-quality-result.json'), 'build': load(PRIVATE/'revision-build-result.json')}
assert scope['phpstan']['result'] == 'passed' and scope['response_root_regression']['result'] == 'passed'
scope['increment_revision_sha256'] = hashlib.sha256(json.dumps(scope['tested_sources'], sort_keys=True).encode()).hexdigest()
scope['evidence_limitations'] = ['Materiality browser receipt v1 scalar price/title; v2 coordinate ordering proven by isolated semantic test, not another browser geometry edit.', 'Full regression23/552 followed by targeted Response root1/34 after metadata addition; not a global proof.', 'Library original withdrawal is not a new scan or proof of all generated PDFs.', 'COUNTRY02 source candidate is fixed; production assets not staged; active authorization separate.']
write(PRIVATE/'revision-final-scope-qa-20261011.json', scope)
notes = {
'workflow': 'ChromiumQA21→24→25 na oportunidade31: títuloeditorial conserva3pareceres/hashoriginais; preço reabreproperty_review,conserva2pareceres, nova aprovação final independente/publicação restrita. Response200,completion100,UUIDinline e estranho23=403. Recibo revision-materiality. Regra v2 preserva ordem de coordenadas; teste semântico distingue reordenação de geometria de ordem de chaves, migração do escopo preserva hash/regra anteriores.',
'response': 'AdaptadorOpportunityCanonicalResponse-3 recupera raiz/recurso/UUIDinline/versão/autor/tempo/exclusão da auditoria e JSONexistentes;GET não altera registo. Completion requerido/aplicável e validação atual separadas de assinatura antiga. normative_response_complete=false por diferenças enumeradas. Metadados históricos desconhecidos permanecemnull, não inventados. FonteDOCX integralmente lida,§26.1/27/28.1; testes23/552 e complementoResponse1/34 com guardunitQA.',
'land': 'QA21 /investidor/terra-duat/01a12568-c330-73ec-a1ac-543ac0116a26: paísMZ/origemPT→grava apoio302→support_requestedv5→nova sessão recupera→gestor14recupera→estranho23=404. Rascunho incompleto01a1284f-8b99-7384-bc6a-8bc6b5d68dab conserva erros e foco na etapa certa. País do requerente e País de origem declarado com nomes distintos. Recibos revision-land/land-errors/land-replay e SQLREADONLY.',
'stats': 'GET /management/interests?origin=PT mostra interestsPT1/deskPT0/businessesPT0; repetiçãoPOSTassistência409 conserva versão/referência; SQLREADONLY comprova uma ligação case→interest_request, sem duplicação. Não somar coleções diferentes como demonstração universal.',
'maps': 'GET público /zonas filtra province_idCaboDelgado com opções preenchidas. /zonas/ed4d2fba-95d5-42e3-8ca7-1f520de1036a: tecladoEnter→zone-map200→features0/estado explícito→390px sem extravasamento; nenhum polígono próprio inventado. Editor management/investment/zones comQA17 consulta seletor/limite administrativo, não o mapa próprio público. QA14=403porMFA não confirmado, sem alteração de privilégios. revision-zones/revision-zones-map; C107 corrige referência textual longa e contém tiles.',
'library': 'Recurso6/Agronegócio: divulgação existente retiradaQA→URL200/404 na mesma sessão/nova sessão→gestororiginal200mesmoSHA→divulgação existente restaurada→público200mesmoSHA. originalJSONscan clean/SHA=scannedSHA confirmado porSQLREADONLY; semupload/importação/novo scan. revision-library + revision-persistence.',
'candidate': 'CAND-COUNTRY02-20261011, fingerprint'+candidate['code_hash']+':21fontes,0500002colunas/0tabelas/0contas/0organizações,030000/040000/190000 não exigidos. Backup ativoREADONLY restaurado'+str(candidate['backup']['tables'])+'tabelas/'+str(candidate['backup']['files_and_runtime_assets'])+'ficheiros,0ausentes;2MFAoriginais recuperados. Cloneguard55441 comprova instalação autenticada/policyMFAfixture, repetição/consumidorprofile/reversãoflag. NÃO provaMFAvisualnovo/neminstalaçãoativa. ManifestoQA correspondente,buildativoinalterado;produçãoassetspendente.',
}
mapping = {key: ['response','workflow'] for key in remaining}
for key in ['AUD-COUNTRY-04','AUD-COUNTRY-07']: mapping[key] = ['land','stats']
mapping['AUD-STATS-01'] = ['stats','land']
for key in ['AUD-GEO-01','AUD-GEO-02','AUD-GEO-03']: mapping[key] = ['maps']
for key in ['AUD-LIB-02','AUD-SCAN-01']: mapping[key] = ['library']
for key in ['AUD-COUNTRY-02','AUD-OPS-01']: mapping[key] = ['candidate']
mapping['AUD-UI-01'] = ['land','maps','library']
mapping['AUD-DOC-01'] = ['candidate','response']
edits = []
for filename in ['matriz-requisitos.csv','backlog-priorizado.csv']:
path = DOC/filename
source = list(csv.reader(path.open(encoding='utf-8-sig',newline='')))
headers = source[0]
patches = []
for index,row in enumerate(source[1:],1):
old = dict(zip(headers,row)); key = old['ID']
if key not in remaining: continue
evidence = ' | '.join(notes[n] for n in mapping[key])+' Evidência ligada:revision-final-scope-qa-20261011.json. Sem ativação.'
if filename.startswith('matriz'):
values = {f:old[f]+' | '+TAG+evidence for f in ['Evidência funcional','Resultado apresentado']}
values['Lacuna'] = TAG+remaining[key]
values['Interface'] = old['Interface']+' | '+TAG+evidence+' Os testes técnicos de ordem geométrica/metadados de raiz não são convertidos em aceitação visual.'
values['API'] = old['API']+' | '+TAG+'Pedidos reais18097/alvoQA; estados por consumidor nos recibos;GETResponse sem gravação e recusas403/404/409 preservadas.'
values['Gravação'] = old['Gravação']+' | '+TAG+'Serviços/JSON/organizações/audit_events existentes; SQLREADONLY e guardaunitQA; hashes assinados antigos preservados; nenhuma escrita ativa.'
values['Recuperação'] = old['Recuperação']+' | '+TAG+'Novas sessões fundiário/biblioteca e consumidores já provados; clonecountry profile sem demais instalações. A/B/C37 não repetidos.'
values['Consolidação'] = old['Consolidação']+' | '+TAG+'Critérios demonstrados individualmente; estados globais conservados; candidataCOUNTRY02 separada das exclusões.'
versions = json.loads(old['Critérios e versões por origem (JSON)'])
for version in versions:
version['revision_20261011'] = {'at':scope['at'],'evidencia':evidence,'criterios_restantes':remaining[key],'active':False}
values['Critérios e versões por origem (JSON)'] = json.dumps(versions,ensure_ascii=False,separators=(',',':'))
if key in ['AUD-COUNTRY-02','AUD-OPS-01']:
values['Disponibilidade ativa'] = old['Disponibilidade ativa']+' | '+TAG+'SnapshotREADONLY restaurado correspondente:030000/040000/09-190000/050000ausentes;organizations0;paísflagfalse/colunasausentes;produçãoassetspendente.'
if key.startswith('OP-FIELD-'):
values['Rotas'] = old['Rotas']+' | GET /minhas-oportunidades/{id}/response; PUT /minhas-oportunidades/{id}/draft; POST /minhas-oportunidades/{id}/workflow'
else:
values = {'Pendência':TAG+remaining[key]+' | Histórico preservado:'+old['Pendência'], 'Aceitação':old['Aceitação']+' | '+TAG+evidence, 'Correção/componentes':old['Correção/componentes']+' | '+TAG+'Reutilizar entidades/serviços existentes; executar somente critérios restantes por consumidor; módulos/ofertas/SLA dependem das decisões explícitas, não da aprovação do legado.'}
patches.append({'row':index,'values':values})
assert len(patches) == len(remaining), (filename,len(patches))
(PACKET/(filename+'.before')).write_bytes(path.read_bytes())
edits.append({'filename':filename,'input_sha256':sha(path),'rows':source,'patches':patches,'allowed_columns':sorted({f for p in patches for f in p['values']})})
write(PACKET/'csv-edits.json',{'files':edits})
write(PACKET/'criterion-evidence.json',{'scope':scope,'notes':notes,'remaining':remaining})
print(json.dumps({'prepared':len(remaining),'csv_authored':False,'revision':scope['increment_revision_sha256']}))
def verify():
packet = load(PACKET/'csv-edits.json')
for edit in packet['files']:
now = list(csv.reader((DOC/edit['filename']).open(encoding='utf-8-sig',newline='')))
before = edit['rows']; assert len(now) == len(before)
patchmap = {p['row']:p['values'] for p in edit['patches']}
for i,(old,new) in enumerate(zip(before,now)):
for j,(a,b) in enumerate(zip(old,new)):
field = before[0][j]
assert b == patchmap.get(i,{}).get(field,a), (edit['filename'],i,field)
if edit['filename'].startswith('matriz'):
for i in patchmap:
col = before[0].index('Critérios e versões por origem (JSON)')
a,b = json.loads(before[i][col]),json.loads(now[i][col]); assert len(a)==len(b)
for old,new in zip(a,b): assert all(new[k]==v for k,v in old.items())
current = rows(DOC/'matriz-requisitos.csv')
counts = {'requirements':len({r['ID']for r in current}),'rows':len(current),'source_occurrences':sum(int(r['Quantidade de linhas de origem'])for r in current),'states':dict(collections.Counter(r['Estado']for r in current)),'updated_requirements':len(remaining),'all_original_cells_except_targeted_changes_preserved':True,'historical_source_versions_preserved':True,'active_writes':0}
assert counts['requirements']==counts['rows']==3393 and counts['source_occurrences']==6845
assert counts['states']['concluído e demonstrado']==7
write(PACKET/'preservation.json',counts)
print(json.dumps(counts,ensure_ascii=False))
def map_note():
note = 'Observação visualC108: fundo OpenStreetMap indisponível na captura pública; causa de transporte externa ainda não determinada. Lista/limite interno e estado de geometria própria ausente continuam apresentados. Verificar o fundo deste consumidor separadamente; não aceitar mapa integral nem confundir essa indisponibilidade com geometria própria ausente.'
earlier_note = note
diagnosis = sys.argv[1] == 'map-diagnosis'
if diagnosis:
note = 'ObservaçãoC108 esclarecida: o harness bloqueava todos os pedidos fora doQA por route.abort; não foi falha do portal/rede/TLS. Ensaio separado permite somenteGETde tile.openstreetmap.org:200/3tilescarregados/fundo apresentado. Recibo revision-map-basemap-allowed-20261011.json. Geometria própria da zona continua ausente; fundo e limite administrativo não a substituem.'
directory = PRIVATE/('revision-candidate-map-diagnosis-20261011' if diagnosis else 'revision-candidate-map-note-20261011'); directory.mkdir(exist_ok=True)
edits=[]; initial=load(PACKET/'csv-edits.json')
for filename in ['matriz-requisitos.csv','backlog-priorizado.csv']:
path=DOC/filename; source=list(csv.reader(path.open(encoding='utf-8-sig',newline=''))); headers=source[0]
row=next(i for i,r in enumerate(source) if r[0 if filename.startswith('matriz') else 1]=='AUD-GEO-03')
old=dict(zip(headers,source[row]))
if filename.startswith('matriz'):
variants=json.loads(old['Critérios e versões por origem (JSON)'])
for v in variants: v['map_base_diagnosis_20261011' if diagnosis else 'map_base_visual_20261011']={'observacao':note,'source':'revision-map-basemap-allowed-20261011.json' if diagnosis else 'revision-zones-map-chromium-20261011/zone-map-public.png','active':False}
values={'Lacuna':old['Lacuna'].replace(earlier_note,note) if diagnosis else old['Lacuna']+' | '+note,'Evidência funcional':old['Evidência funcional'].replace(earlier_note,note) if diagnosis else old['Evidência funcional']+' | '+note,'Critérios e versões por origem (JSON)':json.dumps(variants,ensure_ascii=False,separators=(',',':'))}
else:
values={'Pendência':old['Pendência'].replace(earlier_note,note) if diagnosis else old['Pendência']+' | '+note}
edits.append({'filename':filename,'input_sha256':sha(path),'rows':source,'patches':[{'row':row,'values':values}],'allowed_columns':list(values)})
baseline=next(x for x in initial['files']if x['filename']==filename)
patch=next(x for x in baseline['patches']if x['row']==row); patch['values'].update(values)
write(directory/'csv-edits.json',{'files':edits})
write(PACKET/'csv-edits.json',initial)
scoped=load(PRIVATE/'revision-final-scope-qa-20261011.json')
if diagnosis:
scoped['additional_visual_finding_history']=[scoped['additional_visual_finding']]
proof=load(PRIVATE/'revision-map-basemap-allowed-20261011.json'); assert proof['result']=='passed' and proof['loaded_tiles']==3
scoped['receipts']['revision-map-basemap-allowed-20261011.json']=sha(PRIVATE/'revision-map-basemap-allowed-20261011.json')
scoped['additional_visual_finding']=note
write(PRIVATE/'revision-final-scope-qa-20261011.json',scoped)
evidence=load(PACKET/'criterion-evidence.json'); evidence['scope']=scoped; write(PACKET/'criterion-evidence.json',evidence)
print(json.dumps({'prepared_additional_map_criterion':True,'same_requirement':'AUD-GEO-03','csv_authored':False}))
def documents():
evidence = load(PACKET/'criterion-evidence.json')
preserved = load(PACKET/'preservation.json')
scope = evidence['scope']; candidate = scope['candidate']; backup = candidate['backup']
revision = scope['increment_revision_sha256']
intro = f'''## Lote QA de 11/10/2026 — Response, materialidade e candidata COUNTRY02
**A/B e C37 QA18 conservam os fechos aceites. Zero escritas na base ativa.** Esta secção é o retrato vigente dos critérios abaixo; as secções anteriores são retratos datados, preservados como histórico. Não aprova módulos/ofertas/SLA nem ativa funcionalidades. Revisão do incremento QA: `{revision}`. Candidata independente: **CAND-COUNTRY02-20261011**, `{candidate['code_hash']}`.
| Requisito / consumidor | Resultado demonstrado | Critério que continua aberto |
| --- | --- | --- |
| OP03 — imóvel31, Promotor21 → Validador24 → Publicador25 | Correção editorial conserva os três pareceres e os seus hashes originais; preço+25 reabre somente `property_review`, conserva os outros dois; nova aprovação final independente e publicação restrita. Interface → pedidos reais → versões/eventos persistidos → recuperação/Response. | Materialidade de outras categorias/recursos na interface; condições com responsável/prazo/evidência; coordenação/atribuições por consumidor. Aprovação interna não representa decisão externa. |
| OP04 / Response / §27 | Metadados de raiz/recursos recuperados da auditoria; UUIDs e versões de itens no JSON canónico existente; exclusões históricas conservadas; completude dos campos aplicáveis e erros atuais apresentados; GET sem alteração do registo, estranho403. | `normative_response_complete=false`: bigint raiz/atores/setor, referência existente, metadados históricos desconhecidos, estados paralelos sem equivalência inventada. Não criar tabelas/cadastros paralelos. |
| COUNTRY07/04, STATS01 — assistência fundiária | QA21 guarda apoio com paísMZ/origemPT, recupera noutra sessão; gestor recupera, estranho404. Estatística `interestsPT=1`; as coleções Balcão/negócios permanecem distintas. Repetição409 e exatamente um pedido ligado, mesma versão/referência. | Outros tipos de apoio/requerente, origem desconhecida, cancelamento/arquivo e filtros combinados por consumidor. Constituição, residência e origem continuam dados diferentes. |
| GEO01/02/03 — zonas | Filtro provincial público preenchido e parâmetro real; ficha pública Balama: teclado→API200→estado explícito de geometria própria ausente;390px sem extravasamento. Editor autorizadoQA17 consulta seletor/contexto administrativo; QA14 sem MFA recebe403 fundamentado. | Geometria administrativa não é geometria da zona. Falta geometria própria correspondente e consumidores restantes. Não inferir parcela ou associação. 408 correspondências/173 pendências anteriores preservadas; Mandie/Guro ambíguo; nenhuma importação neste lote. |
| LIB02/SCAN01 — recurso6 Agronegócio | Retirada temporária da divulgação existente: público200→404 na sessão anterior e noutra sessão; gestor mantém original200/hash; reposição pública200/hash igual. Scan próprio anterior clean/SHA correspondente, sem novo upload/importação. | Outros formatos/consumidores, PDFs/exportações/derivadas e versões/conflitos de importação. Esta retirada não é uma análise ClamD nova; provas ClamD real anteriores preservadas. Mercado Financeiro privado. |
| UI01 | Erros fundiários regressam à etapa/campo e têm rótulo/foco/descrição; países nomeados separadamente; zonas390px e ação por teclado; axe/capturas por rota nos recibos. | Acessibilidade integral, leitor de ecrã, restantes estados/rotas, PDF etiquetado,320px dos novos consumidores e dispositivos reais são critérios distintos. |
| COUNTRY02 / OPS01 | Mestre, cópia explícita e versões anteriores já demonstrados; extensão050000 independente comprovada no clone restaurado do ativo, sem030000/040000/190000. Desativar a flag preserva dados e colunas. | Compilação de produção ainda não preparada; organizações/representações reais autorizadas; futura autorização de ativação. `ready=true` só preparação. |
**Falhas corrigidas:** C104 reinício indiscriminado dos pareceres; C105 erro/foco na etapa errada do fundiário; C106 normalização que ordenava coordenadas e podia ocultar uma alteração geométrica; C107 referências longas extravasavam a ficha móvel. C106 tem prova semântica em QA isolada, distinta de uma alteração geométrica visual. C107 foi localizada por medição dos elementos: a referência textual ultrapassava o contentor; os tiles passaram também a ter contenção explícita. QA14 sem MFA é recusa autorizada, não falha corrigida por retirar segurança.
**C108 esclarecida, sem falha do portal:** a primeira captura mostrava “Mapa base indisponível” porque o harness abortava todos os pedidos fora doQA. No ensaio separado, permitido apenasGETpara `tile.openstreetmap.org`, houve200 e3tilescarregados, com fundo apresentado. Prova `revision-map-basemap-allowed-20261011.json`; não se alteraram TLS,permissões ou dados. A lista/estados e o limite administrativo interno já estavam disponíveis. Fundo carregado não resolve a ausência de geometria própria de Balama; GEO03 permanece parcial.
**Provas:** `revision-materiality`, `revision-land`, `revision-land-errors`, `revision-land-replay`, `revision-zones`, `revision-zones-map`, `revision-library` — recibos `*-chromium-20261011.json`; `revision-persistence-qa-20261011.json` comprova leitura SQL, pedido único, pareceres/assinaturas e hash/scan originais. `revision-final-scope-qa-20261011.json` liga fontes, build e recibos. Regressão23 testes/552 asserções, seguida de1teste/34asserções do adaptador após adição dos metadados de raiz; não somar como prova funcional global. PHPStan, tipos, lint e buildQA passaram. Não repetir A/B/C37.
**Mesma matriz:** {preserved['requirements']} requisitos/{preserved['rows']} linhas/{preserved['source_occurrences']} ocorrências; {preserved['updated_requirements']} requisitos atualizados, demais células e todas as referências/versões anteriores conservadas. Estados mantidos:3234parciais,145não implementados,5dependentes de decisão,2implementados mas não ativados,7concluídos e demonstrados. Fecham-se os subcritérios datados acima; os requisitos transversais continuam parciais pelas lacunas explícitas. Não promover um requisito apenas por testes ou contagem.
**Candidata COUNTRY02:**21ficheiros/050000/2colunas/0tabelas/0contas/0organizações automáticas. Backup correspondente em `{backup['at']}`: origem ativa READ ONLY; clone `{backup['restored_target']['database']}:55441`; {backup['tables']} tabelas e {backup['files_and_runtime_assets']}ficheiros/ativos restaurados,0ausentes;2envelopesMFA originais recuperados sem expor segredos. Compatibilidade do consumidor profile com os outros instaladores ausentes e reversão da flag passaram. MFA do instalador no clone foi fixture de política, não um novo login visual; provas normais anteriores mantidas. Configuração/chave efetivas, documentos e runtime foram guardados só em arquivo privado, nunca no manifesto público. Manifesto ativo permanece `{candidate['active_manifest_sha256']}`. Ativo sem030000/040000/09-190000/050000,sem as duas colunas e com0organizações; não há instalação ativa. Manifesto sanitizado: [candidata](../../../../outputs/investmoz-country02-candidate-20261011/manifesto-candidata.json).
**Decisões separadas:** financiar apenas procura ou também ofertas? fornecimento apenas pedidos de compra ou também ofertas? quem tem competência funcional em cada módulo e quais subtipos de “outros” podem submeter? Autorizar as regras propostas de intenção de concessão/condições indicativas/ausência de histórico? O SLA interno exige calendário, responsáveis e suplentes, pausas da tarefa afetada, escalamento e distinção de crítico imediato/ocorrência comum; não é prazo de entidade externa. Perguntas/regras/efeitos em [propostas-perfis-modulares-sla.md](propostas-perfis-modulares-sla.md). A aprovação da integração do legado não respondeu a essas decisões. Ensaios históricos modulares são experimentação QA, não aprovação funcional.
'''
backlog = '''## Critérios restantes por consumidor — 11/10/2026
**Projeção legível da matriz existente; A/B e C37 mantidos.** Os critérios demonstrados neste lote estão no relatório executivo. Nenhum requisito global foi encerrado por extrapolação.
| Prioridade / requisito | Rota / perfil / ação | Critério restante e próxima ação |
| --- | --- | --- |
| P1 OP03/04 | `/minhas-oportunidades/{id}`, `/draft`, `/workflow`, `/response`; Promotor21/Validador24/Publicador25 | Manter os pareceres afetados do imóvel31 já provados. Ensaiar DUAT/título/área/geometria/encargos, percentagem/finanças/governação/licenças e alteração do recurso/documento em cada consumidor; verificar conservação do hash original e reabertura só das revisões pertinentes. Condições precisam responsável/prazo/evidência e resolução antes da ação bloqueada. |
| P1 OP04 / §27 | `/minhas-oportunidades/{id}/response`; autor/representante/revisor/Auditor/estranho | Completion/erros/metadados e ausência de gravação noGET provados. Completar diferenças de estado e metadata legado sem inventar dados nem renumerar IDs. Criação histórica desconhecida é null com lacuna; primeira observação não é criação. Revisões paralelas não são um único estado normativo. |
| P1 COUNTRY02 / candidata | `/investidor/profile`, `/investidor/organizations/{id}/country`, `/investidor/companies/{id}/organization-country`, `/investidor/manifestacoes/{id}` | QA e clone concluídos no âmbito delimitado; preparar bundleprodução fora do ativo, definir dados/representações reais no cadastro mestre existente e obter autorização de ativação separada. Não copiar fixture/organização QA nem reescrever submissões anteriores. |
| P1 COUNTRY04/07; STATS01 | `/investidor/terra-duat/{id}`, `/support`, `/management/terra-duat/{id}`, `/management/interests?origin=PT`; QA21/gestor/estranho | Um apoioMZ/PT e repetição409 provados. Ensaiar restantes tipos/requerentes/seletores, origem desconhecida, filtros combinados, cancelamento/arquivo e recuperação dos campos no pedido. Rascunho incompleto não integra estatísticas de submissão. |
| P1 COUNTRY06; STATS01 | `/negocios/{slug}/manifestar-interesse`, `/investidor/negocios/{id}`, `/management/negocios`; público/investidor/revisor | País/origem e provas anteriores conservados; faltam variantes de submissão/transição/indicadores e repetição por este consumidor. Distinguir constituição/residência/origem e coleções de estatísticas. Não atribuir a negócios a prova fundiária deste lote. |
| P1 COUNTRY03/04; DESK01 | `/investidor/manifestacoes/{id}`, `/balcao-virtual/pedidos/{id}`; investidor/representante/gestor | Conservar A/B e Balcão já provados. Serviços/condições adicionais, cancelamento/arquivo, mensagens/documentos/partilhas, origens desconhecidas e filtros combinados continuam por critério. |
| P1 GEO01/02/03 | `/zonas`, `/zonas/ed4d2fba-95d5-42e3-8ca7-1f520de1036a`, `/management/investment/zones/{id}`; público/QA17 | Filtro/estado de geometria ausente/contexto administrativo provados. Falta geometria própria correspondente de Balama; editor não tem o mapa próprio da ficha pública. Ensaiar gravação/recuperação de cada nível territorial, edição/revisões e apresentação dos mapas nos restantes consumidores. Não substituir geometria ausente por limite do pai. |
| P1 GEO02/03 | `/minhas-oportunidades/{id}/resources`, `/oportunidades?view=map`, `/carteira`, `/investidor/terra-duat/{id}`, Balcão; perfis próprios | Executar os critérios restantes já listados por ramo/perfil, preservando provas anteriores.408correspondências/173pendências mantidas; Mandie/Guro fica ambíguo. Importação e divulgação continuam ações autenticadas diferentes. |
| GEO03 C108 — observação esclarecida | `/zonas/ed4d2fba-95d5-42e3-8ca7-1f520de1036a`; público | O harness bloqueava pedidos externos. Ensaio separado apenasGETpara o fornecedor de tiles apresentou200/3tiles/fundo; critério inicial do fundo demonstrado, sem falha do portal. Restam geometria própria e mapas/perfis/estados já enumerados; não declarar cobertura integral. |
| P1 LIB02/SCAN01/PART01 | `/management/biblioteca`, `/biblioteca/recursos/{id}/ficheiro`, `/parcerias/salas/{id}`, recursos de oportunidade/Balcão/zonas; proprietário/destinatário/público/gestor | Retirada/reposição do originalAgronegócio6 e revogaçãoPDF/PNG/JPG da sala1 já demonstradas; não repetir. Restam links assinados/outros âmbitos, PDFs gerados/exports/derivadas por consumidor, permissão/apresentação/scan próprio e original preservado. ClamD real separado das operações ainda não ensaiadas. |
| P1 UI01 | Formulário fundiário, zonas e biblioteca deste lote; teclado390px | Critérios datados de erro/foco/rótulos/estado/axe fechados; restam leitor de ecrã,320px destes consumidores, demais rotas/estados e PDF etiquetado. Dispositivos reais são prova distinta, não substituídos por viewport. |
| Decisão OP03/04 — ofertas/módulos | `/minhas-oportunidades/{id}/draft` e `/workflow`; perfis propostos de concessão/financiamento/fornecimento/outros | Decidir D-M1/M2/M3/M4/S1/F1/SUP1 separadamente, com regras/efeitos da proposta reconciliada. Procura≤necessidade só na procura; oferta exige contrato/percurso próprio. Rascunho incompleto permanece permitido. Não ativar contratos experimentais nem inventar aprovadores reais. |
| Decisão SLA / recuperação | `/perfis/gestao`, `/perfis/recuperacao`, filas/tarefas/coordenação | Prazos sópropostaQA, compromissos internos. Decidir calendário, titulares/suplentes, pausas/escalamento, crítico imediato vs comum. Exatamentedoissuperutilizadores/recovery exigem designação real para habilitação ativa; fixture não escolhe titulares. |
Próximo lote executável: condições de revisão/documentos e materialidade por categoria aprovada; partilha/revogação/derivadas dos consumidores restantes; país/origem/estatísticas de negócios e serviços adicionais; hierarquias/mapas restantes; acessibilidade por rota. Ofertas/SLA pendentes não bloqueiam estas frentes independentes. COUNTRY02 é entrega independente; não esperar pelos3386requisitos abertos.
'''
plan = f'''## Candidata delimitada COUNTRY02 e sequência reconciliada — 11/10/2026
**Somente preparação; ativação fora da autorização.** A/B e C37 conservam fecho. CAND-COUNTRY02-20261011 fixa21fontes com hash `{candidate['code_hash']}` e manifestoQA `{candidate['qa_assets_manifest_sha256']}`. O bundle ativo não foi alterado. Manifesto sanitizado em `outputs/investmoz-country02-candidate-20261011/manifesto-candidata.json`; dumps,originais,configuração,chave e credenciais ficam exclusivamente privados.
**Inclui:** país de constituição no mestreorganizations existente com fonte/versão/auditoria; reutilização explícita em empresas privadas/submissões sem reescrever valores históricos; memberships/policies existentes; prévia/instalação autenticada050000 e reversão por flag. **Exclui:** workflow/Response/módulos/ofertas/SLA,importação/publicação cartográfica,expansão/divulgação do legado,percurso190000,QAfixtures/contas/configuração/dados,acessibilidade integral e dispositivos reais. Essas frentes continuam na matriz e não bloqueiam o país quando as suas dependências próprias estiverem resolvidas.
### Aceitação objetiva da candidata
1. Os21hashes,composer/configuração estável e manifesto preparado correspondem à revisão fixada. Bundle de produção compilado em diretório separado, com caminhos de serviço e hashes conferidos; **ainda pendente**, não trocar o build ativo durante preparação.
2. Prévia GET `/management/opportunity-resources-install/organization-country` por gestor autorizado existente/verificado apresenta o alvo certo,050000ausente ou instalado,dependências/schema/ledger consistentes,hash esperado e prova de restauro fresca até24h. Não gerar chave nem desativarMFA.
3. POST no mesmoURL exige sessãoMFA e versão/chaveidempotência: instala apenas050000quando ausente; acrescenta2colunas/0tabelas/0contas/0organizações,regista auditoria,recusa alvo/hash/desfasamento e repetição não duplicaDDL/dados. Clone correspondente demonstrou a política autenticada; fixtureMFA do clone não é ensaio visual novo.
4. Com organização/representação autorizadas no cadastro existente: UI→PUTmestre→fonte/versão/audit→nova sessão/profile→cópia explícita emempresa/submissão→recuperação. Históricos conservados; viewer/estranho/revogação/Auditor com papéis acumulados sem edição. Provas QA já ligadas; não copiarfixture ao ativo. **Ativo contém0organizações**, logo a origem e habilitação dos dados reais permanecem dependência concreta.
5. O módulo mantém residência/constituição/origem separadas. Desativar `organization_country_enabled` retira disponibilização mas mantém2colunas,metadata,histórico e cópias anteriores. Clone comprovou consumidorprofile sem030000/040000/190000.
6. Falhas de hash,alvo,schema/ledger,MFA/permissão,duplicação,perda/regravação de histórico ou consumidor sem dados recuperáveis suspendem o corte. Sucesso por API ou `ready=true` não substitui provas nem aprovação de ativação.
### Ordem exata preparada, ainda não executada no ativo
1. Preparar/rever bundle de produção fora do diretório servido; comparar manifesto,fixar caminhos e plano de troca. Confirmar esta candidata e excluir as flags das funcionalidades parciais.
2. Conferir base/configuração efetiva somenteporleitura,instalações/colunas,organizations/memberships e elegibilidade do ator. Se mudou alvo/hash/source/runtime/prova>24h, renovar snapshot/restauro antes de aplicar; não usar backup de outra revisão.
3. Obter autorização separada para a ação ativa e para os dados/representações reais necessários. Não designar pessoas nem retirar privilégios automaticamente.
4. Ator autenticado comMFA abre a prévia,revê correspondência/impacto e aplica **apenas050000seausente**. Não executar `migrate`global nem030000/040000/190000por arrasto. Repetição consultada como instalada, sem reinstalar.
5. Habilitar só a flag do país, com configuração/cache efetivos consistentes; disponibilizar bundle fixado; manter flags dos módulos parciais no estado anterior. Cadastro real/representações pelo mecanismo existente e fontes autorizadas; sem cópia deQA.
6. Conferir consumo do mestre/cópia/histórico/permissões e auditoria no ambiente disponibilizado. Qualquer falha prevista aciona reversão abaixo. A autorização atual não executa este passo ativo.
**Sequência das outras frentes preservada:**030000(recursos)→040000(perfis/bootstrap)→2026_10_09_190000(percurso).190000continua necessária,ausente no ativo,não substituída;COUNTRY02não a exige.030/040/190 têm instalação/readiness próprios e fingerprints devem incorporar as novas fontes; provas antigas não são automaticamente atuais. Importação cartográfica autenticada e publicação de camadas ficam depois das dependências correspondentes,como ações separadas,com contagens da prévia. Não repetir instalações já presentes nem ativar cálculos por importação/publicação.
### Restauro correspondente e reversão
Backup renovado `{backup['at']}` doativoREADONLY; clone `{backup['restored_target']['database']}:55441`;288tabelas comparadas linha/hash,154ficheiros/ativos restaurados e0ausentes; chave/configuração efetiva recuperada permitiu desencriptar2MFAoriginais sem revelarsegredos. Compatibilidadecold050000/consumidorprofile/repetição/reversãoflag confirmadas no cloneQA; originais privados intactos. DumpSHA `{backup['dump_sha256']}`; archiveSHA `{backup['private_archive_sha256']}`. Hashes identificam o arquivo privado, não disponibilizam os seus conteúdos.
Reversão normal: desabilitar flag país,restabelecer configuração/cache correspondente e bundle anterior verificado; conservar2colunas,país/histórico/proveniência/cópias/audit_events. **Não executar down/drop/limpeza nemkey:generate.** Recuperação de dados se necessária requer plano específico/autorização ativa e ensaio prévio no clone; snapshot comprovado não autoriza sobrescrever alterações legítimas posteriores. Preservar chave/configuração e autenticação/MFA. O plano reversível mantém as instalações aditivas e os dados disponíveis para correção posterior.
Estado: código preparado+QA demonstrada no escopo incluído+backup/restauro correspondente; **produçãoassetspendente/ativação não autorizada/não instalada no ativo**.
'''
cases = '''## Casos novos reproduzidos — 11/10/2026
| Caso / requisito | Reprodução | Esperado / observado / causa | Correção e prova |
| --- | --- | --- | --- |
| C104 P1 OP03/04 | Imóvel31publicado; Promotor21 prepara revisão, altera sótítuloeditorial ou preço,guarda/submete. | Esperado: só pareceres substantivamente afetados reabertos; nova assinaturafinal. Antes: save/submit apagava indiscriminadamente todos os pareceres; nenhuma comparação deescopo. | Escopos pergate +baseline +hashassinadooriginal preservado; título mantém3,preço reabrepropertye mantém2; Validador24/aprovaçãoindependente/Publicador25republicação no Chromium. Não generalizar ao restante workflow. |
| C105 P1 UI01/COUNTRY07 | QA21 rascunhofundiário incompleto; avançar àconfirmação e pedirapoio sem campos anteriores. | Esperado: voltar àetapacom campo,erro associado e foco. Observado: permanecia na confirmação,campos com erro ocultos e mensagensgenéricas. Causa: validação multietapa sem direcionamento/foco apóscommitReact. | Estado pendingErrorField/useEffects,IDs/aria e resumo nomeado; rascunho recuperável,erro support_kind focado,sem submissão/estatística indevida. revision-land-errors e novo rótulo/recovery emland-replay. |
| C106 P1 OP03/04 | MesmoGeoJSON: trocar ordem de pontos mas conservar os valores; comparar assinatura deescopo antiga/atual. | Esperado: ordem coordenada ématerial; chavesJSON porordem não. Antes: Arr::sortRecursive ordenava listas e podia tornar geometrias distintas iguais. | CanonicalValue ordena apenas objetos,preserva listas; regra review-scopes-v2,comparação anterior/audit de migração conservam hashes/regraoriginais. Teste semântico isolado provaREeDUAT; não foi novo ensaio visual de edição degeometria. |
| C107 P1 GEO03/UI01 | Público: fichaBalama,carregar mapa,viewport390px; medir scrollWidth e elementos. | Esperado: largura390,semrolagemhorizontal. Observado438; referência documental longa deponteaoexemplar excedia parágrafo/contentor. Tiles também eram extensosmas não demonstravam sozinhos a causa. | overflow-wrap:anywhere no componente de fontes territoriais e contenção explícita dos tiles; reteste público390semextravasamento/axe e ação de mapa por teclado. Geometria própria continua ausente,explicitamente apresentada. |
| C108 GEO03 — observação do ensaio esclarecida | Ficha pública Balama; consultar mapa dos investimentos no ensaio Chromium. | Primeira captura: mensagemcontrolada “Mapa base indisponível”, mapa cinzento; lista/estados disponíveis. Causa identificada: o próprio harness abortava todos os pedidos deorigem diferente daQA. Não foi falha do portal/TLS. | Ensaio separado permite sóGETde tile.openstreetmap.org:200/3tiles e fundo apresentado. revision-map-basemap-allowed-20261011.json. Sem alteração de geometrias,fontes,permissões ou TLS. Geometria própria continua ausente. Não conta como defeito corrigido do portal. |
Não são casos do portal: harness com ator/propriedade/rótulo incorreto,espera pelo endpoint do mapa público no editor privado,ou espera URL antes da navegação. QA14=403porMFA não confirmado é autorização exigida; QA17mantém acesso comMFA normal. A/B/C37 conservam fecho; não se repetiram os seus percursos.
'''
blocks = {'relatorio-executivo.md':intro,'backlog.md':backlog,'plano-ativacao.md':plan,'casos-reproducao.md':cases}
begin,end = '<!-- qa-revision-20261011:begin -->','<!-- qa-revision-20261011:end -->'
for name,block in blocks.items():
path=DOC/name; old=path.read_text(encoding='utf-8-sig')
if begin in old:
start=old.index(begin); finish=old.index(end,start)+len(end)
old=old[:start]+old[finish:].lstrip('\n')
else:
(PACKET/(name+'.before')).write_bytes(path.read_bytes())
path.write_text(begin+'\n\n'+block.strip()+'\n\n'+end+'\n\n'+old,encoding='utf-8')
print(json.dumps({'documents':list(blocks),'same_matrix':True,'active_writes':0}))
if __name__=='__main__':
{'prepare':prepare,'verify':verify,'documents':documents,'map-note':map_note,'map-diagnosis':map_note}[sys.argv[1]]()