281 lines
35 KiB
Python
281 lines
35 KiB
Python
"""Prepare JSON cell edits and reconcile existing Markdown from scoped visual QA receipts.
|
||
CSV authoring remains exclusively in the existing Artifact Tool builder.
|
||
"""
|
||
import collections
|
||
import csv
|
||
import hashlib
|
||
import json
|
||
import re
|
||
from datetime import datetime, timezone
|
||
from pathlib import Path
|
||
|
||
csv.field_size_limit(16000000)
|
||
ROOT = Path(__file__).resolve().parent.parent
|
||
DOC = ROOT / 'docs/investmoz/v2/auditoria-portal'
|
||
PRIVATE = ROOT / 'storage/app/private/investmoz-v2/portal-audit'
|
||
OUT = PRIVATE / 'visual-finalize-20261010'
|
||
OUT.mkdir(exist_ok=True)
|
||
stamp = datetime.now(timezone.utc).isoformat()
|
||
tag = '[Visual QA 10/10/2026] '
|
||
|
||
def receipt(name):
|
||
return json.loads((PRIVATE / name).read_text(encoding='utf-8'))
|
||
|
||
ab = receipt('investor-ab-chromium-20261010.json')
|
||
promoter = receipt('promoter-dashboard-chromium-20261010.json')
|
||
follow = receipt('visual-followup-chromium-20261010.json')
|
||
countries = receipt('consumers-mobile-accessibility-chromium-20261010.json')
|
||
manual = receipt('c37-qa18-post-mfa-destination-20261010.json')
|
||
denied = receipt('qa-report-access-chromium-20261010.json')
|
||
backup = receipt('profile-installation-backup-qa.json')
|
||
active = json.loads((ROOT / 'storage/app/private/virtual-desk/backup-proof.json').read_text(encoding='utf-8'))
|
||
assert ab['result'] == denied['result'] == 'passed'
|
||
assert all(ab['checks'].values()) and all(follow['checks'].values())
|
||
assert promoter['result'] == 'passed_scoped_restore_pending'
|
||
assert backup['restore_verified'] and backup['files_restore_verified'] and backup['files_verified']
|
||
assert active['restore_verified'] and active['files_verified']
|
||
assert manual['manual_access_confirmed_by_user'] and not manual['manual_refresh_logout_new_login_confirmed']
|
||
assert len(follow['accessibility']) == 15 and len(follow['maps']) == 4
|
||
|
||
rows = list(csv.reader((DOC / 'matriz-requisitos.csv').open(encoding='utf-8-sig', newline='')))
|
||
headers = rows[0]
|
||
canonical = {r[0]: dict(zip(headers, r)) for r in rows[1:]}
|
||
assert len(canonical) == len(rows)-1 == 3393
|
||
states = collections.Counter(r['Estado'] for r in canonical.values())
|
||
assert sum(int(r['Quantidade de linhas de origem']) for r in canonical.values()) == 6845
|
||
updates = {}
|
||
|
||
def update(ids, evidence, gap, interface=None, api=None, save=None, recover=None, result=None):
|
||
for rid in ids:
|
||
old = dict(canonical[rid])
|
||
# Re-running this preparation refines the same increment, without duplicating prose.
|
||
for field in ['Evidência funcional','Consolidação','Interface','API','Gravação','Recuperação','Resultado apresentado']:
|
||
old[field] = old[field].split(' | '+tag)[0]
|
||
if old['Lacuna'].startswith(tag) and ' | Histórico preservado: ' in old['Lacuna']:
|
||
old['Lacuna'] = old['Lacuna'].split(' | Histórico preservado: ', 1)[1]
|
||
patch = {'Evidência funcional': old['Evidência funcional'] + ' | ' + tag + evidence,
|
||
'Lacuna': tag + gap + ' | Histórico preservado: ' + old['Lacuna'],
|
||
'Consolidação': old['Consolidação'] + ' | ' + tag + 'Subcritérios atualizados por prova própria; estado global, IDs, fontes, versões e decisões preservados.'}
|
||
for field, value in [('Interface', interface), ('API', api), ('Gravação', save), ('Recuperação', recover), ('Resultado apresentado', result)]:
|
||
if value:
|
||
patch[field] = old[field] + ' | ' + tag + value
|
||
variants = json.loads(old['Critérios e versões por origem (JSON)'])
|
||
for variant in variants:
|
||
variant['incremento_visual_qa_20261010'] = {'atualizado_em': stamp, 'ambito': 'QA18097/55441 exclusivamente',
|
||
'evidencia': evidence, 'lacuna_atual': gap, 'estado_global_preservado': old['Estado'], 'ativacao': False}
|
||
patch['Critérios e versões por origem (JSON)'] = json.dumps(variants, ensure_ascii=False, separators=(',', ':'))
|
||
updates[rid] = patch
|
||
|
||
c37 = ('MFA manual18 aceite às17:40:21 e17:46:42UTC; sessão regenerada e autenticada18 persistida. '
|
||
'Destino inicialmente pretendido /qa-auditoria/legado-piloto-qa negado403 por perfil, sem perder sessão; /dashboard200 posterior. '
|
||
'Responsável confirmou acesso. Mensagem403QA e retorno visível corrigidos, com navegação Inertia409→GET403 única; ensaioQA21 preservou403 e abriu painel/investimentos/F5. '
|
||
'Rastreio: c37-qa18-post-mfa-destination-20261010.json; navegador: qa-report-access-chromium-20261010.json. '
|
||
'Ensaio18 isolado anterior de login/MFA/investimentos/F5/logout/nova sessão passou; não substitui o navegador habitual. Nenhum segredo/código/cookie registado nas provas entregáveis; MFA/chave/conta18 não alterados.')
|
||
update(['AUD-PROFILE-24','AUD-PROFILE-25','AUD-UI-01'], c37,
|
||
'C37: confirmar atualização, logout e novo login no navegador habitual; confirmação de acesso não encerra todo o ciclo. '
|
||
'ERR_TOO_MANY_REDIRECTS não foi demonstrado como ciclo persistente pelo rastreio; distinguir409 de atualização Inertia de rejeiçãoMFA e de autorização403. Outros perfis/dispositivos/consumidores pendentes.',
|
||
interface='QA18 acesso manual confirmado; QA21 retorno visível após relatório403 demonstrado. Ciclo manual C37 ainda parcial.',
|
||
api='POST MFA302, auth18 persistida; relatório403 e dashboard200. Não houve DecryptException nesses pedidos.',
|
||
recover='Sessão autenticada mantida após destino proibido; nova sessão manual18 chegou ao dashboard200.',
|
||
result='QA mostra acesso reservado e links úteis; nenhum privilégio administrativo concedido ao investidor.')
|
||
|
||
abevidence = ('Chromium desktop18: dois dossiers próprios A/B criados, guardados e recuperados noutra sessão; cinco fases e19 etapas preservadas. '
|
||
'A dispensaS10; B espera externaS10 comS02 independente. Plano contextual guardado/reaberto, resultados devolvidos e recuperados no dossier: ponto de equilíbrio20unidades; défice A0/B70000MZN. '
|
||
'Balcão preparado comoDRAFT, paísesPT/origemZA guardados; tarefa interna atribuída/assumida/iniciada/concluída no UI pelo gestor14 e recuperada pelo investidor. '
|
||
'Gestor sem acesso a resultados financeiros privados; QA23 dossier403/rascunho404; sem mistura entre projetos. investor-ab-chromium-20261010.json e visual-persistence-qa.json. '
|
||
'C80: bloquear edição/avanço enquanto GET do rascunho não terminou; diagnóstico guardado, correção e resposta deliberadamente atrasada no Chromium demonstrados em portal-simulator-loading-visual-qa.cjs/recibo.')
|
||
update(['AUD-GUIDE-01','AUD-DESK-01','AUD-DESK-02','AUD-CRM-01','AUD-SIM-02'], abevidence,
|
||
'A/B completos para o cenário interno aprovado, em desktop isolado; Balcão apenas preparaçãoDRAFT, não submissão/decisão externa. '
|
||
'Restantes contratos, documentos, fiscalidade/parâmetros legais, etapas externas e todos os consumidores/perfis não encerrados. '
|
||
'Móvel A/B teve achados anteriores: correção comum verificada com dossier próprioQA21, sem substituir repetição final A/B móvel ou aceitação manual.',
|
||
interface='A/B desktop demonstrados por ações visíveis, incluindo ferramenta, resultado, apoio e acompanhamento.',
|
||
api='Respostas guardadas por método/rota/status, sem corpos de autenticação; QA23 negado403/404.',
|
||
save='Casos, aplicabilidade, ferramenta, resultado, pedidoDRAFT e tarefa guardados pelo UI; originais/histórico preservados.',
|
||
recover='Logout e contexto novo: dossier, resultados, tarefa e inputs próprios da ferramenta recuperados para cada A/B.',
|
||
result='20unidades; défice financeiro A0/B70000MZN. Ferramenta e resultado ligados ao dossier correto, sem aprovação/score inventados.')
|
||
|
||
pevidence = (f'QA21 Chromium: dashboard mostra Minhas oportunidades e Nova oportunidade; abrir pelos links→criar rascunho incompleto#{promoter["opportunity"]["id"]}→guardar→logout/novaMFA→recuperar→editar/gravar. '
|
||
'Promotor solicita arquivo; Publicador25 arquiva por decisão fundamentada, estadoArquivado recuperado. '
|
||
'Restauração no workflow por perfis ausente; não atribuir essa função ao Promotor nem contornar com acesso legado. '
|
||
'promoter-dashboard-chromium-20261010.json. Leitura ativa dashboard-active-readonly.json: buildantigo, flags desligadas e030/040/09-190ausentes; não ativado.')
|
||
update(['AUD-DASH-01','AUD-PROFILE-05','AUD-PROFILE-32'], pevidence,
|
||
'Ciclo de restauração por perfil ainda não implementado; definir ator/regra coerentes com arquivo solicitado e histórico. '
|
||
'Workflow especializado completo, restantes perfis e disponibilização ativa continuam separados. Não conceder arquivo direto ao Promotor por inferência.',
|
||
interface='Dashboard QA21→links visíveis→criar/guardar/recuperar/editar; pedido de arquivo; Publicador arquiva.',
|
||
save='Rascunho e edição persistidos; pedido e decisão de arquivo fundamentados.',
|
||
recover='Recuperação noutra sessão e estadoArquivado após atualização.',
|
||
result='Oportunidade privada própria distinta do dossier e da publicação no catálogo; restauração explicitamente pendente.')
|
||
|
||
cevidence = ('Chromium QA18: residênciaPT no perfil de investimento guardada eF5 recuperada; negócio privado e apoio fundiárioDRAFT paísPT/origemFR guardados e recuperados por UI. '
|
||
'BalcãoA/B DRAFT paísPT/origemZA recuperados noutra sessão. Quatro formulários públicos/investidor/empresa/manifestação/apoio mostram249países (250opções incluindo vazio), mas sem submissão/gravação desta prova. '
|
||
'Gestor14: filtro origemZA eNãoinformado/unknown; UI estatísticas mostra4manifestaçõesNãoinformado e0pedidosZA submetidos, excluindo corretamente rascunhos A/B. '
|
||
'CRM filtrounknown renderizado. consumers-mobile-accessibility-chromium-20261010.json; visual-followup-chromium-20261010.json. Não inferir origem da residência ou constituição.')
|
||
update(['AUD-COUNTRY-01','AUD-COUNTRY-02','AUD-COUNTRY-03','AUD-COUNTRY-04','AUD-COUNTRY-06','AUD-COUNTRY-07'], cevidence,
|
||
'Concluir submissão e recuperação por nova sessão nos formulários públicos/empresa/manifestação/apoio; ensaio visual de estatísticas com pedidos submetidos de origem conhecida. '
|
||
'Negócios/fundiário deste lote sãoDRAFT e recuperaçãoF5, não provam submissão nem todos os perfis. País e origem são campos distintos, sem preencher faltas históricas por inferência.',
|
||
interface='Cobertura explicitada por formulário;249países nos quatro formulários públicos; seleções visíveis em perfil/negócios/fundiário/Balcão.',
|
||
save='ResidênciaPT; paísPT/origemFR nos dois rascunhos; BalcãoA/B paísPT/origemZA. Formulários públicos não submetidos nesta prova.',
|
||
recover='F5 perfil/negócios/fundiário; Balcão A/B noutra sessão. Não equiparar opções preenchidas a dados guardados.',
|
||
result='FiltrosZA/unknown e contagens visíveis;4manifestações sem origem; rascunhos excluídos. Falta estatística positiva por origem submetida neste ensaio visual.')
|
||
|
||
gevidence = ('Chromium: seletores e mapa de Cabo Delgado na oportunidade própriaQA21#51, criação deprojeto, cluster ecorredor pelo gestor14. '
|
||
'Cada consumidor: território registado, selected_geometry_available=true, context_geometry_count=1 e1polígonoSVG visível. '
|
||
'Seleção não gravada nesta prova; não fecha recuperação territorial ou hierarquia completa. Tiles externos bloqueados no browser isolado, basecartográfica externa não testada. '
|
||
'CorreçãoC78 regionARIA; visual-followup-chromium-20261010.json. A tentativa antiga deQA21 na oportunidade30 era403; não foi mapa demonstrado e foi substituída por recurso próprio51. '
|
||
'Nenhuma importação/publicação cartográfica neste lote;408associaçõesQA anteriores preservadas,173pendentes (26distritos/147postos), Mandie/Guro ambíguo; sem polígonos de localidades no exemplar2019.')
|
||
update(['AUD-GEO-01','AUD-GEO-02','AUD-GEO-03'], gevidence,
|
||
'Gravar/reabrir seleção e relações em cada consumidor; níveis descendentes e restantes mapas/perfis. Tiles externos/fallback sem ensaio conectado. '
|
||
'Resolver173correspondências inequívocas por pai/identidade, preservando Mandie/Guro ambíguo e ausência de polígonos de localidades. Publicação de camada permanece ação separada.',
|
||
interface='Quatro consumidores com seleção Cabo Delgado e1polígono visível cada; oportunidade30negada não contou como cobertura.',
|
||
api='API de contexto confirma1geometria acessível para o território selecionado em cada consumidor.',
|
||
result='Território cadastrado ≠ geometria disponível ≠ mapa apresentado ≠ seleção persistida; últimos3avaliados separadamente, gravação ainda pendente.')
|
||
for rid in ['AUD-GEO-01','AUD-GEO-02','AUD-GEO-03']:
|
||
updates[rid]['Evidência funcional'] += ' | Ensaio manual 17:52:33UTC na oportunidade51 recebeu403 com auth18, não21; recusa correta de acesso alheio. Sessão preservada, mapa visual manualQA21 continua pendente. Não conceder acesso nem contar esta recusa como mapa demonstrado.'
|
||
|
||
aevidence = ('FalhasC77–C83 corrigidas emQA: landmarks, nomeARIA do mapa, hierarquia de títulos do texto legado sem alteraroriginal/hash, '
|
||
'tabelas financeiras focáveis por teclado, reflow de tarefas/select/data no dossier, langpt-MZ apenas no routerQA e proteção da recuperação assíncrona do simulador. '
|
||
'Após correções15execuções axe/390–320–1440px,0violações automáticas/0overflow e idiomaPT; skiplink primeiroTab→conteúdo em3páginas; tabelas financeiras focadas/teclas no Chromium. '
|
||
'Não equivale a leitor de ecrã, zoom/dispositivo real ou cobertura de todo oportal. LegacyContentTest5/84 e rastreioMFA1/14 são provas específicas, não contagem substituta de percursos. '
|
||
'visual-followup-chromium-20261010.json e screenshots; históricos de falhas mantidos.')
|
||
update(['AUD-LEG-02','AUD-LEG-03','AUD-LEG-06','AUD-LEG-07'], aevidence,
|
||
'Cobertura integral, mais perfis/dispositivos, leitor de ecrã e editor administrativoC74 não demonstrados. '
|
||
'Mercado Financeiro permanece privado/corpo vazio confirmado na origem; rotas404. Sem associação automática Turismo→cadastro territorial, sem fundir Energia/Mineração.',
|
||
interface='Leitura pública/móvel dirigida, teclado e4consumidores do mapa demonstrados;15execuções de acessibilidade semviolação automática.',
|
||
result='Conteúdo/títulos originais preservados e hierarquia apresentada corrigida; imagem/pesquisa de setores nas provas prévias mantidas. Mercado Financeiro404.')
|
||
update(['AUD-DOC-01'],
|
||
'C84 corrigido em QA: recursos mostram scan e motor por ficheiro/versão; removida afirmação incondicional ClamD real pendente. '
|
||
'Chromium QA21 recursos51 confirmou apresentação sem texto obsoleto.22scans reais do lote legado anterior preservados; nenhum novo scan/documento/derivada neste ensaio. visual-followup-chromium-20261010.json.',
|
||
'PDFs, exportações, partilhas, derivadas e consumidores restantes precisam de operação/scan/permissão/recuperação próprios. '
|
||
'Um original limpo não prova scan das derivadas; disponibilização ativa separada.',
|
||
interface='Texto de scan individual corrigido e observado em QA; restantes ações documentais não encerradas.',
|
||
result='Estado e motor de scan pertencem a cada ficheiro/versão, sem invalidar ClamD real já demonstrado nem inventar scans.')
|
||
|
||
bevidence = (f'Backup/restauroQA renovado {backup["verified_at"]}:295tabelas/103ficheiros; restauro em{backup["restored_target"]["database"]}:55441, bytes extraídos e compatibilidade verificados. '
|
||
f'Ativo somenteREADONLY: backup{active["verified_at"]},288tabelas/5ficheiros incluindo runtime privado, restauroSQL em{active["restored_target"]["database"]}:55441 com todos valores/hashes iguais; bytes do arquivo verificados. '
|
||
'Não demonstra ainda migrações/código candidato no clone do ativo, nem aceitação global/corte. Regressão dirigida instalador/publicação9/93 eTypeScript passaram no alvo unitário isolado. '
|
||
'Sequência030000→040000→2026_10_09_190000_prepare_investor_path; QA instaladas não repetir, ativa ausentes porleitura. '
|
||
'Fingerprint técnico e manifesto privado vinculam provas; candidata global não fixada.0escritas ativas.')
|
||
update(['AUD-OPS-01'], bevidence,
|
||
'C37manual ciclo final, restauração porperfil, cobertura territorial/documental/países, decisões modulares/SLA e aceitações globais pendentes. '
|
||
'Clone fresco do ativo restaurado mas sem aplicar/desfazer sequência da futura candidata; preparar ensaio compatibilidade e ponto autenticado de importação em lote. '
|
||
'Backup renovado não autoriza ativação nem declaraI1–I3concluídos; repetir snapshot se dados/revisão mudarem.',
|
||
recover='QA295/103 com recuperação real de ficheiros; ativo288tabelas SQL recuperadas numa QA distinta e5ficheiros do arquivo verificados; sem escrita ativa.')
|
||
|
||
summary = f'''<!-- qa-visual-results-20261010:begin -->
|
||
## Ensaios visuais e pendências atuais — {stamp}
|
||
|
||
Trabalho exclusivo na QA da porta 18097, base `desk_restore_20261009_174726_test:55441`. Nenhuma alteração na base ativa. A mesma matriz mantém **3393 requisitos / 3393 linhas / 6845 ocorrências de origem**: 3235 parciais, 145 não implementados, 5 dependentes de decisão, 1 implementado mas não ativado e 7 concluídos e demonstrados. Os estados globais não mudaram; foram atualizados subcritérios com provas próprias. A ausência de requisitos globalmente classificados “com falha” não significa ausência de casos abertos.
|
||
|
||
| Requisito | Demonstrado em QA | Critérios ainda pendentes |
|
||
| --- | --- | --- |
|
||
| C37; UI01; PROFILE24–25 | MFA manual QA18 aceite às 17:40:21 e 17:46:42 UTC; autenticação persistida e dashboard 200. Responsável confirmou acesso. O 403 inicial era do relatório administrativo reservado. Mensagem e retorno ao painel corrigidos; Chromium QA21 provou recusa 403, retorno e F5 sem perder sessão. | F5, logout e novo login no navegador habitual QA18. Não declarar o loop anterior resolvido por inferência: o rastreio não demonstra um ciclo persistente. Uma atualização Inertia 409 isolada não é perda de autenticação. |
|
||
| A/B; GUIDE01; SIM02; DESK01–02; CRM01 | Chromium desktop QA18: criação → gravação → nova sessão → recuperação → aplicabilidade → ferramenta → resultado no dossier → preparação no Balcão → acompanhamento interno → nova recuperação. Cinco fases e 19 etapas preservadas. A dispensa S10; B aguarda informação externa em S10 enquanto S02 avança. Resultado: equilíbrio de 20 unidades e défice A=0 / B=70.000 MZN. QA23 recebe 403/404; gestor não vê resultados financeiros privados. | Balcão demonstrado em preparação DRAFT, sem submissão ou decisão externa. Restantes critérios especializados, fiscalidade, documentos e consumidores não encerrados. Repetição final A/B móvel ainda necessária. |
|
||
| DASH01; PROFILE05/32 | QA21, a partir do dashboard: “Minhas oportunidades” e “Nova oportunidade” → rascunho incompleto #50 → guardar → nova sessão → recuperar → editar. Promotor solicita arquivo; Publicador QA25 arquiva com fundamento; estado recuperado. | Restauração por perfil ausente no workflow. Definir e implementar ator/regra coerentes sem conceder arquivo direto ao Promotor por inferência. Workflow e contratos especializados ainda exigem demonstrações próprias. |
|
||
| COUNTRY01–04/06/07 | Residência PT guardada no perfil e recuperada após F5. Negócio e fundiário DRAFT: país PT / origem FR guardados e recuperados. Balcão A/B: PT / ZA recuperados noutra sessão. Quatro formulários públicos apresentam 249 países cada. Gestão: filtros ZA/unknown, quatro manifestações sem origem e zero pedidos submetidos ZA, com rascunhos excluídos. | Nos formulários públicos, a presença de opções não prova submissão ou recuperação. Concluir cada gravação e estatística positiva de origem conhecida no UI. Nos rascunhos de negócio/fundiário, F5 não substitui nova sessão. |
|
||
| GEO01–03 | Oportunidade própria QA21 #51 e criação de projeto, cluster e corredor: Cabo Delgado registado → API com uma geometria acessível → um polígono SVG visível em cada consumidor. | Seleções desse ensaio não guardadas. Recuperar relações por consumidor e testar descendentes, restantes mapas e perfis. Tiles externos bloqueados no ensaio, sem confirmação da base externa. 408 associações anteriores e 173 pendentes preservadas; Mandie/Guro ambíguo. |
|
||
| LEG02/03/06/07; C77–C83 | Corrigidos landmarks, ARIA, hierarquia de títulos, reflow, foco das tabelas e idioma HTML pt-MZ em QA. Quinze execuções dirigidas de axe, em 320/390/1440px, sem violações automáticas ou overflow. Três páginas com skiplink primeiro e tabelas financeiras operadas por teclado. | Cobertura integral, leitor de ecrã, dispositivos reais, mais perfis e editor C74. O dossier QA21 verifica o componente móvel corrigido, sem substituir a repetição final A/B. |
|
||
| DOC01; C84 | Retirada mensagem global falsa de ClamD pendente; scan e motor apresentados por ficheiro/versão. Os 22 scans reais anteriores permanecem demonstrados. | PDFs, exportações, partilhas, derivadas e operações restantes exigem provas próprias. Um original limpo não comprova a derivada. |
|
||
| OPS01 | Backup QA {backup['verified_at']}: 295 tabelas e 103 ficheiros restaurados. Ativo READ ONLY, backup {active['verified_at']}: 288 tabelas restauradas numa base QA distinta e cinco ficheiros conferidos no arquivo. Instalador/publicação: 9 testes / 93 asserções, além de TypeScript. | Aplicação, compatibilidade e reversão da futura revisão candidata no clone fresco do ativo. Revisão candidata global e ativação continuam por fixar. Contagens de testes não substituem critérios funcionais. |
|
||
|
||
**C80 corrigido:** a resposta tardia do GET do rascunho podia substituir preenchimento já iniciado. Edição e avanço ficam bloqueados até a recuperação terminar; o retorno exige o snapshot completo guardado. Chromium com resposta deliberadamente atrasada comprovou bloqueio, recuperação e libertação. A/B recuperaram os inputs corretos noutra sessão, preservando versões anteriores.
|
||
|
||
**Recusa manual do mapa às 17:52:33 UTC:** o pedido à oportunidade 51 foi feito com QA18, não QA21; recebeu 403 com sessão preservada. É uma prova de autorização, não de apresentação do mapa. Usar QA21 (`profiles-v1-promoter@example.invalid`) para esse consumidor; o Chromium anterior QA21 recebeu 200. Nenhuma permissão alargada para contornar a recusa.
|
||
|
||
**Provas privadas sanitizadas:** `investor-ab-chromium-20261010.json`, `promoter-dashboard-chromium-20261010.json`, `consumers-mobile-accessibility-chromium-20261010.json`, `visual-followup-chromium-20261010.json`, `qa-report-access-chromium-20261010.json`, `c37-qa18-post-mfa-destination-20261010.json`, `visual-persistence-qa.json`, `profile-installation-backup-qa.json` e `virtual-desk/backup-proof.json`. A tentativa antiga de QA21 na oportunidade 30 recebeu 403 e não contou como mapa; foi substituída pelo consumidor próprio 51. Tentativas e falhas de harness preservadas; sem códigos, segredos ou cookies nas evidências entregáveis.
|
||
|
||
Mercado Financeiro permanece privado, sem corpo inventado; rotas públicas 404. Energia e Mineração mantêm artigos distintos e Turismo não criou associações geográficas automáticas. Fontes oficiais aceites não foram reabertas. Próximo lote: C37 manual, restauração por perfil, submissão/estatística por formulário, gravação/recuperação territorial, documentos, Response/§27 e workflow. Perfis modulares/SLA continuam decisões separadas; não iniciar novas jornadas educativas.
|
||
|
||
As versões históricas abaixo ficam preservadas. Afirmações antigas sobre falta de navegador, A/B não testados ou ClamD global pendente são superadas apenas pelos subcritérios acima. Código preparado, QA demonstrada e disponibilidade ativa são estados distintos.
|
||
<!-- qa-visual-results-20261010:end -->
|
||
'''
|
||
|
||
activation = f'''<!-- qa-visual-activation-20261010:begin -->
|
||
## Ativação preparada, sem execução no ativo — {stamp}
|
||
|
||
Leitura ativa `investemoz:5432`: flags de dashboard/perfis/percurso desligadas; migrações 030000, 040000 e **2026_10_09_190000_prepare_investor_path** ausentes. A extensão 09/190000 continua necessária e não foi substituída pelo Balcão 07/190000. As três já estão instaladas na QA; não repetir.
|
||
|
||
1. Concluir os critérios pendentes e fixar revisão, hashes, build e flags candidatos. O snapshot técnico atual não representa aceitação global.
|
||
2. Backup ativo renovado em {active['verified_at']}: 288 tabelas / cinco ficheiros, restauro SQL em `{active['restored_target']['database']}:55441`, dados e bytes do arquivo conferidos. Preparar aplicação e reversão das migrações e compatibilidade da candidata neste clone fresco. Repetir o snapshot se dados/revisão mudarem antes do corte. A prova QA separada é 295 tabelas / 103 ficheiros em `{backup['restored_target']['database']}:55441`.
|
||
3. Disponibilizar código/build compatíveis com flags desligadas, preservando URLs, IDs, classificações, consentimentos, cinco fases e 19 etapas. Zero contas, dossiers ou oportunidades sintéticas transferidos.
|
||
4. Prévia e aplicação autenticadas **030000 → 040000 → 09/190000**, apenas se ausentes e com dependências coerentes. 030000 prepara recursos versionados. 040000 instala quatro tabelas, uma coluna em users e três em opportunities; não cria titulares ou grants. 09/190000 prepara duas colunas e uma definição; não cria dossiers ou tarefas. Conferir contagens na prévia real. Bootstrap separado, exatamente dois titulares reais ainda não designados.
|
||
5. Correções de interface, API e recuperação como lote próprio. Demonstrar C37 e dashboard por perfil; restauração de oportunidades continua por implementar e aceitar.
|
||
6. Conteúdos: prévia/hash/identidade/versão → armazenamento privado → ClamD → recuperação/replay. Lote de 22 originais (17 artigos / cinco imagens), reutilizando hashes existentes; 21 recursos divulgáveis e um exemplar vazio de Mercado Financeiro privado. Falta preparar e demonstrar o mecanismo autenticado de lote; CLI --actor QA não é autenticação ativa. Divulgação separada não ativa regras/cálculos.
|
||
7. Cartografia em lote independente: prévia, correspondências inequívocas, aplicação idempotente, auditoria e reversão. Retrato QA: 408 candidatas / 173 pendentes (26 distritos / 147 postos); Mandie/Guro ambíguo e ausência de polígonos de localidades no exemplar 2019. Publicação das camadas separada. Gravar/recuperar relações e demonstrar cada mapa consumidor.
|
||
8. Habilitar consumidores e percurso incrementalmente com permissões e provas próprias. `ready=true` significa preparação, não instalação, divulgação ou conclusão funcional.
|
||
|
||
**Reversão:** desligar flags e retirar divulgação por ações auditadas, preservando originais, versões, IDs, documentos, consentimentos, relações e eventos. Não executar DROP em tabelas utilizadas ou copiar fixtures QA. Restauro completo exige ensaio em QA distinta e reconciliação de escritas posteriores ao snapshot antes do corte. Não alterar chave nem retirar MFA para recuperar acesso. O backup ativo verificado não comprova ainda aplicação/reversão da futura candidata.
|
||
<!-- qa-visual-activation-20261010:end -->
|
||
'''
|
||
|
||
cases = '''<!-- qa-visual-cases-20261010:begin -->
|
||
## Casos adicionais C77–C84 e estado C37 — 10/10/2026
|
||
|
||
| Caso | Reprodução e observado | Esperado, causa e correção QA |
|
||
| --- | --- | --- |
|
||
| C37 | MFA manual aceite; destino pretendido era relatório administrativo, negado 403 com sessão autenticada. Nova tentativa 17:46:42 UTC chegou ao dashboard 200; responsável confirmou acesso. | Recusa administrativa correta. Página QA explica o acesso reservado e oferece retorno; pedido Inertia passa uma vez por 409 para renderizar a página completa, evitando modal na página MFA. QA21 provou 403 → painel → investimentos → F5. C37 habitual ainda pede F5/logout/novo login; ciclo anterior não demonstrado como falha persistente. |
|
||
| C77 / UI01, LEG02, DASH01 | axe: mains aninhados, conteúdo fora de landmarks e cabeçalho Sheet fora do Dialog. | Um main, navegação e apoio nomeados, SheetHeader dentro do SheetContent. Quinze verificações dirigidas posteriores sem violações automáticas. |
|
||
| C78 / GEO03 | Mapas com aria-label num div sem role. | Região nomeada. Cabo Delgado apresentou um polígono em quatro consumidores; seleção ainda não guardada e tiles externos não testados. |
|
||
| C79 / LEG02 | Turismo começava com h3 sob o título h1, omitindo h2. | Normalização apenas na apresentação para h2..h6 e saltos máximos de um nível, mantendo original, bytes, hash e texto. LegacyContentTest 5/84 e Chromium passaram. |
|
||
| C80 / SIM02, GUIDE01 | Preenchimento iniciado antes de terminar o GET podia ser sobrescrito pelo rascunho remoto; faltavam campos de A na recuperação. | Impedir edição e avanço até recuperar; cancelar efeito obsoleto; devolver resultado só após guardar snapshot completo. Chromium com GET deliberadamente atrasado e nova sessão A/B demonstraram recuperação. Histórico preservado. |
|
||
| C81 / SIM02 | Tabelas financeiras com scroll sem foco por teclado. | tabindex=0, região nomeada e foco visível. QA21 focou ambas e usou ArrowDown; axe passou. Leitor de ecrã real pendente. |
|
||
| C82 / GUIDE01 | Dossier a 390px alargava a página para 413px devido a labels/select/datetime da atribuição. | min-width:0, max-width:100% e quebra de linha, sem esconder ações. Dossier próprio QA21 corrigido sem overflow; repetir A/B móvel final. |
|
||
| C83 / UI01, LEG02 | HTML lang=en para interface em português. | Configuração pt-MZ somente no router QA e fallback anterior no template. Quinze verificações com pt-MZ; locale de validação preservado. |
|
||
| C84 / DOC01 | Mensagem global “ClamD real pendente” contrariava os scans reais. | Mostrar scan/motor por ficheiro/versão; retirar texto global. QA21 recursos51 confirmou apresentação; nenhuma derivada/PDF recebeu scan fictício. |
|
||
|
||
**Mapa manual 51, 17:52:33 UTC:** conta autenticada QA18 recebeu 403; QA21 havia recebido 200. Sessão mantida. É recusa correta por perfil/autoria e não um mapa funcional demonstrado pelo responsável. Credenciais e entradas MFA são distintas; não ampliar autorização para permitir o ensaio na conta errada.
|
||
|
||
Provas: `visual-followup-chromium-20261010.json`, `qa-report-access-chromium-20261010.json`, `investor-ab-chromium-20261010.json`, `visual-persistence-qa.json` e `layout-map-diagnosis-chromium-20261010.json`. Falhas do harness preservadas separadamente: ações antes de guardar, moeda não selecionada, esperas e oportunidade30 alheia a QA21. Não contar o 403 dessa oportunidade como cobertura do mapa. Nenhuma alteração ativa.
|
||
<!-- qa-visual-cases-20261010:end -->
|
||
'''
|
||
|
||
def readable(text):
|
||
return text
|
||
|
||
def prepend(file, text, marker):
|
||
old = file.read_text(encoding='utf-8')
|
||
old = re.sub(re.escape('<!-- '+marker+':begin -->') + r'.*?' + re.escape('<!-- '+marker+':end -->') + r'\s*', '', old, flags=re.S)
|
||
(OUT / (file.name+'.before')).write_text(old, encoding='utf-8')
|
||
file.write_text(readable(text).strip()+'\n\n'+old, encoding='utf-8')
|
||
|
||
prepend(DOC/'relatorio-executivo.md', summary, 'qa-visual-results-20261010')
|
||
prepend(DOC/'backlog.md', summary, 'qa-visual-results-20261010')
|
||
prepend(DOC/'plano-ativacao.md', activation, 'qa-visual-activation-20261010')
|
||
prepend(DOC/'casos-reproducao.md', cases, 'qa-visual-cases-20261010')
|
||
prepend(DOC/'legado-piloto-qa.md', summary, 'qa-visual-results-20261010')
|
||
prepend(ROOT/'docs/investmoz/v2/gestao-oportunidades-dashboard.md', summary, 'qa-visual-results-20261010')
|
||
|
||
for patch in updates.values():
|
||
for key,value in patch.items():
|
||
if key != 'Critérios e versões por origem (JSON)':
|
||
patch[key] = readable(value)
|
||
|
||
matrix_patches = [{'row':i,'values':updates[row[0]]} for i,row in enumerate(rows[1:],1) if row[0] in updates]
|
||
packet = {'files':[{'filename':'matriz-requisitos.csv','input_sha256':hashlib.sha256((DOC/'matriz-requisitos.csv').read_bytes()).hexdigest(),
|
||
'rows':rows,'patches':matrix_patches,'allowed_columns':list(next(iter(updates.values())).keys()) + ['Interface','API','Gravação','Recuperação','Resultado apresentado'],'append':[]}],
|
||
'proof_filename':'visual-csv-preservation-proof.json'}
|
||
backrows = list(csv.reader((DOC/'backlog-priorizado.csv').open(encoding='utf-8-sig', newline='')))
|
||
bh = backrows[0]
|
||
bp = []
|
||
for i,row in enumerate(backrows[1:],1):
|
||
rid = row[bh.index('ID')]
|
||
if rid not in updates:continue
|
||
old = dict(zip(bh,row)); bp.append({'row':i,'values':{'Pendência':updates[rid]['Lacuna'],
|
||
'Correção/componentes':old['Correção/componentes']+' | '+tag+updates[rid]['Evidência funcional'].split(tag)[-1],
|
||
'Ordem do próximo lote':old['Ordem do próximo lote']+' | '+tag+'C37manual→restauração porperfil→submissão/estatística→recuperação territorial→documentos/workflow; decisões e ativação separadas.'}})
|
||
packet['files'].append({'filename':'backlog-priorizado.csv','input_sha256':hashlib.sha256((DOC/'backlog-priorizado.csv').read_bytes()).hexdigest(),
|
||
'rows':backrows,'patches':bp,'allowed_columns':['Pendência','Correção/componentes','Ordem do próximo lote'],'append':[]})
|
||
sourcefile = DOC/'inventario-fontes.csv'
|
||
sr = list(csv.reader(sourcefile.open(encoding='utf-8-sig',newline=''))); sh=sr[0];sp=[];hashupdates=[]
|
||
sourcepaths = [ROOT/'docs/investmoz/v2/gestao-oportunidades-dashboard.md', DOC/'legado-piloto-qa.md']
|
||
for i,row in enumerate(sr[1:],1):
|
||
if Path(row[sh.index('Exemplar/caminho')]) not in sourcepaths:continue
|
||
old=dict(zip(sh,row));sp.append({'row':i,'values':{'Nota de leitura':old['Nota de leitura']+' | '+tag+'Versão atual com provas visuais dirigidas. SHA anterior '+old['SHA256']+', bytes anteriores '+old['Bytes']+'; histórico preservado.'}});hashupdates.append({'row':i})
|
||
assert len(hashupdates)==2
|
||
packet['files'].append({'filename':'inventario-fontes.csv','input_sha256':hashlib.sha256(sourcefile.read_bytes()).hexdigest(),
|
||
'rows':sr,'patches':sp,'allowed_columns':['Nota de leitura'],'append':[],'source_hash_updates':hashupdates})
|
||
(OUT/'csv-edits.json').write_text(json.dumps(packet,ensure_ascii=False,indent=2),encoding='utf-8')
|
||
proof={'at':stamp,'requirements':3393,'rows':3393,'source_occurrences':6845,'states_unchanged':dict(states),'updated_requirements':list(updates),
|
||
'active_writes':0,'global_candidate_fixed':False,'qa_backup_at':backup['verified_at'],'active_readonly_backup_at':active['verified_at']}
|
||
(OUT/'preparation-proof.json').write_text(json.dumps(proof,ensure_ascii=False,indent=2),encoding='utf-8')
|
||
print(json.dumps({'prepared':len(updates),'canonical_requirements':3393,'csv_authored':False,'global_candidate_fixed':False}))
|