313 lines
38 KiB
Python
313 lines
38 KiB
Python
"""Current QA increment in the existing matrix and reports, with source conservation."""
|
||
import collections
|
||
import csv
|
||
import hashlib
|
||
import json
|
||
import re
|
||
import sys
|
||
from pathlib import Path
|
||
from datetime import datetime, timezone
|
||
|
||
ROOT = Path(__file__).resolve().parent.parent
|
||
PRIVATE = ROOT / 'storage/app/private/investmoz-v2/portal-audit'
|
||
DOCS = ROOT / 'docs/investmoz/v2/auditoria-portal'
|
||
csv.field_size_limit(16 * 1024 * 1024)
|
||
sys.stdout.reconfigure(encoding='utf-8')
|
||
|
||
def read(name):
|
||
return json.loads((PRIVATE / name).read_text(encoding='utf-8-sig'))
|
||
|
||
def readable(text):
|
||
replacements = {
|
||
'ClamDreal': 'ClamD real', 'HTTP18097': 'HTTP 18097', 'HTTP/MFA': 'HTTP com MFA',
|
||
'dump consistente→nova base': 'dump consistente → nova base', 'MFA/bytes': 'MFA / bytes',
|
||
'15inseridos/393reutilizados': '15 inseridos / 393 reutilizados', '15inserts/393reuse': '15 inseridos / 393 reutilizados',
|
||
'15limites': '15 limites', '393associações': '393 associações', '1990cadastros': '1.990 cadastros',
|
||
'361geometrias': '361 geometrias', '361originais': '361 originais', '47incrementaisprivados': '47 incrementais privados',
|
||
'26distritos': '26 distritos', '147postos': '147 postos', '173pendentes': '173 pendentes',
|
||
'0contas/grants/flags': 'zero contas, atribuições ou flags', '0documentos': 'zero documentos',
|
||
'3coleções': 'três coleções', '4tabelas+1colunausers+3colunasoportunidades': 'quatro tabelas + uma coluna em users + três em opportunities',
|
||
'2colunas+1definição publicada19etapas/5fases;0dossiers': 'duas colunas + uma definição publicada de 19 etapas / cinco fases; zero dossiers',
|
||
'190000faltantes': '190000 faltantes', '0fixtures QA noativo': 'zero fixtures QA no ativo',
|
||
'2contas explicitamente designadas,3.ºgestor comMFAaprova': 'duas contas explicitamente designadas; um terceiro gestor com MFA aprova',
|
||
'porperfil': 'por perfil', 'porformulário': 'por formulário', 'porconsumidor': 'por consumidor',
|
||
'futura candidata/ativo apósvisual': 'futura candidata e do ativo após demonstração visual',
|
||
'apósvisual': 'após demonstração visual', 'Semvisual': 'Sem demonstração visual', 'semvisual': 'sem demonstração visual',
|
||
'porcritério': 'por critério', 'nolote': 'no lote', 'nolhistórico': 'no histórico',
|
||
'todosos': 'todos os', 'Todasasfontes': 'Todas as fontes', 'fontes,nomes econtagensanteriores seguemabaixo': 'fontes, nomes e contagens anteriores seguem abaixo',
|
||
'umdos': 'um dos', 'usadosimutáveis': 'usados imutáveis', 'perfís': 'perfis',
|
||
'nopróprio': 'no próprio', 'doPublicador': 'do Publicador', 'doPromotor': 'do Promotor',
|
||
'contas/grants/titulares': 'contas, atribuições ou titulares', 'scanClamD': 'scan ClamD',
|
||
'emUI': 'na interface', 'UI.': 'interface.', 'ledger+duascolunas': 'ledger + duas colunas',
|
||
'backupDB+originais+runtime/chaveestável': 'backup da base + originais + runtime e chave estável',
|
||
'VisualC37/QA21/A-B ecorreções→fixar candidata/hash/manifesta/config': 'Demonstração visual de C37, QA21 e A/B e correções → fixar candidata, hashes, manifesto e configuração',
|
||
'→backup': ' → backup', '→restauro': ' → restauro', '→scan': ' → scan', '→download': ' → download',
|
||
'→novaMFA': ' → nova sessão com MFA', '→retry': ' → repetir', '→rollback completo': ' → reversão completa',
|
||
'aprovado/configurado': 'aprovado ou configurado', 'mfa': 'MFA',
|
||
'prévia/schema/dependências030+020': 'prévia, schema e dependências 030000 + 020000',
|
||
'dependências/preview/hash/MFA': 'dependências, prévia, hash e MFA', 'noopsemDDL': 'operação sem alterações DDL',
|
||
'noop auditado': 'operação sem alterações, auditada', 'Runtimeestável': 'runtime estável',
|
||
'Publicador porversão': 'Publicador por versão', 'arquivo privado QA+runtime': 'arquivo privado QA + runtime',
|
||
'confiabilidade': 'credibilidade', 'mostraJSON': 'mostra JSON', 'falha auditiva': 'falha de auditoria',
|
||
}
|
||
for old, new in replacements.items():
|
||
text = text.replace(old, new)
|
||
return text
|
||
|
||
checks, http, office = [read(n) for n in ['profile-installation-checks-qa.json', 'profile-installation-http-qa.json', 'opportunity-office-documents-http-qa.json']]
|
||
independent = read('independent-documents-http-qa.json')
|
||
document_verify = read('independent-documents-verification-qa.json')
|
||
geo = read('geometry-crosswalk-v2-http-qa.json')
|
||
preserve = read('geometry-crosswalk-preservation-qa.json')
|
||
active = read('dashboard-active-readonly.json')
|
||
backup = json.loads((PRIVATE.parent / 'portal-profile-installation/readiness.json').read_text(encoding='utf-8-sig'))
|
||
assert all(x['result'] == 'passed' and x['active_writes'] == 0 for x in [checks, http, office])
|
||
assert all(x['result'] == 'passed' and x['active_writes'] == 0 and not x['interface_observed'] for x in [independent, document_verify])
|
||
assert len(independent['land']) == len(independent['room']) == 3 and len(document_verify['documents']) == 6
|
||
assert checks['code_hash'] == http['code_hash'] == backup['code_hash']
|
||
assert geo['applied'] and geo['active_writes'] == 0 and not geo['published']
|
||
assert preserve['catalogue_preserved'] and preserve['original_geometries_preserved'] == 361 and preserve['total_active'] == 408
|
||
assert sum(r['inserted'] for r in geo['results']) == 15 and backup['restore_verified']
|
||
assert active['identity']['read_only'] == 'on' and active['active_writes'] == 0
|
||
|
||
# Preserve a dedicated receipt of the enlarged scope and all source associations.
|
||
(PRIVATE / 'geometry-crosswalk-v2-preservation-qa.json').write_bytes((PRIVATE / 'geometry-crosswalk-preservation-qa.json').read_bytes())
|
||
plans = {p['level']: p for p in geo['plans']}
|
||
gaps = []
|
||
for level in [1, 2, 3]:
|
||
source_file = PRIVATE / f'moz-adm{level}-ine-20190607.geojson'
|
||
assert hashlib.sha256(source_file.read_bytes()).hexdigest() == plans[level]['source_sha256']
|
||
source = json.loads(source_file.read_text(encoding='utf-8-sig'))
|
||
rows = {r['external_id'].rsplit(':', 1)[1]: r for r in plans[level]['rows']}
|
||
for feature in source['features']:
|
||
p = feature['properties']
|
||
r = rows[p[f'ADM{level}_PCODE']]
|
||
if r['status'] in ['insert', 'reuse']:
|
||
continue
|
||
gaps.append({'level': level, 'name': r['name'], 'pcode': p[f'ADM{level}_PCODE'], 'parent_name': p.get(f'ADM{level-1}_PT'),
|
||
'parent_pcode': p.get(f'ADM{level-1}_PCODE'), 'province': p['ADM1_PT'], 'status': r['status'], 'candidates': r['candidates'],
|
||
'reason': {'parent_unresolved': 'Pai ainda sem correspondência; não associar por nome isolado', 'name_unresolved': 'Nome sem correspondência única sob o pai resolvido',
|
||
'ambiguous_name': 'Múltiplos cadastros sob o mesmo pai; preservar ambos'}.get(r['status'], r['status'])})
|
||
assert len(gaps) == 173
|
||
named = PRIVATE / 'geometry-named-gaps.json'
|
||
archive = PRIVATE / 'geometry-named-gaps-v1-188.json'
|
||
if named.exists() and not archive.exists():
|
||
archive.write_bytes(named.read_bytes())
|
||
named.write_text(json.dumps(gaps, ensure_ascii=False, indent=2), encoding='utf-8')
|
||
|
||
tag = '[Instalação/cartografia QA 10/10/2026] '
|
||
matrix = DOCS / 'matriz-requisitos.csv'
|
||
rows = list(csv.DictReader(matrix.open(encoding='utf-8-sig', newline='')))
|
||
protected = ['ID', 'Pedido/trecho', 'Fonte/ocorrências', 'Critério de aceitação', 'Versões divergentes', 'IDs das linhas de origem', 'Quantidade de linhas de origem', 'Critérios e versões por origem (JSON)']
|
||
before = [{k: r[k] for k in protected} for r in rows]
|
||
|
||
def append(row, column, value):
|
||
old = re.sub(r'\s*\|\s*' + re.escape(tag) + r'.*?(?=\s*\|\s*\[|$)', '', row[column], flags=re.S)
|
||
row[column] = old + ' | ' + tag + readable(value)
|
||
|
||
entries = {
|
||
'AUD-PROFILE-31': ('Instalador autenticado040000: prévia/schema/dependências030+020, hash/reason/MFA, idempotência UUID e auditoria transacional; bootstrap separado e explícito reutiliza PortalSuperusers. Schema parcial bloqueado; rollback vazio/reinstalação e falha de auditoria recuperados em teste isolado.',
|
||
'Visual da instalação/bootstrap, designação real dos dois titulares e política da recuperação simultânea; backup da futura candidata/ativo apósvisual. Schema040 jáQA, sem reinstalação destrutiva ou novos titulares no servidor18097.'),
|
||
'AUD-OPS-01': ('030→040→190 preservada:030/040/09-190 QA instaladas e ausentes no ativo lido READONLY. GET/POST/management/profile-install disponíveisQA; preparação não instala nem disponibiliza o percurso.',
|
||
'Candidata não fixada; visualC37/QA21/A-B, prova ativa de backup/restauro e autorização de ativação futura; não aplicar ativos nesta autorização.'),
|
||
'AUD-GEO-02': ('Configcrosswalkv2 conserva Maputo/Matola e acrescenta cinco distritos e três aliases de postos; sete postos exatos resolvem pelos novos pais. Importação autenticada+MFA+replay:15inseridos/393reutilizados, zero publicação/cadastro/reparenting.',
|
||
'173 correspondências nomeadas:26distritos+147postos. Mandie e agregados/edições divergentes permanecem ambíguos. Fonte2019 não fornece geometrias de localidades ou de parcelas.'),
|
||
'AUD-GEO-03': ('QA408 geometrias=11províncias+133distritos+264postos; API do consumidor recupera os15 novos limites. 1990cadastros e361geometrias originais preservados;393associações prévias reutilizadas sem alteração.',
|
||
'Seleção/gravação/recuperação/render por consumidor continuam sem ensaio visual. Países/origem/filtros/estatísticas transversais não encerrados pela cartografia ou pelos contratos.'),
|
||
'AUD-OP-02': ('DOCX/XLSX OOXML válidos:upload18097→scanClamDreal→download21/24/25;23negado403;delete404/opção retirada→restore→novaMFA/bytes idênticos. PDFs/imagens/derivadas anteriores mantidos, originais preservados.',
|
||
'Partilhas/exportações/PDFs derivados e cobertura de todos os consumidores e perfis ainda exigem provas próprias; validação de pacoteOOXML não demonstra renderWord/Excel. Visual documental pendente.'),
|
||
'AUD-OP-03': ('Apresentação distingue aprovação interna, versão disponibilizada sob acesso restrito e catálogo público; estado técnico published preservado. Scope efetivo consulta política real do catálogo, não só valor do enum.',
|
||
'Visual por perfil/workflow completo emUI pendente; incidentes, reatribuição e recurso integral continuam no backlog existente.'),
|
||
'AUD-OP-04': ('Seis contratos semânticos anteriores preservados; DOCX/XLSX e rótulos/Response de âmbito efetivo completados neste lote. Quatro propostas concretas de campos/documentos/gates/versões preparadas no documento de decisão, sem ativá-las.',
|
||
'Response integral/§27 e condições por tipo/unidades, urbanismo/materialidade e entidade sem exercícios ainda parciais. Propostas D-M1–M4/D-S1–S2 requerem decisão; quatro categorias não submetem/publicam até perfil aprovado e adaptador demonstrado.'),
|
||
'AUD-PROFILE-22': ('Proposta SLA versionada:triagem2, revisõestécnica/jurídica10 emparalelo, parecer5, correção15, revisão5, editorial3 e recurso2+10 diasúteis; coordenação/responsabilidades e regras de pausa/lembrete/escalamento explícitas.',
|
||
'D-SLA:aprovar/rever prazos, calendário/fuso, tolerância e terceirociclo, responsabilidades/suplentes. Nenhuma due_at ou atribuição real alterada; motor/visual do SLA apósdecisão.'),
|
||
'AUD-PROFILE-28': ('Propostas inativas concession/financing/supply/other baseadas em§25.4(P2101–2105), reutilizam campos,documentos,geo eworkflow existentes; decisões regra/fundamento/efeito individualizadas.',
|
||
'Perfis não aprovados/nem carregadospelo motor; guardar/editar4drafts anterior provado, submissão/revisão/publicação mantidas bloqueadas. Seis contratos completos emUI ainda não demonstrados.'),
|
||
}
|
||
evidence = 'profile-installation-checks-qa.json (testes isolados por critério),profile-installation-http-qa.json,opportunity-office-documents-http-qa.json,geometry-crosswalk-v2-http-qa.json/preservation,readinessQAbackup e dashboard-active-readonly.json; fontes/propostas-perfis-modulares-sla.md. Sem visual/sem escrita ativa.'
|
||
for row in rows:
|
||
if row['ID'] in entries:
|
||
implementation, gap = entries[row['ID']]
|
||
append(row, 'Implementação encontrada', implementation)
|
||
append(row, 'Lacuna', gap)
|
||
append(row, 'Evidência funcional', evidence)
|
||
append(row, 'API', 'HTTP18097/MFA e alvoQA guardado; ver recibo específico. TestesHTTP de feature eminvestmoz_test separados do servidor usado.')
|
||
append(row, 'Gravação', 'Migração/ledger/auditoria atómicosPG no ensaio isolado; servidor já040usado:noopsemDDL. Recursos/originais e15geometrias gravados somenteQA.')
|
||
append(row, 'Recuperação', 'Instalador/âmbito eDOCX/XLSX recuperados noutra sessãoFortify/MFA; geometrias porendpoint do componente. SQLrestauro comparado por tabela e ficheiros porhash, com lacunas declaradas na prova.')
|
||
append(row, 'Interface', 'Código ecompilaçãoQA preparados; nenhum clique/render observado. C37/PromotorQA21/A-B permanecempendentes.')
|
||
append(row, 'Resultado apresentado', 'JSON/HTMLInertia ebinários demonstrados; apresentação visual não demonstrada.')
|
||
append(row, 'Disponibilidade ativa', 'AtivoREADONLY:030/040/09-190ausentes,0geometrias,flagsfalse/manifestopreservado; não ativado.')
|
||
append(row, 'Consolidação', 'Pedido10/10/2026:040autenticado,188correspondênciasbaseline,propostasmodulares/SLA epublicaçãointerna. Estado atual deste incremento prevalece sobre relatos anteriores conservados.')
|
||
document_entries = {
|
||
'AUD-LAND-01': ('PDF, PNG e JPEG sintéticos: upload pelo servidor QA, scan ClamD real, original recuperado pelo autor QA21 noutra sessão MFA. Gestor QA14 e outro investidor QA23 recebem 404 no percurso privado. País MZ e origem PT persistidos e recuperados. Modelo de impressão responde com aviso de ausência de decisão externa.',
|
||
'Aspeto da interface e PDF gerado pelo navegador pendentes; não demonstra todos os perfis, filtros, estatísticas ou todos os campos fundiários.'),
|
||
'AUD-PART-01': ('Sala QA1: PDF, PNG e JPEG privados do autor; candidato recebe 404 e não pode alterar partilha (403). Partilha explícita permite recuperar bytes noutra sessão MFA; revogação retira listagem e download (404), mantendo original do autor. Gestor não participante e organização alheia recebem 403; seis transições de partilha auditadas.',
|
||
'Resultado visual, impressão/exportações e restantes ações da bolsa continuam com critérios próprios pendentes.'),
|
||
'AUD-SCAN-01': ('Seis originais fundiários/parceria possuem scan real ClamAV limpo, data de assinaturas e hash analisado igual ao original armazenado e recuperado por HTTP. Não foram alterados originais nem simulados scans.',
|
||
'ClamD real demonstrado nestas operações; arranque operacional, PDFs gerados, novos tipos e derivadas não cobertos continuam pendentes por operação.')
|
||
}
|
||
for row in rows:
|
||
if row['ID'] in document_entries:
|
||
implementation, gap = document_entries[row['ID']]
|
||
append(row, 'Implementação encontrada', implementation)
|
||
append(row, 'Lacuna', gap)
|
||
append(row, 'Evidência funcional', 'independent-documents-http-qa.json e independent-documents-verification-qa.json; servidor QA 18097, API/persistência demonstradas, interface não observada.')
|
||
append(row, 'API', 'Rotas reais de terra-duat e parcerias/salas; sessões Fortify, verificações permitidas/negadas e cabeçalho do alvo QA antes de escrever.')
|
||
append(row, 'Gravação', 'Três originais por consumidor e eventos de partilha persistidos exclusivamente na base QA isolada.')
|
||
append(row, 'Recuperação', 'Nova sessão MFA recupera os mesmos hashes; revogação mantém os originais e remove o acesso do candidato.')
|
||
append(row, 'Interface', 'Modelo Inertia de impressão recuperado; nenhum clique, mapa ou PDF de navegador observado.')
|
||
append(row, 'Resultado apresentado', 'JSON, modelo de impressão e binários demonstrados; resultado visual pendente.')
|
||
append(row, 'Disponibilidade ativa', 'Este ensaio é exclusivamente QA; não inspecionou nem alterou documentos ou partilhas ativos.')
|
||
assert before == [{k: r[k] for k in protected} for r in rows]
|
||
assert len(rows) == 3386 and sum(int(r['Quantidade de linhas de origem']) for r in rows) == 6838
|
||
with matrix.open('w', encoding='utf-8-sig', newline='') as f:
|
||
w = csv.DictWriter(f, fieldnames=list(rows[0])); w.writeheader(); w.writerows(rows)
|
||
|
||
marker = 'qa-installation-cartography-20261010'
|
||
def prepend(path, block):
|
||
old = path.read_text(encoding='utf-8-sig')
|
||
old = re.sub(r'<!-- ' + marker + r':begin -->.*?<!-- ' + marker + r':end -->\s*', '', old, flags=re.S)
|
||
old = old.replace('Este bloco é a evidência mais recente desta matriz;', 'Este bloco conserva a revisão semântica anterior; o estado atual está no início do documento;')
|
||
first, _, rest = old.partition('\n')
|
||
path.write_text(first + '\n\n<!-- ' + marker + ':begin -->\n' + readable(block.strip()) + '\n<!-- ' + marker + ':end -->\n\n' + rest.lstrip(), encoding='utf-8')
|
||
|
||
files_ready = 'todos os ficheiros referenciados recuperados por hash' if backup['files_verified'] else f"{len(backup['missing_document_files'])} ficheiros referenciados ausentes; readiness bloqueada até esclarecer/restaurar"
|
||
summary = f'''## Estado atual — instalador, documentos e cartografia
|
||
|
||
QA exclusivamente: **http://127.0.0.1:18097**, `desk_restore_20261009_174726_test:55441`. Ativo inspecionado apenas por leitura em {active['captured_at']}: 030000, 040000 e 09/190000 ausentes; zero geometrias; manifesto preservado. **C37, Promotor QA21 e A/B permanecem visualmente pendentes. Candidata à ativação ainda não fixada.**
|
||
|
||
| Requisitos | Código preparado / prova QA | Critério ainda aberto |
|
||
| --- | --- | --- |
|
||
| PROFILE-31 / OPS-01 | GET/POST `/management/profile-install`, bootstrap separado; prévia, dependências, hash, MFA, repetição e auditoria. Instalação vazia revertida/reinstalada; falha de auditoria reverte DDL e ledger, permitindo repetir. Uso/histórico impedem remoção. No servidor já utilizado, `changed=false`; nenhum titular ou privilégio alterado. Promotor, Auditor e organização alheia recebem 403. | Instalação/bootstrap visual e titulares reais designados; recuperação simultânea da dupla depende de D-R1. Backup do schema não recupera MFA perdido. |
|
||
| GEO-02/03 | **15 novas**: cinco distritos e dez postos; **393 reutilizadas**. Total **408**: 11 províncias, 133 distritos e 264 postos. API do mapa recupera os 15 novos limites. 1.990 cadastros e 361 geometrias originais preservados; sem publicação. | **173 lacunas nomeadas**: 26 distritos e 147 postos; ambiguidades e edições distintas. Seletores/mapas por consumidor continuam com aceitação visual pendente. Localidades/parcelas não inferidas. |
|
||
| OP-02 / documental | DOCX/XLSX: pacote válido, scan ClamD real, acesso de QA21/24/25 e QA23 negado; exclusão lógica, restauro e original idêntico noutra sessão MFA. | Renderização de Office, exportações/PDFs derivados e restantes consumidores continuam pendentes por operação. |
|
||
| LAND-01 / PART-01 / SCAN-01 | PDF, PNG e JPEG em fundiário e sala de parceria: seis scans reais com hashes iguais. Autor recupera originais; partilha explícita permite nova sessão; revogação retira acesso/listagem, sem apagar original. Gestor não ganha acesso por esse papel. **{len(independent['events'])} pedidos** no recibo próprio. | Modelo de impressão recuperado, mas PDF gerado no navegador e interface não demonstrados. Estes seis scans não encerram outros tipos, derivadas ou permissões. |
|
||
| OP-03 / OP-04 | Código da interface e resposta distinguem aprovação interna, disponibilização restrita e catálogo público. Casos #30 restrito, #33 público e #32 aprovado sem publicação recuperados pela API; consulta da política pública efetiva. | Interface; Response integral/§27 e condições sem regra inequívoca continuam parciais. Testes dos seis contratos não encerram a demonstração visual. |
|
||
| PROFILE-22/28 | [Quatro perfis e SLA com regra, fundamento, efeito e decisões concretas](propostas-perfis-modulares-sla.md). | Propostas **inativas**; quatro categorias continuam com rascunhos recuperáveis, mas submissão/publicação bloqueadas. Nenhum prazo ou responsável real configurado. |
|
||
|
||
Prova de recuperação QA: dump consistente → nova base `{backup['restored_target']['database']}:55441` → **{len(backup['tables'])} tabelas com linhas/hashes iguais e {len(backup['files'])} ficheiros fisicamente recuperados por hash**; {files_ready}. Arquivo privado inclui o runtime e a chave QA estável. Readiness só atesta preparação técnica desta revisão. Repetir após fixar a candidata e preparar backup ativo apenas numa futura ativação autorizada.
|
||
|
||
**Matriz única:** 3.386 requisitos/linhas e 6.838 ocorrências de origem; todas as fontes, critérios e versões preservados; sem novos fechos globais. Contagens por estado abaixo e na projeção dos pedidos abertos.
|
||
|
||
**Próximo lote executável:** completar Response/§27 reutilizando entidades; verificar exportações e PDFs derivados por consumidor; prosseguir correspondências territoriais por nome, pai e edição; completar países/origem, filtros e estatísticas por formulário. Incorporar provas visuais quando retomadas. Decisões D-M1–M4, D-S1/S2, D-SLA e D-R1 estão individualizadas e não bloqueiam o trabalho independente.
|
||
|
||
**Histórico preservado:** os blocos seguintes descrevem revisões anteriores. 393 geometrias/188 lacunas eram o estado antes deste lote; “instalador 040000 em falta” deixou de descrever o código QA. Esses estados anteriores não provam esta revisão nem autorizam instalação ativa.
|
||
'''
|
||
prepend(DOCS / 'relatorio-executivo.md', summary)
|
||
prepend(DOCS / 'backlog.md', summary)
|
||
prepend(DOCS / 'workflow-oportunidades-qa.md', summary)
|
||
prepend(ROOT / 'docs/investmoz/v2/gestao-oportunidades-dashboard.md', summary.replace('(propostas-perfis-modulares-sla.md)', '(auditoria-portal/propostas-perfis-modulares-sla.md)'))
|
||
|
||
activation = '''## Sequência reconciliada — preparada, não executada no ativo
|
||
|
||
**030000 → 040000 → 190000** mantém-se. QA já tem as três; **não repetir instalações QA**. No ativo lido READ ONLY, as três extensões estão ausentes; Balcão07/190000 eorientação08/120000 estão instalados. A extensão **09/190000 não foi substituída**: faltam ledger,duascolunas e definição19etapas. Fonte:dashboard-active-readonly.json eopportunity-semantic-environment-qa.json.
|
||
|
||
| Ordem | Ação futura, efeito econtagem prevista | Dependência / reversão |
|
||
| --- | --- | --- |
|
||
| 0 | VisualC37/QA21/A-B ecorreções→fixar candidata/hash/manifesta/config→backupDB+originais+runtime/chaveestável→restauro emQA nova, tabelas/bytes/acessos comparados. | QA backup deste lote não é backup ativo nem candidata definitiva. Ativação continua fora da autorização. |
|
||
| 1 | Disponibilizar código/Composer/build compatíveis, flagsfalse eURLs preservadas. | Não retirar privilégios reais ou migraridentidades automaticamente. Reverter flags/build/código compatível preservando dados novos. |
|
||
| 2 —030 | GETprévia→POST `/management/opportunity-resources-install`, hash/confirm/MFA/provas atuais, sóse ausente/coerente. **3coleções+metadado cifra;0documentos importados/publicados.** | Base020/documentos existentes. Não drop apósuso; restaurar/reconciliar backup em vez de apagar originais. |
|
||
| 3 —040 | GETprévia→POST `/management/profile-install`; **4tabelas+1colunausers+3colunasoportunidades,0contas/grants/flags.** BootstrapPOST `/management/profile-install/bootstrap` é **separado**:2contas explicitamente designadas,3.ºgestor comMFAaprova. | Instalador exige020+030/schema/organização/auditoria, hash ebackup/restauro atuais. Idempotência eDDL/ledger/auditoria atómicos. Schema parcial bloqueia aplicação; down apenassemqualqueruso/contexto/revisão/histórico. Recuperação deMFA simultânea exigeD-R1,sembootstraprepetido. |
|
||
| 4 —09/190 | GETprévia→POST `/management/balcao-install`, sómigraçãofaltante: **2colunas+1definição publicada19etapas/5fases;0dossiers automáticos.** | BaseBalcão/orientação jáinstaladas: não repetir. SemdepDDLde040,masordem operacional preservada. Template eversões usados imutáveis; reverterdisponibilização,preservar snapshots/roteiros. |
|
||
| 5 —cartografia | GETprévia→POST `/management/territory-geometry-import`, níveis1→2→3 ehash/idempotência. **408 correspondências candidatas** se cadastro/hash coincidem:11/133/264;ativo0na leitura. **173pendentes** não importadas. | Fontes/hash/pai/nome/código vinculados;resultado/contagem medida no alvo,semfusões ougeometrias inventadas. Reversão só lotesnovos nãoalterados/usados,postos→distritos→províncias comaudit;seFK/uso,preservar/reconciliar,nãoforçar. |
|
||
| 6 —disponibilização | Aprovar perfis/reconciliação depermissões reais econfiguração; habilitar navegação/editores/percurso porincremento comprovado. **0fixtures QA noativo.** | I1–I3 não concluídos semA/B/UI. Contratos4categorias eSLA sóapósdecisão própria. Importação não ativa cálculo fiscal. |
|
||
| 7 —publicação | Revisão dedivulgação eação doPublicador porversão; **camadas cartográficas públicas são ação separada** daimportação. | Aprovaçãointerna≠acessorestrito≠catálogopúblico≠decisãoexterna. Desabilitar publicação/flags antes derestauro;preservar originais,consentimentos eauditoria. |
|
||
|
||
Sharedcode/fingerprint muda:renovar provas da revisão ealvo;ready=true não instala,não escolhe titulares,não publica enão disponibiliza percurso. Guarda ativa/QA conferida antes decada escrita futura. A prova040 emschema vazio foi isolada; no servidorQA jáusado sónoop auditado.
|
||
'''
|
||
prepend(DOCS / 'plano-ativacao.md', activation)
|
||
|
||
cases = '''## Casos adicionais deste lote — sem reiniciar o inventário
|
||
|
||
| Caso / requisito | Reprodução e resultado esperado | Observado, causa e correção | Prova / estado |
|
||
| --- | --- | --- | --- |
|
||
| C59 — PROFILE-31 / OPS-01 | Gestor confirmado com MFA consulta e aplica apenas 040000, após030000; repetir o mesmo pedido. Esperado:prévia, dependências e provas, uma instalação e um recibo, sem contas/grants automáticos. | A migração já tinha guarda, mas faltava rota/serviço/interface autenticados. Reutilizados Fortify, audit_events e cerimónia da dupla. Plano obsoleto/chave alterada409; falta de provas/dependência422; Auditor/Promotor403. | Testes de feature em schema vazio, repetição e bootstrap separados; servidor18097 já instalado:noop, replay idêntico, novaMFA e negações. Visual pendente. |
|
||
| C60 — PROFILE-31 / recuperação | Em base isolada e schema vazio, down→instalar; depois impedir insert na auditoria e instalar. Esperado:reversão total deDDL/ledger se não houver recibo; retry possível; nenhum drop com uso/histórico. | No desenvolvimento do incremento foi introduzida remoção duplicada da colunausers; o ensaio encontrou Undefined column. Corrigida. Também reforçado down para contexto/organização sem autor e revisões de sessão; falha de auditoria devolve503 e não deixa schema/ledger parcial. | Testes específicos de reinstalação e falha auditiva; históricos/contextos conservados. Não executado down no servidorQA utilizado, nem no ativo. |
|
||
| C61 — OP-03 / publicação | Recuperar #30,#33,#32 e comparar estado da preparação, âmbito efetivo e catálogo. Esperado:restrito,público,aprovado sem disponibilização respetivamente. | Rótulo genérico “Publicado”/estado published não explicava o âmbito. Novo label/projeção consulta política efetiva do catálogo; botões distinguem disponibilização restrita de publicação pública. | JSON/Inertia e política pública verificados; nenhuma fuga pública observada. Perceção/ação visual pendente. |
|
||
| C62 — PROFILE-31 / bootstrap | Propor duas contas distintas e terceiro gestor; candidato com MFA cifrado ilegível ou Auditor. Esperado:recusa controlada e nenhum titular criado. | A cerimónia conferia presença/confirmaçãoMFA, sem comprovar leitura do segredo no ambiente. Agora exige cifragem válida/estruturaMFA e recusa Auditor, mesmo com flags ainda desabilitadas. Sem key:generate/fallback ou remoção deMFA. | Teste422 e dupla permanece vazia; inicialização positiva com identidades sintéticas distintas. Não substitui C37/login visual. |
|
||
| C63 — ambiente de testes / documental | Executar IndependentDocumentOperationsTest com o PHPCLI padrão. Esperado:GD disponível para gerar as imagens do ensaio. | Dois testes falharam por imagecreatetruecolor inexistente noCLI; o servidorQA já tinhaGD. Runner passa a carregar explicitamente a mesma extensãoGD para o processo de testes. | Regressão posterior27/600 semskips, scans reais e testes finais separados. Não atribuir esta falha ao servidorweb ou declarar GD ativo verificado. |
|
||
| C64 — PROFILE-31 / recuperação e proteção | Em schema isolado com perfis habilitados, retirar a coluna de revisão e tentar desativar MFA/excluir conta. Esperado:sem alteração de identidade, recuperação e logout acessíveis. | Esquema parcial podia tornar o serviço de perfis indisponível e deixar middleware de proteção passar à frente. Agora recusa escritas com503, preserva MFA/conta e deixa o instalador apresentar a incoerência409; logout permanece permitido. | Teste específico, schema parcial não aplicado ao servidorQA nem ao ativo. Preserva leitura permitida e não cria bypass de recuperação. |
|
||
|
||
C55 continua corrigida por operação:PDF eagoraDOCX/XLSX excluídos dão404, scanpendente continua423; restore preserva original. C37/C43/QA21/A-B permanecempendentes. Cada prova mantém API/persistência/interface separadas.
|
||
'''
|
||
prepend(DOCS / 'casos-reproducao.md', cases)
|
||
manual = f'''## Ensaio visual futuro deste lote — contas QA, sem credenciais
|
||
|
||
Servidor exclusivo **http://127.0.0.1:18097**. Para a instalação, usar o **Superutilizador sintético QA27** já designado na dupla27/30; obter credenciais eMFA somente no ficheiro privado local. **Promotor QA21 continua a conta do dashboard, não substituí-lo por27. C37/QA18 e A/B continuam pendentes; não é pedida nova tentativa agora.**
|
||
|
||
1. ComoQA27, login/MFA normal e abrir **http://127.0.0.1:18097/management/profile-install**. Conferir oalvo apresentado `desk_restore_20261009_174726_test:55441`, schema instalado, dependências020/030 presentes, provas atuais e dupla existente. Não executarbootstrap ou escolher titulares novos neste servidor usado.
|
||
2. Atualizar a página, terminar sessão einiciar outra. Esperado:mesmo ledger e dupla,semnova instalação ou atribuição. Os testes de instalação desde schema vazio/reversão pertencem àbaseisolada do runner; não remover o040 do servidorQA emuso para demonstrar o botão.
|
||
3. Quando retomarQA21, dashboard→Minhas oportunidades; #30 apresenta acesso restrito e não catálogo; #33 apresenta catálogo público; #32 apresenta aprovação interna,sempublicação. Os originais/contatos privados não entram na projeção pública.
|
||
4. ComoQA21, abrir **http://127.0.0.1:18097/minhas-oportunidades/36/resources**:consultar PDF,DOCX eXLSX sintéticos. Guardar original, excluir logicamente um recurso, esperar404/downloadindisponível; restaurar einiciar outra sessão para recuperar os mesmosbytes. QA24/25 podemconsultar; organizaçãoQA23 énegada.
|
||
5. Selecione CahoraBassa/Chitima ouCidadeMaxixe no consumidor territorial. Esperado:mesmoIDexistente, geometria administrativa comedição2019, legenda eárea selecionada. Guardar/recuperar seleção; não confundir limite administrativo comparceladeinvestimento. Inspecionar cadaoutroconsumidor no roteiroexistente.
|
||
6. Registar URL,conta/ação/resultado visível eestado recuperado. Omitir Cookie,X-XSRF-TOKEN,password,segredo/códigoMFA. Incorporar falhasàmesma matriz antes defixar candidata.
|
||
|
||
Documentos fundiários QA21: **http://127.0.0.1:18097/investidor/terra-duat/{independent['receipts']['case_id']}**. Conferir PDF/PNG/JPEG e país MZ/origem PT; abrir impressão e gerar PDF no navegador. A recuperação do modelo por HTTP não demonstra o PDF final.
|
||
|
||
Partilha QA21/22: **http://127.0.0.1:18097/parcerias/salas/{independent['receipts']['room_id']}**. Os três originais estão privados após o ensaio. QA21 partilha explicitamente um; QA22 recupera noutra sessão. QA21 revoga; QA22 deixa de o listar/obter; QA21 mantém o original. QA14 não participante e QA23 não têm acesso à sala.
|
||
|
||
Este roteiro não declara os passos executados. Instalação ativa e publicação de camadas permanecem ações futuras separadas.
|
||
'''
|
||
prepend(DOCS / 'roteiro-manual-perfis-v1.md', manual)
|
||
prepend(DOCS / 'perfis-v1-reconciliacao.md', summary)
|
||
|
||
cartography = '''## Correspondências atuais — v2
|
||
|
||
Esperado refere o exemplar aceite INE/UNOCHA07/06/2019, não uma certificação da divisão atual. Fonte oficial continua aceite. Nome/pai/edição são problemas de correspondência, não revalidação da credibilidade.
|
||
|
||
| Nível | Esperado | Resolvido QA | Pendente | Incremento deste lote |
|
||
| --- | ---: | ---: | ---: | ---: |
|
||
| Províncias |11|11|0|0|
|
||
| Distritos |159|133|26|5|
|
||
| Postos administrativos |411|264|147|10|
|
||
| Total |581|408|173|15|
|
||
|
||
Baseline anterior393/188 mantida nohistórico. As antigas2/39/179 eram a situação09/10; não descrevem oestadoatual. Cadastro do seletor1920referências (11+148+437+1324),mais70países;geometria eapresentação funcional têm denominadores próprios. Semgeometriasdelocalidades/parcelas desteexemplar.
|
||
|
||
Cinco aliasesdedistritos: **Cahora Bassa→CAHORA-BASSA;Ngauma→N`GAUMA;Ilha De Moçambique→CIDADE ILHA DE MOCAMBIQUE;Maxixe→CIDADE DE MAXIXE;Nacala-A-Velha→NACALA-VELHA**. Postos exatos pelospais:Chitima,Covo,Ilha De Moçambique,Itepela,Lumbo,Massangulo,Songo;aliasesapóstrofo:M'Sawize→M`SAWIZE,N'Cumpe→NCUMPE,N'Gapa→N`GAPA. Fonte,código,pai ecadastro existentes conferidos pela prévia;fundamento ehash emconfig/geometry-correspondences.phpv2 eprovaHTTP. Semrenomeação oualteraçãohierárquica.
|
||
|
||
geometry-crosswalk-v2-http-qa.json:15inserts/393reuse,replayigual e15limites recuperadospelaAPI domapa;sempublicação/semvisual. geometry-crosswalk-v2-preservation-qa.json:1990cadastros/361originaispreservados e47incrementaisprivados. Reversão existente não apaga originais;atualizaçãousada recusada. Mandie continua ambíguo entre2cadastros/códigos sobGuro;CidadeDeMaputoagregado nãoequivalea1distritourbano. Novosdistritos/edições nãoassociadosporproximidade.
|
||
'''
|
||
for level in [2, 3]:
|
||
items = [g for g in gaps if g['level'] == level]
|
||
cartography += f"\n### {'Distritos' if level == 2 else 'Postos'} — {len(items)} lacunas por nome\n\n| Código | Nome | Província / pai | Causa |\n| --- | --- | --- | --- |\n"
|
||
for g in items:
|
||
cartography += f"|{g['pcode']}|{g['name']}|{g['province']} / {g['parent_name']}|{g['reason']}|\n"
|
||
cartography += '\n**Visual pendente:**cada seletor/país/origem/mapa porformulário, gravação erecuperação;408 APIs não demonstram render. Todasasfontes,nomes econtagensanteriores seguemabaixo como história.\n'
|
||
prepend(DOCS / 'correspondencias-cartograficas.md', cartography)
|
||
|
||
states = collections.Counter(r['Estado'] for r in rows)
|
||
state_text = '## Contagens atuais por requisito\n\n| Estado | Requisitos / linhas da matriz | Ocorrências de origem |\n| --- | ---: | ---: |\n'
|
||
for state, count in states.items():
|
||
state_text += f"|{state}|{count}|{sum(int(r['Quantidade de linhas de origem']) for r in rows if r['Estado'] == state)}|\n"
|
||
state_text += '\n3.386 requisitos verificáveis e 3.386 linhas consolidadas; 6.838 ocorrências de origem. Sem novo fecho integral; provas QA descritas por critério.\n'
|
||
for name in ['relatorio-executivo.md', 'backlog.md']:
|
||
path = DOCS / name
|
||
text = path.read_text(encoding='utf-8-sig').replace('<!-- '+marker+':end -->', state_text + '<!-- '+marker+':end -->', 1)
|
||
path.write_text(text, encoding='utf-8')
|
||
counts_path = PRIVATE / 'contagens-requisitos.json'
|
||
counts = json.loads(counts_path.read_text(encoding='utf-8-sig'))
|
||
counts.update({'installation_updated_at': datetime.now(timezone.utc).isoformat(), 'requisitos': len(rows), 'linhas_matriz': len(rows),
|
||
'linhas_origem': 6838, 'por_estado': {s: {'requisitos': n, 'linhas_origem': sum(int(r['Quantidade de linhas de origem']) for r in rows if r['Estado'] == s)} for s, n in states.items()},
|
||
'browser_current_version_verified': False})
|
||
counts_path.write_text(json.dumps(counts, ensure_ascii=False, indent=2), encoding='utf-8')
|
||
|
||
inventory = DOCS / 'inventario-fontes.csv'
|
||
inv = list(csv.DictReader(inventory.open(encoding='utf-8-sig', newline='')))
|
||
proposal = DOCS / 'propostas-perfis-modulares-sla.md'
|
||
name = str(proposal)
|
||
entry = next((r for r in inv if r['Exemplar/caminho'] == name), None)
|
||
if entry is None:
|
||
entry = {k: '' for k in inv[0]}; entry['Exemplar/caminho'] = name; inv.append(entry)
|
||
entry.update({'Bytes': str(proposal.stat().st_size), 'SHA256': hashlib.sha256(proposal.read_bytes()).hexdigest(), 'Tipo': 'proposta interna QA',
|
||
'Nota de leitura': 'Documento integral; regras/fundamento/efeito/decisões efonte§25.4. Não fonteoficial,nenhuma aprovaçãoinferida.',
|
||
'Disponibilidade': 'Consulta no repositório; publicação não inferida', 'Uso em cálculo': 'Nenhuma regra/SLA/cálculo ativado',
|
||
'Situação jurídica/documental': 'Proposta para decisão, não ato jurídico', 'Edição/versão identificada': 'proposta20261010v1'})
|
||
with inventory.open('w', encoding='utf-8-sig', newline='') as f:
|
||
w = csv.DictWriter(f, fieldnames=list(inv[0])); w.writeheader(); w.writerows(inv)
|
||
(PRIVATE / 'installation-audit-update-qa.json').write_text(json.dumps({'updated_at': datetime.now(timezone.utc).isoformat(), 'requirements': 3386,
|
||
'source_occurrences': 6838, 'protected_fields_preserved': True, 'states': dict(states), 'updated': list(entries), 'geometry_total': 408,
|
||
'document_originals_verified': 6, 'document_requirements_updated': list(document_entries), 'named_pending': 173, 'active_writes': 0}, ensure_ascii=False, indent=2), encoding='utf-8')
|
||
print(json.dumps({'requirements': 3386, 'source_occurrences': 6838, 'updated': list(entries) + list(document_entries), 'geometries': 408, 'pending': 173, 'active_writes': 0}))
|