Files
Investidor-V2/tools/portal-audit-semantic-docs.py
nala 01405cbf8a
tests / ci (push) Canceled after 0s
oportunidades 5
2026-10-10 14:15:28 +02:00

174 lines
25 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.
"""Update the existing executive report, backlog and evidence; retain earlier versions as history."""
import collections
import csv
import json
import re
import sys
from pathlib import Path
ROOT = Path(__file__).resolve().parent.parent
PRIVATE = ROOT / 'storage/app/private/investmoz-v2/portal-audit'
DOCS = ROOT / 'docs/investmoz/v2/auditoria-portal'
sys.stdout.reconfigure(encoding='utf-8')
csv.field_size_limit(16 * 1024 * 1024)
http = json.loads((PRIVATE / 'opportunity-semantic-http-qa.json').read_text(encoding='utf-8-sig'))
editorial = json.loads((PRIVATE / 'opportunity-semantic-publication-http-qa.json').read_text(encoding='utf-8-sig'))
verified = json.loads((PRIVATE / 'opportunity-semantic-verification-qa.json').read_text(encoding='utf-8-sig'))
environment = json.loads((PRIVATE / 'opportunity-semantic-environment-qa.json').read_text(encoding='utf-8-sig'))
active = json.loads((PRIVATE / 'dashboard-active-readonly.json').read_text(encoding='utf-8-sig'))
preserved = json.loads((PRIVATE / 'profiles-v1-preservation-qa.json').read_text(encoding='utf-8-sig'))
counts = json.loads((PRIVATE / 'contagens-requisitos.json').read_text(encoding='utf-8-sig'))
assert http['result'] == editorial['result'] == 'passed'
assert http['target'] == 'desk_restore_20261009_174726_test:55441'
assert all(p['active_writes'] == 0 for p in [http, editorial, verified, environment, active, preserved])
documents_path = PRIVATE / 'opportunity-semantic-documents-http-qa.json'
document = json.loads(documents_path.read_text(encoding='utf-8-sig')) if documents_path.exists() else {}
names = {'investment_project': 'Investimento', 'real_estate': 'Imobiliário', 'land_rights': 'Fundiário/DUAT',
'business_sale': 'Venda de negócio', 'business_partnership': 'Parceria de negócio', 'franchise': 'Franquia',
'concession': 'Concessão', 'financing': 'Financiamento', 'supply': 'Fornecimento', 'other': 'Outros'}
category_rows = []
for case in http['cases']:
category = case['category']
publication = ('Pública, só teaser; prova editorial posterior' if category == 'business_sale' else
'Bloqueada: autorização externa pendente' if category == 'land_rights' else
'Interna; sem exposição pública' if case['publication'] == 'published-internal' else 'Bloqueada')
ready = case['submission'] == 'passed'
category_rows.append(f"| {names[category]} — #{case['id']} | Incompleto guardado e recuperado | Edição gravada, nova sessão MFA | {'Aceite' if ready else '422: perfil modular por aprovar'} | {'Gates e aprovação interna' if ready else 'Não elegível'} | {publication} |")
category_table = '\n'.join(category_rows)
state_table = '\n'.join(f"| {state} | {v['requisitos']} | {v['linhas_origem']} |" for state, v in counts['por_estado'].items())
pdf_evidence = ('PDF válido de uma página, parser estrito, upload/scan ClamD real e SHA; download 21/24/25, negação 23(403); exclusão lógica→download404/opção removida; restauração→nova sessão→bytes/hash idênticos. Prova `opportunity-semantic-documents-http-qa.json`.'
if document.get('result') == 'passed' else 'Ensaio PDF web18097 ainda não encerrado; consultar a prova sanitizada da operação, sem extrapolar PNG a outros tipos.')
summary = f'''## Resultado atual por categoria — semântica e atribuições em QA, 10/10/2026
Este bloco é a evidência mais recente desta matriz; as secções anteriores abaixo permanecem como histórico. Estados antigos sobre objetos opacos, coordenação editorial e ausência de 190000 ficam confrontados com os resultados concretos deste bloco. **Código preparado e HTTP/persistência em QA não encerram interface nem funcionalidade ativa.** Não houve escrita na base ativa. C37 e aceitação visual permanecem pendentes.
Servidor **http://127.0.0.1:18097**, base **desk_restore_20261009_174726_test:55441**, Fortify e MFA normais. **Promotor QA 21** continua a conta de ensaio do dashboard. Não usar Auditor ou Superutilizador em seu lugar.
| Categoria / ID QA | Rascunho | Edição / recuperação | Submissão | Revisão | Publicação |
| --- | --- | --- | --- | --- | --- |
{category_table}
As dez categorias recuperaram dados noutra sessão autenticada. As seis com contrato definido aceitaram os cenários semânticos documentados; as quatro restantes conservaram rascunhos, sem inventar um perfil modular para as submeter. Fundiário #32 separa aprovação interna da decisão externa. Venda #33 passou depois pelo percurso editorial público, sem divulgar resumo/dados privados.
**Campos e regras preparados:** cronograma com início/fim/responsável e dependências acíclicas; maturidade com os oito códigos normativos; viabilidade com cenário/indicadores/modelo e estudo, sem score de sucesso; serviços/título/entidade emissora/ocupação/avaliação imobiliários; parcelas com editor de vértices/anéis MultiPolygon, benfeitorias e consulta comunitária; constituição/beneficiário efetivo, exercícios existentes, ativos/passivos, obrigações laborais, licenças, NDA, governação e franquia. O contrato é elaborado no servidor e consumido pelo editor, sem templates JSON divergentes no cliente. Objetos legados conservam chaves anteriores. O schema original de 110 definições mantém SHA `{verified['source_schema_sha256']}`.
**Falhas corrigidas com prova:** {len(http['negative_fields'])} recusas 422 identificaram o campo e conservaram o rascunho inválido: fim anterior ao início, ciclo, falta de cenário/modelo, serviços incompletos, título de outra oportunidade, comunidades sem consulta, financeiros insuficientes e contingências não declaradas. Erros `canonical.<campo>` são devolvidos pela API e ligados aos controlos; a posição/clareza visual ainda não foi observada. O formulário núcleo duplicado foi retirado do modo perfil; gestão legada mantém o seu acesso próprio.
**Filas e perfis:** revisão, preparação editorial, recurso e tarefas atribuídas reutilizam `tasks`, com ligação explícita ao bigint da oportunidade. #30 pediu parecer a 29: o destinatário errado recebeu403, a aprovação com parecer pendente422; a resposta foi guardada e concluiu a tarefa, sem aprovar o gate automaticamente. #33: coordenador editorial25 atribuiu a29; tarefa recuperada na fila; outro Publicador403; o designado publicou. Papéis/âmbitos/independência/responsabilidades são verificados no servidor; revogação após aprovação bloqueia publicação no teste específico. Responsabilidades atribuídas neste ensaio são sintéticas, sem competência institucional real inferida. SLA permanece `unconfigured` e `due_at=null`.
**Consumidores territoriais/documentais:** seis contratos recuperaram país MZ e uma localização com limite disponível; API retornou dois limites de contexto. O editor e os campos `location_id` consultam o território mais profundo e mantêm a parcela separada do limite administrativo. País FR do mercado do investimento foi gravado/recuperado; isto não encerra origem/filtros/estatísticas de todos os formulários. Originais dos seis casos permitiram21/24/25 e negaram organização estrangeira23(403). O download/prévia reutiliza `VerifiedDocumentBinary`, que verifica cifra/hash/scan e controla falhas; não altera os originais. #33 recuperou original da imagem e duas derivadas limpas pelo ClamD real. {pdf_evidence}
**Limites concretos:** correspondência entre titular/documento/cadastro e âmbito urbanístico exige evidência e decisão do gate, sem inferir autenticidade jurídica pela existência de um UUID. Limites urbanísticos e materialidade não são inventados. Unidades aplicáveis, Response integral e relações do §27 ainda precisam de reconciliação. O schema original exige pelo menos um exercício financeiro; casos com zero exercícios devem ter regra expressa reconciliada antes de serem declarados cobertos. A moeda obedece ao padrão do contrato; não existe aqui prova de catálogo completo ISO4217. DOCX/XLSX, partilhas e PDFs/exportações/derivadas sem prova específica continuam abertos. C37, A/B e todo o percurso visual por perfil permanecem pendentes.
**Preservação e compilação:** 18 conjuntos de linhas anteriores por ID/colunas originais preservados; MFA18 intacto. Manifesto ativo `{verified['active_manifest_sha256']}` preservado. Manifesto QA `{verified['qa_manifest_sha256']}`; bundle próprio `build-qa-opportunities`, sem recompilar o ativo. TypeScript, PHPStan e testes específicos passaram; as contagens de testes não são usadas como critério de conclusão.
Mesma matriz: **3.386 requisitos / 3.386 linhas**, **6.838 ocorrências de origem**. IDs, fontes, critérios, versões divergentes e referências anteriores conservados.
| Estado canónico | Requisitos | Ocorrências de origem |
| --- | ---: | ---: |
{state_table}
Linhas principais: **AUD-OP-02/03/04, AUD-GEO-03, AUD-PROFILE-11/12/19/25/28/31 e AUD-OPS-01**, em [matriz-requisitos.csv](matriz-requisitos.csv). Nenhum desses critérios visuais foi fechado por API.
'''
backlog = '''## Próximo trabalho executável e decisões separadas — lote semântico atual
| Prioridade / requisito | O que já tem prova QA | Falta / próxima ação e critério concreto |
| --- | --- | --- |
| P0 — UI-01/C37 | Diagnóstico/MFA preservados; ensaios HTTP21 não fecham18. | C37 e confirmação visual continuam pendentes conforme pedido. Quando retomados, dashboard→MFA→sessão→refresh→logout→novo login; rastreio sanitizado e primeiro ponto de perda. Nenhum novo teste visual solicitado agora. |
| P1 — OP-04 / PROFILE-28/25 | Seis editores/regras no contrato do servidor; dez drafts e doze rejeições por campo recuperadas. | Observar cada categoria na UI21, erro junto ao controlo e nova sessão; reconciliar unidades aplicáveis, entidade/título/cadastro e limites urbanísticos com gates; completar Response/§27 apenas reutilizando entidades. Não regredir source/enums/IDs ou tornar campos incompletos obrigatórios para guardar. |
| P1 — OP-03 / PROFILE-11/12/19 | Filas, parecer atribuído, resposta, revisão e coordenação editorial com task/hash/papéis; negações específicas. | Interface por24/25/29, reatribuição e revogação; incidentes, disponibilidade e revisão periódica. Parecer externo requer responsabilidade institucional concreta, não um ator fictício; aprovação interna não substitui decisão externa. |
| P1 — GEO-03 / COUNTRY/STATS | Seis contratos ligam localização/documentos por IDs e país MZ; investimento recupera mercadoFR; editores consultam geometrias no nível selecionado. | Verificar visual de todos os consumidores afetados, país/origem→recuperação→filtro→estatística por formulário. Resolver nomes pendentes do pacote:31distritos+157postos; localidades/parcelas não inferidas de limites administrativos. Reutilizar importação autenticada/idempotente; publicação de camada é separada. |
| P1 — OP-02 / LIB / PROFILE-23 | Originais por perfil, negação de organização23, cifragem/hash, PNG e duas derivadas ClamD; PDF do ensaio atual separado. | Continuar DOCX/XLSX, partilhas, PDFs/exportações/derivadas sem prova específica; preservar originals/versões. Consistência de erro404 recurso excluído versus423 scan pendente foi corrigida no consumidor de oportunidades. |
| P1 — PROFILE-23 | Dois avisos preexistentes de league/commonmark identificados no lote anterior. | Avaliar consumidores e corrigir compatibilidade emQA; não apagar o achado nem atribuir a Opis. |
| P2 — PROFILE-31 / OPS-01 | 190000confirmadaQA/ausenteativo;030/040QA;baseBalcãoativa instalada, hashes atualizados. | Construir instalação/bootstrap autenticados040 com duasidentidades reais explicitamente designadas; não mudar titulares nem retirar privilégio ativo automaticamente. Apósvisual, fixar candidata e renovarbackup/restauro dessa revisão; executar apenas migrações faltantes pelo mecanismo adequado. |
Decisões independentes, sem bloquear este lote:
1. **Concessão, financiamento, fornecimento e outros:** quais versões imutáveis de perfil modular, campos obrigatórios, documentos e gates são aprovadas para cada categoria? Sem esse contrato, guardar/editar funciona; submissão/revisão/publicação continuam bloqueadas.
2. **SLA:** qual política versionada fixa prazo por tipo de tarefa, tolerância, número de ciclos e escalamento? Quem assume formalmente cada competência de coordenação, parecer técnico/jurídico, publicação e recurso? Manter prazo nulo e atribuição explícita até existir decisão; não usar nomes/prazos de QA como norma.
3. **Condições sem regra inequívoca:** qual âmbito/citação estabelece a aplicabilidade de unidades/limites urbanísticos e materialidade? Como reconciliar o mínimo de um exercício do schema com entidades sem exercícios existentes? Não substituir valores ausentes por zero nem alterar silenciosamente a versão aceita.
Não iniciar jornadas educativas novas. Mantêm-se cinco fases/19 etapas e URLs, dossier privado, oportunidade publicada, fontes oficiais aceites e histórico.
'''
activation = f'''## Ativação reconciliada: 030000, 040000 e **09/190000** — nenhuma execução no ativo
A omissão de **2026_10_09_190000_prepare_investor_path** na sequência anterior foi um erro documental; a extensão **continua necessária e não foi substituída**. Não confundir com **2026_10_07_190000_create_internal_virtual_desk**, que já está instalada no ativo.
| Recurso | QA restaurada:55441 | Ativo investemoz:5432, READ ONLY | Evidência |
| --- | --- | --- | --- |
| Gestão020000 | Instalada | Instalada | Ledger/schema anteriores preservados |
| Balcão07/190000 e orientação08/120000 | Instalados | Ambos no ledger | Leitura efetiva das migrações, sem boot de sessão ativa |
| Recursos09/030000 | Instalada | Ausente | Tabelas e ledger; não repetirQA |
| Perfis10/040000 | Instalada | Ausente | Tabelas/verificaçãoQA; nunca instalada no ativo neste trabalho |
| Percurso09/190000 | **Ledger + duas colunas + definição publicada** | **Ledger ausente; duas colunas ausentes; nenhuma definição** | `dashboard-active-readonly.json`, `opportunity-semantic-environment-qa.json` |
DefiniçãoQA `61000919-0000-4000-8000-000000000001`, publicada, SHA256 **{environment['definitions'][0]['sha256']}**. Duas colunas: `investor_journeys.path_definition_id` e `journey_steps.path_detail`. Mantém cinco fases e rastreabilidade das19etapas; não renumera/reescreve os roteiros existentes. Recurso preparado/definição publicada não demonstra A/B na interface.
Dependências verificadas no código: **030000** usa oportunidades/documentos existentes e cria três coleções de recursos; **040000** acrescenta quatro tabelas de autorização/auditoria, uma coluna de revisão em users e três colunas em opportunities, preservando identidades/histórico; não usa as colunas de190000. **190000** usa investor_journeys/journey_steps/questionnaire_versions e depende da baseBalcão/orientação incluída no instalador. Não existe dependência DDL entre030/040 e190; disponibilizar o workflow requer020+030+040 e tasks/catalogue existentes; disponibilizar o percurso integrado requer190 e as capacidades instaladas/configuradas. Os fingerprints incluem código comum de perfis: alterar o código invalida provas antigas de readiness, sem converter isso numa dependência de schema.
| Ordem futura | Ação concreta e efeito / contagem esperada | Dependência e reversão |
| --- | --- | --- |
| 0 | Incorporar visualC37/Promotor21/A-B; corrigir falhas e fixar revisão/manifesta/configuração. Renovar dumpDB+ficheirosoriginaisprivados+chaveestável e restauroverificado numa novaQA. | Candidata ainda não fixada; provas antigas não são backup dessa revisão. Nunca key:generate indiscriminado. |
| 1 | Disponibilizar código/Composer/bundle após aprovação de ativação, flagsfalse. | Nesta análise só buildQA foi atualizado; manifestativo preservado. Reversão para código/build anteriores e flagsfalse; assegurar compatibilidade de leitura. |
| 2 | **030000**, se ledger/schema realmente ausentes: préviaGET e aplicaçãoPOST autenticada `/management/opportunity-resources-install` com hash/confirm. | Três coleções resource e metadado de cifra; **zero documentos/dados publicados/importados**. Base020/documentos existentes. Downgrade destructive recusado se originais cifrados usados. |
| 3 | **040000**, se ausente: instalar quatro tabelas/colunas através de mecanismo autenticado e bootstrap/recuperação da dupla, ainda por implementar paraativo. | Não substituir por migrateglobal/configmanual para contornar guarda. **Zero contas/grants QA** noativo. Titulares/reconciliação de permissões reais precisam de decisão explícita. Código independente de190. |
| 4 | **09/190000**, se ausente: préviaGET e aplicaçãoPOST autenticada `/management/balcao-install`; instalador seleciona só migrações faltantes. | BaseBalcão07/190000 e orientação08/120000 jáconstamdoativo: não repetir. **Duas colunas + uma definição publicada**, se não existir equivalente; **zero dossiers/journeys criados automaticamente**. Versões publicadas imutáveis. |
| 5 | Configurar catálogos/responsabilidades reais e instalar apenas contratos modulares aprovados; comparar IDs/citações/versões. | A autoridade sintética QA3e1ff315… e atores21–31 não são dados deativação. Sem alteração automática de valores legados. |
| 6 | Importação cartográfica autenticada privada `/management/territory-geometry-import`: prévia/correspondências/hash→aplicação idempotente→auditoria. | QA393=11províncias+128distritos+254postos; ativo0naúltimaleitura. **393 candidatas inequívocas**, sujeitas à prévia da revisão e aos mesmos IDs;188pendentes(31/157), sem criar geometrias falsas. Contagem final deve ser medida no alvo. Reversão por lote/metadados e histórico, sem apagar cadastros. |
| 7 | Ativar flags de perfis/oportunidades e percurso separadamente, testar navegação completa e permissões. | 030/040 antesdoscontratos/workflow;190antesdodisponibilizarpercurso; C37/visual/A-B não encerrados. Preparação paralela e acesso direto continuam. |
| 8 | **Publicar camadas, oportunidades ou documentos como ações separadas e explícitas**. | Nenhuma instalação/importação publica automaticamente; publicação não ativa cálculo fiscal. Usar decisões/evidências/hash atuais e autorização de divulgação. |
Reversão: desativarflags/voltarbundle compatível e conservar originais/auditoria. Migrações usadas não devem ser eliminadas por rollback genérico;190recusadownquandohápercurso,030recusacomcifrados,040preservaperfil/oportunidade. Quando necessária reversão de dados, usar o restauroverificado da candidata com reconciliação das escritas posteriores. **ready=true é preparação; não instalação, ativação nem aceitação funcional.**
'''
cases = '''## Reprodução e evidências adicionais — mesmo inventário
| Caso / requisito | Passos e esperado | Observado / causa | Correção e estado |
| --- | --- | --- | --- |
| C55 — OP-02 | QA21→PDF limpo→exclusão lógica→GETdownload. Esperado: recurso indisponível, sem atribuir falha de scan. Restaurar e recuperar original. | HTTP423/quarentena porque download juntava row inexistente à condição de scan. | Separa ausência/exclusão404 de quarentena423; teste específico e ensaioPDFweb18097. Originais/hash preservados; visualpendente. Prova da falha `opportunity-semantic-documents-http-qa-attempt1.json`. |
| C56 — OP-04/PROFILE-25 | Promotor21 abre editor de categoria; campos de núcleo devem ter um único valor/controlo e erros associados. | Revisão do código: formulário legado e contrato canónico duplicavam título/território/campos, com valores independentes. | Contrato canónico único no modo perfil; formulário legado continua na gestão própria. BuildQA/TS e payloadAPI; observação visualpendente. |
| C57 — OP-04/PROFILE-28 | Guardar fim anterior ao início/ciclo/serviços incompletos/documento estrangeiro/comunidade sem consulta/financeiros incompletos; recuperar noutra sessão; submeter. | Regras anteriores aceitavam objetos opacos sem semântica suficiente. | 12negativas422 com caminho de campo; rascunhos recuperados; seis cenários completos submetidos/revistos. Não extrapolar a regras jurídicas ainda sem âmbito/gate. |
| C58 — OP-03/PROFILE-19/12 | Parecer com destinatário técnico/jurídico; responder outro ator; aprovar pendente. Depois atribuir publicação e tentar outro Publicador. | Fluxo anterior não tinha destinatário/resposta/fila completa nem atribuição editorial. | #30destinatário29/resposta/tarefa;outro403/aprovação422; #33filaeditorial29/outro25publica403/designado29publica. Sem gateautoaprovado/prazofictício; visualpendente. |
| 190000 — OPS-01/PROFILE-31 | Comparar ledger/duascolunas/definição QA eativo, distinguir migraçãoBalcão07 depercurso09. | Plano anterior omitia recurso ainda ausenteativo. | 09/190000confirmadaQA e ausenteativo, necessária/nãosubstituída; seqüência autenticada recolocada. Nenhuma execuçãoativa. |
'''
manual = '''## Ensaio visual preparado — permanece pendente conforme pedido
URL QA **http://127.0.0.1:18097/dashboard**. Conta **Promotor QA21**, MFA normal, credenciais/código obtidos apenas localmente. Não incluir cookies, cabeçalhos X-XSRF-TOKEN, segredos ou corpo do código em capturas. C37/conta18 é um ensaio independente e continua pendente.
1. Entrar no dashboard como21. Esperado: **Minhas oportunidades** e **Nova oportunidade**, ambos funcionais, no contexto autorizado. URLlista `/minhas-oportunidades`; criação `/minhas-oportunidades/create`.
2. Abrir cada ID QA30–39 em `/minhas-oportunidades/<id>/view` e, nos estados editáveis, `/edit`. Esperado: estado privado e publicação distintos; núcleo único, categoria correta e permissões aplicadas. #36–39devem continuar drafts.
3. Criar rascunho incompleto, guardar; logout/nova sessãoMFA e recuperar. Não usar as fichas publicadas para substituir este passo. Esperado: mesmos dados/ID, sem mistura com outrosprojetos/oportunidades. Se ainda não houver localizações/documentos, guardar primeiro e abrir **Recursos**.
4. Nos recursos `/minhas-oportunidades/<id>/resources`, selecionar país e hierarquia; conferir mensagem de território cadastrado, geometria selecionada disponível/ausente e mapa. A parcela declarada em DUAT deve manter geometria própria, sem substituir por limiteadministrativo. Conferir original/prévia antes/depois de recuperar.
5. Num rascunho de ensaio das seis categorias, introduzir erro semântico e guardar; submeter. Esperado:422, mensagem junto ao campo, valores preservados; depois corrigir e resubmeter. Concessão/financiamento/fornecimento/outros devem informar perfilmodularpendente.
6. Como24, abrir mesma ficha/fila (`/minhas-oportunidades?queue=review`), atribuir/iniciar→solicitarcorreção→21corrige→nova revisão/gates. Registar motivo e documentos da própria versão. Ensaiar parecer por destinatário29: outra resposta negada e tarefa/parecer recuperados sem gateautoaprovado.
7. Como25, fila editorial (`?queue=publication`), designar Publicador independente; como29, fila `?queue=assigned`, comparar prévia/hash/teaser e publicar. OutroPublicadornãodesignado deve recebernegação; aprovarprópria/mesmaorganização/múltiplospapéis deve continuar negado. Não atribuirresponsabilidadesreais aos atoresQA.
8. Depois de publicar, solicitar revisão/materialalteração; esperado: publicação anterior preservada até nova revisão. Fundiário32não pode contornar autorizaçãoexternapendente. Conferir suspensão e recuperação dos motivos/tarefas.
Registar por ação URL/ator/ID/estado/API/persistência/resultado visível; associar a mesma matriz. A/B completos e cincofases/19etapas continuam no roteiro existente, sem renumerar. Não fechar critérios por leitura dos dados ou totaldetestes.
'''
marker = 'qa-semantic-20261010'
def prepend(path, text):
existing = path.read_text(encoding='utf-8')
existing = re.sub(r'\n?<!-- ' + marker + r':begin -->.*?<!-- ' + marker + r':end -->\n?', '', existing, flags=re.S)
parts = existing.split('\n', 1)
rest = parts[1].lstrip() if len(parts) > 1 else ''
path.write_text(parts[0] + '\n\n<!-- ' + marker + ':begin -->\n' + text.strip() + '\n<!-- ' + marker + ':end -->\n\n' + rest, encoding='utf-8')
for name, body in [('relatorio-executivo.md', summary), ('backlog.md', backlog), ('plano-ativacao.md', activation),
('casos-reproducao.md', cases), ('roteiro-manual-perfis-v1.md', manual),
('perfis-v1-reconciliacao.md', summary), ('cobertura-percursos-i0-i3.md', summary),
('execucao-percurso-i1-i3.md', summary), ('workflow-oportunidades-qa.md', summary + '\n\n' + backlog + '\n\n' + activation + '\n\n' + cases + '\n\n' + manual)]:
prepend(DOCS / name, body)
prepend(ROOT / 'docs/investmoz/v2/gestao-oportunidades-dashboard.md', manual + '\n\n' + summary)
print(json.dumps({'updated_documents': 10, 'requirements': 3386, 'sources': 6838, 'visual_pending': True, 'active_writes': 0}))
if (PRIVATE / 'profile-installation-http-qa.json').exists():
import subprocess
subprocess.run([sys.executable, str(ROOT / 'tools/portal-audit-installation-update.py')], check=True)