"""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.` 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//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//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?.*?\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\n' + text.strip() + '\n\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)