38 KiB
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 rastrearorigin/main; as referências locais indicavam divergência0/0na leitura. - Remoto configurado:
origin. - Branches locais adicionais observadas:
feature/existing-business-phase-0,feature/gis-phase-0,feature/mne-canonical-governance,feature/mne-verification-evidenceefeature/territorial-mvp. - Não foi encontrada política de branching/PR versionada nesta leitura.
pull.rebase=false;pull.ffnão está configurado. O histórico recente contém commits directos emmain; 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,IntegrationseModels. - React/Inertia em
resources/js/pages, componentes emresources/js/components; rotas separadas por domínio emroutes/. - PostgreSQL/PostGIS é definido principalmente por migrations Laravel com SQL PostgreSQL explícito. A migration de extensões instala
postgisepgcrypto. - 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 middlewarecan: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 SECURITYouCREATE 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 emdocs/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_runse 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:
- ADR-001 — IAM e acesso anónimo
- ADR-002 — ETag e optimistic locking
- ADR-003 — Idempotência
- ADR-004 — Motor seguro de expressões
- ADR-005 — Vigência temporal e snapshots
- ADR-006 — Moeda e arredondamento
- ADR-007 — RLS
- ADR-008 — Outbox
- ADR-009 — Gestão de documentos
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--seedglobal das duas operações limitadas ao baseline histórico; esse seeder depende deinvestment_reference_options, criada depois do baseline. O teste continua a invocar explicitamenteInvestMozReferenceSeederno 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, comINVESTMOZ_SCHEMA_TESTS=1e ligação à base temporáriainvestmoz_testno porto 55441. - Primeira execução falhou antes das asserções porque
DatabaseSeedertentou 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, tiposgeometry/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é-existenteportal_services_testnã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; seederReferenceCatalogSeeder.php; controllerApi/V1/ReferenceCatalogController.php; contrato OpenAPIdocs/investmoz/catalogues/openapi.json; helper JS e declaração de tiposresources/js/lib/territory-path.js/.d.ts; testes PHPReferenceCatalogTest.phpe Nodeterritory-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 opcionalfontaine.- Laravel Pint em modo
--testnos ficheiros PHP novos/alterados — aprovado. - Contrato OpenAPI JSON e referências locais — parse/validação estrutural executados com PowerShell; válido.
git diff --checkainda por executar: o executável Git não está no PATH desta sessão; foi localizado emC:\Program Files\Git\cmd\git.exepara 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 preexistenteportal_services_testnã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
- 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_sourcesouobligationsexigiria inferir estrutura e significado. - 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.mdtambé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. - 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. - 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 eFORCE 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-arithmeticv1 (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,SimulationReporteSimulationJob. SimulationExecutionfaz 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 LOCALe é 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.RunSimulationtem 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.SimulationPolicye policies SQL reaproveitammembershipse 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.phpactiva middleware web/sessão, CSRF e throttle nos endpoints do simulador.docs/investmoz/simulations/openapi.jsondocumenta cabeçalhos, payloads e resultados HTTP.SimulationExecutionTest.phpcobre 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_testisolada 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
--testnos 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, semBYPASSRLS, com grants mínimos asimulations/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_testestava ausente antes desta execução e foi criada apenas para estes testes. A base preexistenteportal_services_testnã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.