93 lines
15 KiB
Python
93 lines
15 KiB
Python
"""Reconcile workflow evidence in the sole existing matrix; preserve all source references and versions."""
|
|
import csv
|
|
import json
|
|
import hashlib
|
|
import collections
|
|
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'
|
|
MATRIX=ROOT/'docs/investmoz/v2/auditoria-portal/matriz-requisitos.csv'
|
|
csv.field_size_limit(16*1024*1024)
|
|
sys.stdout.reconfigure(encoding='utf-8')
|
|
rows=list(csv.DictReader(MATRIX.open(encoding='utf-8-sig')))
|
|
before={r['ID']:dict(r) for r in rows}
|
|
fields=list(rows[0])
|
|
assert len(rows)==len(before)==3386
|
|
proof=json.loads((PRIVATE/'opportunity-workflow-http-qa.json').read_text())
|
|
extended=json.loads((PRIVATE/'opportunity-workflow-extended-http-qa.json').read_text())
|
|
multiple=json.loads((PRIVATE/'opportunity-workflow-multirole-http-qa.json').read_text())
|
|
territory_tasks=json.loads((PRIVATE/'opportunity-workflow-territory-tasks-http-qa.json').read_text())
|
|
verified=json.loads((PRIVATE/'opportunity-workflow-verification-qa.json').read_text())
|
|
assert all(p['result']=='passed' and p['active_writes']==0 for p in [proof,extended,multiple,territory_tasks])
|
|
assert all(p['target']=='desk_restore_20261009_174726_test:55441' for p in [proof,extended,multiple,territory_tasks])
|
|
snapshot=PRIVATE/'opportunity-workflow-matrix-before.csv'
|
|
if not snapshot.exists():snapshot.write_bytes(MATRIX.read_bytes())
|
|
marker='[Workflow QA 10/10/2026]'
|
|
|
|
def append(row,field,value):
|
|
old=row[field]
|
|
if marker in old:old=old.split(marker,1)[0].rstrip(' |')
|
|
row[field]=old+' | '+marker+' '+value
|
|
|
|
common='QA18097/desk_restore_20261009_174726_test:55441; Promotor21, Validador24, Publicador25, recurso29; FortifyMFA normal. Provas privadas opportunity-workflow-http-qa.json, -extended-http-qa.json, -multirole-http-qa.json, -territory-tasks-http-qa.json e opportunity-workflow-verification-qa.json. Oportunidade28:localização principal segunda na lista selecionada corretamente, tarefas concluídas por correção/suspensão, resubmissão sem atribuição herdada;29:rejeição conclui revisão. Interface renderizada/C37 pendentes; zero escritas ativas.'
|
|
updates={
|
|
'AUD-OP-03':('Submeter→atribuir/iniciar→solicitar correção→corrigir→resubmeter→gates→aprovar→publicar implementados, hash/ETag/revisões e auditoria. Revisão privada conserva publicação; rejeição, recurso independente, suspensão; publicação explícita com prévia/confirm/hash.', 'Ciclo investimento18, publicação interna; imobiliário19:rejeição24→recurso29→nova revisão24→publicação25 pública→proposta privada→suspensão404. Fundiário20:aprovado internamente, publicação422 por autorização externa pendente. Autor comProm+Val+Pub403; aprovadorVal+Pub403; 3atribuições temporárias revogadas.', 'Visual integral; contratos/objetos opacos e gates legais completos; pareceres externos/incidentes, coordenação editorial, SLA/política versionada e publicação agendada. Não declarar workflow integral fechado.'),
|
|
'AUD-OP-04':('Adaptador canónico do schema110defs; condições originais allOf recuperadas; OpisDraft2020-12. IDs bigint existentes mantidos, subsector/atividade no catálogo atual; recursos por IDs nas coleções existentes; JSONprivado/versionado, sem cadastros paralelos.', '10categorias guardam rascunho incompleto e recuperam noutra sessão MFA. Create/submissão passa6(investimento,imobiliário,DUAT,venda,parceria,franquia);4 recusadas por falta de perfil modular aprovado. Venda exige preço>0; null422. Camposreadonly/verified/land_sale recusados. Ligações/contacto/hierarquia/hash/scan/parcelas validadas.', 'Formulários/grids e validações semânticas adicionais do §25:valorações completas, comunidades/consulta, beneficiário efetivo/financeiros existentes, compatibilidade cadastral e urbanística, NDA/teaser, Response integral e editores ainda incompletos. Perfis concession/financing/supply/other não configurados; não inventar contratos.'),
|
|
'AUD-PROFILE-08':('Estados privados/versionados separados do estado publicado, conteúdo/recursos bloqueados após submissão; revisões/decisões por hash e histórico.', 'Ciclo positivo realHTTP com contas distintas; correção/resubmissão, aprovação/pub, recuperação, revisão material, preservação pública e suspensão demonstradas.', 'Interface/C37; incidentes/opiniões completos, agendamento e revisão periódica, contratos/editores restantes; estados legados preservados.'),
|
|
'AUD-PROFILE-09':('Independência verificada sob lock:autor/contribuinte/membroorg não valida; aprovador não publica; revisores e aprovador são reavaliados perante revogação/conflito.', 'HTTP403 autor21 comProm+Val+Pub, aprovador24 comVal+Pub, organização/múltiplos papéis31; MFA normal. Histórico das3atribuições temporárias/revogações preservado.', 'Visual dos controlos/negativas; segregações adicionais por perfil modular e gates de instância externa.'),
|
|
'AUD-PROFILE-10':('Negação prevalece nos acessos diretos e transições, incluindo papéis acumulados e Auditor com escrita.', '403 nos percursos normais de MFA; temporárias revogadas, Promotor21 entra noutra sessão com o perfil original.', 'Demonstração visual e políticas adicionais territoriais/perfis modulares.'),
|
|
'AUD-PROFILE-11':('Validador atribuível explícito, inicia revisão, solicita correção, rejeita com fundamento, regista gates aplicáveis e aprova versão completa.', 'Validador24 fez o ciclo positivo; aprovação prematura422, motivo curto422, own403. Evidências apontam documentos desta oportunidade/versão.', 'Visual, distribuição/disponibilidade completa, revisões técnicas/jurídicas semânticas por todos os contratos e política SLA.'),
|
|
'AUD-PROFILE-12':('Publicador independente confirma prévia/hash/âmbito, publica projeção mínima, suspende e arquiva; não edita dados técnicos.', 'Publicador25:pub interna18; pública19 com2derivadas ClamD; confirmfalse422/hashdiverso409; alteração privada não vazou; suspensãoGETpúblico404. Fundiário20externapendente422.', 'Visual e cobertura editorial completa:imagens/mapas na ficha, tradução/NDA/teaser específico, agendamento, monitoria, coordenação editorial e reversão de edição publicada.'),
|
|
'AUD-PROFILE-19':('Tarefas CRM existentes com source_system/external_id/correlation_id ligados ao ID da oportunidade; fila sem responsável/prazo inventado, atribuição explícita pelo coordenador validada. Gates requerem responsabilidade técnica/jurídica.', 'Atribuição24 persistida, tarefas review/correction/publication criadas e concluídas conforme ações; due_at=null/SLAunconfigured. Papéis/responsabilidades reais reavaliados; não cria crm_opportunity paralela.', 'Fila/editor próprios, reatribuição/disponibilidade, coordenação editorial, parecer externo e escalamento completos; testes visuais. Responsáveis/prazos reais ainda não definidos.'),
|
|
'AUD-PROFILE-21':('Recurso conserva rejeição/motivo; exige appeal_reviewer distinto do rejeitor e independente da autoria/organização; manter decisão ou reabrir, auditar e concluir tarefa existente.', 'Imobiliário19:rejeição24→pedido21→rejeitor24negado403/membroorg22negado403→revisor29reabre200→nova sessão21recupera. Decisão original permanece.', 'Visual; filas/designação/competências institucionais, políticas de prazo e instância ativa; manter decisão original e nenhuma autoridade real inferida.'),
|
|
'AUD-PROFILE-22':('SLA explicitamente unconfigured e due_at=null; preparação e tarefas continuam. Cliente não fixa prazo arbitrário.', 'Tarefas e contexto recuperados com política/prazo nulos, sem tolerância/ciclos máximos inventados.', 'Decisão concreta futura:qual política/versionamento de prazos,tolerância,ciclos e escalamento é aprovada? Motor/publicação de parâmetros pendentes; não bloqueia preparação.'),
|
|
'AUD-PROFILE-23':('Auditoria existente recupera atores, razões, estados/versões/gates/hash e tarefas/recurso; razões de edição preservadas no histórico cifrado.', 'Eventos workflow_submit/request_correction/review_gate/approve/publish/request_revision/suspend/appeal_reopen e revogações temporárias; nenhuma credencial/OTP/cookie exportados.', 'Visual de filtros/exportações e comparação detalhada; auditabilidade de operações futuras ainda não implementadas.'),
|
|
'AUD-PROFILE-25':('Editor declarativo comum/especializado, escolha de referências/recursos; ações após submissão abrem /view, edição privada por link autorizado; fila/estado/título privados distinguem ficha pública.', 'PropsAPI e assetsQA:CRUD/ações/gates/prévia/tarefas; teste filtro necessidades/título privado; aprovação/pub com confirmação atual. NomePromotor QA — conta sintética21.', 'Nenhum clique/render observado:dashboard→contratos/erros→workflow→recuperação ainda exige navegador; geometrias/parcela e objetos sem editor completo permanecem abertos.'),
|
|
'AUD-PROFILE-28':('Contrato canónico derivado do mesmo schema/condições normativas, sem alterar silenciosamente enums/IDs. Núcleo reutilizado, bindings/contactos/resources, submissão estrita e draft parcial.', '10drafts recuperados,6contratos completos estruturalmente aceites/4bloqueados por perfil;6verificados novamente no snapshot atual. Source.json false é histórico da extração, runtime rastreado no relatório.', 'Semântica §25/objetos opacos, Response integral, grillas específicas e quatro perfis aprovados; visual, publicação de todos os contratos com dados/provas aplicáveis ainda não demonstrada.'),
|
|
'AUD-PROFILE-30':('PromotorQA21 claramente identificado, tipos autorizados por pedido/decisão independente. Validador24 reatribuído explicitamente, Publicador25 e instância sintética29 separados.', 'NormalFortify/MFA na18097; cenário18,19 e recuperação dos10drafts; negar self/multirole e revogar permissões temporárias. Não escolher titulares/responsáveis reais.', 'C37 e visual por cada perfil; novos processos/contratos ainda não cobertos. QA não equivale à instalação ativa.'),
|
|
'AUD-PROFILE-31':('Código/Composer3novos pacotes, assets exclusivosQA, guardas e migrações existentes; recursos030000/profiles040000 precedem disponibilização das flags. Novos contratos não criam tabelas próprias.', 'AtivoREADONLY:020000sim/030000não/040000não/flagsfalse/0geom; QA393privadas,18conjuntosoriginais preservados/MFA18intacto/manifativo753... .', 'Aceitação visual, fechar editores/gates prioritários e fixar candidata; mecanismo040000ativo/authedbootstrap ainda não completo; renovarbackup/restauro dessa revisão, sem repetir instalações nem importar fixtures sintéticas.'),
|
|
}
|
|
for row in rows:
|
|
if row['ID'] in updates:
|
|
implementation,evidence,gap=updates[row['ID']]
|
|
row['Estado']='parcial'
|
|
append(row,'Implementação encontrada',implementation)
|
|
append(row,'Evidência funcional',evidence+' '+common)
|
|
append(row,'Lacuna',gap)
|
|
append(row,'Disponibilidade ativa','Leitura ativa10/10: PostgreSQLinvestemoz:5432READONLY;020000instalada;030000/040000ausentes;flagsdashboard/especializados/perfisfalse. Lote instalado e demonstrado apenasQA18097;zeroescritasativas,393geometrias privadasQA/0ativas. Não confundir código preparado,QA e instalação ativa.')
|
|
append(row,'Dependência','Continuar componentes canónicos/workflow/tarefas existentes; seis categorias estruturais disponíveisQA, quatro dependem de perfil modular versionado aprovado. Visual/C37, semântica/editor e políticas reais mantêm-se abertos conforme lacuna. Não requer integração externa para preparação interna.')
|
|
append(row,'API',evidence)
|
|
append(row,'Gravação','Persistência QA em opportunities/management_draft/workflow_context, coleções documentais/territoriais existentes, tasks/audit_events. IDs/originais/histórico conservados.')
|
|
append(row,'Recuperação','Nova sessão Fortify/MFA recupera payloads/recurso/estado, paísMZ/localização/documentos por IDs. Provas atuais identificadas por caso, sem usar contagem global como substituto.')
|
|
append(row,'Interface','Código e assetsQA984244d5dd9ece719d5c2d2ab852a6019aae8f728dab654c5a8bd6e75adec5a8; props/mensagens/ações demonstradasHTTP. Render/cliques pendentes; C37 permanece aberto.')
|
|
append(row,'Resultado apresentado','Resultados disponíveis nas respostasQA; aceitação visual e funcionalidade ativa permanecem separadas.')
|
|
elif row['ID'] in ['AUD-DASH-01','AUD-GEO-03','AUD-COUNTRY-01','AUD-STATS-01']:
|
|
append(row,'Evidência funcional','Consumidor oportunidades:Promotor21, contrato/localizaçãoMZ/documentos/refs recuperados por categoria; APIprivateboundaries existente. Não encerra países/origem/estatísticas por outros formulários, 188lacunas cartográficas nem render de mapas. '+common)
|
|
if row['ID'] == 'AUD-PROFILE-23':
|
|
row['Lacuna'] += ' Composer audit apontou dois avisos preexistentes em league/commonmark (GHSA-97jj-33gv-5xf9 e GHSA-3q6v-r5mr-hxv8); confirmar consumidores/configuração e corrigir emQA. Prova privada workflow-composer-audit.json; não são dos três pacotes Opis novos.'
|
|
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)']
|
|
assert all(all(r[k]==before[r['ID']][k] for k in protected) for r in rows)
|
|
with MATRIX.open('w',encoding='utf-8-sig',newline='') as f:
|
|
writer=csv.DictWriter(f,fieldnames=fields);writer.writeheader();writer.writerows(rows)
|
|
origins=[item for r in rows for item in json.loads(r['Critérios e versões por origem (JSON)'])]
|
|
assert len(origins)==6838
|
|
counts=json.loads((PRIVATE/'contagens-requisitos.json').read_text())
|
|
states=collections.defaultdict(lambda:{'requisitos':0,'linhas_origem':0})
|
|
for row in rows:
|
|
states[row['Estado']]['requisitos']+=1;states[row['Estado']]['linhas_origem']+=int(row['Quantidade de linhas de origem'])
|
|
counts.update({'requisitos':len(rows),'linhas_matriz':len(rows),'linhas_origem':len(origins),'por_estado':dict(states),
|
|
'metodo':'Mesma matriz: 3.354 requisitos e 5.830 referências anteriores preservados; 32 requisitos verificáveis de perfis, 1.001 referências documentais e sete pedidos diretos. O incremento de workflow mantém requisitos parciais; não encerra aceitação visual nem contratos incompletos.',
|
|
'conservation_sha256':hashlib.sha256(json.dumps(origins,ensure_ascii=False,sort_keys=True,separators=(',',':')).encode()).hexdigest(),
|
|
'browser_current_version_verified':False,'workflow_updated_at':datetime.now(timezone.utc).isoformat()})
|
|
(PRIVATE/'contagens-requisitos.json').write_text(json.dumps(counts,ensure_ascii=False,indent=2),encoding='utf-8')
|
|
print(json.dumps({'requirements':len(rows),'source_occurrences':len(origins),'updated':list(updates),'states':dict(states),'active_writes':0},ensure_ascii=False))
|
|
|
|
# Newer semantic evidence takes precedence over dated workflow claims.
|
|
semantic = PRIVATE / 'opportunity-semantic-publication-http-qa.json'
|
|
if semantic.exists() and json.loads(semantic.read_text()).get('result') == 'passed':
|
|
import runpy
|
|
runpy.run_path(str(ROOT / 'tools/portal-audit-semantic-update.py'), run_name='__main__')
|