Files
Investidor-V2/tools/portal-audit-response-update.py
nala 20971d523f
tests / ci (push) Canceled after 0s
oportunidades 6
2026-10-10 21:17:46 +02:00

76 lines
8.3 KiB
Python
Raw Permalink 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.
"""Append a scoped response/§27 receipt to the existing matrix; no new inventory."""
import csv
import hashlib
import json
import re
from pathlib import Path
ROOT = Path(__file__).resolve().parent.parent
DOCS = ROOT / 'docs/investmoz/v2/auditoria-portal'
PRIVATE = ROOT / 'storage/app/private/investmoz-v2/portal-audit'
csv.field_size_limit(16 * 1024 * 1024)
proof = json.loads((PRIVATE / 'opportunity-response-http-qa.json').read_text(encoding='utf-8-sig'))
checks = json.loads((PRIVATE / 'profile-installation-checks-qa.json').read_text(encoding='utf-8-sig'))
assert proof['result'] == checks['result'] == 'passed' and proof['active_writes'] == 0 and not proof['interface_observed']
assert len(proof['responses']) == 10 and len(proof['semantics']) == 2
schema_sha = hashlib.sha256((ROOT / 'resources/data/investmoz-opportunity-modular.schema.json').read_bytes()).hexdigest()
assert schema_sha == '9ce0acfa5df1f81a3357dee9e5a9a1c4e77e4eefc8bce294497d18f4caa8e5c0'
matrix = DOCS / 'matriz-requisitos.csv'
with matrix.open(encoding='utf-8-sig', newline='') as source:
rows = list(csv.DictReader(source))
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)', 'Estado']
before = [{k: r[k] for k in protected} for r in rows]
tag = '[Response/§27 QA 10/10/2026] '
values = {
'Implementação encontrada': 'Resposta de leitura versionada com IDs bigint/referências legados intactos, recursos UUID hidratados e metadados efetivamente gravados. Integra o editor existente e GET /minhas-oportunidades/{id}/response. Succession aceite na venda de negócio conforme contrato original; §27 recusa cadastro de parcela repetido na mesma oportunidade antes de submeter.',
'Evidência funcional': 'opportunity-response-http-qa.json: dez respostas recuperadas noutra sessão MFA e iguais ao consumidor Inertia; Validador permitido, organização alheia 403 e sessão anónima 401. QA40: rascunho duplicado recuperável, submissão 422 no campo cadastral, correção e submissão aceite. QA41: succession guardada, submetida e recuperada. Testes isolados específicos em OpportunityCanonicalWorkflowTest; nenhum ensaio visual.',
'Lacuna': 'Response normativo integral ainda parcial: algoritmo de completude, identidade/metadados de itens inline, correspondência explícita de estados e autor de alteração/exclusão de recursos. §27 conservado como fonte, sem criar tabelas especializadas paralelas. Quatro perfis e SLA não aprovados; condições de unidades/urbanismo/entidade sem exercícios mantêm decisões próprias.',
'API': 'GET privado /minhas-oportunidades/{id}/response; recurso autorizado antes de hidratar, Cache-Control private/no-store. Escritas existentes e validações 422 preservadas; nunca publica nem aprova por leitura.',
'Gravação': 'QA40 e QA41 sintéticos, usando oportunidades e coleções existentes. Nenhuma alteração das dez oportunidades anteriores, titulares, prazos ou base ativa.',
'Recuperação': 'Novas sessões Fortify/MFA recuperam IDs, referências, valores e metadados. Números cadastrais ausentes permanecem desconhecidos; sem renumerar UUIDs/bigints ou fabricar aprovação/completude.',
'Interface': 'Resposta entregue ao editor Inertia existente; interface, C37, Promotor QA21 e A/B continuam pendentes. Não confundir recuperação das props com clique ou render observado.'
}
for row in rows:
if row['ID'] == 'AUD-OP-04':
for key, value in values.items():
old = re.sub(r'\s*\|\s*' + re.escape(tag) + r'.*?(?=\s*\|\s*\[|$)', '', row[key], flags=re.S)
row[key] = old + ' | ' + tag + value
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:
writer = csv.DictWriter(f, fieldnames=list(rows[0])); writer.writeheader(); writer.writerows(rows)
marker = 'qa-response-20261010'
def prepend(path, content):
text = path.read_text(encoding='utf-8-sig')
text = re.sub(r'<!-- ' + marker + r':begin -->.*?<!-- ' + marker + r':end -->\s*', '', text, flags=re.S)
first, _, rest = text.partition('\n')
path.write_text(first + '\n\n<!-- ' + marker + ':begin -->\n' + content.strip() + '\n<!-- ' + marker + ':end -->\n\n' + rest.lstrip(), encoding='utf-8')
summary = f'''## Incremento atual — Response/§27 e propostas para decisão
Somente QA **http://127.0.0.1:18097**, `{proof['target']}`. Mesmo inventário/matriz; nenhum fecho integral ou escrita ativa.
- **AUD-OP-04:** `/minhas-oportunidades/{{id}}/response` integra a leitura do editor existente. Preserva IDs, códigos, referências UUID e estados atuais. Dez casos #30–39 recuperados noutra sessão MFA; Validador permitido, organização alheia 403 e anónimo 401. A versão do adaptador declara expressamente que a resposta normativa completa continua parcial.
- **C65 / QA40:** §27 exige unicidade de número cadastral por oportunidade. Rascunho incompleto/duplicado permanece recuperável; submissão devolve 422 junto a `canonical.land_rights.parcels.1.cadastral_number`. Após correção, submissão aceite. Números ausentes não são inventados.
- **C66 / QA41:** `succession` constava no contrato original mas era recusada pela lista semântica de venda. Lista reconciliada; criação/edição, submissão e nova sessão demonstradas. Não alterar silenciosamente outras modalidades.
- **Ainda parcial:** algoritmo de completude, metadados/identidade de itens inline, equivalência explícita dos estados e recuperação dos autores de alteração/exclusão dos recursos. §27 não cria tabelas paralelas. Condições sem regra inequívoca continuam como decisões D-S1/S2.
Provas: `opportunity-response-http-qa.json`, `OpportunityCanonicalWorkflowTest`, `profile-installation-checks-qa.json` e schema original preservado. O recibo identifica separadamente a tentativa anterior em que passaram os dez critérios de leitura e o ensaio semântico posterior; falhas do harness foram conservadas, não apresentadas como falhas do portal.
**Propostas modulares e SLA continuam não aprovados.** O ficheiro `propostas-perfis-modulares-sla.md` explicita compromissos internos de atendimento, janela operacional do Promotor, pausas por tarefa, espera externa separada e ausência de decisão automática. Nenhum `due_at`, titular ou perfil modular ativado.
**Ativação:** o restauro reportado é QA. Para uma futura ativação, conferir a identidade, schema, dados e configuração efetivos da base ativa para a revisão candidata, capturar hashes/manifesto dessa revisão e provar o seu backup/restauro. As provas QA ou `ready=true` não substituem essa ligação. Sequência 030000 → 040000 → 190000; C37, QA21 e A/B visual pendentes, candidata não fixada.
**Próximo trabalho independente:** completar metadados/Response sem novos cadastros; PDFs gerados/exportações/derivadas por consumidor; países/origem com filtros e estatísticas por formulário; correspondências inequívocas, mantendo Mandie/Guro ambíguo. Cartografia permanece 408 resolvidas e 173 lacunas neste incremento.
'''
for name in ['relatorio-executivo.md', 'backlog.md', 'workflow-oportunidades-qa.md']:
prepend(DOCS / name, summary)
prepend(DOCS / 'plano-ativacao.md', '**Preparação da futura ativação.**' + summary.split('**Ativação:**', 1)[1].split('**Próximo trabalho independente:**', 1)[0])
case_block = '- **C65' + summary.split('- **C65', 1)[1].split('- **Ainda parcial:**', 1)[0]
prepend(DOCS / 'casos-reproducao.md', case_block + '\nC65 foi identificado por confronto código/§27; C66 por divergência da lista do servidor com o contrato. A prova atual é pós-correção. Não atribuir a estes casos falhas antigas não reproduzidas.')
prepend(ROOT / 'docs/investmoz/v2/gestao-oportunidades-dashboard.md', summary)
(PRIVATE / 'response-audit-update-qa.json').write_text(json.dumps({'requirements': 3386, 'source_occurrences': 6838, 'protected_fields_preserved': True,
'schema_sha256': schema_sha, 'states_unchanged': True, 'updated': ['AUD-OP-04'], 'active_writes': 0}), encoding='utf-8')
print(json.dumps({'requirements': 3386, 'source_occurrences': 6838, 'updated': ['AUD-OP-04'], 'states_unchanged': True, 'active_writes': 0}))