Files
Investidor-V2/docs/implementation/baseline.md
T
nala 4f35313d06
tests / ci (push) Canceled after 0s
novas funcionalidades dos formulario
2026-10-02 11:13:37 +02:00

35 KiB
Raw Blame History

Baseline técnico — 2026-10-01

Âmbito e método

Auditoria somente de leitura do estado do repositório em main, commit a6afb4e97ae8afc01f3455e9e67656a130c60432. Não se inspeccionou o conteúdo de .env nem se ligou à base de desenvolvimento/produção. As consultas PostgreSQL abaixo usaram exclusivamente portal_services_test em 127.0.0.1:55441, com o utilizador de teste education_test e senha vazia.

O repositório já tinha alterações funcionais locais no início da auditoria: 26 caminhos modificados e 12 não rastreados. Foram preservados. Esta entrega só cria este documento.

Stack e versões confirmadas

Componente Baseline observado Evidência
Backend Laravel 13.30.1 (composer.lock); composer.json exige PHP ^8.3 e Laravel ^13.17 composer.lock, composer.json
PHP CLI PHP 8.5.11; php.ini em C:\Users\macbook\.config\investmoz\php-8.5.11\php.ini; módulos pdo_pgsql, pgsql, mbstring, openssl, intl, fileinfo, sodium, curl e zip carregados php.exe -v, --ini, -m
Composer Composer 2.8.12; composer.json validado com o PHP 8.5.11 explícito composer.phar validate --no-check-publish
Frontend React 19.2.8, React DOM 19.2.8, Inertia React 3.7.0, TypeScript 5.9.3, Vite 8.2.2, Vite Plus 0.3.0, Leaflet 1.9.4 package-lock.json
Node/npm Node 24.20.0, npm 11.19.0 node --version, npm --version
Dados PostgreSQL 17.11 na base isolada; 147 tabelas através dos schemas public, investmoz e skills_edu artisan db:show --database=pgsql
GIS Migração requer PostGIS e CI usa postgis/postgis:17-3.5; a versão exacta da extensão na base isolada não foi consultada migration 2026_09_24_000100_investmoz_extensions_functions.php, .github/workflows/tests.yml
UI/SSR Laravel + Inertia + React/Vite; não é Next.js composer.json, package.json, vite.config.ts, resources/js

O comando global composer resolve para o wrapper Herd/PHP 8.5.0. Para esta baseline, a validação Composer foi executada chamando composer.phar com o binário PHP 8.5.11 indicado pelo utilizador.

Git e estado de trabalho

  • Branch actual: main, a rastrear origin/main; as referências locais indicavam divergência 0/0 na leitura.
  • Remoto configurado: origin.
  • Branches locais adicionais observadas: feature/existing-business-phase-0, feature/gis-phase-0, feature/mne-canonical-governance, feature/mne-verification-evidence e feature/territorial-mvp.
  • Não foi encontrada política de branching/PR versionada nesta leitura. pull.rebase=false; pull.ff não está configurado. O histórico recente contém commits directos em main; isto descreve evidência Git, não recomenda o processo.
  • Estado inicial: 26 caminhos modificados e 12 não rastreados. Entre eles há controllers, services, rotas, migração, React, testes e documentação da implementação de negócios existentes. A auditoria não os alterou nem os atribui ao trabalho deste relatório.

Migrations e base de dados

artisan migrate:status --database=pgsql foi executado com estas substituições de ambiente, sem ler as credenciais de .env:

$env:DB_CONNECTION='pgsql'
$env:DB_DATABASE='portal_services_test'
$env:DB_HOST='127.0.0.1'
$env:DB_PORT='55441'
$env:DB_USERNAME='education_test'
$env:DB_PASSWORD=''
$env:DB_URL=''
& 'C:\Users\macbook\.config\investmoz\php-8.5.11\php.exe' artisan migrate:status --database=pgsql

Resultado: 52 migrations listadas como Ran; zero pendentes nesta base de teste. artisan db:show confirmou PostgreSQL 17.11, portal_services_test, porta 55441 e 147 tabelas. Isto não comprova o estado de migrations em desenvolvimento, staging ou produção. O repositório contém 52 ficheiros de migration no momento da auditoria. Migrações posteriores à linha base podem ainda estar apenas no working tree; não foram aplicadas a outra base.

Estrutura e convenções observadas

  • Backend organizado em app/Http/Controllers, Requests, Resources, Policies, Services, Jobs, Console/Commands, Integrations e Models.
  • React/Inertia em resources/js/pages, componentes em resources/js/components; rotas separadas por domínio em routes/.
  • PostgreSQL/PostGIS é definido principalmente por migrations Laravel com SQL PostgreSQL explícito. A migration de extensões instala postgis e pgcrypto.
  • Existe uma migração canónica de negócios existentes, além de catálogos de oportunidades, CRM, geografia e território. Não foi presumida uma segunda stack.
  • Catálogos e território têm documentação de dados/OpenAPI por domínio; não foi localizado um contrato OpenAPI canónico na raiz que cubra todas as rotas.
  • Os artefactos OpenAPI existentes são parciais e misturam versões 3.0.3/3.1.0. Há geradores locais, mas o workflow CI não apresenta validação semântica global dos contratos.

Autenticação, autorização e segurança de dados

  • Laravel Fortify fornece login, verificação de email, recuperação de password, 2FA e passkeys configuráveis.
  • manage-portal é um Gate baseado em email verificado e lista configurada. Rotas de gestão usam também middleware can:manage-portal; alguns domínios sensíveis acrescentam MFA e designações específicas.
  • A autorização é principalmente aplicada na aplicação via middleware, Gate/Policy e serviços. A pesquisa de migrations não encontrou ENABLE ROW LEVEL SECURITY, FORCE ROW LEVEL SECURITY ou CREATE POLICY.
  • Portanto, isolamento RLS não está demonstrado; a aplicação não deve ser considerada protegida por RLS. Privilégios SQL por papel e teste de bypass de RLS não foram auditados porque não se acedeu a ambientes fora do banco isolado.
  • Existem funções e triggers PostgreSQL para integridade/auditoria. Estes mecanismos não substituem políticas RLS.

OpenAPI, integrações e catálogos

  • OpenAPI está documentado por áreas, incluindo negócio existente, registo territorial, zonas e unificação do investimento. Os geradores estão em tools/ e os JSON em docs/investmoz/.
  • Cobertura, versionamento e validação são específicos de cada domínio; não há evidência de contrato único para todas as rotas web/API ou verificação OpenAPI global no CI.
  • SuiteCRM tem cliente HTTP, configuração OAuth e comando de probe. SUITECRM_ENABLED é falso por omissão em .env.example; esta leitura não confirmou credenciais nem conectividade CRM.
  • O schema tem outbox_events, inbox_events, sync_runs e ligações externas. A existência dessas tabelas/clientes não demonstra um consumidor completo, integração homologada ou sincronização operacional de todos os domínios.
  • Há catálogos de referência, sectores, oportunidades e território. A existência de dados/seeders não comprova por si só processo completo de versionamento e aprovação para cada catálogo.

CI e testes

Workflow observado: .github/workflows/tests.yml, executado em push para main e pull request. Usa PostgreSQL/PostGIS 17/3.5, PHP 8.5, Node 24; instala Composer/npm, compila assets, chama composer ci:check, executa ciclo de schema PostgreSQL/PostGIS opt-in e testes API frontend. A configuração indica coverage: none; não há evidência de limiar de cobertura.

Inventário local: 36 ficheiros de testes Feature e 5 Unit, mais testes Node. phpunit.xml inclui Feature e Unit; phpunit.investmoz.xml isola tests/InvestMoz para ciclo de schema destrutivo protegido. Os testes Laravel fazem reset apenas em banco de teste reconhecido pela guarda de tests/TestCase.php.

Gate executado nesta auditoria:

Comando Resultado
PHP 8.5.11 -v, --ini, -m Aprovado; caminho/configuração e extensões verificados
Composer 2.8.12 validate --no-check-publish, invocado pelo PHP 8.5.11 Aprovado: composer.json is valid
artisan migrate:status --database=pgsql no banco de teste 55441 Aprovado: 52 Ran, zero pendentes nessa base
artisan db:show --database=pgsql no banco de teste 55441 Aprovado: PostgreSQL 17.11; 147 tabelas
npm.cmd run check Falhou: Vite Plus reportou problemas de formatação em 108 ficheiros. Não foi aplicado autofix. A execução parou aqui, conforme o gate pedido.
PHPStan, Pint global, suite PHPUnit completa, cobertura, build e execução GitHub Actions Não executados nesta auditoria após o gate falhar; sem resultados novos a declarar.

As pastas/ficheiros listados pelo Vite Plus incluem código e documentos de diversas áreas, inclusive páginas e artefactos já alterados no working tree. Não foram modificados para preservar o escopo da Etapa 0.

Matriz de bloqueadores

Severidade Bloqueador e evidência Responsável por função Condição de desbloqueio
Crítica — gate actual npm.cmd run check falha em 108 ficheiros; o CI composer ci:check inclui esta verificação. Responsável frontend / mantenedor CI Rever os ficheiros reportados, acordar formatação sem autofix indiscriminado, obter npm run check verde e executar novamente o workflow.
Alta Working tree em main com 26 modificados e 12 não rastreados; inclui código, migration e testes não commitados. Responsável de release / mantenedor Git Identificar autoria e intenção, rever diff, separar trabalho por branch/PR e manter artefactos aprovados.
Alta Estado de migrations só foi confirmado em portal_services_test; nenhum acesso/estado de dev, staging ou produção foi verificado. DBA / responsável de release Consultar cada ambiente com acesso read-only aprovado e arquivar migrate:status/inventário, sem inferir pelo banco QA.
Alta Nenhuma política RLS foi encontrada nas migrations; isolamento depende das barreiras da aplicação e os privilégios SQL não foram auditados. Arquitecto de segurança / DBA Definir modelo de papéis e ameaça, decidir RLS ou controlo compensatório documentado, testar acesso directo e bypass com papéis reais.
Alta Suite completa, PHPStan/Pint e cobertura não foram executados após o gate falhar; CI declara coverage: none. Mantenedor CI / QA Depois de resolver e aprovar o gate de formatação, correr CI completo em ambiente isolado; decidir e documentar política de cobertura.
Média OpenAPI fragmentado por domínio, versões 3.0.3/3.1.0 e sem validação global observada no workflow. API lead / arquitectura Definir contratos canónicos/versionamento, cobertura de rotas, validação e testes de compatibilidade no CI.
Média SuiteCRM está desactivado por omissão e a auditoria não confirma homologação/consumidor para todos os eventos. Responsável de integração / CRM Confirmar ambiente e credenciais por processo seguro, mapear eventos/dados, idempotência/reconciliação e executar homologação sem dados reais.
Média Política de branches/PR não está documentada; commits recentes estão directamente em main. Mantenedor do repositório Aprovar política de revisão, protecção de branch, releases e rollback e registá-la no repositório.

Responsáveis são funções, não pessoas; não foram inferidos nomes nem delegações.

Reprodutibilidade, limites e rollback

Comandos relevantes usados: git status --short --branch, git branch -vv, git log -5 --oneline --decorate, git rev-list --left-right --count main...origin/main, leitura de composer.json/locks, package.json/lock, phpunit*.xml, migrations, policies, rotas e workflow; php.exe -v, --ini, -m; Composer validate pelo PHP 8.5.11; migrations status e db:show com overrides para portal_services_test; npm.cmd run check.

As consultas SQL foram read-only. npm run check foi verificação sem --fix. O único artefacto criado é docs/implementation/baseline.md; rollback: remover esse ficheiro e a pasta docs/implementation se ficar vazia. Não há migration, alteração funcional, teste ou dado de produção a reverter.

Estado proposto do gate

Etapa 0: baseline documentado; gate técnico BLOQUEADO para avançar. A evidência suficiente para descrever stack e estado local foi recolhida, mas a verificação frontend falhou e existem bloqueadores críticos/altos por resolver. Não se propõe iniciar a Etapa 1 até revisão humana e desbloqueio da matriz.

Gate da Etapa 1 — 2026-10-01

Resultado: BLOQUEADO; Etapa 1 não iniciada. A pré-condição expressa pelo pedido — Etapa 0 aprovada e bloqueadores arquitecturais resolvidos — não está satisfeita: o estado registado acima continua bloqueado e não há evidência de aprovação humana ou de resolução dos bloqueadores. Em particular, o comando npm.cmd run check falhou na baseline e a decisão sobre RLS/privilégios continua pendente. O working tree também foi registado como sujo em main; o estado de migrations em produção não foi verificado.

Por respeito ao gate, não foram criados ADRs nem o mapa SQL–OpenAPI, e não foi iniciada implementação funcional. Não foram executados novos testes nesta etapa, pois fazê-lo não resolveria nem substituiria a aprovação e os desbloqueios exigidos. Para retomar: rever e fechar a matriz de bloqueadores, obter aprovação explícita da Etapa 0 e só então executar a inspecção de contratos da Etapa 1.

Execução da Etapa 1 — 2026-10-01

O utilizador instruiu assumir a Etapa 0 aprovada e autorizou as etapas subsequentes. Com base nessa autorização, a Etapa 1 foi executada apenas no âmbito documental; este registo actualiza o gate anterior, que permanece como histórico da auditoria inicial.

ADRs registados como aceites para orientar contratos e trabalho futuro:

O mapa SQL–OpenAPI parte das 684 linhas do dicionário docs/investmoz/data-dictionary.csv e contém uma linha por campo, tipo/nulabilidade/restrições SQL, descrição, migration de origem, ponteiro/tipo/formato OpenAPI quando há coincidência nominal e estado de classificação dos dados. O cruzamento nominal encontrou 294 candidatos que ainda precisam validação semântica e 390 campos sem propriedade OpenAPI documentada. A classificação por campo não existe no dicionário de origem e está marcada como não registada; não foi inferida. Portanto, o inventário é completo, mas o requisito de correspondência SQL–OpenAPI sem lacunas não foi satisfeito. A matriz também deixa explícito que não se deve expor automaticamente campos SQL privados/internos.

Não foram alterados contratos OpenAPI existentes, migrations, aplicação, configurações ou dados. Não foi executado teste funcional. Revisão documental efectuada por contagem do CSV (684 linhas de dados), cruzamento mecânico exacto de nomes contra propriedades nos cinco OpenAPI JSON e leitura dos nove ADRs. O próximo gate da Etapa 1 requer confirmar candidatos, declarar por campo se é exposto ou interno, completar contratos autorizados e obter classificação de dados aprovada por domínio, sem inventar campos nem classificações.

Estado proposto: Etapa 0 assumida aprovada por instrução do utilizador; ADRs da Etapa 1 registados como aceites; gate de conclusão documental da Etapa 1 PENDENTE até fechar os 390 campos sem contrato, validar os 294 candidatos e aprovar classificações. Não iniciar estrutura persistente ou interface pública baseada em campos ainda não aprovados.

Gate da Etapa 2 — 2026-10-01

Resultado: BLOQUEADO antes de alterações de schema. A aprovação das etapas anteriores foi assumida conforme instrução explícita do utilizador. Contudo, a pré-condição técnica desta etapa exige PostgreSQL 16 com PostGIS disponível para fresh/rollback. A base isolada acessível (portal_services_test, 127.0.0.1:55441) reportou PostgreSQL 17.11; o único serviço local observado foi postgresql-x64-17 (Running), não foi encontrado serviço PostgreSQL 16 e o comando docker não está disponível. A versão exacta de PostGIS nesta base não foi confirmada: a tentativa de consulta via PsySH falhou por erro de sintaxe/citação. A migration existente 2026_09_24_000100_investmoz_extensions_functions.php já instala postgis/pgcrypto e define três funções; não foi alterada nem repetida.

Não foram criadas migrations ou tabelas e não foram executados testes de fresh/rollback, pois não existe o servidor PostgreSQL 16 requerido para validar compatibilidade real. Para desbloquear, disponibilizar um PostgreSQL 16 isolado com PostGIS, acesso de teste e possibilidade de criar/eliminar schema/base; confirmar a versão da extensão antes de implementar. Nenhum serviço de domínio deve ser migrado nesta etapa.

Estado proposto: Etapa 2 BLOQUEADA por ambiente incompatível/não confirmado. Rollback desta tentativa: remover somente esta secção da baseline; não há alterações de schema ou dados a reverter.

Execução da Etapa 2 — PostgreSQL 17.11 — 2026-10-01

O utilizador corrigiu o requisito: a versão-alvo é PostgreSQL 17.11, não 16. A aprovação das etapas precedentes foi assumida pela instrução explícita do utilizador. O ambiente foi confirmado como PostgreSQL 17.11, PostGIS 3.6.2 e pgcrypto 1.3; existe o tipo PostGIS geometry/geography e estão instaladas as três funções investmoz_* existentes.

Decisão de escopo

Não foi criada migration nova. A migration database/migrations/2026_09_24_000100_investmoz_extensions_functions.php já instala as duas extensões com IF NOT EXISTS e cria as funções utilitárias necessárias. O schema e testes existentes usam/verificam UUID, JSONB e tipos espaciais. Não existe decisão de domínio que justifique novo ENUM ou DOMAIN; criar tipos não usados acrescentaria objectos persistentes sem benefício demonstrado. O down() existente remove as funções próprias com RESTRICT e conserva extensões partilhadas, como confirmou o teste de ciclo. Migrations existentes não foram reescritas.

Ficheiros e verificações

  • tests/InvestMoz/SchemaLifecycleTest.php: removido --seed global das duas operações limitadas ao baseline histórico; esse seeder depende de investment_reference_options, criada depois do baseline. O teste continua a invocar explicitamente InvestMozReferenceSeeder no ponto em que as tabelas correspondentes existem.
  • Comando: C:\Users\macbook\.config\investmoz\php-8.5.11\php.exe vendor/bin/phpunit --configuration=phpunit.investmoz.xml --do-not-cache-result, com INVESTMOZ_SCHEMA_TESTS=1 e ligação à base temporária investmoz_test no porto 55441.
  • Primeira execução falhou antes das asserções porque DatabaseSeeder tentou inserir em tabela posterior ao baseline. Após corrigir o harness, resultado: 1 teste aprovado, 3.106 asserções, 0 falhas. O caso executa fresh, rollback por etapas, preservação de extensões partilhadas e reaplicação das migrations.
  • Consulta read-only na base de ciclo: server=17.11, postgis=3.6.2, pgcrypto=1.3, tipos geometry/geography=2, funções utilitárias próprias =3.
  • A base temporária investmoz_test, inicialmente ausente, foi criada apenas para o teste e removida no fim; a confirmação posterior encontrou zero bases com esse nome. A base pré-existente portal_services_test não foi limpa ou modificada por este ciclo.
  • Nenhuma tabela de domínio nova foi criada por esta etapa; o teste apenas recriou schema na base descartável e esta foi eliminada após a verificação.

Estado proposto: Etapa 2 CONCLUÍDA para a fundação actualmente necessária em PostgreSQL 17.11/PostGIS 3.6.2. Extensões, tipos espaciais, funções, fresh/rollback/reaplicação foram verificados. Não foram introduzidos enums/domínios novos por falta de necessidade contratual aprovada. Rollback desta entrega: reverter a alteração do teste; não há migration nova. A anotação documental pode ser removida sem afectar schema ou aplicação.

Execução da Etapa 3 — Catálogos e geografia — 2026-10-01

Implementação

Foram adicionadas migrations para reference_catalog_versions, authorities e economic_activities, com chaves estrangeiras para organizações, territórios e sectores existentes, índices, pt-MZ, vigência [valid_from, valid_to) e protecção contra alteração de versões publicadas. Não foram duplicados os geo_locations nem os catálogos de sectores já existentes.

O seeder ReferenceCatalogSeeder é determinístico e idempotente: valida a origem/versionamento geográfico já empacotado, cria a versão de actividades económicas com referências aos seis sectores disponíveis e deixa autoridades sem registos fictícios enquanto não houver fonte oficial aprovada. As actividades são agrupamentos do portal, não uma classificação estatística/legal oficial.

Foram expostos endpoints anónimos de leitura para autoridades, jurisdições e actividades económicas, com pesquisa, paginação, versão, metadados pt-MZ e ETag. Jurisdições respeitam o filtro pai-filho e só expõem referências públicas. A UI territorial passou a consumir o endpoint versionado e limpa a selecção e descendentes quando o pai muda. O contrato está em docs/investmoz/catalogues/openapi.json.

Ficheiros desta etapa

  • Criados: migration 2026_10_01_000100_create_reference_catalogs.php; seeder ReferenceCatalogSeeder.php; controller Api/V1/ReferenceCatalogController.php; contrato OpenAPI docs/investmoz/catalogues/openapi.json; helper JS e declaração de tipos resources/js/lib/territory-path.js / .d.ts; testes PHP ReferenceCatalogTest.php e Node territory-path.test.mjs.
  • Alterados: ProductionReferenceSeeder.php, routes/api.php, resources/js/components/investment-territory.tsx, resources/js/pages/investment/catalogue.tsx.

Comandos e evidências

  • PHP 8.5.11 explícito: suíte PHPUnit focada (ReferenceCatalogTest|InvestmentTerritoryTest|TerritorialRegistryTest) — 22 testes, 188 asserções, aprovado.
  • node --test tests/frontend/territory-path.test.mjs tests/frontend/portal-api.test.mjs — 15 testes, aprovado; os testes do helper de geografia isolados — 2 aprovados.
  • npm.cmd run types:check — aprovado.
  • npm.cmd run build — aprovado; aviso não bloqueante de ausência do pacote opcional fontaine.
  • Laravel Pint em modo --test nos ficheiros PHP novos/alterados — aprovado.
  • Contrato OpenAPI JSON e referências locais — parse/validação estrutural executados com PowerShell; válido.
  • git diff --check ainda por executar: o executável Git não está no PATH desta sessão; foi localizado em C:\Program Files\Git\cmd\git.exe para verificação final.
  • Os testes Laravel usaram a base descartável investmoz_test, inicialmente ausente, no serviço PostgreSQL 17.11/PostGIS 3.6.2; a base será removida após a execução. A base preexistente portal_services_test não foi resetada nesta etapa.

Riscos, pendências e rollback

Não há nomes de autoridades sem fonte oficial aprovada; o endpoint informa catálogo ainda não publicado até haver dados e aprovação. O catálogo de actividades não substitui códigos oficiais nacionais/internacionais. O filtro territorial depende da classificação pública existente no pacote cartográfico; confirmar versão/origem com o responsável de dados antes da publicação operacional. Não foi aplicado migration em base de produção.

Rollback: executar php artisan migrate:rollback --step=1 apenas num ambiente onde esta migration seja a última aplicada e após exportar/publicar nenhum conteúdo de catálogo; o down() protege-se contra remoção de dados de autoridade/actividade. Reverter também o seeder, controller/rotas, contrato e consumo UI desta etapa. O deploy deve publicar primeiro migration/seeds e só depois a UI que chama os endpoints.

Estado proposto: Etapa 3 tecnicamente implementada e testes aprovados; gate de publicação PENDENTE da revisão humana sobre fonte e publicação dos dados de autoridades e da aprovação da classificação de actividades. Não iniciar a etapa seguinte sem fechar esse gate.

Gate da Etapa 6 — Simulações, jobs, relatórios e segurança — 2026-10-01

Resultado: BLOQUEADO antes de alterações funcionais. A inspecção confirmou Laravel 13.30.1, PHP CLI 8.5.11, React 19 com Inertia, PostgreSQL 17.11/PostGIS 3.6.2 e CI com PostgreSQL/PostGIS 17. O working tree já contém alterações não commitadas anteriores; foram preservadas.

Bloqueadores de pré-condição

  1. Modelo de parâmetros não aprovado. Não foi localizado um contrato aprovado que defina questionários/versionamento, tipos de resposta, semântica de cenários, snapshots de entradas e resultados, nem os limites e estados de simulação. O ADR-004 decide que expressões devem ser seguras, mas não aprova o modelo funcional de parâmetros. Sem isso, criar questionnaire_versions, simulations, scenarios, results, result_sources ou obligations exigiria inferir estrutura e significado.
  2. Matriz RLS/IAM ausente. O ADR-007 declara explicitamente RLS bloqueado até aprovação de papéis, fronteiras de isolamento/tenant, papéis de runtime/migration e contexto de jobs. docs/investmoz/decisions.md também proíbe inventar roles, tenants ou policies. A baseline não encontrou políticas RLS activas. Não há autorização para assumir qual actor pode ver cada execução ou dado de relatório.
  3. Contratos operacionais por domínio pendentes. Os ADRs-002 e -003 aprovam padrões de ETag/If-Match e Idempotency-Key, mas deixam detalhes como representação/versionamento do recurso, escopo e retenção do ledger e replay por operação para cada API. Os requisitos de jobs também não fixam filas, limites de retry/backoff, idempotência de efeitos nem regras de reconciliação. Não foram encontrados contratos aprovados desta Etapa 6 que fechem esses detalhes.
  4. Soft delete e token anónimo. Não foi identificada decisão que determine em quais tabelas se aplica soft delete nem como o hash/token anónimo se relaciona com autenticação, escopo, retenção e revogação. O ADR-001 não permite tratar IDs enviados pelo cliente como autorização.

A autorização anterior para considerar aprovadas as etapas sequenciais não fornece os valores e decisões de domínio ausentes, e não revoga as condições explícitas dos ADRs. Por isso, nenhum schema ou comportamento foi inferido. Não foram executados migrations, seeds ou testes funcionais desta etapa.

Plano proposto após desbloqueio

Após aprovação do modelo de parâmetros e das matrizes IAM/RLS, inspeccionar novamente os contratos aprovados e apresentar antes de editar a lista exacta de migrations, modelos, serviços, policies, jobs, rotas/OpenAPI e testes. A validação deverá incluir acesso autorizado e negado por actor/organização, teste SQL directo com papel runtime e contexto limpo entre transacções, ETag ausente/desactualizado, replay e conflito de idempotência, concorrência real, retry limitado, reconciliação após falha e auditoria sem PII. Até lá, não iniciar estes artefactos.

Condição para desbloquear: aprovar o esquema de parâmetros e snapshots; fornecer matriz actor × recurso × operação e tenant/contexto; aprovar papéis PostgreSQL separados, grants, comportamento de jobs e estratégia de conexão/pool; definir escopo/retensão de idempotência, política de soft delete e ciclo de vida do token anónimo; fixar retries/reconciliação por job.

Estado proposto: Etapa 6 BLOQUEADA por pré-condições arquitecturais em falta. Alteração desta tentativa: somente esta secção documental. Rollback: remover esta secção de baseline.md; não há alteração de código, migration, dado ou ambiente a reverter.

Execução da Etapa 6 — Simulações, jobs, relatórios e segurança — 2026-10-01

O utilizador instruiu assumir todas as aprovações anteriores. A secção de gate bloqueado acima fica como registo histórico e é substituída para esta execução pela aprovação assumida explicitamente nesta conversa. A inspecção confirmou Laravel 13.30.1, PHP 8.5.11, React 19/Inertia, PostgreSQL 17.11/PostGIS 3.6.2 e os padrões locais de membership, outbox, idempotência, auditoria e filas. Não foram alteradas migrations anteriores nem .env.

Diff lógico

  • Migration aditiva 2026_10_01_000200_create_simulation_execution_core.php: cria versões de questionário, simulações, cenários, resultados/fontes, obrigações, snapshots de relatório, ledger de jobs, recibos idempotentes e auditoria append-only. Inclui FKs, unicidades, hashes, RLS habilitado e FORCE ROW LEVEL SECURITY, policies separadas de leitura/escrita e triggers de imutabilidade para questionário publicado, resultados, fontes, relatório pronto e auditoria. down() recusa rollback se houver dados, excepto a versão base fixa que a própria migration instala.
  • Questionário publicado user-entered-arithmetic v1 (pt-MZ): os três parâmetros aritméticos são entradas do utilizador. A taxa e redução ficam limitadas ao intervalo já usado pelo simulador existente (0–100); a base não tem tecto fiscal configurado. A fórmula é base × taxa ÷ 100 × (1 − redução ÷ 100). A resposta não infere moeda, taxa legal, elegibilidade ou obrigação jurídica.
  • Models Eloquent novos: QuestionnaireVersion, Simulation, SimulationScenario, SimulationResult, SimulationResultSource, SimulationObligation, SimulationReport e SimulationJob.
  • SimulationExecution faz transacções, lock/advisory lock por chave, replay idempotente e conflito para payload divergente; o token anónimo é HMAC-SHA256 e só o hash chega à base/fila. Contexto RLS é SET LOCAL e é limpo com o fim da transacção. Edição compara ETag em lock transaccional, devolve 428/412 e incrementa versão; conclusão/falha do job também avança versão e ETag.
  • RunSimulation tem três tentativas, backoff [10,60], estado reconciliável e erros persistidos apenas como códigos. Resultados, proveniência de entradas e relatórios guardam snapshots/hash imutáveis. Não grava texto de excepção, token ou PII no log de auditoria.
  • SimulationPolicy e policies SQL reaproveitam memberships e os papéis existentes (admin, manager, officer, viewer); não criam tenants ou papéis novos. Membros podem ler simulações da organização; escrita exige owner pessoal, token válido ou membership com papel de escrita. Revogar membership retira acesso mesmo ao criador de uma simulação organizacional.
  • SimulationController/requests/rotas API adicionam consulta da versão publicada, criação/consulta, edição de cenário e consulta do relatório. routes/api.php activa middleware web/sessão, CSRF e throttle nos endpoints do simulador.
  • docs/investmoz/simulations/openapi.json documenta cabeçalhos, payloads e resultados HTTP. SimulationExecutionTest.php cobre os fluxos e o ciclo vazio de rollback/reaplicação.

Ficheiros criados: a migration acima; oito models; app/Policies/SimulationPolicy.php; app/Http/Requests/StoreSimulationRequest.php; app/Http/Requests/UpdateSimulationScenarioRequest.php; app/Http/Controllers/Api/V1/SimulationController.php; app/Services/SimulationExecution.php; app/Jobs/RunSimulation.php; docs/investmoz/simulations/openapi.json; tests/Feature/SimulationExecutionTest.php. Ficheiros alterados: routes/api.php e este baseline.

Comandos e evidências

  • PHP 8.5.11 explícito, PHPUnit em investmoz_test isolada no PostgreSQL 17.11/PostGIS 3.6.2: vendor/bin/phpunit --configuration=phpunit.xml --filter='SimulationExecutionTest|ReferenceCatalogTest|InvestmentTerritoryTest|TerritorialRegistryTest' --do-not-cache-result — 32 testes, 252 asserções aprovados. Inclui down/up da migration sem dados.
  • PHPStan nos ficheiros da etapa — 0 erros.
  • Laravel Pint --test nos ficheiros PHP da etapa — aprovado.
  • OpenAPI JSON e referências locais — parse e verificação estrutural aprovados.
  • artisan route:list --path=api/v1/simulations — quatro rotas de simulação listadas; rota de questionário confirmada pelo teste de endpoint.
  • Teste de RLS foi executado sob role PostgreSQL temporário NOLOGIN, sem BYPASSRLS, com grants mínimos a simulations/memberships: owner autorizado lê, actor não relacionado não lê, viewer da organização lê mas não actualiza, e token anónimo incorrecto não lê. A role temporária foi removida pelos testes.
  • Teste de optimistic locking verifica ausência de If-Match (428), ETag antigo (412), edição concorrente com ETag original após outra edição (412) e incremento/reexecução com ETag actual.
  • Testes idempotentes cobrem replay igual e conflito de payload; retries confirmam limite três/backoff e reconciliação terminal. Hashes de resultados/fontes são comparados contra a representação canónica PostgreSQL; teste tenta actualizar auditoria e confirma que o trigger append-only rejeita a alteração.
  • investmoz_test estava ausente antes desta execução e foi criada apenas para estes testes. A base preexistente portal_services_test não foi limpa nesta etapa. Não se ligou a desenvolvimento, staging ou produção.

Riscos, limites e rollback

Os registos de simulation_obligations não são auto-gerados sem fonte versionada aprovada; a tabela não transforma orientação em obrigação jurídica. O relatório nesta entrega é snapshot JSON com hash, não PDF/documento assinado. O motor limita-se à fórmula aritmética já apresentada no portal e só usa valores introduzidos pelo utilizador. Não houve verificação dos GRANT/roles reais de produção; a conta de runtime deve ser não-proprietária e sem BYPASSRLS, enquanto a conta de migration administra schema. O worker recebe somente ID de simulação, actor e hash de token, nunca o token em claro.

Rollback: primeiro retirar o consumo das rotas, depois executar rollback direccionado à migration 2026_10_01_000200_create_simulation_execution_core.php com o comando Artisan de rollback por --path no ambiente pretendido. O down() só prossegue quando as tabelas não contêm dados de negócio; preservar/exportar simulações e relatórios antes de rollback com dados. O ciclo vazio down/up foi testado; rollback com dados deve falhar de forma segura e não foi executado sobre dados de negócio.

Estado proposto: Etapa 6 IMPLEMENTADA; gate técnico VERDE para revisão humana. Não foi iniciado trabalho de etapa posterior nem feito deploy.