Recomendo que o 
Simulador Fiscal do InvestMoz
, acessível em 
/Simulador
, seja desenvolvido como uma ferramenta de 
orientação fiscal, legal e financeira
, e não apenas como uma calculadora de impostos.
1. Objectivo
Permitir que investidores, empresários e cidadãos:
estimem os impostos aplicáveis a um investimento;
identifiquem incentivos fiscais disponíveis;
conheçam obrigações declarativas e prazos;
avaliem custos de importação, contratação e operação;
compreendam regras cambiais para entrada e repatriamento de capitais;
consultem a legislação aplicável;
obtenham um relatório preliminar do enquadramento fiscal do projecto.
O portal deve apresentar claramente que os resultados são 
simulações indicativas
 e não substituem pareceres da Autoridade Tributária, do Banco de Moçambique, da APIEX ou de consultores habilitados.
2. Fluxo do simulador
Etapa 1 — Perfil do utilizador
Pessoa singular ou colectiva;
Investidor nacional ou estrangeiro;
Empresa existente ou projecto a constituir;
Residência fiscal;
Forma jurídica;
Pequeno contribuinte, PME ou grande empresa.
Etapa 2 — Caracterização do investimento
Sector de actividade;
Província, distrito e localidade;
Montante do investimento;
Capital próprio e financiamento;
Origem nacional ou estrangeira dos fundos;
Número previsto de trabalhadores;
Trabalhadores nacionais e expatriados;
Volume de negócios estimado;
Custos operacionais;
Importações previstas;
Exportações previstas;
Localização em ZEE, ZFI ou fora de regime especial;
Duração e calendário do projecto.
Etapa 3 — Apuramento fiscal
O motor deve avaliar, conforme o perfil:
IRPC;
IRPS;
IVA;
ISPC;
ICE;
retenções na fonte;
direitos aduaneiros;
imposto do selo;
contribuições para a segurança social;
impostos autárquicos;
tributação de dividendos, juros, royalties e serviços;
mais-valias;
preços de transferência;
dupla tributação;
impostos e regimes específicos do sector;
benefícios fiscais aplicáveis ao projecto.
Etapa 4 — Obrigações cambiais
Registo do investimento estrangeiro;
Entrada de capitais;
Contas em moeda estrangeira;
Pagamento de importações;
Empréstimos externos;
Repatriamento de lucros e dividendos;
Pagamento de juros, royalties e serviços ao exterior;
Desinvestimento;
Documentos de suporte e intervenção de bancos autorizados.
A matéria cambial deve considerar a 
Lei n.º 28/2022, de 29 de Dezembro
, o respectivo regulamento aprovado pelo 
Decreto n.º 56/2023, de 3 de Outubro
, e as normas operacionais do 
Aviso n.º 3/GBM/2024
.bancomoc+2
3. Resultado da simulação
O relatório gerado deve apresentar:
Perfil do investimento;
Pressupostos utilizados;
Impostos potencialmente aplicáveis;
Base tributável, taxa e valor estimado;
Benefícios fiscais identificados;
Custos aduaneiros estimados;
Obrigações cambiais;
Obrigações declarativas;
Calendário fiscal;
Cenários optimista, base e conservador;
Riscos e matérias que exigem validação;
Legislação utilizada;
Data de actualização das regras;
Declaração de limitação de responsabilidade.
O utilizador deverá poder exportar o relatório em PDF e guardar a simulação na sua conta do InvestMoz.
4. Repositório de legislação
O repositório deve ser organizado por domínio.
A. Sistema tributário geral
Lei de Bases do Sistema Tributário;
Lei Geral Tributária;
legislação do contencioso tributário;
regime das infracções tributárias;
execução fiscal;
Número Único de Identificação Tributária;
facturação, documentos fiscalmente relevantes e arquivo;
declarações electrónicas e obrigações acessórias.
B. Impostos sobre o rendimento
Código do IRPC e regulamento;
Código do IRPS e regulamento;
retenções na fonte;
tributação de não residentes;
preços de transferência;
convenções para evitar a dupla tributação;
regras sobre dividendos, juros, royalties e serviços.
A própria Autoridade Tributária disponibiliza conteúdos processuais sobre IRPC e formulários fiscais, mas algumas páginas podem conservar informação histórica; por isso, os parâmetros do simulador devem ser confirmados contra o Boletim da República e alterações mais recentes.at+2
C. Tributação do consumo
Código do IVA e regulamento;
Código do ICE;
ISPC;
isenções;
regimes especiais;
reembolsos;
importação e exportação.
Existem alterações fiscais recentes, incluindo reformas do IRPC, IVA, IRPS e ISPC aprovadas no quadro legislativo de 2025, pelo que a base normativa deve possuir controlo de vigência e versões.portaldogoverno
D. Aduanas e comércio externo
Lei n.º 17/2022 — Pauta Aduaneira;
Instruções Preliminares da Pauta;
direitos aduaneiros;
ICE na importação;
IVA na importação;
regimes suspensivos;
importação temporária;
trânsito;
regras de origem;
acordos comerciais aplicáveis;
benefícios para projectos de investimento.
A Lei n.º 17/2022 aprovou a Pauta Aduaneira e revogou a Lei n.º 11/2016.mef
E. Investimento privado
Lei de Investimentos;
regulamento da Lei de Investimentos;
Código dos Benefícios Fiscais;
procedimentos de registo e autorização;
garantias ao investidor;
investimento directo estrangeiro;
repatriamento de capitais;
Zonas Económicas Especiais;
Zonas Francas Industriais;
projectos de grande dimensão;
parcerias público-privadas e concessões.
O simulador deve calcular separadamente os benefícios de ZEE e ZFI, incluindo isenções e reduções de IRPC e encargos aduaneiros, sempre sujeitos à elegibilidade e certificação do projecto.apiex
F. Legislação cambial e financeira
Lei Cambial;
Regulamento da Lei Cambial;
avisos do Banco de Moçambique;
regras sobre investimento estrangeiro;
operações de capitais;
dívida externa;
contas bancárias em moeda estrangeira;
exportação e importação de capitais;
repatriamento de receitas de exportação;
prevenção do branqueamento de capitais.
G. Trabalho, imigração e segurança social
Lei do Trabalho;
contratação de trabalhadores estrangeiros;
vistos e autorizações de residência;
contribuições para o INSS;
remunerações e benefícios tributáveis;
retenções sobre salários;
quotas de trabalhadores estrangeiros;
segurança e saúde no trabalho.
H. Legislação sectorial
O motor deve activar regras próprias para:
mineração;
petróleo e gás;
energia;
agricultura;
pescas;
turismo;
construção e imobiliário;
telecomunicações;
banca, seguros e microfinanças;
transportes e logística;
indústria;
comércio;
ambiente;
concessões e PPP;
economia digital.
5. Estrutura técnica recomendada
A legislação e os parâmetros não devem ficar directamente gravados no código. Recomendo uma base configurável com:
instrumento legal;
número, data e entidade emissora;
tipo de diploma;
artigos relevantes;
imposto ou obrigação abrangida;
sector;
localização;
perfil do contribuinte;
taxa;
fórmula;
limite ou escalão;
benefício fiscal;
data de entrada em vigor;
data de revogação;
diploma alterador;
fonte oficial;
estado: vigente, alterado ou revogado;
data da última validação;
responsável pela validação.
6. Governação e segurança jurídica
Antes da publicação, deverão existir:
validação jurídica e fiscal;
validação pela Autoridade Tributária, quando possível;
validação cambial pelo Banco de Moçambique;
validação dos incentivos pela APIEX;
histórico de versões;
regra de “quatro olhos” para alterações;
testes automatizados das fórmulas;
data de vigência de cada parâmetro;
auditoria de mudanças;
revisão periódica do Boletim da República.
O Ministério das Finanças mantém uma área de legislação que pode servir como uma das fontes oficiais de actualização.mef
7. Implementação faseada
Fase Zero:
 levantamento e validação integral da legislação e das fórmulas.
MVP:
 IRPC, IRPS, IVA, ISPC, retenções, segurança social, direitos aduaneiros, investimento e regime cambial.
Fase 2:
 benefícios fiscais, ZEE, ZFI, convenções de dupla tributação e relatórios comparativos.
Fase 3:
 mineração, petróleo, energia, agricultura, turismo e outros regimes sectoriais.
Fase 4:
 integração com CRM, oportunidades de investimento, mapas geográficos, conta do investidor e assistente inteligente.
A expressão “toda a legislação” deve ser tratada como um 
repositório jurídico continuamente actualizado
, e não como uma lista estática, porque diplomas, taxas, regulamentos e procedimentos podem ser alterados ao longo do tempo.
8. Arquitectura técnica do Simulador Fiscal
Princípios:
 arquitectura modular, API-first, configuração sem taxas gravadas no código, vigência temporal das regras, separação entre cálculo e conteúdo jurídico, explicabilidade de cada resultado, auditoria integral, segurança por desenho e integração sem duplicação com os cadastros do InvestMoz.
Camada
Componentes
Responsabilidade
Experiência
React/Next.js em /Simulador; interface responsiva e acessível; assistente por etapas; painel administrativo
Recolher dados, validar entradas, apresentar explicações, comparação de cenários e relatório.
API
Laravel; REST/JSON; OpenAPI; autenticação do InvestMoz; limitação de pedidos
Orquestrar simulações, legislação, parâmetros, relatórios e integrações.
Domínio
Motor de elegibilidade; motor de regras; motor de cálculo; motor de obrigações; motor de explicação
Determinar impostos aplicáveis, calcular valores e ligar cada resultado à regra e ao fundamento legal vigentes.
Dados
PostgreSQL; PostGIS para jurisdições; armazenamento de documentos compatível com S3; Redis
Guardar legislação versionada, regras, taxas, simulações, resultados, ficheiros, cache e filas.
Integração
InvestMoz IAM/SSO, cadastro do investidor, APIEX, fontes oficiais, notificações e serviço PDF
Reutilizar dados mestre, importar metadados e entregar relatórios sem criar cadastros paralelos.
Operação
Contentores; CI/CD; observabilidade; cofre de segredos; cópias de segurança
Garantir implantação reversível, monitorização, recuperação e segregação entre desenvolvimento, testes e produção.
Fluxo de execução:
 a interface envia um conjunto de respostas versionado; o serviço de elegibilidade selecciona regimes; o motor carrega apenas regras aprovadas cuja vigência inclui a data de referência; cada fórmula produz base, taxa, montante, arredondamento, pressupostos e referências legais; o serviço de obrigações acrescenta prazos e alertas; o resultado é persistido como instantâneo imutável e entregue em ecrã e relatório.
Serviços lógicos:
 Identity, Questionnaire, Simulation, Rules, Legislation, Customs, Obligations, Reporting, Administration e Audit.
Endpoints principais:
 GET /api/simulator/schema; POST /api/simulations; POST /api/simulations/{id}/calculate; GET /api/simulations/{id}; GET /api/simulations/{id}/report; GET /api/legislation; GET /api/rules/effective; POST /api/admin/rules/{id}/submit e /approve.
Controlo editorial:
 rascunho → revisão técnica → revisão jurídica → aprovado → publicado → suspenso/arquivado; autor e aprovador distintos.
Segurança:
 RBAC, encriptação em trânsito e repouso, minimização de dados pessoais, registo de acessos administrativos, protecção contra adulteração de fórmulas e retenção configurável.
Qualidade:
 testes unitários por regra, casos dourados validados por fiscalista, testes de vigência, regressão e reconciliação do relatório com o resultado JSON.
Disponibilidade:
 cálculo síncrono para cenários simples; fila assíncrona para relatórios; idempotência; cache identificada pela versão do conjunto de regras.
9. Modelo de dados do repositório legislativo
Entidade
Campos essenciais
Relações e regras
legal_domains
id, code, name, parent_id
Taxação, investimento, câmbios, aduanas, trabalho e sectores; hierarquia sem duplicação.
legal_instruments
id, type, number, year, title, issuer_id, publication_date, gazette_ref, status
Registo canónico do diploma; unicidade por tipo, número e ano.
legal_versions
id, instrument_id, version_no, effective_from, effective_to, publication_url, file_hash, review_status
Múltiplas versões; intervalos aprovados do mesmo diploma não se sobrepõem.
legal_provisions
id, version_id, article, paragraph, item, heading, text, search_vector
Unidade citável do diploma; pesquisa textual integral.
legal_relations
source_instrument_id, target_instrument_id, relation_type, effective_date, notes
Altera, regulamenta, revoga, republica ou complementa.
tax_types
id, code, name, authority_id, periodicity
IRPC, IRPS, IVA, ISPC, INSS, direitos, ICE e retenções.
tax_parameters
id, tax_type_id, parameter_key, value_decimal, value_json, unit, currency, effective_from, effective_to, source_provision_id, status
Taxas, escalões, limites e arredondamento; nenhum valor fiscal fica no código.
calculation_rules
id, code, name, expression, condition_json, priority, output_code, effective_from, effective_to, status
Expressão validada e condicionais declarativas; versão imutável após publicação.
rule_sources
rule_id, provision_id, justification
Relação muitos-para-muitos entre fórmula e disposições legais.
regimes
id, code, name, regime_type, authority_id
Geral, simplificado, ZEE, ZFI e outros regimes.
eligibility_rules
id, regime_id, condition_json, evidence_json, effective_from, effective_to
Determina elegibilidade e documentos necessários.
obligations
id, code, name, trigger_json, frequency, due_rule, authority_id, source_provision_id
Declaração, pagamento, registo, autorização ou retenção.
jurisdictions
id, parent_id, type, code, name, geometry
Província, distrito, autarquia, ZEE e ZFI; suporte PostGIS.
economic_activities
id, code, name, parent_id
Classificação sectorial usada nas regras e no questionário.
simulations
id, user_id nullable, reference_date, status, currency, rule_set_version, input_json, created_at
Permite utilização anónima e autenticada; dados de entrada preservados.
simulation_results
id, simulation_id, output_code, taxable_base, rate, amount, formula_snapshot, assumptions_json
Resultado reproduzível, incluindo fórmula e parâmetros utilizados.
result_sources
result_id, provision_id, legal_version_id
Rastreabilidade do valor até ao artigo e versão legal.
change_audit
id, entity_type, entity_id, action, before_json, after_json, actor_id, timestamp
Auditoria inviolável das alterações administrativas.
Integridade temporal:
 consultas usam a data de referência da simulação; exclusões por intervalo impedem sobreposição de versões, parâmetros e regras aprovadas.
Proveniência:
 toda regra publicada deve ter pelo menos uma disposição legal, ficheiro com hash, fonte oficial e validação registada.
Pesquisa:
 índices por número/ano, estado, vigência, domínio e vector textual; índice espacial para jurisdições.
Publicação:
 um conjunto de regras é promovido atomicamente e recebe versão; simulações antigas continuam reproduzíveis.
Documentos:
 o ficheiro original fica no armazenamento de objectos; a base mantém metadados, hash, localização e texto extraído.
10. Perguntas e fórmulas do MVP
O questionário deve ser dinâmico: perguntas dependentes só aparecem quando a resposta anterior as torna relevantes. Todos os valores monetários são introduzidos em MZN ou convertidos por uma taxa de câmbio e data claramente registadas.
Grupo
Perguntas obrigatórias ou condicionais
Uso
Contexto
Data de referência? Simulação anual ou mensal? Moeda? Pretende cenário único ou três cenários?
Seleccionar vigência, período e apresentação.
Contribuinte
Pessoa singular, colectiva ou trabalhador por conta própria? Residente fiscal? Actividade 
existente ou nova? Forma jurídica? NUIT opcional?
Seleccionar IRPC, IRPS, ISPC e obrigações.
Actividade e localização
Sector e código de actividade? Província, distrito, autarquia? Está certificado em ZEE/ZFI? Qual entidade e validade do certificado?
Regras sectoriais, locais e benefícios; benefício não é presumido sem prova.
Investimento
Montante total? Capital próprio? Financiamento nacional e externo? Data de início? Vida útil? Activos por classe?
Perfil financeiro, depreciação futura e obrigações cambiais.
Operação
Vendas sem IVA? Vendas isentas, taxa zero e exportações? Outros rendimentos? Custos dedutíveis? Gastos não dedutíveis? Prejuízos reportáveis?
IVA e lucro tributável.
Trabalho
Número de trabalhadores? Remuneração bruta e adicionais por trabalhador ou faixa? Trabalhadores estrangeiros? Trabalhador por conta própria?
IRPS, INSS e alertas laborais.
Pagamentos
Dividendos, juros, royalties, rendas ou serviços? Valor? Beneficiário residente? País? Existe convenção aplicável e documentação?
Retenções e alerta de dupla tributação.
Importações
Descrição, código pautal, origem, quantidade, valor da factura, frete, seguro, moeda, taxa de câmbio, ICE, isenção e documentação?
Valor aduaneiro, direitos, ICE e IVA de importação.
Regime simplificado
Volume de negócios previsto? Reúne requisitos do ISPC? Pretende comparar regime simplificado e geral?
Elegibilidade e comparação.
Câmbios
Há capital estrangeiro, empréstimo externo, pagamento ao exterior, exportação, dividendos ou desinvestimento?
Checklist cambial, sem converter obrigação em imposto.
Cálculo
Fórmula funcional
Observações do MVP
Lucro tributável IRPC
LT = rendimentos tributáveis − gastos fiscalmente dedutíveis + correcções positivas − correcções negativas − prejuízos dedutíveis
IRPC bruto = max(0, LT) × taxa_IRPC; benefícios e pagamentos por conta em linhas separadas.
IRPC estimado
IRPC a pagar = max(0, IRPC bruto − crédito por benefício − retenções/créditos admissíveis)
Taxa, limites e elegibilidade são parâmetros vigentes; não presumir benefício.
IVA de operações
IVA liquidado = soma(base tributável por operação × taxa aplicável)
Separar operações tributadas, isentas, taxa zero e fora do âmbito.
IVA a pagar
IVA a pagar = max(0, IVA liquidado − IVA dedutível − crédito anterior)
Se negativo, apresentar crédito estimado; dedutibilidade deve ser perguntada ou justificada.
ISPC
ISPC = base definida no regime × taxa_ISPC ou valor por escalão
Executar somente após regra de elegibilidade; comparar com regime geral sem aconselhamento conclusivo.
IRPS de trabalho
Rendimento colectável = remuneração tributável − deduções admitidas; IRPS = parcela fixa do escalão + (rendimento colectável − limite inferior) × taxa marginal
Tabela, periodicidade, deduções e situação familiar são dados versionados.
INSS — empregador
Contribuição da entidade = remuneração contributiva × taxa_empregador
Como configuração inicial sujeita a validação: 4% para entidade empregadora.
INSS — trabalhador
Desconto do trabalhador = remuneração contributiva × taxa_trabalhador
Como configuração inicial sujeita a validação: 3%; total 7%. Para TCP, taxa configurável própria.
Retenção na fonte
Retenção = pagamento bruto sujeito × taxa por natureza, residência e convenção
Uma regra por dividendos, juros, royalties, rendas e serviços; exigir documentação para taxa convencional.
Valor aduaneiro
VA = valor da factura convertido + frete + seguro + ajustamentos incluíveis
Aplicar método e câmbio aduaneiro vigentes.
Direitos aduaneiros
DA = VA × taxa_pautal
Taxa depende do código pautal, origem e preferência; nunca usar taxa genérica.
ICE na importação
ICE = base_ICE configurada × taxa_ICE específica ou ad valorem
Produto, unidade, taxa e base provêm da Pauta/ICE vigente.
IVA na importação
IVA_importação = (VA + DA + ICE + outras imposições incluíveis) × taxa_IVA
Componentes exactos da base são parametrizados e citados.
Custo fiscal total
CFT = impostos não recuperáveis + direitos + ICE + retenções suportadas + contribuição patronal
Mostrar separadamente IVA recuperável, desconto do trabalhador e fluxos meramente retidos.
Taxa efectiva
Taxa efectiva = CFT ÷ base económica seleccionada × 100
O relatório identifica se a base é lucro, receita ou investimento.
10.1 Validações e critérios de aceitação
Montantes não podem ser negativos; percentagens ficam entre 0 e 100; somas de financiamento devem reconciliar com o investimento ou ser sinalizadas.
O código pautal é obrigatório para cálculo aduaneiro; sem ele, apresentar apenas uma estimativa incompleta e pedir classificação.
Benefícios exigem regime, certificado, período e critérios satisfeitos; caso contrário, são apresentados como oportunidade a validar.
Cada linha calculada exibe fórmula, valores substituídos, versão da regra, data de vigência e fundamento legal.
Casos de teste cobrem limites de escalões, datas de mudança de taxa, zero, valores elevados, isenções, arredondamento e combinações incompatíveis.
O total do relatório deve reconciliar com os resultados da API e com o instantâneo guardado.
Uma regra vencida, sem fonte ou não aprovada não pode ser usada em produção.
Nota de implementação:
 antes do Go-Live, as taxas do IRPC, IRPS, IVA, ISPC, retenções, pauta, ICE e benefícios devem ser carregadas a partir dos diplomas vigentes e aprovadas por revisão fiscal e jurídica. A fonte oficial consultada confirma, para o INSS, a repartição de 4% para a entidade empregadora e 3% para o trabalhador; a Lei n.º 17/2022 aprovou a Pauta Aduaneira; e a Lei Cambial n.º 28/2022 é regulamentada pelo Decreto n.º 56/2023. Alterações posteriores devem entrar apenas através de nova versão efectiva do conjunto de regras.
11. Esquema SQL corrigido e consistente
Versão de referência para PostgreSQL 16/PostGIS. Corrige os bloqueadores identificados: vigência dos rule sets, escopo temporal dos parâmetros, ETag, eliminação lógica, token anónimo, cenários, jobs, idempotência composta, integridade das fontes e evidências de revisão/teste.
CREATE EXTENSION IF NOT EXISTS pgcrypto;
CREATE EXTENSION IF NOT EXISTS postgis;
CREATE EXTENSION IF NOT EXISTS btree_gist;
CREATE TYPE publication_status AS ENUM ('draft','technical_review','legal_review','approved','published','suspended','archived');
CREATE TYPE simulation_status AS ENUM ('draft','validated','calculating','completed','failed','expired');
CREATE TYPE job_status AS ENUM ('queued','running','completed','failed','dead_letter');
CREATE TYPE review_stage AS ENUM ('technical','legal','publication');
CREATE TYPE review_decision AS ENUM ('approve','return');
CREATE OR REPLACE FUNCTION set_updated_at() RETURNS trigger LANGUAGE plpgsql AS $$ BEGIN NEW.updated_at=now(); IF TG_TABLE_NAME='simulations' THEN NEW.lock_version=OLD.lock_version+1; END IF; RETURN NEW; END $$;
CREATE TABLE authorities(id uuid PRIMARY KEY DEFAULT gen_random_uuid(),code varchar(50) UNIQUE NOT NULL,name text NOT NULL,created_at timestamptz NOT NULL DEFAULT now(),updated_at timestamptz NOT NULL DEFAULT now());
CREATE TABLE legal_instruments(id uuid PRIMARY KEY DEFAULT gen_random_uuid(),instrument_type varchar(60) NOT NULL,number varchar(30) NOT NULL,year smallint NOT NULL,title text NOT NULL,issuer_id uuid REFERENCES authorities(id),publication_date date,gazette_reference text,created_at timestamptz NOT NULL DEFAULT now(),updated_at timestamptz NOT NULL DEFAULT now(),UNIQUE(instrument_type,number,year));
CREATE TABLE legal_versions(id uuid PRIMARY KEY DEFAULT gen_random_uuid(),instrument_id uuid NOT NULL REFERENCES legal_instruments(id) ON DELETE CASCADE,version_no integer NOT NULL CHECK(version_no>0),effective_period daterange NOT NULL,object_key text,file_hash char(64) NOT NULL,review_status publication_status NOT NULL DEFAULT 'draft',created_at timestamptz NOT NULL DEFAULT now(),updated_at timestamptz NOT NULL DEFAULT now(),UNIQUE(instrument_id,version_no));
ALTER TABLE legal_versions ADD CONSTRAINT legal_versions_no_overlap EXCLUDE USING gist(instrument_id WITH =,effective_period WITH &&) WHERE(review_status IN ('approved','published'));
CREATE TABLE legal_provisions(id uuid PRIMARY KEY DEFAULT gen_random_uuid(),legal_version_id uuid NOT NULL REFERENCES legal_versions(id) ON DELETE CASCADE,article varchar(40),paragraph_no varchar(40),item_no varchar(40),heading text,provision_text text NOT NULL,sequence_no integer NOT NULL,search_vector tsvector GENERATED ALWAYS AS(to_tsvector('portuguese',coalesce(heading,'')||' '||provision_text)) STORED,UNIQUE(legal_version_id,sequence_no));
CREATE INDEX legal_provisions_search_gin ON legal_provisions USING gin(search_vector);
CREATE TABLE tax_types(id uuid PRIMARY KEY DEFAULT gen_random_uuid(),code varchar(30) UNIQUE NOT NULL,name text NOT NULL);
CREATE TABLE regimes(id uuid PRIMARY KEY DEFAULT gen_random_uuid(),code varchar(50) UNIQUE NOT NULL,name text NOT NULL);
CREATE TABLE tax_parameters(id uuid PRIMARY KEY DEFAULT gen_random_uuid(),tax_type_id uuid NOT NULL REFERENCES tax_types(id),regime_id uuid REFERENCES regimes(id),jurisdiction_id uuid,activity_id uuid,currency char(3),parameter_key varchar(100) NOT NULL,scope_key text GENERATED ALWAYS AS(tax_type_id::text||':'||coalesce(regime_id::text,'*')||':'||coalesce(jurisdiction_id::text,'*')||':'||coalesce(activity_id::text,'*')||':'||coalesce(currency,'*')) STORED,value_decimal numeric(24,8),value_text text,value_json jsonb,effective_period daterange NOT NULL,source_provision_id uuid NOT NULL REFERENCES legal_provisions(id),status publication_status NOT NULL DEFAULT 'draft',submitted_by uuid,approved_by uuid,created_at timestamptz NOT NULL DEFAULT now(),updated_at timestamptz NOT NULL DEFAULT now(),CHECK(num_nonnulls(value_decimal,value_text,value_json)=1),CHECK(approved_by IS NULL OR approved_by IS DISTINCT FROM submitted_by));
ALTER TABLE tax_parameters ADD CONSTRAINT tax_parameter_scope_no_overlap EXCLUDE USING gist(scope_key WITH =,parameter_key WITH =,effective_period WITH &&) WHERE(status IN ('approved','published'));
CREATE TABLE rule_sets(id uuid PRIMARY KEY DEFAULT gen_random_uuid(),version varchar(40) UNIQUE NOT NULL,name text NOT NULL,scope_key text NOT NULL DEFAULT 'GLOBAL',effective_period daterange NOT NULL,status publication_status NOT NULL DEFAULT 'draft',checksum char(64),submitted_by uuid,approved_by uuid,published_at timestamptz,created_at timestamptz NOT NULL DEFAULT now(),CHECK(approved_by IS NULL OR approved_by IS DISTINCT FROM submitted_by));
ALTER TABLE rule_sets ADD CONSTRAINT rule_sets_no_overlap EXCLUDE USING gist(scope_key WITH =,effective_period WITH &&) WHERE(status='published');
CREATE TABLE calculation_rules(id uuid PRIMARY KEY DEFAULT gen_random_uuid(),rule_set_id uuid NOT NULL REFERENCES rule_sets(id),tax_type_id uuid REFERENCES tax_types(id),code varchar(100) NOT NULL,name text NOT NULL,expression text NOT NULL,condition_json jsonb NOT NULL DEFAULT '{}'::jsonb,input_schema jsonb NOT NULL DEFAULT '{}'::jsonb,output_code varchar(100) NOT NULL,priority integer NOT NULL DEFAULT 100,rounding_scale smallint NOT NULL DEFAULT 2,rounding_mode varchar(20) NOT NULL DEFAULT 'HALF_UP',status publication_status NOT NULL DEFAULT 'draft',checksum char(64),UNIQUE(rule_set_id,code));
CREATE TABLE rule_sources(rule_id uuid NOT NULL REFERENCES calculation_rules(id) ON DELETE CASCADE,provision_id uuid NOT NULL REFERENCES legal_provisions(id),justification text NOT NULL,PRIMARY KEY(rule_id,provision_id));
CREATE TABLE review_decisions(id uuid PRIMARY KEY DEFAULT gen_random_uuid(),resource_type varchar(40) NOT NULL,resource_id uuid NOT NULL,stage review_stage NOT NULL,decision review_decision NOT NULL,reviewer_id uuid NOT NULL,comment text,created_at timestamptz NOT NULL DEFAULT now(),UNIQUE(resource_type,resource_id,stage,reviewer_id));
CREATE TABLE rule_set_test_runs(id uuid PRIMARY KEY DEFAULT gen_random_uuid(),rule_set_id uuid NOT NULL REFERENCES rule_sets(id),status varchar(20) NOT NULL CHECK(status IN('passed','failed')),critical_total integer NOT NULL,critical_passed integer NOT NULL,total integer NOT NULL,passed integer NOT NULL,evidence_uri text,checksum char(64) NOT NULL,executed_at timestamptz NOT NULL DEFAULT now(),CHECK(critical_passed<=critical_total),CHECK(passed<=total));
CREATE TABLE questionnaire_versions(id uuid PRIMARY KEY DEFAULT gen_random_uuid(),version varchar(40) UNIQUE NOT NULL,schema_json jsonb NOT NULL,ui_schema_json jsonb NOT NULL DEFAULT '{}'::jsonb,status publication_status NOT NULL DEFAULT 'draft',effective_period daterange NOT NULL);
CREATE TABLE simulations(id uuid PRIMARY KEY DEFAULT gen_random_uuid(),owner_user_id uuid,anonymous_token_hash char(64),questionnaire_version_id uuid NOT NULL REFERENCES questionnaire_versions(id),rule_set_id uuid REFERENCES rule_sets(id),parent_simulation_id uuid REFERENCES simulations(id),scenario_name varchar(20) CHECK(scenario_name IN('optimistic','base','conservative')),scenario_changes jsonb,reference_date date NOT NULL,period_type varchar(20) NOT NULL CHECK(period_type IN('monthly','annual','project')),currency char(3) NOT NULL DEFAULT 'MZN',status simulation_status NOT NULL DEFAULT 'draft',input_json jsonb NOT NULL DEFAULT '{}'::jsonb,input_hash char(64),lock_version bigint NOT NULL DEFAULT 1,error_json jsonb,expires_at timestamptz,deleted_at timestamptz,deleted_by uuid,deletion_reason text,created_at timestamptz NOT NULL DEFAULT now(),updated_at timestamptz NOT NULL DEFAULT now(),completed_at timestamptz,CHECK(owner_user_id IS NOT NULL OR anonymous_token_hash IS NOT NULL),CHECK(parent_simulation_id IS NOT NULL OR scenario_name IS NULL));
CREATE UNIQUE INDEX simulation_scenario_unique ON simulations(parent_simulation_id,scenario_name) WHERE deleted_at IS NULL AND parent_simulation_id IS NOT NULL;
CREATE TABLE simulation_results(id uuid PRIMARY KEY DEFAULT gen_random_uuid(),simulation_id uuid NOT NULL REFERENCES simulations(id) ON DELETE CASCADE,rule_id uuid NOT NULL REFERENCES 
calculation_rules(id),output_code varchar(100) NOT NULL,taxable_base numeric(24,2),rate numeric(16,8),amount numeric(24,2) NOT NULL,currency char(3) NOT NULL,formula_snapshot text NOT NULL,parameter_snapshot jsonb NOT NULL,assumptions_json jsonb NOT NULL DEFAULT '[]'::jsonb,warnings_json jsonb NOT NULL DEFAULT '[]'::jsonb,sequence_no integer NOT NULL,UNIQUE(simulation_id,output_code,sequence_no));
CREATE TABLE result_sources(result_id uuid NOT NULL REFERENCES simulation_results(id) ON DELETE CASCADE,provision_id uuid NOT NULL REFERENCES legal_provisions(id),PRIMARY KEY(result_id,provision_id));
CREATE TABLE reports(id uuid PRIMARY KEY DEFAULT gen_random_uuid(),simulation_id uuid NOT NULL REFERENCES simulations(id) ON DELETE CASCADE,format varchar(10) NOT NULL CHECK(format IN('pdf','json')),status job_status NOT NULL DEFAULT 'queued',object_key text,checksum char(64),generated_at timestamptz,expires_at timestamptz,error_json jsonb,created_at timestamptz NOT NULL DEFAULT now());
CREATE TABLE jobs(id uuid PRIMARY KEY DEFAULT gen_random_uuid(),owner_user_id uuid,simulation_id uuid REFERENCES simulations(id) ON DELETE CASCADE,job_type varchar(30) NOT NULL CHECK(job_type IN('calculation','report','import','purge')),status job_status NOT NULL DEFAULT 'queued',progress smallint NOT NULL DEFAULT 0 CHECK(progress BETWEEN 0 AND 100),attempts integer NOT NULL DEFAULT 0,max_attempts integer NOT NULL DEFAULT 3,payload_hash char(64) NOT NULL,resource_type varchar(30),resource_id uuid,error_json jsonb,available_at timestamptz NOT NULL DEFAULT now(),started_at timestamptz,completed_at timestamptz,created_at timestamptz NOT NULL DEFAULT now());
CREATE TABLE idempotency_keys(owner_key varchar(100) NOT NULL,key varchar(100) NOT NULL,http_method varchar(10) NOT NULL,request_path text NOT NULL,request_hash char(64) NOT NULL,response_code integer,response_body jsonb,expires_at timestamptz NOT NULL,created_at timestamptz NOT NULL DEFAULT now(),PRIMARY KEY(owner_key,key));
CREATE INDEX idempotency_lookup ON idempotency_keys(owner_key,http_method,request_path,expires_at);
CREATE TABLE change_audit(id bigserial PRIMARY KEY,entity_type varchar(80) NOT NULL,entity_id uuid,action varchar(40) NOT NULL,before_json jsonb,after_json jsonb,actor_id uuid,correlation_id uuid,occurred_at timestamptz NOT NULL DEFAULT now());
CREATE TRIGGER simulations_updated BEFORE UPDATE ON simulations FOR EACH ROW EXECUTE FUNCTION set_updated_at();
11.1 Regras de integridade e geração de código
O ETag é calculado sobre id, lock_version e updated_at; PATCH e calculate incrementam lock_version na mesma transacção.
O token anónimo é devolvido uma única vez; apenas SHA-256 é persistido. A aplicação define app.user_id e app.anonymous_token_hash por ligação, e as políticas RLS autorizam o proprietário ou o hash correspondente, sempre com deleted_at IS NULL.
Regras, parâmetros e versões published são imutáveis por trigger; correcções criam nova versão.
A publicação exige fontes legais, decisões técnica e jurídica de revisores distintos do autor e rule_set_test_run com critical_total=critical_passed.
A URL assinada do relatório é gerada na leitura e nunca guardada na base.
Gate SQL: migrations up/down em base vazia; pg_dump --schema-only; validação das exclusões temporais; testes de RLS, concorrência, rollback e integridade referencial.
12. Contrato OpenAPI 3.1 corrigido
O contrato abaixo elimina a chave components duplicada, explicita o token anónimo, associa scopes a cada operação protegida e alinha IDs, versões, jobs, cenários, relatórios, ETag e soft delete com o SQL corrigido.
openapi: 3.1.0
info: {title: InvestMoz Fiscal Simulator API, version: 1.0.0}
servers:
  - {url: 'https://investmoz.gov.mz/api/v1'}
components:
  securitySchemes:
    bearerAuth: {type: http, scheme: bearer, bearerFormat: JWT}
    oauth2Client:
      type: oauth2
      flows:
        clientCredentials:
          tokenUrl: 'https://identity.investmoz.gov.mz/oauth2/token'
          scopes: {simulator.read: 'Ler simulações', simulator.write: 'Alterar simulações', legislation.admin: 'Gerir legislação', rules.admin: 'Gerir regras', audit.read: 'Ler auditoria'}
  parameters:
    Id: {name: id, in: path, required: true, schema: {type: string, format: uuid}}
    CorrelationId: {name: X-Correlation-Id, in: header, schema: {type: string, format: uuid}}
    IdempotencyKey: {name: Idempotency-Key, in: header, required: true, schema: {type: string, minLength: 16, maxLength: 100}}
    IfMatch: {name: If-Match, in: header, required: true, schema: {type: string}}
    AnonymousToken: {name: X-Anonymous-Simulation-Token, in: header, required: false, schema: {type: string, minLength: 32}}
  schemas:
    ErrorResponse: {type: object, required: [error], properties: {error: {type: object, required: [code,message,correlation_id], properties: {code: {type: string}, message: {type: string}, correlation_id: {type: string, format: uuid}, fields: {type: array, items: {type: object}}}}}}
    VersionRef: {type: object, required: [id,version], properties: {id: {type: string, format: uuid}, version: {type: string}}}
    SimulationCreate: {type: object, additionalProperties: false, required: [reference_date,period_type,currency,questionnaire_version_id,answers], properties: {reference_date: {type: string, format: date}, period_type: {type: string, enum: [monthly,annual,project]}, currency: {type: string, pattern: '^[A-Z]{3}$'}, questionnaire_version_id: {type: string, format: uuid}, answers: {type: object}}}
    Simulation: {type: object, required: [id,status,lock_version,reference_date,period_type,currency,questionnaire,answers], properties: {id: {type: string, format: uuid}, status: {type: string, enum: [draft,validated,calculating,completed,failed,expired]}, lock_version: {type: integer, minimum: 1}, reference_date: {type: string, format: date}, period_type: {type: string}, currency: {type: string}, questionnaire: {$ref: '#/components/schemas/VersionRef'}, rule_set: {oneOf: [{$ref: '#/components/schemas/VersionRef'},{type: 'null'}]}, parent_simulation_id: {type: [string,'null'], format: uuid}, scenario_name: {type: [string,'null'], enum: [optimistic,base,conservative,null]}, answers: {type: object}, results: {type: array, items: {type: object}}, obligations: {type: array, items: {type: object}}}}
    Job: {type: object, required: [id,type,status,progress,attempts,max_attempts], properties: {id: {type: string, format: uuid}, type: {type: string, enum: [calculation,report,import,purge]}, status: {type: string, enum: [queued,running,completed,failed,dead_letter]}, progress: {type: integer, minimum: 0, maximum: 100}, attempts: {type: integer}, max_attempts: {type: integer}, resource_type: {type: [string,'null']}, resource_id: {type: [string,'null'], format: uuid}, error: {type: [object,'null']}}}
    Report: {type: object, required: [id,simulation_id,format,status], properties: {id: {type: string, format: uuid}, simulation_id: {type: string, format: uuid}, format: {type: string, enum: [pdf,json]}, status: {type: string, enum: [queued,running,completed,failed,dead_letter]}, checksum: {type: [string,'null']}, download_url: {type: [string,'null'], format: uri}, download_expires_at: {type: 
[string,'null'], format: date-time}}}
  responses:
    Unauthorized: {description: Não autenticado, content: {application/json: {schema: {$ref: '#/components/schemas/ErrorResponse'}}}}
    Forbidden: {description: Sem permissão, content: {application/json: {schema: {$ref: '#/components/schemas/ErrorResponse'}}}}
    NotFound: {description: Não encontrado, content: {application/json: {schema: {$ref: '#/components/schemas/ErrorResponse'}}}}
    Conflict: {description: Conflito, content: {application/json: {schema: {$ref: '#/components/schemas/ErrorResponse'}}}}
    PreconditionFailed: {description: ETag desactualizado, content: {application/json: {schema: {$ref: '#/components/schemas/ErrorResponse'}}}}
    Unprocessable: {description: Validação falhou, content: {application/json: {schema: {$ref: '#/components/schemas/ErrorResponse'}}}}
    TooManyRequests: {description: Limite excedido, content: {application/json: {schema: {$ref: '#/components/schemas/ErrorResponse'}}}}
    InternalError: {description: Erro interno, content: {application/json: {schema: {$ref: '#/components/schemas/ErrorResponse'}}}}
paths:
  /simulator/schema:
    get: {operationId: getSimulatorSchema, security: [], responses: {'200': {description: Questionário vigente}, '422': {$ref: '#/components/responses/Unprocessable'}}}
  /legislation:
    get: {operationId: searchLegislation, security: [], responses: {'200': {description: Resultados paginados}}}
  /legislation/{id}:
    get: {operationId: getLegalInstrument, security: [], parameters: [{$ref: '#/components/parameters/Id'}], responses: {'200': {description: Diploma}, '404': {$ref: '#/components/responses/NotFound'}}}
  /simulations:
    post:
      operationId: createSimulation
      security: [{bearerAuth: []}, {}]
      parameters: [{$ref: '#/components/parameters/IdempotencyKey'},{$ref: '#/components/parameters/CorrelationId'}]
      requestBody: {required: true, content: {application/json: {schema: {$ref: '#/components/schemas/SimulationCreate'}}}}
      responses: {'201': {description: Criada; para anónimo inclui anonymous_token uma única vez e ETag}, '409': {$ref: '#/components/responses/Conflict'}, '422': {$ref: '#/components/responses/Unprocessable'}, '429': {$ref: '#/components/responses/TooManyRequests'}, '500': {$ref: '#/components/responses/InternalError'}}
  /simulations/{id}:
    get: {operationId: getSimulation, security: [{
bearerAuth: []
}, {}], parameters: [{$ref: '#/components/parameters/Id'},{$ref: '#/components/parameters/AnonymousToken'}], responses: {'200': {description: Simulação; inclui ETag}, '401': {$ref: '#/components/responses/Unauthorized'}, '403': {$ref: '#/components/responses/Forbidden'}, '404': {$ref: '#/components/responses/NotFound'}}}
    patch: {operationId: patchSimulation, security: [{
bearerAuth: []
}, {}], parameters: [{$ref: '#/components/parameters/Id'},{$ref: '#/components/parameters/IfMatch'},{$ref: '#/components/parameters/AnonymousToken'}], responses: {'200': {description: Actualizada; inclui novo ETag}, '412': {$ref: '#/components/responses/PreconditionFailed'}, '422': {$ref: '#/components/responses/Unprocessable'}}}
    delete: {operationId: deleteSimulation, security: [{
bearerAuth: []
}, {}], parameters: [{$ref: '#/components/parameters/Id'},{$ref: '#/components/parameters/IfMatch'},{$ref: '#/components/parameters/AnonymousToken'}], responses: {'204': {description: Soft delete concluído}, '412': {$ref: '#/components/responses/PreconditionFailed'}}}
  /simulations/{id}/validate:
    post: {operationId: validateSimulation, security: [{
bearerAuth: []
}, {}], parameters: [{$ref: '#/components/parameters/Id'},{$ref: '#/components/parameters/AnonymousToken'}], responses: {'200': {description: Resultado de validação}, '422': {$ref: '#/components/responses/Unprocessable'}}}
  /simulations/{id}/calculate:
    post: {operationId: calculateSimulation, security: [{
bearerAuth: []
}, {}], parameters: [{$ref: '#/components/parameters/Id'},{$ref: '#/components/parameters/IfMatch'},{$ref: 
'#/components/parameters/IdempotencyKey'},{$ref: '#/components/parameters/AnonymousToken'}], responses: {'200': {description: Cálculo síncrono}, '202': {description: Job criado, content: {application/json: {schema: {$ref: '#/components/schemas/Job'}}}}, '409': {$ref: '#/components/responses/Conflict'}, '412': {$ref: '#/components/responses/PreconditionFailed'}, '422': {$ref: '#/components/responses/Unprocessable'}}}
  /simulations/{id}/scenarios:
    post: {operationId: createScenarios, security: [{
bearerAuth: []
}, {}], parameters: [{$ref: '#/components/parameters/Id'},{$ref: '#/components/parameters/IdempotencyKey'},{$ref: '#/components/parameters/AnonymousToken'}], responses: {'201': {description: Variantes criadas com parent_simulation_id e scenario_name}, '409': {$ref: '#/components/responses/Conflict'}}}
  /simulations/{id}/reports:
    post: {operationId: createReport, security: [{
bearerAuth: []
}, {}], parameters: [{$ref: '#/components/parameters/Id'},{$ref: '#/components/parameters/IdempotencyKey'},{$ref: '#/components/parameters/AnonymousToken'}], responses: {'202': {description: Job de relatório, content: {application/json: {schema: {$ref: '#/components/schemas/Job'}}}}}
  /reports/{id}:
    get: {operationId: getReport, security: [{
bearerAuth: []
}, {}], parameters: [{$ref: '#/components/parameters/Id'},{$ref: '#/components/parameters/AnonymousToken'}], responses: {'200': {description: Relatório e URL temporária, content: {application/json: {schema: {$ref: '#/components/schemas/Report'}}}}, '404': {$ref: '#/components/responses/NotFound'}}}
  /jobs/{id}:
    get: {operationId: getJob, security: [{
bearerAuth: []
}, {}], parameters: [{$ref: '#/components/parameters/Id'},{$ref: '#/components/parameters/AnonymousToken'}], responses: {'200': {description: Estado do job, content: {application/json: {schema: {$ref: '#/components/schemas/Job'}}}}, '404': {$ref: '#/components/responses/NotFound'}}}
  /admin/instruments:
    post: {operationId: createInstrument, security: [{oauth2Client: [legislation.admin]}], responses: {'201': {description: Diploma criado}, '409': {$ref: '#/components/responses/Conflict'}}}
  /admin/parameters:
    post: {operationId: createParameter, security: [{oauth2Client: [rules.admin]}], responses: {'201': {description: Parâmetro criado}, '422': {$ref: '#/components/responses/Unprocessable'}}}
  /admin/rule-sets:
    post: {operationId: createRuleSet, security: [{oauth2Client: [rules.admin]}], response
s: {'201': {description: Conjunto criado}}}
  /admin/rule-sets/{id}/publish:
    post: {operationId: publishRuleSet, security: [{oauth2Client: [rules.admin]}], parameters: [{$ref: '#/components/parameters/Id'},{$ref: '#/components/parameters/IdempotencyKey'}], responses: {'200': {description: Publicado atomicamente}, '409': {$ref: '#/components/responses/Conflict'}, '422': {$ref: '#/components/responses/Unprocessable'}}}
  /admin/audit:
    get: {operationId: searchAudit, security: [{oauth2Client: [audit.read]}], responses: {'200': {description: Auditoria paginada}, '403': {$ref: '#/components/responses/Forbidden'}}}
12.1 Validação prévia à geração de código
Validação
Resultado da revisão
Comando/gate no CI
Chave components
Corrigido: existe um único bloco com securitySchemes, parameters, schemas e responses.
Parser OpenAPI 3.1 deve terminar com zero erros.
Referências
Todos os $ref usados apontam para componentes declarados.
Resolver bundle; referências quebradas bloqueiam.
operationId
Os identificadores apresentados são únicos.
Lint de unicidade obrigatório.
SQL–API
Simulation, Job e Report possuem correspondência persistente; versões têm id e versão; cenários usam parent_simulation_id.
Teste de contrato e mapa campo-a-campo.
Concorrência
lock_version suporta ETag/If-Match.
Teste paralelo deve produzir uma actualização e uma resposta 412.
Anonimato
Header definido; token emitido uma vez e hash persistido.
Teste com token correcto, ausente e inválido.
Vigência
Rule sets e parâmetros usam daterange e exclusões por âmbito.
Testes de fronteira e sobreposição.
Idempotência
Chave composta por proprietário e chave; método e path persistidos.
Mesmo corpo retorna resposta original; corpo diferente retorna 409.
Geração de código
Autorizada apenas após validação automática; esta revisão documental não substitui a execução no repositório.
openapi validate + lint + migration:fresh + rollback + testes de contrato.
13. Matriz de testes das fórmulas do MVP
Os valores de taxa marcados como parâmetro de teste são dados controlados do cenário, não afirmações da taxa legal vigente. Assim, os testes permanecem determinísticos quando a legislação muda. Para INSS usam-se, apenas nos casos indicados, 4% e 3% como configuração inicial a validar.
Leitura da matriz:
 cada linha é um caso automatizável. O ID deve ser reutilizado no nome do teste, no relatório de CI/CD e na evidência de aceitação. “Crítica” bloqueia a publicação; “Alta” exige correcção ou excepção formal aprovada antes do Go-Live.
ID
Regra/cenário e entradas
Resultado esperado
Prioridade
IRPC-01
Rendimentos 1 000 000; gastos dedutíveis 600 000; correcções +50 000 e −20 000; prejuízos 30 000; taxa teste 32%.
LT=400 000; IRPC bruto=128 000.
Crítica
IRPC-02
LT calculado −100 000; taxa teste 32%.
Base sujeita=0; imposto=0; prejuízo sinalizado, nunca imposto negativo.
Crítica
IRPC-03
IRPC bruto 128 000; benefício elegível 20 000; créditos 15 000.
IRPC a pagar=93 000; linhas separadas e fontes presentes.
Alta
IRPC-04
Mesmo caso, mas certificado de benefício ausente.
Benefício não aplicado; aviso de evidência; IRPC=113 000 após créditos.
Crítica
IVA-01
Venda tributável 100 000; taxa teste 16%; IVA dedutível 6 000; crédito 2 000.
Liquidado=16 000; IVA a pagar=8 000.
Crítica
IVA-02
Venda tributável 100 000 e exportação 50 000 a taxa zero; taxa teste 16%.
Liquidado=16 000; exportação não gera IVA liquidado e permanece reportada.
Alta
IVA-03
Liquidado 5 000; IVA dedutível 7 500; crédito anterior 1 000.
IVA a pagar=0; crédito estimado=3 500.
Crítica
IVA-04
Operação isenta e IVA de compra sem direito a dedução.
IVA liquidado=0; IVA de compra não reduz imposto; motivo indicado.
Alta
ISPC-01
Elegível; base 500 000; taxa teste 3%.
ISPC=15 000; versão do parâmetro registada.
Crítica
ISPC-02
Volume acima do limite de elegibilidade configurado.
Não calcular ISPC; encaminhar para regime geral e emitir aviso.
Crítica
IRPS-01
Rendimento colectável exactamente no limite inferior de um escalão.
Aplicar parcela fixa sem diferença marginal; escolher um único escalão.
Crítica
IRPS-02
Rendimento um cêntimo abaixo e um cêntimo acima do limite.
Continuidade conforme tabela parametrizada; sem dupla tributação ou lacuna.
Crítica
INSS-01
Remuneração contributiva 100 000; empregador 4%; trabalhador 3%.
Empregador=4 000; trabalhador=3 000; total=7 000.
Crítica
INSS-02
Salário 80 000 e adicional não contributivo 20 000.
Base=80 000; contribuições não incluem o adicional; pressuposto exibido.
Alta
RET-01
Pagamento bruto 200 000; taxa doméstica teste 20%.
Retenção=40 000.
Crítica
RET-02
Convenção indica taxa teste 10%, mas certificado de residência ausente.
Não aplicar convenção; usar taxa doméstica e emitir aviso.
Crítica
RET-03
Convenção válida, país e certificado presentes; taxa teste 10%.
Retenção=20 000; fundamento doméstico e convencional incluídos.
Alta
ADU-01
Factura 1 000 USD; câmbio 65; frete 10 000 MZN; seguro 5 000; ajustamentos 0.
VA=80 000 MZN.
Crítica
ADU-02
VA=80 000; taxa pautal teste 20%.
Direitos=16 000.
Crítica
ADU-03
Sem código pautal.
Cálculo de direitos bloqueado com erro de validação; nenhum valor genérico.
Crítica
ICE-01
Base ICE 50 000; taxa ad valorem teste 10%.
ICE=5 000.
Alta
ICE-02
Quantidade 100 unidades; taxa específica teste 25 MZN/unidade.
ICE=2 500; unidade e quantidade preservadas.
Alta
IMP-IVA-01
VA 80 000; DA 16 000; ICE 5 000; outras imposições 1 000; taxa IVA teste 16%.
Base=102 000; IVA importação=16 320.
Crítica
CFT-01
IRPC 93 000; direitos 16 000; ICE 5 000; contribuição patronal 4 000; IVA recuperável 16 320.
CFT=118 000; IVA recuperável excluído do custo e mostrado separadamente.
Crítica
TEF-01
CFT 118 000; base económica lucro 400 000.
Taxa efectiva=29,50%; rótulo da base “lucro”.
Alta
ID
Teste transversal
Resultado esperado
Prioridade
TMP-01
Data na véspera, no dia e após mudança de taxa.
Cada simulação usa exactamente a versão válida na data; fronteiras [início,fim).
Crítica
TMP-02
Duas taxas publicadas com períodos sobrepostos.
Base rejeita a publicação por restrição temporal.
Crítica
RND-01
Valor que produz três ou mais casas decimais.
Arredondamento ocorre no ponto e modo configurados; relatório e API coincidem.
Crítica
CUR-01
Mesma factura com taxa de câmbio e data diferentes.
Resultados distintos e reproduzíveis; valor original, taxa e fonte guardados.
Alta
RULE-01
Regra publicada sem fonte legal.
Publicação bloqueada.
Crítica
RULE-02
Expressão contém função ou identificador não permitidos.
Validação rejeita sem executar código.
Crítica
RULE-03
Duas regras dependem circularmente uma da outra.
Cálculo bloqueado; ciclo identificado no erro administrativo.
Crítica
API-01
Repetir POST calculate com a mesma Idempotency-Key e corpo.
Uma execução; resposta original devolvida.
Crítica
API-02
Mesma chave com corpo diferente.
409 IDEMPOTENCY_CONFLICT.
Crítica
API-03
PATCH com ETag antigo.
412 PRECONDITION_FAILED; nenhuma alteração.
Alta
SEC-01
Utilizador tenta ler simulação de outro utilizador.
404 ou 403 conforme política; nenhum dado revelado.
Crítica
SEC-02
Autor tenta aprovar a própria regra.
Aprovação rejeitada pela segregação de funções.
Crítica
AUD-01
Publicar parâmetro.
Antes/depois, actor, data e correlação registados; registo não editável.
Alta
REP-01
Gerar PDF de simulação concluída.
Totais e referências reconciliam com JSON; checksum e versão presentes.
Crítica
PERF-01
Cenário normal com 100 regras e 500 parâmetros.
p95 do cálculo inferior a 2 segundos no ambiente de referência.
Alta
ACC-01
Navegação integral do questionário apenas por teclado.
Foco visível, ordem lógica, erros associados aos campos e resultado legível.
Alta
13.1 Critérios Go/No-Go do MVP
Go:
 100% dos testes críticos aprovados; pelo menos 95% do total aprovado; zero vulnerabilidades críticas/altas abertas; reconciliação integral entre motor, base e relatório; revisão fiscal e jurídica assinada; rollback ensaiado; cópias de segurança e observabilidade verificadas.
No-Go:
 qualquer fórmula crítica incorrecta; regra sem fonte; sobreposição de vigência; benefício aplicado sem elegibilidade; fuga de dados; divergência entre PDF e API; possibilidade de auto-aprovação; recuperação ou rollback não demonstrados.
Evidências:
 relatório automatizado por versão do rule_set, conjunto de casos dourados, assinatura dos revisores, logs de CI/CD, resultados de segurança e acta de aceitação operacional.
14. Validação de consistência entre SQL e OpenAPI
Conclusão:
 os bloqueadores de estrutura foram corrigidos nas Secções 11 e 12. O DDL e o contrato estão agora alinhados para jobs, cenários, ETag, eliminação lógica, acesso anónimo, vigência dos rule sets, escopo temporal dos parâmetros, idempotência, relatórios, fontes legais e governação. A geração de código continua condicionada à execução dos validadores automáticos no repositório.
Área
Estado
Constatação
Correcção necessária
Simulações
Parcial
Simulation corresponde a simulations e simulation_results, mas a API expõe questionnaire_version e rule_set_version como texto, enquanto o SQL guarda chaves UUID.
Resolver versões por JOIN e declarar no contrato id e version separadamente.
Concorrência
Inconsistente
A API exige ETag/If-Match, mas simulations não possui lock_version ou versão de linha.
Adicionar lock_version bigint e gerar ETag a partir de id, lock_version e updated_at.
Eliminação lógica
Ausente
DELETE promete eliminação lógica, mas simulations não possui deleted_at/deleted_by.
Adicionar deleted_at, deleted_by e motivo; excluir registos eliminados nas políticas RLS.
Jobs
Ausente
GET /jobs/{id} e respostas 202 usam Job, mas não existe tabela jobs.
Criar jobs com tipo, estado, progresso, tentativas, payload_hash, resource_type/id, erro e timestamps.
Cenários
Ausente
POST /scenarios cria variantes, mas não existe relação entre cenário-base e variantes.
Adicionar simulation_scenarios ou parent_simulation_id, scenario_name e changes_json.
Anonimato
Parcial
SQL guarda anonymous_token_hash, mas o contrato não define emissão nem envio do token.
Devolver token apenas na criação e exigir X-Anonymous-Simulation-Token nas operações seguintes; nunca devolver o hash.
Vigência de rule set
Inconsistente
O fluxo selecciona conjunto vigente por data, mas rule_sets tem reference_date e não effective_period.
Substituir ou complementar por effective_period daterange e impedir sobreposição de conjuntos publicados por âmbito.
Parâmetros
Crítico
A exclusão temporal ignora jurisdiction_id, activity_id e currency; pode bloquear combinações legítimas e permitir ambiguidade noutros filtros.
Definir uma scope_key canónica com imposto, regime, jurisdição, actividade, moeda e demais dimensões; aplicar exclusão por scope_key, parameter_key e período.
Idempotência
Inconsistente
key é chave primária global, embora owner_key exista.
Usar PRIMARY KEY(owner_key,key) e incluir método, rota e expiração no índice de pesquisa.
OpenAPI
Inválido
O texto possui dois blocos components no mesmo nível; YAML rejeita chave duplicada. Os endpoints administrativos estão descritos em tabela, mas não em paths.
Unificar schemas, parameters, responses e securitySchemes num único components; formalizar todos os paths administrativos.
Erros e segurança
Parcial
O checklist exige 401/403/429/500 e correlation ID, mas vários paths não os declaram; scopes não estão associados operação a operação.
Criar respostas reutilizáveis e declarar segurança, scopes e cabeçalhos em cada operação protegida.
Relatórios
Parcial
reports suporta estado e ficheiro, mas o schema da resposta não está formalizado e a URL temporária não deve ser persistida.
Criar ReportResponse; gerar download_url no momento da consulta; manter apenas object_key, checksum e expiração.
Fontes legais
Parcial
result_sources guarda provision_id e legal_version_id, embora a disposição já determine a versão.
Remover legal_version_id ou garantir por trigger que corresponde à legal_provisions.legal_version_id.
Publicação
Parcial
Há estados e checks de auto-aprovação, mas não há histórico normalizado de decisões nem prova de testes.
Criar review_decisions e rule_set_test_runs; publicação exige decisões válidas e execução crítica aprovada.
Gate de consistência corrigido:
 as discrepâncias de desenho deixaram de bloquear o modelo. Antes de gerar código, o pipeline deve comprovar: parsing OpenAPI 3.1 sem erros, referências resolvidas, operationId únicos, migrações up/down numa base vazia, constraints temporais, RLS, ETag, idempotência, transacções e reconciliação campo-a-campo. Qualquer falha mantém o estado No-Go.
15. Lacunas críticas da matriz de testes
ID proposto
Lacuna/cenário
Resultado esperado
Prioridade
SQL-01
Executar todas as migrações numa base vazia e depois rollback completo.
Up e down terminam sem erro; extensões, tipos, constraints, views e triggers reconciliam.
Crítica
SQL-02
Parâmetros iguais no mesmo período, mas com jurisdição, actividade ou moeda diferentes.
Combinações legítimas coexistem; duplicado no mesmo scope é rejeitado.
Crítica
OAS-01
Validar openapi.yaml com parser OpenAPI 3.1.
Zero erros, chaves duplicadas ou referências quebradas.
Crítica
OAS-02
Executar testes de contrato positivos e negativos para todos os paths.
Pedidos e respostas conformes; campos adicionais proibidos onde definido.
Crítica
AUTH-01
JWT expirado, assinatura inválida e scope insuficiente.
401 ou 403 consistente; nenhum detalhe sensível revelado.
Crítica
ANON-01
Criar simulação anónima e reutilizar token correcto, ausente e incorrecto.
Acesso apenas com token correcto; hash nunca é exposto.
Crítica
RLS-01
Aceder directamente às tabelas com identidades de dois proprietários.
RLS impede leitura e alteração cruzadas, incluindo relatórios e soft-deleted.
Crítica
CON-01
Dois cálculos concorrentes para a mesma simulação e ETag.
Uma execução vence; a outra recebe conflito/precondição, sem resultados duplicados.
Crítica
TX-01
Falha após gravar parte dos resultados.
Rollback integral; não ficam resultados, fontes ou obrigações parciais.
Crítica
JOB-01
Falha transitória e retry de cálculo/relatório.
Retry limitado e idempotente; dead-letter após máximo; estado final consultável.
Crítica
PUB-01
Publicação concorrente de dois rule sets sobrepostos.
Apenas um é publicado; cache muda somente após commit.
Crítica
IMM-01
Tentar alterar ou eliminar regra, parâmetro ou versão publicada.
Operação bloqueada; correcção exige nova versão.
Crítica
SRC-01
Fonte do resultado aponta para versão legal divergente da disposição.
Constraint/trigger rejeita ou o modelo derivado impede a inconsistência.
Crítica
IRPC-05
Prejuízo reportável excede o limite temporal ou quantitativo configurado.
Dedução limitada e saldo remanescente explicado.
Crítica
IVA-05
Pró-rata de IVA dedutível em operações mistas.
Apenas fracção parametrizada é deduzida; arredondamento documentado.
Crítica
IRPS-03
Múltiplos trabalhadores, periodicidade mensal/anual, deduções e situação familiar distintas.
Cálculo individual correcto; agregados não misturam bases.
Crítica
ADU-04
Preferência de origem alegada sem prova válida.
Taxa preferencial não aplicada; taxa geral e aviso apresentados.
Crítica
ADU-05
Frete/seguro em moeda diferente e ajustamento negativo.
Conversão por data/fonte correctas; ajustamento permitido validado.
Alta
CFT-02
Retenção recuperável versus custo definitivo.
Fluxo recuperável não integra CFT; classificação e fonte explícitas.
Crítica
ZERO-01
Base da taxa efectiva igual a zero.
Não dividir por zero; taxa efectiva indisponível com explicação.
Crítica
PREC-01
Valores máximos de numeric e grande lote de importações.
Sem overflow ou perda silenciosa de precisão; erro controlado quando excede limites.
Crítica
RETEN-01
Expiração e expurgo de simulação anónima.
Dados operacionais removidos conforme política; auditoria mínima preservada.
Alta
REP-02
URL de relatório expirada ou usada por outro proprietário.
Acesso negado; nova URL pode ser emitida ao proprietário.
Crítica
ACC-02
Leitor de ecrã, zoom 200%, contraste, mensagens de erro e interface móvel.
Fluxo utilizável e conforme critérios WCAG adoptados.
Alta
DR-01
Restauro de base e objectos a partir de backup.
RPO/RTO atingidos; hashes e ligações relatório–simulação reconciliam.
Crítica
Avaliação:
 a matriz original cobre bem fórmulas básicas, fronteiras temporais, idempotência, autorização, relatório e desempenho. As lacunas mais graves estavam na executabilidade do DDL/OpenAPI, isolamento anónimo/RLS, concorrência transaccional, publicação, integridade das fontes e casos fiscais compostos. Os casos acima devem integrar a matriz oficial; SQL-01, OAS-01, AUTH-01, RLS-01, TX-01, PUB-01 e ZERO-01 são pré-condições do MVP.
16. Plano de implementação do MVP por sprint
Cadência proposta:
 Sprint 0 de uma semana e seis sprints de duas semanas, totalizando 13 semanas, seguidas de estabilização assistida. Equipa mínima: Product Owner, fiscalista, jurista, UX/UI, dois back-end, dois front-end, QA/automação e DevOps/segurança, com apoio de dados/conteúdo jurídico.
Sprint
Objectivo e entregáveis
Critérios de saída
Dependências
Sprint 0 — Fundação e decisões (1 semana)
Fechar escopo do MVP; corrigir mapa SQL–OpenAPI; ADRs de autenticação, token anónimo, motor de expressões, moedas, arredondamento e retenção; backlog e Definition of Done; ambientes e pipeline inicial.
OpenAPI e DDL corrigidos em revisão; riscos e responsáveis aprovados; protótipo do questionário validado; fontes legais prioritárias inventariadas.
PO, fiscalista, jurista, arquitectura, IAM e critérios de segurança.
Sprint 1 — Dados e repositório legislativo
Migrações PostgreSQL/PostGIS; entidades legais, versões, artigos, relações, jurisdições e actividades; armazenamento de objectos; pesquisa; seeds não fiscais; auditoria base.
SQL-01 aprovado; restrições temporais testadas; pesquisa e importação de diploma demonstradas; rollback validado.
DDL corrigido, ambiente CI e convenção de migrações.
Sprint 2 — Governação de parâmetros e regras
Tax types, regimes, parâmetros, rule sets, regras, fontes, elegibilidade, obrigações, decisões de revisão e test runs; painel administrativo mínimo; workflow de quatro olhos.
Scope temporal sem ambiguidades; regra sem fonte não publica; auto-aprovação bloqueada; PUB-01 e IMM-01 aprovados.
Modelo jurídico validado e linguagem de expressões escolhida.
Sprint 3 — API e questionário
OpenAPI válido; schema e catálogos; criar, consultar, editar, validar e eliminar logicamente simulações; JWT, token anónimo, RLS, ETag, idempotência, erros e rate limiting.
OAS-01/OAS-02, AUTH-01, ANON-01, RLS-01 e API-01–03 aprovados; questionário responsivo navegável por teclado.
IAM/SSO, catálogos e JSON Schema do questionário.
Sprint 4 — Motor fiscal principal
Grafo de dependências; sandbox segura; parâmetros vigentes; explicabilidade; IRPC, IVA, ISPC, IRPS, INSS e 
retenções; persistência atómica de resultados e fontes.
Todos os testes críticos destas fórmulas aprovados; TX-01, RULE-02/03, 
TMP-01 e RND-01 aprovados; casos dourados assinados.
Taxas e escalões oficialmente validados; decisões sobre 
pró-rata e prejuízos.
Sprint 5 — Aduanas, cenários e obrigações
Valor aduaneiro, direitos, ICE, IVA de importação, câmbio, preferências de origem; cenários; obrigações fiscais/cambiais; jobs persistidos e retries.
ADU-01–05, ICE, IMP-IVA, CUR-01, JOB-01 e cenários aprovados; ausência de código pautal bloqueia cálculo definitivo.
Pauta/códigos, taxas de câmbio e regras de origem carregadas e validadas.
Sprint 6 — Relatórios, endurecimento e Go-Live
PDF/JSON, reconciliação e checksum; observabilidade; backups/restauro; testes de carga, segurança e acessibilidade; conteúdos de aviso; formação e runbooks; homologação.
REP-01/02, PERF-01, ACC-01/02 e DR-01 aprovados; zero falhas críticas; revisão fiscal/jurídica assinada; Go/No-Go aprovado.
Ambiente de produção, certificados, monitorização, suporte e donos operacionais.
16.1 Trilhos transversais e gates
Fiscal e jurídico:
 validar legislação, parâmetros, fórmulas, exemplos, avisos e data de vigência em todas as sprints; nenhuma taxa entra por decisão técnica isolada.
Qualidade:
 teste acompanha a história no mesmo sprint; cada regra tem testes unitários, contrato, integração e caso dourado.
Segurança e privacidade:
 threat modelling no Sprint 0, revisão de código contínua, SAST/DAST, RLS e testes de autorização antes de cada release.
Acessibilidade:
 revisão de UX desde o protótipo; teclado, leitor de ecrã, contraste, zoom e móvel não ficam apenas para o final.
Dados e conteúdo:
 importação controlada de diplomas e parâmetros decorre em paralelo, com hash, fonte, revisão e evidência.
Gate A — fim do Sprint 2:
 modelo de governação publicável e reversível.
Gate B — fim do Sprint 4:
 motor principal reconciliado com casos dourados.
Gate C — fim do Sprint 6:
 Go-Live apenas com 100% dos testes críticos, aceitação operacional, rollback, backup/restauro e aprovações fiscal e jurídica.
Fora do MVP:
 benefícios complexos ZEE/ZFI, convenções completas de dupla tributação, regimes sectoriais, integração directa com entidades externas, conta avançada do investidor e assistente inteligente permanecem no backlog, salvo se forem elevados a requisito legal indispensável durante a validação.
17. Formulário integrado de simulação e manifestação de investimento ou negócio
O formulário deve servir simultaneamente para recolher os dados necessários à simulação fiscal e, mediante consentimento explícito do utilizador, reutilizar esses dados para criar uma 
Manifestação de Interesse de Investimento
 ou uma 
Manifestação de Oportunidade de Negócio
. A simulação mantém natureza informativa; a manifestação cria um processo formal, sujeito a autenticação, validação documental e acompanhamento no InvestMoz.
17.1 Modalidades de utilização
Modalidade
Descrição
Resultado
Simulação anónima
O utilizador preenche apenas os dados económicos essenciais, sem identificação pessoal obrigatória.
Resultado fiscal indicativo; pode guardar temporariamente por token ou autenticar-se para continuar.
Simulação autenticada
O utilizador inicia sessão e pode associar a simulação a um perfil de investidor ou entidade já existente.
Simulação persistente, cenários, relatório e histórico.
Manifestação formal
O utilizador selecciona “Transformar em manifestação”, confirma os dados reutilizados, completa identificação, contactos, documentos e declaração.
Novo processo de investimento ou negócio, com referência, estado, responsável e trilho de acompanhamento.
17.2 Estrutura do formulário
Etapa
Campos principais
Regras e ligações
1. Finalidade
Objectivo: apenas simular, avaliar investimento próprio, procurar investidor/parceiro, apresentar oportunidade, solicitar facilitação; tipo de manifestação: investimento ou negócio.
A finalidade determina as etapas condicionais e o módulo de destino.
2. Perfil do promotor
Tipo de pessoa; investidor nacional/estrangeiro; residente fiscal; entidade existente/em constituição; país; NUIT opcional na simulação e obrigatório quando legalmente exigido na manifestação.
Quando autenticado, pré-preencher a partir do Perfil do Investidor; alterações não actualizam o cadastro mestre sem confirmação separada.
3. Identificação e contactos
Nome/denominação; representante; documento de identificação ou registo; telefone; correio electrónico; endereço; idioma preferido.
Só obrigatório na manifestação; dados pessoais separados dos dados económicos e protegidos por consentimento.
4. Projecto ou negócio
Título; resumo executivo; tipo: novo projecto, expansão, aquisição, parceria, concessão, fornecedor, comprador ou outro; sector e 
actividade; produtos/serviços; fase de maturidade.
Cria ou liga a um registo de projecto/oportunidade; título e resumo tornam-se a ficha inicial.
5. Localização
Província, distrito, posto administrativo/localidade; coordenadas opcionais; área necessária; ZEE/ZFI; propriedade, DUAT ou necessidade de terreno.
Usa o catálogo geográfico mestre/PostGIS; permite mapear a manifestação e activar regras territoriais.
6. Estrutura financeira
Investimento total; moeda; capital próprio; financiamento; montante já mobilizado; montante procurado; origem dos fundos; prazo; receitas, custos e lucro estimados.
Valores partilhados com a simulação; financiamento deve reconciliar com o montante total ou gerar aviso.
7. Emprego e impacto
Empregos directos e indirectos; nacionais e estrangeiros; qualificações; conteúdo local; exportações; substituição de importações; impacto comunitário e ambiental.
Alimenta avaliação de impacto, emprego e priorização, sem alterar cálculos fiscais não relacionados.
8. Operação fiscal
Vendas tributadas, isentas, taxa zero e exportações; custos dedutíveis; remunerações; pagamentos ao exterior; importações por item; benefícios alegados.
Enviado ao motor fiscal; benefícios ficam “a validar” enquanto a evidência não for aprovada.
9. Necessidade de negócio
Procura/oferta; tipo de parceiro; capital, comprador, fornecedor, terreno, tecnologia, licença ou assistência; valor e condições; prazo; confidencialidade.
Cria uma oportunidade de negócio ligada à manifestação; campos sensíveis não são publicados.
10. Documentos
Identificação; certidão/registo; NUIT; perfil da empresa; estudo de viabilidade; plano de negócio; prova de fundos; certificado ZEE/ZFI; licenças; mapa/localização.
Cada documento possui tipo, hash, versão, validade, nível de confidencialidade e estado de verificação.
11. Consentimentos
Aceitação dos termos; declaração de veracidade; consentimento para reutilizar dados da simulação; autorização de contacto; opção de publicação da oportunidade.
Consentimentos separados, versionados e revogáveis quando aplicável; publicação nunca é presumida.
12. Revisão e submissão
Resumo dos dados; lacunas; resultado fiscal; riscos; dados públicos/privados; declaração final.
Guardar rascunho, validar, submeter ou regressar; submissão gera referência e bloqueia versão enviada.
17.3 Ligações com a aplicação InvestMoz
Entidade fonte
Entidade destino
Ligação e regra de dados
Conta/IAM
Manifestação
owner_user_id identifica o proprietário; autenticação obrigatória para submissão formal.
Perfil do investidor
Manifestação
investor_profile_id referencia o cadastro mestre; a manifestação guarda um snapshot dos dados apresentados.
Simulação
Manifestação
simulation_id liga o cenário escolhido; o resultado fiscal é preservado por rule_set, data e hash.
Manifestação
Projecto de investimento
investment_project_id é criado após triagem favorável ou ligado a projecto existente, evitando duplicação.
Manifestação
Oportunidade de negócio
business_opportunity_id é criado quando existe procura/oferta; a publicação depende de aprovação e consentimento.
Manifestação
CRM
crm_case_id acompanha contacto, responsável, notas, tarefas e follow-up; o CRM não duplica os dados fiscais detalhados.
Localização
Mapa/PostGIS
jurisdiction_id e geometria opcional permitem pesquisa territorial e regras ZEE/ZFI.
Documentos
Gestão documental
document_id aponta para armazenamento seguro; permissões seguem a manifestação e o grau de confidencialidade.
Manifestação
Notificações
Eventos de submissão, pedido de informação, decisão e prazo geram mensagens no portal e por canais autorizados.
17.4 Fluxo entre simulação, manifestação e oportunidade
O utilizador inicia em 
/Simulador
, escolhe “Simular investimento” ou parte de uma oportunidade/projecto já existente.
O portal pré-preenche dados autorizados do perfil, projecto ou oportunidade; o utilizador confirma cada grupo.
O motor valida e calcula o cenário; o resultado recebe simulation_id, versão das regras, data e hash.
Após o resultado, a interface apresenta “Guardar”, “Criar cenários” e “Manifestar interesse”.
Ao escolher “Manifestar interesse”, o utilizador autentica-se, selecciona o cenário e autoriza a reutilização dos dados.
O sistema cria um rascunho de manifestação com simulation_id e snapshot; pede apenas dados e documentos ainda ausentes.
A validação identifica campos obrigatórios, documentos, divergências e dados que serão públicos.
Na submissão, o processo recebe referência, timestamp e versão imutável; o CRM abre um caso de triagem.
O analista verifica identidade, completude, enquadramento, duplicação, viabilidade preliminar e necessidade de informação.
Se aprovada para prosseguimento, a manifestação é ligada/cria um projecto de investimento e, quando aplicável, uma oportunidade de negócio publicável.
Alterações posteriores criam nova versão; a simulação original permanece reproduzível e não é sobrescrita.
Estados da manifestação:
 draft → validation_pending → submitted → under_screening → information_requested → under_assessment → approved_for_facilitation | converted_to_project | converted_to_opportunity | rejected | withdrawn | archived. 
Apenas transições autorizadas são permitidas; cada transição regista actor, data, motivo e anexos.
17.5 Extensão do modelo de dados
Tabela
Campos essenciais
Finalidade
investment_manifestations
id, reference_no, owner_user_id, investor_profile_id, simulation_id, manifestation_type, title, summary, status, confidentiality, submitted_at, current_version, created_at, updated_at
Registo central da manifestação; simulation_id é opcional na criação directa, mas obrigatório quando convertida de uma simulação.
manifestation_versions
id, manifestation_id, version_no, data_snapshot, public_snapshot, checksum, created_by, created_at
Preserva exactamente os dados submetidos e separa informação publicável.
manifestation_locations
manifestation_id, jurisdiction_id, geometry, land_status, area_required
Localização e necessidades territoriais.
manifestation_financials
manifestation_id, currency, total_investment, own_capital, debt, secured_amount, sought_amount, annual_revenue, annual_costs
Valores reconciliáveis e reutilizáveis pelo simulador.
manifestation_impacts
manifestation_id, direct_jobs, indirect_jobs, national_jobs, foreign_jobs, exports_value, local_content, impact_json
Indicadores de emprego e desenvolvimento.
business_opportunities
id, manifestation_id, opportunity_type, offer_or_demand, title, description, value, currency, deadline, publication_status, confidentiality
Oferta/procura de investimento, parceria, compra, fornecimento, tecnologia ou outros negócios.
manifestation_documents
id, manifestation_id, document_type, object_key, file_hash, valid_from, valid_to, confidentiality, verification_status
Anexos seguros, versionados e verificáveis.
manifestation_consents
id, manifestation_id, consent_type, terms_version, granted, granted_at, revoked_at, evidence_hash
Prova de consentimento para reutilização, contacto e publicação.
manifestation_status_history
id, manifestation_id, from_status, to_status, actor_id, reason, occurred_at
Trilho imutável das transições.
manifestation_links
manifestation_id, investment_project_id, business_opportunity_id, crm_case_id
Ligações externas sem replicar dados mestre.
17.6 Endpoints adicionais
Método e rota
Uso
Regra principal
POST /api/v1/simulations/{id}/manifestations
Criar manifestação a partir de uma simulação.
Exige autenticação, Idempotency-Key, consentimento e cenário concluído; copia apenas campos mapeados.
POST /api/v1/manifestations
Criar manifestação directamente.
Permite preenchimento sem simulação; pode invocar o simulador posteriormente.
GET /api/v1/manifestations/{id}
Consultar formulário, estado, lacunas, ligações e histórico.
Apenas proprietário ou perfis autorizados.
PATCH /api/v1/manifestations/{id}
Guardar rascunho.
Usa If-Match; não permite alterar versão já submetida.
POST /api/v1/manifestations/{id}/validate
Validar campos, documentos, reconciliação e permissões de publicação.
Devolve erros, avisos e informação em falta.
POST /api/v1/manifestations/{id}/submit
Submeter formalmente.
Gera referência, versão, checksum e evento CRM numa transacção/outbox.
POST /api/v1/manifestations/{id}/documents
Registar metadados e obter URL segura de carregamento.
Valida tipo, tamanho, hash, antivírus e confidencialidade.
POST /api/v1/manifestations/{id}/transition
Mudar estado.
Reservado a perfis autorizados; requer estado esperado, decisão e motivo.
POST /api/v1/manifestations/{id}/convert/project
Criar ou ligar projecto.
Verifica duplicados e mantém manifestation_id como origem.
POST /api/v1/manifestations/{id}/convert/opportunity
Criar oportunidade de negócio.
Publicação separada da conversão e sujeita a consentimento/aprovação.
GET /api/v1/me/manifestations
Listar processos do utilizador.
Filtros por estado, tipo, data, projecto e oportunidade.
17.7 Regras de experiência, segurança e aceitação
Apresentar barra de progresso, gravação automática, resumo por etapa e indicação clara de campos obrigatórios.
Usar listas oficiais para sectores, países e geografia; permitir pesquisa e selecção sem exigir conhecimento de códigos.
Aplicar validação no campo e no servidor; mensagens devem indicar como corrigir o erro.
Permitir preencher por teclado, leitor de ecrã, zoom e dispositivo móvel; não depender apenas de cor para comunicar estado.
Nunca tornar públicos nomes, contactos, documentos, valores confidenciais ou resultados fiscais sem consentimento explícito e aprovação.
Manter um único dono por dado: identidade no Perfil, geografia no catálogo territorial, simulação no módulo fiscal, processo na Manifestação e relacionamento no CRM.
Detectar possíveis duplicados por promotor, título, sector, localização e montante, sem bloquear automaticamente uma submissão legítima.
A conversão deve ser idempotente: repetir o pedido não pode criar duas manifestações, projectos ou oportunidades.
O relatório da manifestação deve incluir referência, resumo, resultado fiscal associado, documentos, estado, próximos passos e contactos institucionais.
Critério Go-Live:
 testes de pré-preenchimento, consentimento, reconciliação financeira, conversão, duplicação, permissões, confidencialidade, acessibilidade, rollback e integração CRM aprovados.
18. Especificação completa do formulário e acompanhamento
Esta secção transforma a estrutura funcional da Secção 17 num formulário directamente implementável. Cada campo possui uma chave estável para o front-
end, API, base de dados, testes e relatórios. “Obrigatório condicional” significa que a exigência é activada por respostas anteriores.
18.1 Formulário completo por etapa
Etapa 1 — Finalidade
Tipo
Obrigatoriedade e regra
purpose — O que pretende fazer?
Selecção única: simulate_only, own_investment, seek_investor, seek_partner, present_opportunity, request_facilitation
Obrigatório; controla o percurso.
manifestation_type — Tipo de manifestação
Selecção única: investment, business
Obrigatório se purpose não for simulate_only.
entry_source — Origem
Selecção: simulator, project, opportunity, direct
Preenchido pelo sistema e não editável.
source_id
UUID
Obrigatório quando a origem é projecto, oportunidade ou simulação.
preferred_language
Selecção: pt-MZ, en
Obrigatório; predefinido pelo perfil/navegador.
Etapa 2 — Perfil do promotor
Tipo
Obrigatoriedade e regra
promoter.person_type
singular, collective, sole_trader, consortium
Obrigatório.
promoter.investor_origin
national, foreign, mixed
Obrigatório; activa perguntas cambiais.
promoter.tax_residency_country
País do catálogo ISO
Obrigatório.
promoter.entity_stage
existing, being_formed
Obrigatório para pessoa colectiva/consórcio.
promoter.legal_form
Catálogo de formas jurídicas
Obrigatório para entidade existente.
promoter.investor_profile_id
UUID
Opcional; pré-preenchido após autenticação.
promoter.nuit
Texto validado
Opcional na simulação; obrigatório na manifestação quando aplicável.
promoter.profile_confirmation
Booleano
Obrigatório quando existirem dados pré-preenchidos.
Etapa 3 — Identificação e contactos
Tipo
Obrigatoriedade e regra
identity.legal_name
Texto, 2–200 caracteres
Obrigatório para manifestação.
identity.commercial_name
Texto
Opcional.
identity.registration_number
Texto
Obrigatório para entidade existente.
identity.representative_name
Texto
Obrigatório para pessoa colectiva/consórcio.
identity.representative_capacity
Texto/seleção
Obrigatório quando há representante.
contact.email
Correio electrónico
Obrigatório e verificado antes da submissão.
contact.phone
Telefone internacional
Obrigatório; código do país incluído.
contact.address, country, province, district
Texto e catálogos
Obrigatório para manifestação.
contact.preferred_channel
portal, email, phone
Obrigatório; SMS apenas se existir serviço autorizado.
Etapa 4 — Projecto ou negócio
Tipo
Obrigatoriedade e regra
project.title
Texto, 5–200 caracteres
Obrigatório; pesquisa de possíveis duplicados.
project.summary
Texto, 100–3000 caracteres
Obrigatório para manifestação.
project.type
new, expansion, acquisition, partnership, concession, supplier, buyer, other
Obrigatório.
project.sector_id e activity_id
UUIDs de catálogo
Obrigatórios; actividade dependente do sector.
project.products_services
Lista de textos
Pelo menos um item.
project.maturity_stage
idea, concept, pre_feasibility, feasibility, financing, implementation, operation
Obrigatório.
project.expected_start_date
Data
Obrigatório; não anterior à data de referência sem justificação.
project.duration_years
Inteiro positivo
Obrigatório para projectos com prazo.
project.existing_project_id
UUID
Condicional quando se liga a projecto existente.
Etapa 5 — Localização
Tipo
Obrigatoriedade e regra
location.province_id, district_id, locality_id
UUIDs dependentes
Província e distrito obrigatórios; localidade quando disponível.
location.site_known
Booleano
Obrigatório.
location.latitude, longitude
Decimal geográfico
Condicional se o local for conhecido.
location.area_required
Decimal + unidade
Obrigatório quando há necessidade de terreno.
location.land_status
owned, duat, leased, requested, unknown
Obrigatório para investimento físico.
location.special_zone_id
UUID ZEE/ZFI
Opcional; exige certificado para benefício.
location.infrastructure_needs
Multi-selecção: energia, água, estrada, porto, ferrovia, telecomunicações, armazenamento
Opcional; alimenta facilitação.
Etapa 6 — Estrutura financeira
Tipo
Obrigatoriedade e regra
financial.currency
ISO 4217
Obrigatório.
financial.total_investment
Decimal não negativo
Obrigatório.
financial.own_capital, domestic_debt, foreign_debt, grants
Decimais
Obrigatórios com zero permitido; soma deve reconciliar com o total.
financial.secured_amount
Decimal
Obrigatório; não pode exceder o total.
financial.sought_amount
Decimal
Calculado ou confirmado; total menos montante mobilizado.
financial.funds_origin_countries
Lista de países
Obrigatório se existir capital/dívida estrangeira.
financial.annual_revenue, annual_operating_costs, expected_profit
Decimais
Obrigatórios para simulação de rendimento; lucro é reconciliado.
financial.financing_term_months
Inteiro
Condicional quando há dívida.
Etapa 7 — Emprego e impacto
Tipo
Obrigatoriedade e regra
impact.direct_jobs e indirect_jobs
Inteiros não negativos
Obrigatórios para manifestação de investimento.
impact.national_jobs e foreign_jobs
Inteiros
A soma dos empregos directos deve reconciliar ou gerar aviso.
impact.skills_needed
Lista estruturada
Opcional; liga a competências/formação.
impact.local_content_percentage
Percentagem 0–100
Opcional; exige pressuposto.
impact.exports_value e import_substitution_value
Decimais
Condicionais se exporta/substitui importações.
impact.environmental_category
Catálogo
Obrigatório quando conhecido; “por determinar” permitido.
impact.community_benefits
Texto
Opcional.
Etapa 8 — Operação fiscal
Tipo
Obrigatoriedade e regra
tax.sales_taxable, sales_exempt, sales_zero_rate, exports
Decimais
Obrigatórios com zero permitido.
tax.deductible_costs, non_deductible_costs, carried_losses
Decimais
Obrigatórios quando se calcula IRPC.
tax.payroll[]
Lista por trabalhador ou faixa: quantidade, remuneração, adicionais, contributivo
Condicional quando existem trabalhadores.
tax.outbound_payments[]
Natureza, valor, beneficiário, país, residência, convenção e prova
Condicional quando existem pagamentos ao exterior.
tax.imports[]
Descrição, código pautal, origem, quantidade, factura, frete, seguro, moeda e preferência
Condicional quando existem importações; código pautal obrigatório para cálculo definitivo.
tax.claimed_benefits[]
Regime, fundamento, certificado, validade
Opcional; nunca aplicado sem elegibilidade e prova.
tax.reference_date
Data
Obrigatório; selecciona regras vigentes.
Etapa 9 — Necessidade de negócio
Tipo
Obrigatoriedade e regra
opportunity.offer_or_demand
offer, demand, both
Obrigatório para manifestação de negócio.
opportunity.type
capital, partner, buyer, supplier, land, technology, licence, services, other
Obrigatório.
opportunity.description
Texto, 100–3000 caracteres
Obrigatório.
opportunity.value e currency
Decimal + ISO 4217
Opcional, mas ambos em conjunto.
opportunity.conditions
Texto
Opcional.
opportunity.deadline
Data
Opcional; futura.
opportunity.confidentiality
public, registered_users, restricted, confidential
Obrigatório; predefinido restricted.
opportunity.publish_requested
Booleano
Não implica publicação automática.
Etapa 10 — Documentos
Obrigatório quando
Validação
identity_document
Pessoa singular/representante
Tipo permitido, validade e verificação.
company_registration e NUIT
Entidade existente
Correspondência com identificação.
company_profile
Manifestação empresarial
PDF; classificação confidencial.
business_plan ou feasibility_study
Fase de viabilidade ou superior
Versão, data, hash e antivírus.
proof_of_funds
Montante mobilizado alegado
Confidencial; acesso restrito.
special_zone_certificate
Benefício ZEE/ZFI alegado
Entidade emissora e validade.
licences
Actividade licenciada/início operacional
Tipo, número, emissor e validade.
site_map
Local conhecido
Formato permitido e ligação geográfica.
Etapas 11 e 12 — Consentimentos, revisão e submissão
Tipo
Obrigatoriedade e regra
consents.terms_accepted
Booleano + terms_version
Obrigatório para submissão.
consents.truth_declaration
Booleano
Obrigatório.
consents.reuse_simulation
Booleano
Obrigatório quando a origem é simulação.
consents.contact_authorisation
Booleano
Obrigatório para acompanhamento.
consents.publication_authorisation
Booleano
Obrigatório apenas se publish_requested=true.
review.public_fields_confirmation
Booleano
Obrigatório quando há pedido de publicação.
review.validation_acknowledged
Booleano
Obrigatório; utilizador confirma avisos não bloqueadores.
review.final_declaration_name
Texto
Obrigatório; deve corresponder ao representante.
review.submit
Acção
Disponível apenas com validação sem erros bloqueadores.
18.2 Modelo de dados detalhado
Entidade
Chave e relações
Campos e restrições principais
investment_manifestations
PK id; FK owner_user_id externo; FK investor_profile_id externo; FK simulation_id → simulations
reference_no único após submissão; manifestation_type; purpose; status; confidentiality; lock_version; current_version; submitted_at; assigned_team_id; deleted_at.
manifestation_versions
PK id; FK manifestation_id; UNIQUE(manifestation_id, version_no)
data_snapshot JSONB; public_snapshot JSONB; checksum único por manifestação; created_by; submitted boolean; imutável quando submitted=true.
manifestation_contacts
PK id; FK manifestation_id
contact_type; name; email; phone; address; is_primary; verified_at; dados cifrados quando aplicável.
manifestation_locations
PK id; FK manifestation_id; FK jurisdiction_id
geometry; site_known; land_status; area_required; area_unit; special_zone_id; infrastructure_needs JSONB.
manifestation_financials
PK id; FK manifestation_id; UNIQUE(manifestation_id, version_no)
currency; total; own_capital; domestic_debt; foreign_debt; grants; secured; sought; revenue; costs; profit; CHECK valores ≥ 0 e reconciliação configurável.
manifestation_impacts
PK id; FK manifestation_id
jobs; national/foreign; exports; import substitution; local content; skills JSONB; environment category.
business_opportunities
PK id; FK manifestation_id; FK created_from_version_id
type; offer_or_demand; title; description; value; deadline; publication_status; public_snapshot; published_at.
manifestation_documents
PK id; FK manifestation_id; FK manifestation_version_id opcional
document_type; object_key; file_hash; mime_type; size; validity; confidentiality; verification_status; verified_by/at.
manifestation_consents
PK id; FK manifestation_id
consent_type; terms_version; granted; granted_at; revoked_at; evidence_hash; ip_hash; UNIQUE por tipo e versão.
manifestation_assignments
PK id; FK manifestation_id; assignee_user_id/team_id externos
role; assigned_at; unassigned_at; assigned_by; apenas uma atribuição principal activa.
manifestation_status_history
PK id; FK manifestation_id; FK version_id opcional
from_status; to_status; actor_id; reason_code; notes; occurred_at; imutável.
manifestation_requests
PK id; FK manifestation_id
request_type; items JSONB; due_at; status; response_version_id; opened_by; closed_at.
manifestation_tasks
PK id; FK manifestation_id; FK assignment_id
task_type; title; due_at; priority; status; completed_at; CRM external_id opcional.
manifestation_links
PK manifestation_id
investment_project_id; business_opportunity_id; crm_case_id; origem externa e timestamps de sincronização.
integration_outbox
PK id; aggregate_type/id
event_type; payload JSONB; status; attempts; available_at; published_at; idempotency_key única.
Uma simulação pode originar várias manifestações apenas se o tipo/finalidade diferir; a idempotência impede duplicação do mesmo pedido.
Cada manifestação possui muitas versões, documentos, tarefas, pedidos, consentimentos e eventos de estado.
A versão submetida é imutável; respostas a pedidos de informação criam nova versão e mantêm a anterior.
Projecto, oportunidade e caso CRM são referências externas; os dados mestres não são copiados, excepto snapshots necessários à prova e auditoria.
RLS restringe proprietário, equipa atribuída e perfis de supervisão; campos confidenciais exigem permissão adicional.
18.3 Endpoints detalhados
Endpoint
Pedido/Resposta essencial
Segurança e efeitos
POST /simulations/{id}/manifestations
Pedido: manifestation_type, purpose, selected_scenario_id, consent_version. Resposta 201: manifestation_id, status=draft, copied_fields, missing_fields, ETag.
JWT e Idempotency-Key; verifica propriedade e simulação completed; cria snapshot.
POST /manifestations
Pedido: purpose, manifestation_type, investor_profile_id opcional, initial_data. Resposta 201 com id e ETag.
JWT; criação directa; não submete.
GET /manifestations/{id}/schema
Resposta: etapas, campos, enums, condições, obrigatoriedade, documentos e versão do schema.
Proprietário/equipa; cache por versão.
GET /manifestations/{id}
Resposta: dados, completeness, validation, current_version, status, assignment, tasks, requests, links e timeline.
RLS; campos confidenciais filtrados por permissão.
PATCH /manifestations/{id}
Content-Type merge-patch; pedido parcial. Resposta 200: manifestação, validação incremental e novo ETag.
If-Match obrigatório; apenas draft ou information_requested.
POST /manifestations/{id}/validate
Resposta: valid, blocking_errors[], warnings[], missing_documents[], reconciliation[], public_preview.
Não muda para submitted; pode mudar draft para validation_pending.
POST /manifestations/{id}/documents/uploads
Pedido: document_type, filename, mime_type, size, confidentiality. 
Resposta: upload_id e URL temporária.
Lista branca; limites; URL curta.
POST /manifestations/{id}/documents/{upload_id}/complete
Pedido: file_hash. Resposta: document_id, scan_status.
Submissão bloqueada até antivírus aprovado.
POST /manifestations/{id}/submit
Pedido: expected_version, declarations, consent_ids. Resposta 202/201: reference_no, submitted_version, status=submitted, tracking_url.
Idempotente; transacção grava versão, estado e outbox para CRM/notificação.
GET /me/manifestations
Filtros: status, type, from, to, cursor. 
Resposta paginada com resumo e next_action.
Apenas proprietário.
GET /manifestations/{id}/timeline
Resposta: eventos ordenados, actor_display, estado, razão pública, documentos/requests relacionados.
Notas internas não são devolvidas ao investidor.
POST /manifestations/{id}/requests/{request_id}/respond
Pedido: responses, document_ids, comment. Resposta: nova versão e estado under_screening.
Proprietário; fecha pedido numa transacção.
POST /admin/manifestations/{id}/assign
Pedido: expected_status, team_id, assignee_user_id, role.
Gestor; cria assignment, tarefa e notificação.
POST /admin/manifestations/{id}/transition
Pedido: expected_status, target_status, reason_code, public_message, internal_notes.
RBAC e If-Match; valida máquina de estados; auditoria.
POST /admin/manifestations/{id}/information-requests
Pedido: items[], due_at, public_message.
Analista; muda para information_requested e notifica.
POST /admin/manifestations/{id}/convert/project
Pedido: existing_project_id opcional, mapping_overrides. 
Resposta: project_id e links.
Idempotente; só após avaliação favorável.
POST /admin/manifestations/{id}/convert/opportunity
Pedido: publication_scope, public_snapshot, deadline.
Consentimento e aprovação obrigatórios; não publica automaticamente.
18.4 Fluxo de submissão e acompanhamento
Passo/estado
Actor e acção
Sistema, saída e controlo
1. draft
Promotor cria ou converte uma simulação, preenche e guarda.
Autosave, ETag, percentagem de completude, validação incremental e detecção de duplicados.
2. validation_pending
Promotor solicita validação integral.
Verifica perfil, reconciliações, documentos, consentimentos, malware e campos públicos; devolve bloqueios e avisos.
3. submitted
Promotor confirma declarações e submete.
Transacção gera referência, versão imutável, checksum, histórico e evento outbox; envia recibo.
4. under_screening
Coordenador recebe fila e atribui equipa/analista.
Cria caso CRM e tarefa; regista responsável e data-alvo. Prazo operacional configurável.
5. information_requested
Analista pede esclarecimentos/documentos com itens e prazo.
Notifica o promotor; mostra lista accionável; prazo e lembretes; resposta cria nova versão.
6. under_screening retomado
Promotor responde; analista confirma suficiência.
Fecha pedido ou abre complemento; todas as interacções entram na timeline.
7. under_assessment
Equipa técnica avalia enquadramento, impacto, fiscalidade, localização, riscos e elegibilidade.
Checklists por domínio, pareceres e tarefas; resultados internos separados da comunicação pública.
8. approved_for_facilitation
Responsável aprova acompanhamento/facilitação.
Comunica decisão e próximos passos; cria plano de facilitação, marcos e gestor de conta.
9A. converted_to_project
Gestor cria ou associa projecto formal.
Mapeamento idempotente, referência cruzada e preservação da origem.
9B. converted_to_opportunity
Gestor cria oportunidade de negócio.
Gera versão pública; publicação exige consentimento e aprovação editorial.
10. rejected/withdrawn
Entidade decide ou promotor retira.
Motivo público estruturado, recurso/reabertura conforme política e fecho de tarefas.
11. archived
Sistema/gestor encerra após retenção ou conclusão.
Aplica política de retenção; mantém auditoria, referências e provas legalmente necessárias.
18.4.1 Painel do investidor
Cabeçalho:
 referência, título, tipo, estado, data de submissão, responsável institucional e última actualização.
Próxima acção:
 mensagem única e accionável, prazo, botão correspondente e documentos necessários.
Progresso:
 linha temporal de estados com datas; não mostrar notas internas nem classificações reservadas.
Mensagens:
 canal seguro ligado ao processo, com anexos e histórico; correio electrónico apenas notifica e remete ao portal.
Documentos:
 estado recebido, em verificação, válido, rejeitado ou expirado, com motivo e opção de substituir.
Resultado fiscal:
 ligação à simulação exacta, data, versão das regras, resumo e aviso de natureza indicativa.
Ligações:
 projecto, oportunidade, reuniões, tarefas e decisões associadas.
Evento
Destinatário
Notificação e escalonamento
Rascunho incompleto
Promotor
Lembrete opcional; nunca enviar conteúdo sensível por correio.
Submissão recebida
Promotor e fila de triagem
Recibo imediato com referência e próximos passos.
Atribuição
Analista
Tarefa com prazo; alerta ao coordenador se não aceite no período configurado.
Pedido de informação
Promotor
Mensagem no portal e canais autorizados; lembretes antes do vencimento.
Prazo interno em risco
Analista e coordenador
Alerta progressivo; escalonamento ao gestor após ultrapassagem.
Mudança de estado
Promotor
Mensagem pública clara, data, fundamento e próxima acção.
Documento a expirar
Promotor e analista
Aviso antecipado configurável.
Conversão concluída
Promotor, gestor do projecto/oportunidade
Nova referência e ligação directa ao registo criado.
18.5 Critérios de aceitação
19. SQL das manifestações e acompanhamento
O DDL seguinte complementa a Secção 11. Assume que as tabelas 
simulations
 e 
jurisdictions
, a extensão 
pgcrypto
, o PostGIS e a função 
set_updated_at()
 já existem. Identificadores de utilizador, equipas, perfis, projectos e CRM são referências externas em UUID.
CREATE TYPE manifestation_status AS ENUM ('draft','validation_pending','submitted','under_screening','information_requested','under_assessment','approved_for_facilitation','converted_to_project','converted_to_opportunity','rejected','withdrawn','archived');
CREATE TYPE manifestation_type AS ENUM ('investment','business');
CREATE TYPE confidentiality_level AS ENUM ('public','registered_users','restricted','confidential');
CREATE TYPE verification_status AS ENUM ('pending','scanning','valid','rejected','expired');
CREATE TYPE task_status AS ENUM ('open','in_progress','blocked','completed','cancelled');
CREATE TYPE request_status AS ENUM ('open','answered','accepted','superseded','cancelled');
CREATE TABLE investment_manifestations (
 id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
 reference_no varchar(40) UNIQUE,
 owner_user_id uuid NOT NULL,
 investor_profile_id uuid,
 simulation_id uuid REFERENCES simulations(id),
 purpose varchar(40) NOT NULL CHECK(purpose IN('own_investment','seek_investor','seek_partner','present_opportunity','request_facilitation')),
 manifestation_type manifestation_type NOT NULL,
 entry_source varchar(20) NOT NULL CHECK(entry_source IN('simulator','project','opportunity','direct')),
 source_id uuid,
 title varchar(200) NOT NULL,
 summary text,
 status manifestation_status NOT NULL DEFAULT 'draft',
 confidentiality confidentiality_level NOT NULL DEFAULT 'restricted',
 preferred_language varchar(10) NOT NULL DEFAULT 'pt-MZ',
 current_version integer NOT NULL DEFAULT 0 CHECK(current_version>=0),
 lock_version bigint NOT NULL DEFAULT 1,
 assigned_team_id uuid,
 submitted_at timestamptz,
 deleted_at timestamptz,
 deleted_by uuid,
 deletion_reason text,
 created_at timestamptz NOT NULL DEFAULT now(),
 updated_at timestamptz NOT NULL DEFAULT now(),
 CHECK((entry_source='direct' AND source_id IS NULL) OR (entry_source<>'direct' AND source_id IS NOT NULL)),
 CHECK((entry_source<>'simulator') OR simulation_id=source_id)
);
CREATE INDEX manifestations_owner_idx ON investment_manifestations(owner_user_id,created_at DESC) WHERE deleted_at IS NULL;
CREATE INDEX manifestations_queue_idx ON investment_manifestations(status,assigned_team_id,updated_at) WHERE deleted_at IS NULL;
CREATE UNIQUE INDEX manifestation_source_idempotent ON investment_manifestations(owner_user_id,entry_source,source_id,manifestation_type,purpose) WHERE source_id IS NOT NULL AND deleted_at IS NULL;
CREATE TABLE manifestation_versions (
 id uuid PRIMARY KEY DEFAULT gen_random_uuid(), manifestation_id uuid NOT NULL REFERENCES investment_manifestations(id) ON DELETE CASCADE,
 version_no integer NOT NULL CHECK(version_no>0), data_snapshot jsonb NOT NULL, public_snapshot jsonb NOT NULL DEFAULT '{}'::jsonb,
 checksum char(64) NOT NULL, created_by uuid NOT NULL, submitted boolean NOT NULL DEFAULT false, created_at timestamptz NOT NULL DEFAULT now(),
 UNIQUE(manifestation_id,version_no), UNIQUE(manifestation_id,checksum)
);
CREATE TABLE manifestation_contacts (
 id uuid PRIMARY KEY DEFAULT gen_random_uuid(), manifestation_id uuid NOT NULL REFERENCES investment_manifestations(id) ON DELETE CASCADE,
 contact_type varchar(30) NOT NULL, name text NOT NULL, email text, phone text, address text, country_code char(2),
 jurisdiction_id uuid REFERENCES jurisdictions(id), preferred_channel varchar(20) CHECK(preferred_channel IN('portal','email','phone')), is_primary boolean NOT NULL DEFAULT false, verified_at timestamptz
);
CREATE UNIQUE INDEX one_primary_contact ON manifestation_contacts(manifestation_id) WHERE is_primary;
CREATE TABLE manifestation_locations (
 id uuid PRIMARY KEY DEFAULT gen_random_uuid(), manifestation_id uuid NOT NULL REFERENCES investment_manifestations(id) ON DELETE CASCADE,
 jurisdiction_id uuid NOT NULL REFERENCES jurisdictions(id), geometry geometry(Point,4326), site_known boolean NOT NULL DEFAULT false,
 land_status varchar(20) CHECK(land_status IN('owned','duat','leased','requested','unknown')), area_required numeric(18,4) CHECK(area_required>=0), area_unit varchar(10), special_zone_id uuid REFERENCES jurisdictions(id), infrastructure_needs jsonb NOT NULL DEFAULT '[]'::jsonb
);
CREATE INDEX manifestation_locations_gix ON manifestation_locations USING gist(geometry);
CREATE TABLE manifestation_financials (
 id uuid PRIMARY KEY DEFAULT gen_random_uuid(), manifestation_id uuid NOT NULL REFERENCES investment_manifestations(id) ON DELETE CASCADE,
 version_no integer NOT NULL, currency char(3) NOT NULL, total_investment numeric(24,2) NOT NULL CHECK(total_investment>=0),
 own_capital numeric(24,2) NOT NULL DEFAULT 0 CHECK(own_capital>=0), domestic_debt numeric(24,2) NOT NULL DEFAULT 0 CHECK(domestic_debt>=0),
 foreign_debt numeric(24,2) NOT NULL DEFAULT 0 CHECK(foreign_debt>=0), grants numeric(24,2) NOT NULL DEFAULT 0 CHECK(grants>=0),
 secured_amount numeric(24,2) NOT NULL DEFAULT 0 CHECK(secured_amount>=0), sought_amount numeric(24,2) NOT NULL DEFAULT 0 CHECK(sought_amount>=0),
 annual_revenue numeric(24,2), annual_costs numeric(24,2), expected_profit numeric(24,2), created_at timestamptz NOT NULL DEFAULT now(),
 UNIQUE(manifestation_id,version_no), CHECK(secured_amount<=total_investment), CHECK(sought_amount<=total_investment)
);
CREATE TABLE manifestation_impacts (
 id uuid PRIMARY KEY DEFAULT gen_random_uuid(), manifestation_id uuid NOT NULL REFERENCES investment_manifestations(id) ON DELETE CASCADE,
 direct_jobs integer NOT NULL DEFAULT 0 CHECK(direct_jobs>=0), indirect_jobs integer NOT NULL DEFAULT 0 CHECK(indirect_jobs>=0),
 national_jobs integer NOT NULL DEFAULT 0 CHECK(national_jobs>=0), foreign_jobs integer NOT NULL DEFAULT 0 CHECK(foreign_jobs>=0),
 exports_value numeric(24,2), import_substitution_value numeric(24,2), local_content_percentage numeric(5,2) CHECK(local_content_percentage BETWEEN 0 AND 100),
 skills_json jsonb NOT NULL DEFAULT '[]'::jsonb, environmental_category varchar(40), impact_json jsonb NOT NULL DEFAULT '{}'::jsonb,
 CHECK(national_jobs+foreign_jobs<=direct_jobs)
);
CREATE TABLE business_opportunities (
 id uuid PRIMARY KEY DEFAULT gen_random_uuid(), manifestation_id uuid NOT NULL REFERENCES investment_manifestations(id), created_from_version_id uuid NOT NULL REFERENCES manifestation_versions(id),
 opportunity_type varchar(30) NOT NULL, offer_or_demand varchar(10) NOT NULL CHECK(offer_or_demand IN('offer','demand','both')), title varchar(200) NOT NULL,
 description text NOT NULL, value numeric(24,2), currency char(3), deadline date, confidentiality confidentiality_level NOT NULL DEFAULT 'restricted',
 publication_status publication_status NOT NULL DEFAULT 'draft', public_snapshot jsonb NOT NULL DEFAULT '{}'::jsonb, published_at timestamptz,
 CHECK((value IS NULL AND currency IS NULL) OR (value IS NOT NULL AND currency IS NOT NULL))
);
CREATE TABLE manifestation_documents (
 id uuid PRIMARY KEY DEFAULT gen_random_uuid(), manifestation_id uuid NOT NULL REFERENCES investment_manifestations(id) ON DELETE CASCADE,
 manifestation_version_id uuid REFERENCES manifestation_versions(id), document_type varchar(50) NOT NULL, object_key text NOT NULL UNIQUE,
 file_hash char(64) NOT NULL, mime_type varchar(100) NOT NULL, size_bytes bigint NOT NULL CHECK(size_bytes>0), valid_from date, valid_to date,
 confidentiality confidentiality_level NOT NULL DEFAULT 'confidential', verification_status verification_status NOT NULL DEFAULT 'pending',
 verified_by uuid, verified_at timestamptz, rejection_reason text, created_at timestamptz NOT NULL DEFAULT now(), CHECK(valid_to IS NULL OR valid_from IS NULL OR valid_to>=valid_from)
);
CREATE TABLE manifestation_consents (
 id uuid PRIMARY KEY DEFAULT gen_random_uuid(), manifestation_id uuid NOT NULL REFERENCES investment_manifestations(id) ON DELETE CASCADE,
 consent_type varchar(40) NOT NULL, terms_version varchar(40) NOT NULL, granted boolean NOT NULL, granted_at timestamptz NOT NULL,
 revoked_at timestamptz, evidence_hash char(64) NOT NULL, ip_hash char(64), UNIQUE(manifestation_id,consent_type,terms_version),
 CHECK(revoked_at IS NULL OR revoked_at>=granted_at)
);
CREATE TABLE manifestation_assignments (
 id uuid PRIMARY KEY DEFAULT gen_random_uuid(), manifestation_id uuid NOT NULL REFERENCES investment_manifestations(id) ON DELETE CASCADE,
 assignee_user_id uuid, team_id uuid, role varchar(30) NOT NULL, assigned_by uuid NOT NULL, assigned_at timestamptz NOT NULL DEFAULT now(), unassigned_at timestamptz,
 CHECK(assignee_user_id IS NOT NULL OR team_id IS NOT NULL)
);
CREATE UNIQUE INDEX one_active_primary_assignment ON manifestation_assignments(manifestation_id) WHERE role='primary' AND unassigned_at IS NULL;
CREATE TABLE manifestation_requests (
 id uuid PRIMARY KEY DEFAULT gen_random_uuid(), manifestation_id uuid NOT NULL REFERENCES investment_manifestations(id) ON DELETE CASCADE,
 request_type varchar(30) NOT NULL, items jsonb NOT NULL, public_message text NOT NULL, due_at timestamptz, status request_status NOT NULL DEFAULT 'open',
 opened_by uuid NOT NULL, response_version_id uuid REFERENCES manifestation_versions(id), opened_at timestamptz NOT NULL DEFAULT now(), closed_at timestamptz
);
CREATE TABLE manifestation_tasks (
 id uuid PRIMARY KEY DEFAULT gen_random_uuid(), manifestation_id uuid NOT NULL REFERENCES investment_manifestations(id) ON DELETE CASCADE,
 assignment_id uuid REFERENCES manifestation_assignments(id), task_type varchar(30) NOT NULL, title text NOT NULL, due_at timestamptz,
 priority varchar(10) NOT NULL DEFAULT 'normal' CHECK(priority IN('low','normal','high','critical')), status task_status NOT NULL DEFAULT 'open', completed_at timestamptz, crm_external_id text, created_at timestamptz NOT NULL DEFAULT now()
);
CREATE TABLE manifestation_status_history (
 id bigserial PRIMARY KEY, manifestation_id uuid NOT NULL REFERENCES investment_manifestations(id) ON DELETE CASCADE,
 version_id uuid REFERENCES manifestation_versions(id), from_status manifestation_status, to_status manifestation_status NOT NULL,
 actor_id uuid NOT NULL, reason_code varchar(40), public_message text, internal_notes text, occurred_at timestamptz NOT NULL DEFAULT now()
);
CREATE INDEX manifestation_timeline_idx ON manifestation_status_history(manifestation_id,occurred_at,id);
CREATE TABLE manifestation_links (
 manifestation_id uuid PRIMARY KEY REFERENCES investment_manifestations(id) ON DELETE CASCADE,
 investment_project_id uuid, business_opportunity_id uuid REFERENCES business_opportunities(id), crm_case_id text,
 project_synced_at timestamptz, opportunity_synced_at timestamptz, crm_synced_at timestamptz
);
CREATE TABLE integration_outbox (
 id uuid PRIMARY KEY DEFAULT gen_random_uuid(), aggregate_type varchar(40) NOT NULL, aggregate_id uuid NOT NULL, event_type varchar(60) NOT NULL,
 payload jsonb NOT NULL, status varchar(20) NOT NULL DEFAULT 'pending' CHECK(status IN('pending','processing','published','failed','dead_letter')),
 attempts integer NOT NULL DEFAULT 0, available_at timestamptz NOT NULL DEFAULT now(), published_at timestamptz, idempotency_key varchar(120) NOT NULL UNIQUE, created_at timestamptz NOT NULL DEFAULT now()
);
CREATE INDEX outbox_dispatch_idx ON integration_outbox(status,available_at) WHERE status IN('pending','failed');
CREATE TABLE manifestation_allowed_transitions (
 from_status manifestation_status NOT NULL, to_status manifestation_status NOT NULL, required_role varchar(40) NOT NULL, PRIMARY KEY(from_status,to_status)
);
INSERT INTO manifestation_allowed_transitions VALUES
('draft','validation_pending','owner'),('validation_pending','draft','owner'),('validation_pending','submitted','owner'),
('submitted','under_screening','coordinator'),('under_screening','information_requested','analyst'),('information_requested','under_screening','owner'),
('under_screening','under_assessment','analyst'),('under_assessment','information_requested','analyst'),('under_assessment','approved_for_facilitation','approver'),
('under_assessment','rejected','approver'),('approved_for_facilitation','converted_to_project','manager'),('approved_for_facilitation','converted_to_opportunity','manager'),
('draft','withdrawn','owner'),('submitted','withdrawn','owner'),('information_requested','withdrawn','owner'),('rejected','archived','manager'),
('withdrawn','archived','manager'),('converted_to_project','archived','manager'),('converted_to_opportunity','archived','manager');
CREATE OR REPLACE FUNCTION manifestation_touch() RETURNS trigger LANGUAGE plpgsql AS $$ BEGIN NEW.updated_at=now(); NEW.lock_version=OLD.lock_version+1; RETURN NEW; END $$;
CREATE TRIGGER manifestation_updated BEFORE UPDATE ON investment_manifestations FOR EACH ROW EXECUTE FUNCTION manifestation_touch();
CREATE OR REPLACE FUNCTION submitted_version_immutable() RETURNS trigger LANGUAGE plpgsql AS $$ BEGIN IF OLD.submitted THEN RAISE EXCEPTION 'submitted manifestation version is immutable'; END IF; RETURN NEW; END $$;
CREATE TRIGGER manifestation_version_immutable BEFORE UPDATE OR DELETE ON manifestation_versions FOR EACH ROW EXECUTE FUNCTION submitted_version_immutable();
CREATE VIEW manifestation_dashboard AS SELECT m.id,m.reference_no,m.owner_user_id,m.title,m.manifestation_type,m.status,m.lock_version,m.submitted_at,m.assigned_team_id,m.updated_at,(SELECT min(t.due_at) FROM manifestation_tasks t 
WHERE t.manifestation_id=m.id AND t.status IN('open','in_progress','blocked')) AS next_due_at FROM investment_manifestations m WHERE m.deleted_at IS NULL;
19.1 Regras SQL obrigatórias
A submissão valida o ETag, cria a versão imutável, atribui referência, grava consentimentos e histórico, e insere eventos de CRM/notificação na outbox na mesma transacção.
A reconciliação financeira exige total_investment = own_capital + domestic_debt + foreign_debt + grants, salvo tolerância configurada e justificação aprovada.
A manifestação só pode atingir submitted se todos os documentos obrigatórios estiverem em valid, os consentimentos necessários estiverem activos e não existirem erros bloqueadores.
A máquina de estados valida a transição e o papel; actualização directa de status fora da função de domínio é proibida.
RLS autoriza proprietário, equipa activa e perfis de supervisão; public_snapshot é a única fonte para publicação.
Operações de conversão usam chave idempotente por manifestation_id e destino; links são criados apenas após confirmação externa.
20. Schemas OpenAPI completos para manifestações
Esta extensão integra-se no único bloco 
components
 e 
paths
 da Secção 12. Todos os valores monetários são strings decimais, todos os recursos mutáveis usam ETag/If-Match e todas as criações/submissões/conversões usam Idempotency-Key.
components:
  schemas:
    ManifestationStatus:
      type: string
      enum: [draft,validation_pending,submitted,under_screening,information_requested,under_assessment,approved_for_facilitation,converted_to_project,converted_to_opportunity,rejected,withdrawn,archived]
    Money:
      type: object
      required: [amount,currency]
      properties:
        amount: {type: string, pattern: '^[0-9]+(\.[0-9]{1,2})?$'}
        currency: {type: string, pattern: '^[A-Z]{3}$'}
    ManifestationCreate:
      type: object
      additionalProperties: false
      required: [purpose,manifestation_type,title,preferred_language]
      properties:
        purpose: {type: string, enum: [own_investment,seek_investor,seek_partner,present_opportunity,request_facilitation]}
        manifestation_type: {type: string, enum: [investment,business]}
        investor_profile_id: {type: [string,'null'], format: uuid}
        simulation_id: {type: [string,'null'], format: uuid}
        title: {type: string, minLength: 5, maxLength: 200}
        summary: {type: [string,'null'], maxLength: 3000}
        preferred_language: {type: string, enum: [pt-MZ,en]}
        initial_data: {type: object, additionalProperties: true}
    ManifestationPatch:
      type: object
      minProperties: 1
      additionalProperties: false
      properties:
        title: {type: string, minLength: 5, maxLength: 200}
        summary: {type: string, maxLength: 3000}
        confidentiality: {type: string, enum: [public,registered_users,restricted,confidential]}
        form_data: {type: object, additionalProperties: true}
    Manifestation:
      type: object
      required: [id,title,manifestation_type,purpose,status,lock_version,current_version,created_at,updated_at]
      properties:
        id: {type: string, format: uuid}
        reference_no: {type: [string,'null']}
        title: {type: string}
        manifestation_type: {type: string, enum: [investment,business]}
        purpose: {type: string}
        status: {$ref: '#/components/schemas/ManifestationStatus'}
        lock_version: {type: integer, minimum: 1}
        current_version: {type: integer, minimum: 0}
        completeness: {type: integer, minimum: 0, maximum: 100}
        next_action: {type: [object,'null']}
        form_data: {type: object}
        validation: {$ref: '#/components/schemas/ManifestationValidation'}
        created_at: {type: string, format: date-time}
        updated_at: {type: string, format: date-time}
    ValidationIssue:
      type: object
      required: [path,code,message,severity]
      properties:
        path: {type: string}
        code: {type: string}
        message: {type: string}
        severity: {type: string, enum: [blocking,warning,info]}
    ManifestationValidation:
      type: object
      required: [valid,blocking_errors,warnings,missing_documents,reconciliation]
      properties:
        valid: {type: boolean}
        blocking_errors: {type: array, items: {$ref: '#/components/schemas/ValidationIssue'}}
        warnings: {type: array, items: {$ref: '#/components/schemas/ValidationIssue'}}
        missing_documents: {type: array, items: {type: string}}
        reconciliation: {type: array, items: {type: object}}
        public_preview: {type: object}
    DocumentUploadRequest:
      type: object
      required: [document_type,filename,mime_type,size_bytes,confidentiality]
      properties:
        document_type: {type: string}
        filename: {type: string, maxLength: 255}
        mime_type: {type: string, enum: [application/pdf,image/png,image/jpeg]}
        size_bytes: {type: integer, minimum: 1, maximum: 26214400}
        confidentiality: {type: string, enum: [restricted,confidential]}
    Document:
      type: object
      required: [id,document_type,filename,verification_status,confidentiality]
      properties:
        id: {type: string, format: uuid}
        document_type: {type: string}
        filename: {type: string}
        verification_status: {type: string, enum: [pending,scanning,valid,rejected,expired]}
        confidentiality: {type: string}
        valid_to: {type: [string,'null'], format: date}
        rejection_reason: {type: [string,'null']}
    TimelineEvent:
      type: object
      required: [id,to_status,occurred_at,actor_display]
      properties:
        id: {type: integer}
        from_status: {oneOf: [{$ref: '#/components/schemas/ManifestationStatus'},{type: 'null'}]}
        to_status: {$ref: '#/components/schemas/ManifestationStatus'}
        actor_display: {type: string}
        reason_code: {type: [string,'null']}
        public_message: {type: [string,'null']}
        occurred_at: {type: string, format: date-time}
    InformationRequest:
      type: object
      required: [id,items,status,opened_at]
      properties:
        id: {type: string, format: uuid}
        items: {type: array, items: {type: object}}
        public_message: {type: string}
        due_at: {type: [string,'null'], format: date-time}
        status: {type: string, enum: [open,answered,accepted,superseded,cancelled]}
        opened_at: {type: string, format: date-time}
    SubmitManifestationRequest:
      type: object
      additionalProperties: false
      required: [expected_version,consent_ids,declarations]
      properties:
        expected_version: {type: integer, minimum: 0}
        consent_ids: {type: array, minItems: 2, uniqueItems: true, items: {type: string, format: uuid}}
        declarations:
          type: object
          required: [truth_confirmed,final_declaration_name]
          properties:
            truth_confirmed: {const: true}
            final_declaration_name: {type: string, minLength: 2, maxLength: 200}
    AssignmentRequest:
      type: object
      required: [expected_status,role]
      properties:
        expected_status: {$ref: '#/components/schemas/ManifestationStatus'}
        team_id: {type: [string,'null'], format: uuid}
        assignee_user_id: {type: [string,'null'], format: uuid}
        role: {type: string, enum: [primary,reviewer,observer]}
      anyOf: [{required: [team_id]},{required: [assignee_user_id]}]
    TransitionRequest:
      type: object
      additionalProperties: false
      required: [expected_status,target_status,reason_code]
      properties:
        expected_status: {$ref: '#/components/schemas/ManifestationStatus'}
        target_status: {$ref: '#/components/schemas/ManifestationStatus'}
        reason_code: {type: string}
        public_message: {type: [string,'null'], maxLength: 2000}
        internal_notes: {type: [string,'null'], maxLength: 5000}
    InformationRequestCreate:
      type: object
      required: [items,public_message]
      properties:
        items: {type: array, minItems: 1, items: {type: object, required: [code,label], properties: {code: {type: string}, label: {type: string}, required_document_type: {type: [string,'null']}}}}
        due_at: {type: [string,'null'], format: date-time}
        public_message: {type: string, minLength: 10, maxLength: 2000}
    ConvertProjectRequest:
      type: object
      properties:
        existing_project_id: {type: [string,'null'], format: uuid}
        mapping_overrides: {type: object}
    ConvertOpportunityRequest:
      type: object
      required: [publication_scope,public_snapshot]
      properties:
        publication_scope: {type: string, enum: [registered_users,public]}
        public_snapshot: {type: object}
        deadline: {type: [string,'null'], format: date}
paths:
  /simulations/{id}/manifestations:
    post:
      operationId: createManifestationFromSimulation
      security: [{bearerAuth: []}]
      parameters: [{$ref: '#/components/parameters/Id'},{$ref: '#/components/parameters/IdempotencyKey'},{$ref: '#/components/parameters/CorrelationId'}]
      requestBody: {required: true, content: {application/json: {schema: {$ref: '#/components/schemas/ManifestationCreate'}}}}
      responses: {'201': {description: Manifestação criada; inclui ETag}, '409': {$ref: '#/components/responses/Conflict'}, '422': {$ref: '#/components/responses/Unprocessable'}}
  /manifestations:
    post:
      operationId: createManifestation
      security: [{bearerAuth: []}]
      parameters: [{$ref: '#/components/parameters/IdempotencyKey'}]
      requestBody: {required: true, content: {application/json: {schema: {$ref: '#/components/schemas/ManifestationCreate'}}}}
      responses: {'201': {description: Rascunho criado}, '409': {$ref: '#/components/responses/Conflict'}, '422': {$ref: '#/components/responses/Unprocessable'}}
  /manifestations/{id}:
    get:
      operationId: getManifestation
      security: [{bearerAuth: []}]
      parameters: [{$ref: '#/components/parameters/Id'}]
      responses: {'200': {description: Manifestação, content: {application/json: {schema: {$ref: '#/components/schemas/Manifestation'}}}}, '403': {$ref: '#/components/responses/Forbidden'}, '404': {$ref: '#/components/responses/NotFound'}}
    patch:
      operationId: patchManifestation
      security: [{bearerAuth: []}]
      parameters: [{$ref: '#/components/parameters/Id'},{$ref: '#/components/parameters/IfMatch'}]
      requestBody: {required: true, content: {application/merge-patch+json: {schema: {$ref: '#/components/schemas/ManifestationPatch'}}}}
      responses: {'200': {description: Rascunho actualizado; inclui novo ETag}, '409': {$ref: '#/components/responses/Conflict'}, '412': {$ref: '#/components/responses/PreconditionFailed'}, '422': {$ref: '#/components/responses/Unprocessable'}}
  /manifestations/{id}/validate:
    post:
      operationId: validateManifestation
      security: [{bearerAuth: []}]
      parameters: [{$ref: '#/components/parameters/Id'}]
      responses: {'200': {description: Validação, content: {application/json: {schema: {$ref: '#/components/schemas/ManifestationValidation'}}}}, '422': {$ref: '#/components/responses/Unprocessable'}}
  /manifestations/{id}/submit:
    post:
      operationId: submitManifestation
      security: [{bearerAuth: []}]
      parameters: [{$ref: '#/components/parameters/Id'},{$ref: 
'#/components/parameters/IfMatch'},{$ref: '#/components/parameters/IdempotencyKey'}]
      requestBody: {required: true, content: {application/json: {schema: {$ref: '#/components/schemas/SubmitManifestationRequest'}}}}
      responses: {'201': {description: Submetida com referência e versão}, '409': {$ref: '#/components/responses/Conflict'}, '412': {$ref: '#/components/responses/PreconditionFailed'}, '422': {$ref: '#/components/responses/Unprocessable'}}
  /manifestations/{id}/documents/uploads:
    post:
      operationId: createManifestationDocumentUpload
      security: [{bearerAuth: []}]
      parameters: [{$ref: '#/components/parameters/Id'},{$ref: '#/components/parameters/IdempotencyKey'}]
      requestBody: {required: true, content: {application/json: {schema: {$ref: '#/components/schemas/DocumentUploadRequest'}}}}
      responses: {'201': {description: Upload URL temporária criada}, '413': {description: Ficheiro excede limite}, '422': {$ref: '#/components/responses/Unprocessable'}}
  /manifestations/{id}/documents/{upload_id}/complete:
    post:
      operationId: completeManifestationDocumentUpload
      security: [{bearerAuth: []}]
      parameters: [{$ref: '#/components/parameters/Id'},{name: upload_id,in: path,required: true,schema: {type: string,format: uuid}}]
      responses: {'202': {description: Verificação antivírus iniciada}, '409': {$ref: '#/components/responses/Conflict'}}
  /manifestations/{id}/timeline:
    get:
      operationId: getManifestationTimeline
      security: [{bearerAuth: []}]
      parameters: [{$ref: '#/components/parameters/Id'}]
      responses: {'200': {description: Timeline pública, content: {application/json: {schema: {type: array,items: {$ref: '#/components/schemas/TimelineEvent'}}}}}}
  /manifestations/{id}/requests/{request_id}/respond:
    post:
      operationId: respondToInformationRequest
      security: [{bearerAuth: []}]
      parameters: [{$ref: '#/components/parameters/Id'},{name: request_id,in: path,required: true,schema: {type: string,format: uuid}},{$ref: '#/components/parameters/IdempotencyKey'}]
      responses: {'201': {description: Nova versão criada e pedido respondido}, '409': {$ref: '#/components/responses/Conflict'}, '422': {$ref: '#/components/responses/Unprocessable'}}
  /me/manifestations:
    get:
      operationId: listMyManifestations
      security: [{bearerAuth: []}]
      responses: {'200': {description: Lista paginada com next_action}}
  /admin/manifestations/{id}/assign:
    post:
      operationId: assignManifestation
      security: [{oauth2Client: [manifestations.manage]}]
      parameters: [{$ref: '#/components/parameters/Id'},{$ref: '#/components/parameters/IdempotencyKey'}]
      requestBody: {required: true, content: {application/json: {schema: {$ref: '#/components/schemas/AssignmentRequest'}}}}
      responses: {'200': {description: Atribuição criada}, '409': {$ref: '#/components/responses/Conflict'}}
  /admin/manifestations/{id}/transition:
    post:
      operationId: transitionManifestation
      security: [{oauth2Client: [manifestations.manage]}]
      parameters: [{$ref: '#/components/parameters/Id'},{$ref: '#/components/parameters/IfMatch'}]
      requestBody: {required: true, content: {application/json: {schema: {$ref: '#/components/schemas/TransitionRequest'}}}}
      responses: {'200': {description: Estado alterado}, '403': {$ref: '#/components/responses/Forbidden'}, '409': {$ref: '#/components/responses/Conflict'}, '412': {$ref: '#/components/responses/PreconditionFailed'}}
  /admin/manifestations/{id}/information-requests:
    post:
      operationId: createInformationRequest
      security: [{oauth2Client: [manifestations.manage]}]
      parameters: [{$ref: '#/components/parameters/Id'},{$ref: '#/components/parameters/IdempotencyKey'}]
      requestBody: {required: true, content: {application/json: {schema: {$ref: '#/components/schemas/InformationRequestCreate'}}}}
      responses: {'201': {description: Pedido criado e promotor notificado}, '409': {$ref: '#/components/responses/Conflict'}}
  /admin/manifestations/{id}/convert/project:
    post:
      operationId: convertManifestationToProject
      security: [{oauth2Client: [manifestations.manage]}]
      parameters: [{$ref: '#/components/parameters/Id'},{$ref: '#/components/parameters/IdempotencyKey'}]
      requestBody: {required: true, content: {application/json: {schema: {$ref: '#/components/schemas/ConvertProjectRequest'}}}}
      responses: {'200': {description: Projecto ligado ou criado}, '409': {$ref: '#/components/responses/Conflict'}, '422': {$ref: '#/components/responses/Unprocessable'}}
  /admin/manifestations/{id}/convert/opportunity:
    post:
      operationId: convertManifestationToOpportunity
      security: [{oauth2Client: [manifestations.manage]}]
      parameters: [{$ref: '#/components/parameters/Id'},{$ref: '#/components/parameters/IdempotencyKey'}]
      requestBody: {required: true, content: {application/json: {schema: {$ref: '#/components/schemas/ConvertOpportunityRequest'}}}}
      responses: {'200': {description: Oportunidade criada sem publicação automática}, '409': {$ref: '#/components/responses/Conflict'}, '422': {$ref: '#/components/responses/Unprocessable'}}
Integração obrigatória:
 acrescentar ao scope OAuth2 da Secção 12 o valor 
manifestations.manage
. Todos os endpoints devem declarar também 401, 403, 404, 429 e 500 quando aplicável, devolver X-Correlation-Id e filtrar internal_notes das respostas destinadas ao promotor.
21. Critérios de aceitação e testes executáveis do fluxo
ID
Critério de aceitação
Evidência
AC-MAN-01
Uma simulação concluída pode criar um rascunho autenticado, copiando apenas campos mapeados e registando a origem.
Resposta 201, simulation_id, snapshot e mapa copied_fields.
AC-MAN-02
O rascunho pode ser guardado e retomado; cada alteração produz novo ETag.
PATCH 200 e lock_version incrementada.
AC-MAN-03
Uma versão submetida é imutável e reproduzível por checksum.
UPDATE/DELETE rejeitados; hash recalculado coincide.
AC-MAN-04
A submissão só ocorre sem erros bloqueadores, com documentos válidos e consentimentos activos.
Submit 201 ou 422 com paths exactos.
AC-MAN-05
A submissão cria referência, histórico e eventos de integração atomicamente.
Uma transacção; rollback integral em falha.
AC-MAN-06
Acesso obedece a proprietário, equipa e confidencialidade.
RLS e respostas 403/404 sem fuga.
AC-MAN-07
Pedidos de informação possuem itens, prazo, resposta versionada e timeline.
Request aberto, respondido e aceite sem perda de versão.
AC-MAN-08
A máquina de estados impede transições e papéis inválidos.
409/403 e histórico sem alteração.
AC-MAN-09
Conversões são idempotentes e preservam a origem.
Um único projecto/oportunidade e manifestation_links.
AC-MAN-10
A oportunidade publica apenas o public_snapshot aprovado.
Nenhum campo confidencial no payload público.
AC-MAN-11
O painel mostra estado, próxima acção, prazos, documentos e timeline.
Contrato e teste de interface aprovados.
AC-MAN-12
O fluxo é utilizável em móvel, teclado, leitor de ecrã e zoom 200%.
Relatório de acessibilidade sem falhas críticas.
21.1 Casos de teste executáveis
ID
Given / When
Then verificável
Nível
MAN-API-01
Dada simulação completed do utilizador; quando POST /simulations/{id}/manifestations com chave K.
201; draft único; origem e snapshot correctos; repetição com K devolve mesma resposta.
Contrato + integração
MAN-API-02
Dada simulação de outro utilizador; quando converter.
404/403; nenhuma manifestação criada.
Segurança
MAN-ETAG-01
Dado ETag N; quando dois PATCH concorrentes usam N.
Um 200 com ETag N+1; outro 412; dados do vencedor preservados.
Concorrência
MAN-VAL-01
Dado financiamento cuja soma difere do total.
validate devolve bloqueio financial.reconciliation; submit devolve 422.
Unidade + API
MAN-VAL-02
Dado benefit ZEE/ZFI sem certificado válido.
Validação identifica documento em falta; benefício não é confirmado.
Regra de negócio
MAN-DOC-01
Dado PDF permitido dentro do limite; quando iniciar e completar upload.
Upload criado; documento passa pending→scanning→valid após verificação.
Integração
MAN-DOC-02
Dado executável, MIME divergente, hash incorrecto ou malware.
Upload/requisição rejeitado; manifestação não pode ser submetida.
Segurança
MAN-CONS-01
Dado consentimento de reutilização ausente para origem simulator.
Submit 422 com path consents.reuse_simulation.
API
MAN-SUB-01
Dado rascunho válido; quando submit.
reference_no único, versão submitted imutável, status submitted, histórico e dois eventos outbox.
Integração DB
MAN-SUB-02
Dada falha ao inserir evento outbox durante submit.
Rollback: sem referência, versão, histórico ou mudança de estado.
Transacção
MAN-IDEM-01
Dado submit repetido com mesma chave e corpo.
Uma manifestação submetida e mesma resposta.
Integração
MAN-IDEM-02
Dada mesma chave com corpo diferente.
409 IDEMPOTENCY_CONFLICT.
Contrato
MAN-STATE-01
Dada manifestação submitted; quando owner tenta converted_to_project.
403; estado e histórico inalterados.
Autorização
MAN-STATE-02
Dada under_assessment; quando approver aprova.
approved_for_facilitation, histórico criado e notificação emitida.
Fluxo
MAN-REQ-01
Dado under_screening; analista pede dois itens com prazo.
Status information_requested; request open; promotor recebe próxima acção.
Fluxo
MAN-REQ-02
Dado request open; promotor responde com documentos válidos.
Nova versão; request answered; estado under_screening; timeline completa.
Integração
MAN-RLS-01
Dadas manifestações de A e B; A consulta/edita B.
Nenhum registo visível ou alterado; acesso auditado.
Base + segurança
MAN-CONF-01
Dada manifestação confidential com public_snapshot.
Endpoint público devolve exclusivamente public_snapshot.
Privacidade
MAN-CONV-01
Dada approved_for_facilitation; converter projecto duas vezes com mesma chave.
Um projecto; mesmo project_id; link único.
Integração
MAN-CONV-02
Dado pedido de publicação sem consentimento.
Conversão pode criar draft, mas publicação é bloqueada.
Negócio
MAN-OUT-01
Dado CRM indisponível após submit.
Manifestação permanece submetida; outbox faz retry e dead-letter controlado.
Resiliência
MAN-TIME-01
Dada timeline com internal_notes.
Promotor recebe apenas actor_display, public_message e estados.
Contrato + privacidade
MAN-PERF-01
Dadas 100 submissões simultâneas e 10 000 rascunhos por utilizador de teste.
Sem duplicados; p95 de leitura/guardar dentro do SLO definido.
Desempenho
MAN-ACC-01
Dado formulário completo; navegar por teclado/leitor de ecrã em móvel.
Ordem lógica, foco visível, rótulos, erros associados e sem perda no autosave.
Acessibilidade
21.2 Especificações automatizáveis
Feature: Submissão de manifestação
Scenario: submissão válida e idempotente
 Given uma manifestação draft completa com ETag "7"
 And documentos obrigatórios em valid
 And consentimentos activos
 When envio POST /manifestations/{id}/submit com If-Match "7" e Idempotency-Key "submit-00000001"
 Then o estado HTTP é 201
 And status é submitted
 And reference_no não é vazio
 And existe exactamente uma manifestation_versions com submitted=true
 And existe exactamente um evento de CRM e um de notificação na integration_outbox
 When repito o mesmo pedido
 Then recebo a mesma resposta e as contagens não aumentam
Scenario: rollback de submissão
 Given falha injectada na escrita da outbox
 When submeto uma manifestação válida
 Then o estado HTTP é 500 ou 503
 And status continua draft/validation_pending
 And não existe reference_no, versão submitted nem histórico parcial
-- Teste SQL: versão submetida imutável
BEGIN;
INSERT INTO manifestation_versions(manifestation_id,version_no,data_snapshot,checksum,created_by,submitted) VALUES(:id,1,'{}',:hash,:user,true);
UPDATE manifestation_versions SET data_snapshot='{"changed":true}' WHERE manifestation_id=:id AND version_no=1;
-- Esperado: exception submitted manifestation version is immutable
ROLLBACK;
-- Teste SQL: uma atribuição principal activa
INSERT INTO manifestation_assignments(manifestation_id,assignee_user_id,role,assigned_by) VALUES(:id,:analyst1,'primary',:manager);
INSERT INTO manifestation_assignments(manifestation_id,assignee_user_id,role,assigned_by) VALUES(:id,:analyst2,'primary',:manager);
-- Esperado: unique_violation one_active_primary_assignment
21.3 Gate Go/No-Go
Go:
 migrações up/down aprovadas; OpenAPI 3.1 sem erros; 100% dos testes críticos aprovados; zero falhas críticas/altas de segurança; ETag, idempotência, RLS, transacção de submissão e outbox comprovados; acessibilidade aprovada; integrações CRM/projecto/oportunidade testadas com falhas simuladas.
No-Go:
 duplicação de processo; versão submetida editável; exposição de dados confidenciais; transição inválida aceite; submissão parcial; publicação sem consentimento; conversão não idempotente; documento malicioso aceite; timeline com notas internas.
Evidências:
 relatório CI por ID de teste, cobertura, logs sanitizados, relatório OpenAPI, relatório de migrações, análise de segurança, relatório de acessibilidade e acta de aceitação operacional.
Todos os campos possuem chave, tipo, regra de obrigatoriedade e mensagem de erro.
O utilizador pode guardar e retomar sem perda; conflitos de edição devolvem 412 e preservam ambas as versões.
A conversão de simulação copia apenas campos autorizados e indica origem de cada valor.
A soma do financiamento reconcilia; divergências bloqueadoras são resolvidas antes da submissão.
Uma submissão repetida com a mesma chave não cria processo, projecto, oportunidade ou caso CRM duplicado.
O processo submetido possui referência, checksum, versão imutável, consentimentos e recibo.
Pedidos de informação apresentam itens, prazo e estado; a resposta cria nova versão auditável.
O painel mostra estado, próxima acção, prazos, documentos e histórico sem revelar notas internas.
Transições inválidas, auto-aprovação e acesso sem permissão são bloqueados e auditados.
A publicação da oportunidade usa apenas o public_snapshot aprovado.
Tes
tes móveis, teclado, leitor de ecrã, contraste, zoom e mensagens de erro são aprovados antes do Go-Live.
22. Correcções transversais, backlog e artefactos finais
22.1 Regra obrigatória de controlos de selecção
Todo o campo editável pelo utilizador com duas ou mais opções válidas deve ser apresentado como dropdown acessível, pesquisável e operável por teclado. Campos derivados pelo sistema permanecem apenas de leitura. Confirmações verdadeiro/falso usam checkbox apenas quando representam consentimento ou tomada de conhecimento; nos restantes casos usam dropdown de duas opções. Dropdowns dependentes devem limpar valores-filho que se tornem inválidos quando o valor-pai muda. As opções provêm de catálogos versionados, e a API rejeita códigos fora do catálogo ou da versão informada. O texto livre «Outro» só é mostrado quando «Outro» estiver seleccionado. Não deve existir valor predefinido capaz de influenciar resultados fiscais. O placeholder é «Seleccione uma opção». A interface mostra rótulos em português e envia à API códigos estáveis.
Família de campos
Fonte do dropdown
Finalidade / tipo / fonte / idioma
Catálogos de metadados e classificação
Pessoa / origem / residência / entidade / forma jurídica
Catálogos de identidade, residência e formas jurídicas
País / província / distrito / localidade / zona especial
Catálogo geográfico hierárquico versionado
Sector / actividade / tipo de projecto / maturidade
Classificações económicas e catálogo de projectos
Moeda / origem do financiamento / período / cenário
Catálogos financeiros e de cenários
Regime fiscal / tipo de operação / natureza do pagamento / tratado / origem da importação / código pautal / benefício
Catálogos fiscais, aduaneiros, tratados e benefícios
Tipo de oportunidade / confidencialidade / âmbito de publicação
Catálogo de oportunidades e políticas de publicação
Tipo de documento / estado
Catálogo documental e máquina de estados
Estado / motivo / papel / prioridade / canal da manifestação
Catálogos de workflow e atendimento
22.2 Regras para evitar resultados errados ou enganosos
Não aplicar valores predefinidos silenciosos; qualquer assunção deve ser explícita e confirmada.
Revalidar no servidor todos os códigos, relações e regras recebidos da interface.
Persistir a versão do catálogo e a data de vigência usadas em cada cálculo.
Validar consistência pai-filho e rejeitar combinações hierárquicas inválidas.
Para valores monetários, guardar moeda, data de referência e fonte da taxa de câmbio.
Reconciliar fontes e aplicações de financiamento, bem como totais de empregos por categoria.
Aplicar benefícios apenas quando existirem elegibilidade e evidência válidas e vigentes.
Bloquear o cálculo aduaneiro sem código pautal válido.
Marcar a taxa efectiva de imposto como indisponível quando o denominador for zero.
Excluir impostos recuperáveis do custo económico.
Garantir que cálculo e relatório usam o mesmo snapshot imutável de entradas, regras e catálogos.
Apresentar aviso explícito de que o resultado é indicativo e não substitui validação fiscal ou jurídica competente.
22.3 Backlog detalhado por sprint
Sprint
Tarefas
Responsável / Accountable
Papéis de apoio
Evidência / aceitação
Sprint 1
Migrations base; extensões e tipos; núcleo legal; catálogos versionados; seeds; dropdowns e dependências; regras de integridade iniciais.
Data/Backend Lead — responsável; Product Owner — accountable
DBA; Fiscal/Jurídico; QA
migration:fresh e rollback aprovados; catálogos carregados; FKs e checks verificados.
Sprint 2
Regras fiscais e jurídicas; vigências; benefícios e evidências; transições; 
modelo de simulações, empregos e relatórios.
Fiscal/Juridical Lead e Backend — responsáveis; 
Product Owner — accountable
Data; QA; Segurança
Casos fiscais revistos; regras temporais e reconciliações 
testadas; decisões registadas.
Sprint 3
APIs e OpenAPI 3.1; envelopes, erros, ETag e idempotência; UI acessível; dropdowns pesquisáveis; uploads e manifestações.
Frontend/UX e Backend API — responsáveis; Product Owner — accountable
Acessibilidade; Fiscal; QA
Contrato validado; parser/lint sem erros; fluxos por teclado e leitor de ecrã aprovados.
Sprint 4
Motor fiscal; cenários; snapshots; taxa efectiva; impostos recuperáveis; geração de relatórios e rastreabilidade.
Fiscal Engine Backend e Fiscal Specialist — responsáveis; Product Owner — accountable
Data; API; QA
Resultados reconciliados com casos de referência; relatório reproduzível pelo snapshot.
Sprint 5
Motor aduaneiro; pauta e origem; integrações; conversões de manifestações; workflow, documentos, outbox e publicação.
Customs/Integration Backend e Fiscal/Customs Specialist — responsáveis; Product Owner — accountable
Segurança; API; QA
Cálculo bloqueia sem pauta; retries e privacidade comprovados; integrações contratualmente testadas.
Sprint 6
Testes de contrato, SQL e regressão; RLS; concorrência; idempotência; segurança; acessibilidade; deployment, observabilidade e runbooks.
QA/DevOps/Security — accountable; Product Owner — accountable pelo produto
Todas as leads; Operações; Suporte
Pipeline verde; evidências críticas arquivadas; rollback e runbooks exercitados em ambiente controlado.
22.4 Lacunas convertidas em testes executáveis
ID
Severidade
Given
When
Then
Camada de automação
SEL-01
Crítica
Catálogo v3 sem o código X
A API recebe X
Rejeita com erro de validação
Contrato/API
SEL-02
Alta
Filho válido para o pai A
O pai muda para B
O filho é limpo e deve ser novamente seleccionado
UI/E2E
SEL-03
Crítica
Campo fiscal não preenchido
A simulação é calculada
Nenhum default influencia o resultado
Domínio/E2E
CAT-01
Crítica
Pedido referencia catálogo v2
O servidor exige v3
Retorna conflito de versão
Contrato/API
FIN-01
Crítica
Fontes e aplicações divergentes
A submissão ocorre
A operação é bloqueada com diferença explicada
Domínio/SQL
TAX-01
Alta
Denominador igual a zero
A taxa efectiva é pedida
O valor fica indisponível, sem divisão
Unidade
CUS-01
Crítica
Importação sem código pautal
O cálculo aduaneiro inicia
O cálculo é bloqueado
Domínio/API
BEN-01
Crítica
Benefício sem evidência válida
A regra é avaliada
O benefício não é aplicado
Domínio
OAS-01
Crítica
Documento OpenAPI final
O parser 3.1 o carrega
Não existem erros estruturais
Contrato/estática
OAS-02
Alta
Todas as operações
operationId é enumerado
Cada operationId é único
Lint
OAS-03
Crítica
Todos os $ref
O bundle é resolvido
Nenhuma referência está quebrada
Bundle/contrato
SQL-01
Crítica
Base vazia
Migrations up e down são executadas
Schema sobe e reverte sem erro
Integração/SQL
SQL-02
Crítica
Regras com períodos
É inserida sobreposição
A base rejeita a sobreposição
Integração/SQL
SEC-01
Crítica
Utilizadores de tenants distintos
Consultam os mesmos recursos
RLS impede fuga entre tenants
Segurança/SQL
CON-01
Alta
Recurso com ETag actual
Update usa ETag obsoleto
Retorna conflito sem sobrescrever
Contrato/API
IDM-01
Alta
Mesma chave e mesmo body
O pedido é repetido
Retorna o mesmo resultado sem duplicar
Integração/API
IDM-02
Crítica
Mesma chave e body diferente
O pedido é repetido
Retorna conflito
Integração/API
TXN-01
Crítica
Falha a meio da escrita
A transacção aborta
Nenhuma alteração parcial persiste
Integração/SQL
OUT-01
Alta
Entrega externa falha
O worker repete
Retry é controlado e idempotente
Integração/worker
IMM-01
Crítica
Versão submetida
Tentam alterar dados
A base bloqueia a mutação
Integração/SQL
PRV-01
Crítica
Manifestação confidencial
É convertida ou publicada
Dados privados não são expostos
Segurança/E2E
A11Y-01
Alta
Dropdown e formulário
Uso apenas teclado e leitor de ecrã
Foco, nome, estado e pesquisa são anunciados e operáveis
Acessibilidade/E2E
22.5 Validação detalhada do SQL e OpenAPI
Âmbito: validação estática ao nível do documento; não corresponde a execução runtime nem prova de que migrations, especificação ou testes tenham sido executados.
Achados SQL
ID
Correcção requerida
SQL-F01
Adicionar definições em falta de jurisdictions e economic_activities, ou declarar dependência explícita de migration antes das FKs.
SQL-F02
Adicionar FKs para tax_
parameters.jurisdiction
_id e tax_
parameters.activity
_id.
SQL-F03
Evitar falha de CREATE TYPE em reexecução mediante guards nas migrations Laravel.
SQL-F04
Adicionar triggers de updated_at de forma consistente.
SQL-F05
Implementar trigger de imutabilidade para versões publicadas/submetidas.
SQL-F06
Implementar políticas RLS; actualmente estão descritas, mas não definidas.
SQL-F07
Implementar função de transição de estado em vez de update directo do status.
SQL-F08
Adicionar constraint ou função de reconciliação financeira.
SQL-F09
Garantir que o check da fonte da manifestação trata simulation_id e source_id coerentemente.
SQL-F10
Adicionar integridade referencial entre business_
opportunities.created
_from_version_id e a mesma manifestação.
SQL-F11
Validar estado activo do consentimento.
SQL-F12
Qualificar explicitamente a dependência de publication_status.
Achados 
OpenAPI
ID
Correcção requerida
OAS-F01
Fundir as Secções 12 e 20 num único ficheiro físico 
openapi.yaml
.
OAS-F02
Adicionar o scope 
manifestations.manage
 a oauth2Client.
OAS-F03
Definir requestBody e schemas de resposta para todos os endpoints abreviados.
OAS-F04
Definir respostas partilhadas 401/403/404/429/500 em cada operação aplicável.
OAS-F05
Definir separadamente todos os parâmetros de path; não reutilizar Id para upload_id ou request_id.
OAS-F06
Adicionar envelopes de resposta normalizados e headers ETag.
OAS-F07
Alinhar nullable com JSON Schema válido em OpenAPI 3.1.
OAS-F08
Evitar conflito entre ManifestationCreate para conversão de simulação e o id do path.
OAS-F09
Adicionar schemas para listas paginadas, uploads, resposta de submit, assignment e conversões.
OAS-F10
Marcar correctamente alternativas de security.
OAS-F11
Eliminar chaves duplicadas em components e paths.
22.6 Artefactos finais a gerar no repositório
Ordem proposta de migrations:
• 001_extensions_and_types.php
• 002_legal_core.php
• 003_catalogues.php
• 004_tax_rules.php
• 005_simulations_jobs_reports.php
• 006_manifestations_core.php
• 007_manifestations_workflow_documents_outbox.php
• 008_indexes_triggers_rls.php
• 009_seeds_and_transitions.php
• api/openapi.yaml
• tests/contract/
• tests/sql/
Template conciso de migration Laravel corrigida (modelo a gerar; não executado):
public function up(): void\n{\n    DB::transaction(function (): void {\n        DB::statement('CREATE EXTENSION IF NOT EXISTS btree_gist');\n        Schema::create('example', function (Blueprint $table): void {\n            $table->uuid('id')->primary();\n            $table->timestampsTz();\n        });\n        DB::statement('CREATE OR REPLACE FUNCTION enforce_example() RETURNS trigger AS $$ BEGIN RETURN NEW; END; $$ LANGUAGE plpgsql');\n    });\n}\n\npublic function down(): void\n{\n    DB::transaction(function (): void {\n        DB::statement('DROP FUNCTION IF EXISTS enforce_example() CASCADE');\n        Schema::dropIfExists('example');\n    });\n}
Skeleton conciso da raiz OpenAPI final (modelo a gerar; o ficheiro físico não é declarado como existente):
openapi: 3.1.0\ninfo:\n  title: API da Plataforma\n  version: 1.0.0\nservers:\n  - url: https://api.example.invalid\ncomponents:\n  schemas:\n    Error: { $ref: './components/schemas/error.yaml' }\n  responses:\n    Unauthorized: { $ref: './components/responses/unauthorized.yaml' }\n  securitySchemes:\n    oauth2Client: { $ref: './components/security/oauth2-client.yaml' }\npaths:\n  /manifestations:\n    $ref: './paths/manifestations.yaml'\n# É permitida a divisão por referências externas, mantendo uma única raiz física.
22.7 Resultado da validação e gate
A revisão estática encontrou bloqueadores runtime ainda não resolvidos. O estado é NO-GO até que a execução real no repositório aprove: PostgreSQL migration:fresh e rollback; validação, lint e bundle OpenAPI 3.1; testes de contrato; testes de RLS, concorrência e idempotência; e todos os casos críticos. Esta conclusão não afirma que qualquer teste tenha sido executado.
Checklist genérica de comandos para CI:
• [ ] Instalar dependências com versões bloqueadas e verificar integridade.
• [ ] Preparar uma base PostgreSQL efémera e aplicar todas as migrations desde zero.
• [ ] Executar rollback completo e reaplicar as migrations.
• [ ] Carregar seeds e validar constraints, triggers, transições, RLS e imutabilidade.
• [ ] Validar, fazer lint e gerar bundle da especificação OpenAPI 3.1.
• [ ] Executar testes de contrato entre implementação e OpenAPI.
• [ ] Executar testes unitários, de integração, SQL, segurança e regressão.
• [ ] Executar casos de concorrência por ETag, idempotência e rollback transaccional.
• [ ] Executar testes de acessibilidade por teclado e leitor de ecrã.
• [ ] Construir o artefacto, analisar vulnerabilidades e publicar apenas com todos os gates aprovados.
• [ ] Validar deployment e rollback em ambiente controlado e arquivar evidências e runbooks.
23. Sequência de implementação e prompts por etapa
A implementação deve decorrer uma etapa de cada vez: nenhuma etapa avança sem gate verde e aprovação humana. Cada prompt começa por inspeccionar o repositório e adapta nomes, módulos e caminhos ao projecto real. Todas as alterações devem ser não destrutivas, reversíveis e acompanhadas por relatório de alterações e testes; é proibido inventar taxas fiscais, alterar migrações já executadas em produção ou ocultar falhas.
23.1 Ordem obrigatória e dependências
Ordem
Etapa
Depende de
Entregáveis
Gate
0
Auditoria e baseline
—
Inventário técnico; baseline.md; matriz de bloqueadores
Stack, versões, branches, migrações, CI e testes inventariados; riscos classificados; aprovação do Arquitecto/Tech Lead.
1
Contratos e ADRs
0
ADRs; mapa SQL–OpenAPI
ADRs revistos; decisões sem ambiguidade; mapa de campos completo; aprovação de Arquitectura, Segurança, Fiscal e Jurídico.
2
Infraestrutura PostgreSQL/PostGIS e tipos
1
Migrations de extensões, tipos e utilitários
PostgreSQL 16/PostGIS confirmado; migration fresh e rollback verdes; guards idempotentes; sem tabelas de domínio.
3
Catálogos e geografia
2
Tabelas, seeds, endpoints e testes de catálogos
FKs e versões válidas; seeds não fiscais; dropdown pai-filho e versão testados; rollback verde.
4
Repositório legislativo
2, 3
Modelo legal, pesquisa e importador
Vigências sem sobreposição; publicação imutável; hash/object_key verificados; pesquisa e importação segura aprovadas pelo Jurídico.
5
Parâmetros, regras, vigência e governação
1, 3, 4
Modelo fiscal governado e workflow
scope_key completa; quatro olhos; test runs; nenhuma taxa inventada; aprovação do Fiscalista e Jurídico.
6
Simulações, jobs, relatórios e segurança
2, 5
Domínio transaccional, RLS, auditoria e jobs
RLS e autorização verdes; ETag/412, idempotência, retries, token hash, soft delete e reconciliação testados.
7
OpenAPI consolidado
3–6
api/openapi.yaml 3.1 e bundle
Lint, validate e bundle verdes; sem chaves duplicadas; respostas, headers e schemas completos.
8
API Laravel
7
API contract-first e testes
Testes de contrato e críticos verdes; Policies, validação, ETag e idempotência conformes; nenhuma alteração silenciosa do contrato.
9
Formulário React com dropdowns
3, 7, 8
/Simulador responsivo e acessível
Testes UI verdes; WCAG; dropdowns dependentes e multivalor; validação cliente/servidor; sem defaults fiscais.
10
Motor fiscal principal
5, 6, 8
Motor determinístico e casos dourados
DAG sem ciclos; precisão/arredondamento verificados; fontes e explicabilidade; casos dourados; aprovação fiscal.
11
Aduanas/câmbio/cenários
4–6, 8, 10
Cálculo aduaneiro e cenários
Bloqueios de dados insuficientes; taxa/data/fonte cambial guardadas; reconciliação e casos aprovados pelo Fiscalista.
12
Manifestações e acompanhamento
3, 6–9
Domínio, state machine, formulários e endpoints
Versões imutáveis; consentimentos; transições autorizadas; conversão idempotente; testes críticos verdes.
13
Integrações CRM, documentos, notificações e outbox
6, 8, 12
Workers e integrações resilientes
Retries/dead-letter; antivírus; indisponibilidade testada; reconciliação; public_snapshot como única fonte pública.
14
Testes de contrato, segurança, acessibilidade, desempenho e recuperação
2–13
Relatório de testes por ID e correcções confirmadas
SQL/OpenAPI/contrato/RLS/ETag/idempotência/concorrência/rollback/privacidade/WCAG/carga/DR verdes; sem crítico aberto.
15
Homologação, migração e Go-Live
14
Carga validada, runbooks, deployment e checklist
Assinaturas fiscal/jurídica; backup/restauro; rollback ensaiado; observabilidade; Go/No-Go formal; nenhum crítico falhado.
23.2 Convenções obrigatórias para todos os prompts
Inspeccionar antes de alterar e confirmar a stack, versões, estrutura e convenções reais.
Apresentar primeiro o plano e a lista de ficheiros a criar ou alterar.
Não apagar nem renomear artefactos sem necessidade demonstrada.
Não fazer 
hardcode
 nem inventar taxas, limiares ou interpretações fiscais.
Manter catálogos versionados e rastreáveis.
Usar dropdown pesquisável e acessível para campos multivalor ou com duas ou mais opções.
Manter valores estáveis na API e rótulos de apresentação em português de Moçambique.
Aplicar validação coerente no cliente e no servidor.
Usar transacções nas operações compostas e definir limites de atomicidade.
Garantir 
idempotência
 em comandos repetíveis, integrações e jobs.
Aplicar 
optimistic
 
locking
 com ETag/If-Match onde houver concorrência de edição.
Aplicar 
RLS
 e autorização por política, com testes positivos e negativos.
Criar 
snapshots
 imutáveis quando o resultado depender de dados versionados.
Produzir 
logs
 estruturados sem PII, segredos ou tokens em claro.
Criar migrations com 
up
/
down
 seguros; não editar migrations já executadas em produção.
Executar testes adequados, registar comandos, resultados e evidências.
Entregar relatório final com resumo, diff lógico, pendências, riscos e rollback.
Parar imediatamente se o 
gate falhar
; não iniciar a etapa seguinte.
Etapa 0 — Auditoria e baseline
Objectivo: Estabelecer uma fotografia verificável do estado actual sem alterações funcionais.
Pré-condições: Acesso de leitura ao repositório, histórico Git, configuração de CI e ambientes de desenvolvimento.
Prompt para Codex
Antes de actuar, inspecciona o repositório real e identifica Laravel, React/Next.js, PostgreSQL/PostGIS, OpenAPI, CI, testes, versões, estrutura, convenções e restrições. Não assumes nomes nem caminhos: adapta-os ao projecto. Reutiliza padrões existentes e apresenta primeiro um plano curto e a lista exacta de ficheiros a criar ou alterar. Não apagues, renomeies ou reescrevas artefactos sem necessidade demonstrada; não edites migrations já executadas em produção; não inventes taxas ou regras fiscais. Executa a Etapa 0 — Auditoria e baseline. Inventaria stack e versões, branches e estratégia Git, migrations executadas e pendentes, módulos Laravel e React/Next.js, PostgreSQL/PostGIS, autenticação/autorização, OpenAPI, CI, testes, cobertura, integrações e riscos. Não alteres comportamento. Cria docs/implementation/baseline.md e uma matriz de bloqueadores com severidade, evidência, responsável por função e condição de desbloqueio. Implementa apenas este escopo. Mantém alterações não destrutivas e reversíveis, validação cliente/servidor quando aplicável, transacções, idempotência, optimistic locking, RLS, snapshots e logs sem PII conforme as decisões já aprovadas. Executa os testes adequados e corrige os erros encontrados apenas 
dentro deste escopo. Se uma pré-condição ou gate falhar, pára e regista o bloqueio. Não prossigas para a etapa seguinte. No fim, entrega: resumo; diff lógico por ficheiro; comandos executados; resultados e evidências dos testes; riscos e pendências; instruções de rollback; estado proposto do gate para revisão humana. Não afirmes ter executado comandos que não executaste.
Critérios de conclusão: baseline.md reproduzível; matriz de bloqueadores completa; nenhum ficheiro funcional alterado; comandos de diagnóstico e resultados registados.
Etapa 1 — Contratos e ADRs
Objectivo: Fixar decisões transversais antes de criar estruturas persistentes ou interfaces públicas.
Pré-condições: Etapa 0 aprovada e bloqueadores arquitecturais resolvidos.
Prompt para Codex
Antes de actuar, inspecciona o repositório real e identifica Laravel, React/Next.js, PostgreSQL/PostGIS, OpenAPI, CI, testes, versões, estrutura, convenções e restrições. Não assumes nomes nem caminhos: adapta-os ao projecto. Reutiliza padrões existentes e apresenta primeiro um plano curto e a lista exacta de ficheiros a criar ou alterar. Não apagues, renomeies ou reescrevas artefactos sem necessidade demonstrada; não edites migrations já executadas em produção; não inventes taxas ou regras fiscais. Executa a Etapa 1 — Contratos e ADRs. Cria ADRs sobre IAM/JWT e acesso anónimo, ETag/optimistic locking, idempotência, motor de expressões seguro, vigência temporal, moeda e arredondamento, RLS, outbox e gestão de documentos. Consolida um mapa campo SQL–OpenAPI com nomes, tipos, nulabilidade, enumerações, formatos, origem e classificação de dados. Não faças implementação funcional. Implementa apenas este escopo. Mantém alterações não destrutivas e reversíveis, validação cliente/servidor quando aplicável, transacções, idempotência, optimistic locking, RLS, snapshots e logs sem PII conforme as decisões já aprovadas. Executa os testes adequados e corrige os erros encontrados apenas dentro deste escopo. Se uma pré-condição ou gate falhar, pára e regista o bloqueio. Não prossigas para a etapa seguinte. No fim, entrega: resumo; diff lógico por ficheiro; comandos executados; resultados e evidências dos testes; riscos e pendências; instruções de rollback; estado proposto do gate para revisão humana. Não afirmes ter executado comandos que não executaste.
Critérios de conclusão: ADRs aceites e ligados ao baseline; mapa SQL–OpenAPI sem lacunas; impactos, alternativas e decisões registados.
Etapa 2 — Infraestrutura PostgreSQL/PostGIS e tipos
Objectivo: Preparar fundações de dados seguras e reversíveis.
Pré-condições: ADRs e mapa SQL–OpenAPI aprovados; PostgreSQL 16 e PostGIS disponíveis.
Prompt para Codex
Antes de actuar, inspecciona o repositório real e identifica Laravel, React/Next.js, PostgreSQL/PostGIS, OpenAPI, CI, testes, versões, estrutura, convenções e restrições. Não assumes nomes nem caminhos: adapta-os ao projecto. Reutiliza padrões existentes e apresenta primeiro um plano curto e a lista exacta de ficheiros a criar ou alterar. 
Não apagues, renomeies ou reescrevas artefactos sem necessidade demonstrada; não edites migrations já executadas em produção; não inventes taxas ou regras fiscais. Executa a Etapa 2 — Infraestrutura PostgreSQL/PostGIS e tipos. Cria novas migrations para extensões, tipos, domínios e funções utilitárias estritamente necessários, com guards seguros e compatibilidade PostgreSQL 16/PostGIS. Implementa up/down e testes fresh/rollback. Não cries ainda tabelas de domínio e não edites migrations executadas em produção. Implementa apenas este escopo. Mantém alterações não destrutivas e reversíveis, validação cliente/servidor quando aplicável, transacções, idempotência, optimistic locking, RLS, snapshots e logs sem PII conforme as decisões já aprovadas. Executa os testes adequados e corrige os erros encontrados apenas dentro deste escopo. Se uma pré-condição ou gate falhar, pára e regista o bloqueio. Não prossigas para a etapa seguinte. No fim, entrega: resumo; diff lógico por ficheiro; comandos executados; resultados e evidências dos testes; riscos e pendências; instruções de rollback; estado proposto do gate para revisão humana. Não afirmes ter executado comandos que não executaste.
Critérios de conclusão: Migration fresh, rollback e reaplicação verdes; guards idempotentes; extensões e tipos verificados; zero tabelas de domínio.
Etapa 3 — Catálogos e geografia
Objectivo: Disponibilizar referências estáveis, versionadas e próprias para UI dependente.
Pré-condições: Etapa 2 aprovada; convenções de IDs, vigência e tradução definidas.
Prompt para Codex
Antes de actuar, inspecciona o repositório real e identifica Laravel, React/Next.js, PostgreSQL/PostGIS, OpenAPI, CI, testes, versões, estrutura, convenções e restrições. Não assumes nomes nem caminhos: adapta-os ao projecto. Reutiliza padrões existentes e apresenta primeiro um plano curto e a lista exacta de ficheiros a criar ou alterar. Não apagues, renomeies ou reescrevas artefactos sem necessidade demonstrada; não edites migrations já executadas em produção; não inventes taxas ou regras fiscais. Executa a Etapa 3 — Catálogos e geografia. Implementa authorities, jurisdictions, economic_activities e catálogos versionados, com FKs, índices, vigência e seeds mínimos não fiscais. Expõe endpoints de catálogo com valores estáveis e rótulos pt-MZ. Testa versão, paginação, pesquisa, dropdown pai-filho, limpeza de filhos e integridade referencial. Implementa apenas este escopo. Mantém alterações não destrutivas e reversíveis, validação cliente/servidor quando aplicável, transacções, idempotência, optimistic locking, RLS, snapshots e logs sem PII conforme as decisões já aprovadas. Executa os testes adequados e corrige os erros encontrados apenas dentro deste escopo. Se uma pré-condição ou gate falhar, pára e regista o bloqueio. Não prossigas para a etapa seguinte. No fim, entrega: resumo; diff lógico por ficheiro; comandos executados; resultados e evidências dos testes; riscos e pendências; instruções de rollback; estado proposto do gate para revisão humana. Não afirmes ter executado comandos que não executaste.
Critérios de conclusão: Migrations e rollback verdes; seeds determinísticos; endpoints documentados; testes de dropdown pai-filho e versão aprovados.
Etapa 4 — Repositório legislativo
Objectivo: Criar um repositório legal temporal, pesquisável e auditável.
Pré-condições: Infraestrutura e catálogos aprovados; regras de documentos e vigência decididas.
Prompt para Codex
Antes de actuar, inspecciona o repositório real e identifica Laravel, React/Next.js, PostgreSQL/PostGIS, OpenAPI, CI, testes, versões, estrutura, convenções e restrições. Não assumes nomes nem caminhos: adapta-os ao projecto. Reutiliza padrões existentes e apresenta primeiro um plano curto e a lista exacta de ficheiros a criar ou alterar. Não apagues, renomeies ou reescrevas artefactos sem necessidade demonstrada; não edites migrations já executadas em produção; não inventes taxas ou regras fiscais. Executa a Etapa 4 — Repositório legislativo. Implementa legal instruments, versions, provisions, relations e domains, pesquisa e importador seguro. Guarda binários por object_key e hash, não na base de dados. Impede sobreposição de vigência e alteração de versões publicadas; valida tipo, tamanho, hash e origem no importador; mantém rastreabilidade e autorização. Implementa apenas este escopo. Mantém alterações não destrutivas e reversíveis, validação cliente/servidor quando aplicável, transacções, idempotência, optimistic locking, RLS, snapshots e logs sem PII conforme as decisões já aprovadas. Executa os testes adequados e corrige os erros encontrados apenas dentro deste escopo. Se uma pré-condição ou gate falhar, pára e regista o bloqueio. Não prossigas para a etapa seguinte. No fim, entrega: resumo; diff lógico por ficheiro; comandos executados; resultados e evidências dos testes; riscos e pendências; instruções de rollback; estado proposto do gate para revisão humana. Não afirmes ter executado comandos que não executaste.
Critérios de conclusão: Vigência e imutabilidade testadas; pesquisa funcional; hashes verificados; importação maliciosa rejeitada; aprovação jurídica registada.
Etapa 5 — Parâmetros, regras, vigência e governação
Objectivo: Modelar conhecimento fiscal sem embutir valores não aprovados.
Pré-condições: ADRs, catálogos e repositório legislativo aprovados.
Prompt para Codex
Antes de actuar, inspecciona o repositório real e identifica Laravel, React/Next.js, PostgreSQL/PostGIS, OpenAPI, CI, testes, versões, estrutura, convenções e restrições. Não assumes nomes nem caminhos: adapta-os ao projecto. Reutiliza padrões existentes e apresenta primeiro um plano curto e a lista exacta de ficheiros a criar ou alterar. Não apagues, renomeies ou reescrevas artefactos sem necessidade demonstrada; não edites migrations já executadas em produção; não inventes taxas ou regras fiscais. Executa a Etapa 5 — Parâmetros, regras, vigência e governação. Implementa tax types, regimes, parameters, rule sets, calculation rules, sources, eligibility, obligations, reviews e test runs. Define scope_key completa, vigência sem ambiguidades e workflow de quatro olhos com segregação entre autor e aprovador. Liga cada regra à fonte legal. Não inventes taxas, limiares, fórmulas ou datas; usa estados pendentes quando faltarem dados aprovados. Implementa apenas este escopo. Mantém alterações não destrutivas e reversíveis, validação cliente/servidor quando aplicável, transacções, idempotência, optimistic locking, RLS, snapshots e logs sem PII conforme as decisões já aprovadas. Executa os testes adequados e corrige os erros encontrados apenas dentro deste escopo. Se uma pré-condição ou gate falhar, pára e regista o bloqueio. Não prossigas para a etapa seguinte. No fim, entrega: resumo; diff lógico por ficheiro; comandos executados; resultados e evidências dos testes; riscos e pendências; instruções de rollback; estado proposto do gate para revisão humana. Não afirmes ter executado comandos que não executaste.
Critérios de conclusão: Workflow e segregação testados; scope_key cobre todos os contextos; nenhuma taxa inventada; test runs e aprovações fiscal/jurídica disponíveis.
Etapa 6 — Simulações, jobs, relatórios e segurança
Objectivo: Criar o núcleo transaccional e de segurança para execuções rastreáveis.
Pré-condições: Modelo de parâmetros aprovado; políticas IAM, RLS, ETag e idempotência decididas.
Prompt para Codex
Antes de actuar, inspecciona o repositório real e identifica Laravel, React/Next.js, PostgreSQL/PostGIS, OpenAPI, CI, testes, versões, estrutura, convenções e restrições. Não assumes nomes nem caminhos: adapta-os ao projecto. Reutiliza padrões existentes e apresenta primeiro um plano curto e a lista exacta de ficheiros a criar ou alterar. Não apagues, renomeies ou reescrevas artefactos sem necessidade demonstrada; não edites migrations já executadas em produção; não inventes taxas ou regras fiscais. Executa a Etapa 6 — Simulações, jobs, relatórios e segurança. Implementa questionnaire versions, simulations, scenarios, results, result sources, obligations, reports, jobs, idempotency e audit. Usa hash para token anónimo, soft delete onde aprovado, ETag/If-Match, RLS, transacções, retries limitados e snapshots. Garante reconciliação, logs sem PII e isolamento entre utilizadores/organizações. Implementa apenas este escopo. Mantém alterações não destrutivas e reversíveis, validação cliente/servidor quando aplicável, transacções, idempotência, optimistic locking, RLS, snapshots e logs sem PII conforme as decisões já aprovadas. Executa os testes adequados e corrige os erros encontrados apenas dentro deste escopo. Se uma pré-condição ou gate falhar, pára e regista o bloqueio. Não prossigas para a etapa seguinte. No fim, entrega: resumo; diff lógico por ficheiro; comandos executados; resultados e evidências dos testes; riscos e pendências; instruções de rollback; estado proposto do gate para revisão humana. Não afirmes ter executado comandos que não executaste.
Critérios de conclusão: RLS e autorização positivas/negativas verdes; ETag 412, idempotência, retries e concorrência testados; reconciliação e auditoria verificáveis.
Etapa 7 — OpenAPI consolidado
Objectivo: Publicar um contrato único e validável antes da implementação HTTP.
Pré-condições: Modelos e comportamentos das etapas 3 a 6 estabilizados.
Prompt para Codex
Antes de actuar, inspecciona o repositório real e identifica Laravel, React/Next.js, PostgreSQL/PostGIS, OpenAPI, CI, testes, versões, estrutura, convenções e restrições. Não assumes nomes nem caminhos: adapta-os ao projecto. Reutiliza padrões existentes e apresenta primeiro um plano curto e a lista exacta de ficheiros a criar ou alterar. Não apagues, renomeies ou reescrevas artefactos sem necessidade demonstrada; não edites migrations já executadas em produção; não inventes taxas ou regras fiscais. Executa a Etapa 7 — OpenAPI consolidado. Gera um único api/openapi.yaml em OpenAPI 3.1, permitindo refs externos resolvíveis e sem chaves duplicadas. Inclui schemas e endpoints de simulação, legislação, jobs, relatórios, administração e manifestações; respostas 401, 403, 404, 409, 412, 422, 429 e 500; headers ETag, If-Match, Idempotency-Key, Retry-After e correlação quando aplicáveis. Executa lint, validate e bundle. Implementa apenas este escopo. Mantém alterações não destrutivas e 
reversíveis, validação cliente/servidor quando aplicável, transacções, idempotência, optimistic locking, RLS, snapshots e logs sem PII conforme as decisões já aprovadas. Executa os testes adequados e corrige os erros encontrados apenas dentro deste escopo. Se uma pré-condição ou gate falhar, pára e regista o bloqueio. Não prossigas para a etapa seguinte. No fim, entrega: resumo; diff lógico por ficheiro; comandos executados; resultados e evidências dos testes; riscos e pendências; instruções de rollback; estado proposto do gate para revisão humana. Não afirmes ter executado comandos que não executaste.
Critérios de conclusão: Lint, validate e bundle verdes; exemplos e erros coerentes; refs resolvidas; diff do contrato revisto.
Etapa 8 — API Laravel
Objectivo: Implementar exactamente o contrato aprovado, com segurança e consistência.
Pré-condições: OpenAPI consolidado aprovado e testes de contrato preparados.
Prompt para Codex
Antes de actuar, inspecciona o repositório real e identifica Laravel, React/Next.js, PostgreSQL/PostGIS, OpenAPI, CI, testes, versões, estrutura, convenções e restrições. Não assumes nomes nem caminhos: adapta-os ao projecto. Reutiliza padrões existentes e apresenta primeiro um plano curto e a lista exacta de ficheiros a criar ou alterar. Não apagues, renomeies ou reescrevas artefactos sem necessidade demonstrada; não edites migrations já executadas em produção; não inventes taxas ou regras fiscais. Executa a Etapa 8 — API Laravel. Implementa a API Laravel contract-first a partir do OpenAPI. Reutiliza Form Requests, Resources, Policies, Services e middleware existentes; usa repositories apenas se forem padrão do projecto. Implementa transacções, idempotência, ETag/If-Match, RLS/contexto e erros conformes. Cria testes de contrato e não alteres o contrato silenciosamente: qualquer incompatibilidade deve parar para decisão. Implementa apenas este escopo. Mantém alterações não destrutivas e reversíveis, validação cliente/servidor quando aplicável, transacções, idempotência, optimistic locking, RLS, snapshots e logs sem PII conforme as decisões já aprovadas. Executa os testes adequados e corrige os erros encontrados apenas dentro deste escopo. Se uma pré-condição ou gate falhar, pára e regista o bloqueio. Não prossigas para a etapa seguinte. No fim, entrega: resumo; diff lógico por ficheiro; comandos executados; resultados e evidências dos testes; riscos e pendências; instruções de rollback; estado proposto do gate para revisão humana. Não afirmes ter executado comandos que não executaste.
Critérios de conclusão: Testes de contrato e integração verdes; autorização, 412/409/422 e idempotência verificadas; nenhuma divergência não aprovada.
Etapa 9 — Formulário React com dropdowns
Objectivo: Entregar o /Simulador acessível, responsivo e fiel aos catálogos.
Pré-condições: Endpoints de catálogo e API estáveis; contrato e desenho de campos aprovados.
Prompt para Codex
Antes de actuar, inspecciona o repositório real e identifica Laravel, React/Next.js, PostgreSQL/PostGIS, OpenAPI, CI, testes, versões, estrutura, convenções e restrições. 
Não assumes nomes nem caminhos: adapta-os ao projecto. Reutiliza padrões existentes e apresenta primeiro um plano curto e a lista exacta de ficheiros a criar ou alterar. Não apagues, renomeies ou reescrevas artefactos sem necessidade demonstrada; não edites migrations já executadas em produção; não inventes taxas ou regras fiscais. Executa a Etapa 9 — Formulário React com dropdowns. Implementa /Simulador em React/Next.js segundo os padrões existentes. Todo campo com duas ou mais opções usa dropdown pesquisável e acessível; campos multivalor permitem selecção e remoção por teclado. Não definas defaults fiscais. Dropdowns dependentes limpam filhos inválidos; “Outro” abre campo condicional. Implementa autosave, validações cliente/servidor, estados de erro/loading, experiência móvel e WCAG. Implementa apenas este escopo. Mantém alterações não destrutivas e reversíveis, validação cliente/servidor quando aplicável, transacções, idempotência, optimistic locking, RLS, snapshots e logs sem PII conforme as decisões já aprovadas. Executa os testes adequados e corrige os erros encontrados apenas dentro deste escopo. Se uma pré-condição ou gate falhar, pára e regista o bloqueio. Não prossigas para a etapa seguinte. No fim, entrega: resumo; diff lógico por ficheiro; comandos executados; resultados e evidências dos testes; riscos e pendências; instruções de rollback; estado proposto do gate para revisão humana. Não afirmes ter executado comandos que não executaste.
Critérios de conclusão: Testes UI, teclado e leitor de ecrã definidos/verificados; dependências, autosave e validação verdes; sem defaults fiscais.
Etapa 10 — Motor fiscal principal
Objectivo: Calcular de forma determinística, explicável e reproduzível.
Pré-condições: Parâmetros aprovados, snapshots disponíveis e API estabilizada.
Prompt para Codex
Antes de actuar, inspecciona o repositório real e identifica Laravel, React/Next.js, PostgreSQL/PostGIS, OpenAPI, CI, testes, versões, estrutura, convenções e restrições. Não assumes nomes nem caminhos: adapta-os ao projecto. Reutiliza padrões existentes e apresenta primeiro um plano curto e a lista exacta de ficheiros a criar ou alterar. Não apagues, renomeies ou reescrevas artefactos sem necessidade demonstrada; não edites migrations já executadas em produção; não inventes taxas ou regras fiscais. Executa a Etapa 10 — Motor fiscal principal. Implementa o motor para IRPC, IVA, ISPC, IRPS, INSS e retenções usando DSL com whitelist e DAG validado sem ciclos. Resolve parâmetros vigentes por scope_key, usa decimal exacto e arredondamento configurado, produz explicabilidade passo a passo, fontes e snapshot. Cria casos dourados aprováveis. Quando faltarem dados, devolve indeterminação explícita; não produz aconselhamento fiscal conclusivo. Implementa apenas este escopo. Mantém alterações não destrutivas e reversíveis, validação cliente/servidor quando aplicável, transacções, idempotência, optimistic locking, RLS, snapshots e logs sem PII conforme as decisões já aprovadas. Executa os testes adequados e corrige os erros encontrados apenas dentro deste escopo. Se uma pré-condição ou 
gate falhar
, pára e regista o bloqueio. No fim, entrega: resumo; diff lógico por ficheiro; comandos executados; resultados e evidências dos testes; riscos e pendências; instruções de rollback; estado proposto do gate para revisão humana. Não afirmes ter executado comandos que não executaste.
Critérios de conclusão: Determinismo e DAG testados; precisão e arredondamento verificados; casos dourados e fontes aprovados pelo Fiscalista.
Etapa 11 — Aduanas, câmbio e cenários
Objectivo: Cobrir importação e cenários com rastreabilidade cambial e documental.
Pré-condições: Motor principal e fontes legais aprovados; catálogos aduaneiros disponíveis.
Prompt para Codex
Antes de actuar, inspecciona o repositório real e identifica Laravel, React/Next.js, PostgreSQL/PostGIS, OpenAPI, CI, testes, versões, estrutura, convenções e restrições. Não assumes nomes nem caminhos: adapta-os ao projecto. Reutiliza padrões existentes e apresenta primeiro um plano curto e a lista exacta de ficheiros a criar ou alterar. Não apagues, renomeies ou reescrevas artefactos sem necessidade demonstrada; não edites migrations já executadas em produção; não inventes taxas ou regras fiscais. Executa a Etapa 11 — Aduanas, câmbio e cenários. Implementa valor aduaneiro, direitos, ICE, IVA de importação, câmbio, origem/preferência, cenários e obrigações. Bloqueia cálculo definitivo sem código pautal e prova exigida. Guarda taxa, data, fonte e método cambial no snapshot. Trata preferências como condicionais e cria reconciliação e casos dourados sem inventar taxas. Implementa apenas este escopo. Mantém alterações não destrutivas e reversíveis, validação cliente/servidor quando aplicável, transacções, idempotência, optimistic locking, RLS, snapshots e logs sem PII conforme as decisões já aprovadas. Executa os testes adequados e corrige os erros encontrados apenas dentro deste escopo. Se uma pré-condição ou gate falhar, pára e regista o bloqueio. No fim, entrega: resumo; diff lógico por ficheiro; comandos executados; resultados e evidências dos testes; riscos e pendências; instruções de rollback; estado proposto do gate para revisão humana. Não afirmes ter executado comandos que não executaste.
Critérios de conclusão: Bloqueios e cenários testados; rastreabilidade cambial completa; reconciliação verde; aprovação fiscal registada.
Etapa 12 — Manifestações e acompanhamento
Objectivo: Implementar submissão e tratamento operacional ponta a ponta.
Pré-condições: Domínios de segurança, documentos, catálogos e API aprovados.
Prompt para Codex
Antes de actuar, inspecciona o repositório real e identifica Laravel, React/Next.js, PostgreSQL/PostGIS, OpenAPI, CI, testes, versões, estrutura, convenções e restrições. Não assumes nomes nem caminhos: adapta-os ao projecto. Reutiliza padrões existentes e apresenta primeiro um plano curto e a lista exacta de ficheiros a criar ou alterar. Não apagues, renomeies ou reescrevas artefactos sem necessidade demonstrada; não edites migrations já executadas em produção; não inventes taxas ou regras fiscais. Executa a Etapa 12 — Manifestações e acompanhamento. Cria migrations e domínio para manifestações, versões imutáveis, contactos, localizações, finanças, impactos, documentos, consentimentos, assignments, requests, tasks, status history e links. Implementa state machine explícita, formulários e endpoints. Autoriza cada transição, preserva histórico e torna a conversão de simulação em manifestação idempotente. Implementa apenas este escopo. Mantém alterações não destrutivas e reversíveis, validação cliente/servidor quando aplicável, transacções, idempotência, optimistic locking, RLS, snapshots e logs sem PII conforme as decisões já aprovadas. Executa os testes adequados e corrige os erros encontrados apenas dentro deste escopo. Se uma pré-condição ou 
gate falhar
, pára e regista o bloqueio
. .
 No fim, entrega: resumo; 
diff lógico por ficheiro; comandos executados; resultados e evidências dos testes; riscos e pendências; instruções de rollback; estado proposto do gate para revisão humana. Não afirmes ter executado comandos que não executaste.
Critérios de conclusão: Fresh/rollback verdes; transições e permissões testadas; versões imutáveis; consentimento e conversão idempotente verificáveis.
Etapa 13 — Integrações CRM, documentos, notificações e outbox
Objectivo: Integrar sistemas externos sem perder consistência nem expor dados.
Pré-condições: Outbox e manifestações aprovadas; contratos externos e credenciais de teste disponíveis.
Prompt para Codex
Antes de actuar, inspecciona o repositório real e identifica Laravel, React/Next.js, PostgreSQL/PostGIS, OpenAPI, CI, testes, versões, estrutura, convenções e restrições. Não assumes nomes nem caminhos: adapta-os ao projecto. Reutiliza padrões existentes e apresenta primeiro um plano curto e a lista exacta de ficheiros a criar ou alterar. Não apagues, renomeies ou reescrevas artefactos sem necessidade demonstrada; não edites migrations já executadas em produção; não inventes taxas ou regras fiscais. Executa a Etapa 13 — Integrações CRM, documentos, notificações e outbox. Implementa outbox workers, integração CRM, object storage com antivírus e notificações. Usa retries com backoff, dead-letter e chaves idempotentes; evita duplicar dados mestre. Garante que public_snapshot é a única fonte pública. Simula indisponibilidade, timeout, duplicação, recuperação e reconciliação; não regista PII, tokens ou documentos em logs. Implementa apenas este escopo. Mantém alterações não destrutivas e reversíveis, validação cliente/servidor quando aplicável, transacções, idempotência, optimistic locking, RLS, snapshots e logs sem PII conforme as decisões já aprovadas. Executa os testes adequados e corrige os erros encontrados apenas dentro deste escopo. Se uma pré-condição ou gate falhar, pára e regista o bloqueio. No fim, entrega: resumo; diff lógico por ficheiro; comandos executados; resultados e evidências dos testes; riscos e pendências; instruções de rollback; estado proposto do gate para revisão humana. Não afirmes ter executado comandos que não executaste.
Critérios de conclusão: Testes de indisponibilidade e replay verdes; dead-letter observável; reconciliação sem duplicados; public_snapshot verificado.
Etapa 14 — Testes transversais e recuperação
Objectivo: Demonstrar qualidade e corrigir apenas defeitos confirmados.
Pré-condições: Etapas funcionais concluídas e ambientes de teste representativos disponíveis.
Prompt para Codex
Antes de actuar, inspecciona o repositório real e identifica Laravel, React/Next.js, PostgreSQL/PostGIS, OpenAPI, CI, testes, versões, estrutura, convenções e restrições. Não assumes nomes nem caminhos: adapta-os ao projecto. Reutiliza padrões existentes e apresenta primeiro um plano curto e a lista exacta de ficheiros a criar ou alterar. Não apagues, renomeies ou reescrevas artefactos sem necessidade demonstrada; não edites migrations já executadas em produção; não inventes taxas ou regras fiscais. Executa a Etapa 14 — Testes transversais e recuperação. Executa e completa testes SQL, OpenAPI, contrato, unitários, integração, RLS, autorização, ETag, idempotência, 
concorrência, rollback, privacidade, acessibilidade, carga e disaster recovery. Atribui ID, requisito, ambiente, evidência e resultado a cada teste. Corrige apenas defeitos reproduzidos e confirmados, dentro do escopo; repete toda a bateria afectada. Implementa apenas este escopo. Mantém alterações não destrutivas e reversíveis, validação cliente/servidor quando aplicável, transacções, idempotência, optimistic locking, RLS, snapshots e logs sem PII conforme as decisões já aprovadas. Executa os testes adequados e corrige os erros encontrados apenas dentro deste escopo. Se uma pré-condição ou gate falhar, pára e regista o bloqueio. No fim, entrega: resumo; diff lógico por ficheiro; comandos executados; resultados e evidências dos testes; riscos e pendências; instruções de rollback; estado proposto do gate para revisão humana. Não afirmes ter executado comandos que não executaste.
Critérios de conclusão: Relatório por ID completo; evidências anexas; zero crítico aberto; desempenho e recuperação dentro dos limites aprovados.
Etapa 15 — Homologação, migração e Go-Live
Objectivo: Preparar uma entrada em produção controlada, aprovada e reversível.
Pré-condições: Etapa 14 verde; janela, responsáveis por função e critérios Go/No-Go aprovados.
Prompt para Codex
Antes de actuar, inspecciona o repositório real e identifica Laravel, React/Next.js, PostgreSQL/PostGIS, OpenAPI, CI, testes, versões, estrutura, convenções e restrições. Não assumes nomes nem caminhos: adapta-os ao projecto. Reutiliza padrões existentes e apresenta primeiro um plano curto e a lista exacta de ficheiros a criar ou alterar. Não apagues, renomeies ou reescrevas artefactos sem necessidade demonstrada; não edites migrations já executadas em produção; não inventes taxas ou regras fiscais. Executa a Etapa 15 — Homologação, migração e Go-Live. Executa homologação, carga validada de legislação e parâmetros, assinatura fiscal/jurídica, backup/restauro, runbooks, observabilidade e feature flags. Prepara deployment canário ou blue-green conforme a infraestrutura real, ensaia rollback e completa checklist Go/No-Go. Não executes Go-Live se qualquer teste ou controlo crítico estiver falhado, sem evidência ou sem aprovação. Implementa apenas este escopo. Mantém alterações não destrutivas e reversíveis, validação cliente/servidor quando aplicável, transacções, idempotência, optimistic locking, RLS, snapshots e logs sem PII conforme as decisões já aprovadas. Executa os testes adequados e corrige os erros encontrados apenas dentro deste escopo. Se uma pré-condição ou 
gate falhar
, pára e regista o bloqueio.  No fim, entrega: resumo; diff lógico por ficheiro; comandos executados; resultados e evidências dos testes; riscos e pendências; instruções de rollback; estado proposto do gate para revisão humana. Não afirmes ter executado comandos que não executaste.
Critérios de conclusão: Carga reconciliada; assinaturas registadas; restauro e rollback ensaiados; alertas operacionais; decisão Go/No-Go formal sem crítico falhado.
23.3 Prompt orquestrador
Coordena a implementação sequencial das Etapas 0 a 15, mas nunca as executes todas de uma vez. Antes de qualquer acção, inspecciona o repositório real — Laravel, React/Next.js, PostgreSQL/PostGIS, OpenAPI, CI, testes, versões, branches, módulos e convenções — e adapta nomes e caminhos. Lê esta secção e docs/implementation/status.md. Selecciona apenas a primeira etapa com pré-condições 
aprovadas e estado “Não iniciada” ou “Bloqueada corrigível”. Apresenta plano e ficheiros afectados; reutiliza convenções; preserva alterações existentes; não inventa taxas; não edita migrations já executadas em produção. Executa somente essa etapa, os respectivos testes e as correcções confirmadas dentro do escopo. Mantém docs/implementation/status.md com: etapa, estado, branch, commit/tag, data, evidências, comandos e resultados de testes, riscos, pendências, rollback e próxima etapa. Ao terminar, produz resumo e diff lógico e PÁRA. Solicita revisão humana e aprovação explícita do gate. Só numa nova execução, após aprovação registada, pode iniciar a etapa seguinte. Se qualquer gate falhar, marca “Bloqueada”, preserva evidências e não avança.
23.4 Matriz de controlo de execução
Etapa
Estado inicial
Evidência obrigatória
Aprovador por função
Bloqueia
0
Não iniciada
baseline.md, inventário e matriz de bloqueadores
Arquitecto/Tech Lead
Todas as etapas
1
Não iniciada
ADRs e mapa SQL–OpenAPI aprovados
Arquitecto/Tech Lead; Segurança; Jurídico; Fiscalista
2–15
2
Não iniciada
fresh/rollback e compatibilidade PostgreSQL/PostGIS
DevOps; Data Lead
3–15
3
Não iniciada
FKs, seeds e testes de catálogos/dropdowns
Data Lead; Product Owner
4–15
4
Não iniciada
vigência, imutabilidade, pesquisa e importação segura
Jurídico; Segurança
5, 10, 11, 15
5
Não iniciada
scope_key, quatro olhos, fontes e test runs
Fiscalista; Jurídico; Data Lead
6, 10, 11, 15
6
Não iniciada
RLS, ETag, idempotência, auditoria e reconciliação
Segurança; Arquitecto/Tech Lead
7–15
7
Não iniciada
lint, validate, bundle e diff OpenAPI
Arquitecto/Tech Lead; QA
8–15
8
Não iniciada
testes de contrato, integração e autorização
QA; Segurança; Arquitecto/Tech Lead
9–15
9
Não iniciada
testes UI, móvel, teclado e WCAG
UX/Acessibilidade; Product Owner; QA
14–15
10
Não iniciada
casos dourados, precisão, fontes e explicabilidade
Fiscalista; QA
11, 14, 15
11
Não iniciada
casos aduaneiros e reconciliação cambial
Fiscalista; Jurídico; QA
14–15
12
Não iniciada
state machine, consentimentos e conversão idempotente
Gestor operacional; Segurança; Product Owner
13–15
13
Não iniciada
indisponibilidade, replay, dead-letter e reconciliação
DevOps; Segurança; Gestor operacional
14–15
14
Não iniciada
relatório de testes por ID; zero crítico aberto
QA; Segurança; UX/Acessibilidade; DevOps
15
15
Não iniciada
assinaturas, restauro, rollback e checklist Go/No-Go
Product Owner; Fiscalista; Jurídico; DevOps; Gestor operacional
Go-Live
23.5 Regra de recuperação
• Guardar um commit limpo e identificável antes de iniciar cada etapa.
• Usar uma branch por etapa e limitar o diff ao escopo aprovado.
• Testar migration up/down e rollback antes de promover alterações.
• Proteger comportamento novo com feature flag quando aplicável.
• Criar e verificar backup antes de carregar ou transformar dados reais.
• Nunca corrigir uma falha apagando logs, testes, histórico ou outra evidência.
• Se um teste falhar, restaurar o estado seguro, documentar causa e impacto, corrigir no mesmo escopo e repetir todos os testes afectados.