From 23ecccfaad1053f55bf0591fd242cb5fa3406c38 Mon Sep 17 00:00:00 2001 From: nala Date: Thu, 29 Oct 2026 13:03:02 +0200 Subject: [PATCH] feat(clusters): prepare evidence-based five forces analysis --- .../consolidated-review-matrix-2026-10-03.md | 12 + ...ompetitiveness-sequential-qa-20261029.json | 38 +++ .../unification/traceability-and-manual.md | 12 + docs/investmoz/v2/auditoria-portal/backlog.md | 12 + resources/js/lib/cluster-competitiveness.ts | 107 ++++++++ .../js/pages/structures/competitiveness.tsx | 237 ++++++++++++++++++ resources/js/pages/structures/editor.tsx | 2 + .../frontend/cluster-competitiveness.test.mjs | 101 ++++++++ 8 files changed, 521 insertions(+) create mode 100644 docs/investmoz/unification/competitiveness-sequential-qa-20261029.json create mode 100644 resources/js/lib/cluster-competitiveness.ts create mode 100644 resources/js/pages/structures/competitiveness.tsx create mode 100644 tests/frontend/cluster-competitiveness.test.mjs diff --git a/docs/investmoz/consolidated-review-matrix-2026-10-03.md b/docs/investmoz/consolidated-review-matrix-2026-10-03.md index 1f1546e1..3efb9d7a 100644 --- a/docs/investmoz/consolidated-review-matrix-2026-10-03.md +++ b/docs/investmoz/consolidated-review-matrix-2026-10-03.md @@ -1826,3 +1826,15 @@ Estado actual: `investor-usability-git-delivery-20261028.json`. Os relatórios Q Pedido expresso da Tempo: preencher formulário de IVA ou IRPC, em vez de abrir legislação. Base preservada: entrega instalada 789fffd0, Modelo A e aquisições/apuramento já aceites, L6 M20/M22 instalado em 5aaf1a56 e incrementos de L8. Nenhuma perda confirmada neste recorte; não se apagam as aceitações nem se repetem provas fiscais anteriores. Fontes reutilizadas do inventário de 20 exemplares acessíveis em outputs/irpc-l6-values-forms/accessible-source-inventory.json da QA anterior, incluindo temporários identificados; inventário do corpus e originais preservados. M22: três páginas conferidas visualmente nesta revisão, SHA256 4b61bd83d1fbcfd3b7b5c5523cea5923f7d0f65dc4c004eff58e5db38f763c88. Modelo A: correspondência e divergências aceites em modelo-a-reconciliacao-20261021.md; sem nova validação de proveniência. Incremento em preparação QA: entrada directa em Documentos e Formulários, preenchimento manual dos campos correspondidos de IVA e subconjunto M22, identificação/período, revisão, impressão e ficheiro privado para recuperação mediante confirmação. Escolha técnica: sem persistência no servidor nem ligação automática ao dossier neste incremento; não altera schema, dados, permissões ou cálculos. Ficheiro não publicado. IRPC ajustamentos completos, outros regimes, anexos, assinatura e submissão à AT fora da cobertura; L8 permanece parcial. Ainda sem instalação deste incremento. Próximas provas apenas do delta: fidelidade da recuperação, rejeição de ficheiros incompatíveis sem perda, formatos/valores desconhecidos, acesso público e compilação. + + +## 29/10/2026 — ACT-20261029-01, inventário antes da verificação do incremento + +Continuação sequencial solicitada pela Tempo. Correspondência: ACT-20261029-01 reutiliza D-02 §8, clusters/cadeias e a orientação metodológica documentada em 23/10. Concluído previamente e preservado: gestão de clusters, etapas, ligações, lacunas e relações auditadas; disponibilidade instalada de carteira/âncoras e formulários fiscais conforme recibos de 28/10. Nenhuma perda nova confirmada nesta inspecção de código; não foi feita nova comparação de dados ou alegada prova do servidor. A pasta principal está em fde644af, enquanto o último recibo identifica 332c16eb instalado; reconciliar o delta sobre essa origem antes de instalar, sem substituir o destino pela pasta principal. + +Incremento em preparação: diagnóstico qualitativo das cinco forças na edição do cluster, sector/região editáveis, fonte/período/evidência/justificação/limites/oportunidade por força; indeterminação explícita, ficheiro privado transferível e recuperação com confirmação. Sem alteração de schema, dados, permissões, publicações ou motores fiscais. Persistência no servidor, ligação territorial canónica, dados sectoriais fundamentados e consulta pública permanecem por implementar; ACT-01 não está concluída. Verificação dirigida e instalação ainda pendentes neste registo inicial. Restantes ACT-02 a 16 mantêm o estado anterior e a sequência, sem trabalho SGF nesta passagem. +### ACT-20261029-01 — candidato sequencial preparado, 29/10/2026 + +Primeiro incremento implementado, **parcial e ainda não instalado**: gestão → clusters → editar → Competitividade. As cinco forças conservam fonte, período, evidência, apreciação, limites e oportunidade; sector e região explícitos, sem inferências automáticas. Transferência de rascunhos JSON e recuperação restrita ao mesmo cluster, com confirmação antes de substituir. Quatro testes frontend dirigidos, TypeScript e compilação QA passaram tanto na pasta de trabalho como no candidato baseado em 332c16eb. Guarda C132 local passou; não foram efectuados testes nem escritas na base activa. A edição existente recebe apenas a inclusão do componente; contratos e permissões existentes preservados. + +Origem reconciliada: main remoto e revisão pública conferidos em 332c16eb em 29/10. Candidato em C:/laragon/www/portal-investidor-competitiveness-qa-20261029, ramo codex/competitiveness-sequential-20261029. Consulta do estado remoto não equivale a inventário novo da base. Sessão SSH anterior encerrada; autenticação por chave recusada. Instalação requer autenticação SSH, backup fresco, restauro isolado, guarda de versão, pipeline existente e conferência posterior. Não instalar a pasta principal sobre o destino. A sequência solicitada mantém ACT-02 a 16 por iniciar após a instalação e o saldo de ACT-01; persistência no servidor, cobertura factual e consulta pública continuam pendentes. Sem declaração de conferência visual autenticada. \ No newline at end of file diff --git a/docs/investmoz/unification/competitiveness-sequential-qa-20261029.json b/docs/investmoz/unification/competitiveness-sequential-qa-20261029.json new file mode 100644 index 00000000..70aec7ff --- /dev/null +++ b/docs/investmoz/unification/competitiveness-sequential-qa-20261029.json @@ -0,0 +1,38 @@ +{ + "activity": "ACT-20261029-01", + "status": "partial_candidate_verified_not_installed", + "baseline": "332c16ebb555e8d6b244fa519bc777d89c1a8b8f", + "branch": "codex/competitiveness-sequential-20261029", + "target": "https://teste-investidor.tempo.co.mz/", + "checks": { + "diff": "passed", + "types": "passed", + "diagnostics_C132": "passed_in_original_workspace", + "frontend_tests": 4, + "isolated_qa_build": "passed" + }, + "active_database_writes": 0, + "schema_changes": false, + "permission_changes": false, + "coverage": [ + "five qualitative forces in existing cluster management", + "sector and regional scope", + "evidence, source, period, rationale, limitations and investment opportunity", + "explicit uncertainty", + "JSON download and confirmed same-cluster recovery" + ], + "remaining": [ + "authenticated visual verification", + "server persistence and canonical territorial scope", + "factual sector and regional data", + "public consultation", + "fresh target backup and isolated restore", + "existing pipeline installation and post-install preservation verification" + ], + "source_sha256": { + "resources/js/lib/cluster-competitiveness.ts": "1ed496f51f020da9f0c02ab0aa324b58a9dd17fa34a10d8c1a90d9915800fecb", + "resources/js/pages/structures/competitiveness.tsx": "55338c144cefae4f91cef765b8e5e8b28ceb6c76375d46943c3c3430aeddc68e", + "resources/js/pages/structures/editor.tsx": "080a90ab8607f05ec493e560ed8280a6f7eec743ed1954f0057a0ce6caa09208", + "tests/frontend/cluster-competitiveness.test.mjs": "f48a9ed872f49ad900424daab4689cb0eeeb31cc93ebf1c608408c0a8c2b435b" + } +} diff --git a/docs/investmoz/unification/traceability-and-manual.md b/docs/investmoz/unification/traceability-and-manual.md index a6f2752c..a09fc6e7 100644 --- a/docs/investmoz/unification/traceability-and-manual.md +++ b/docs/investmoz/unification/traceability-and-manual.md @@ -7384,3 +7384,15 @@ Estado actual: `investor-usability-git-delivery-20261028.json`. Os relatórios Q Pedido expresso da Tempo: preencher formulário de IVA ou IRPC, em vez de abrir legislação. Base preservada: entrega instalada 789fffd0, Modelo A e aquisições/apuramento já aceites, L6 M20/M22 instalado em 5aaf1a56 e incrementos de L8. Nenhuma perda confirmada neste recorte; não se apagam as aceitações nem se repetem provas fiscais anteriores. Fontes reutilizadas do inventário de 20 exemplares acessíveis em outputs/irpc-l6-values-forms/accessible-source-inventory.json da QA anterior, incluindo temporários identificados; inventário do corpus e originais preservados. M22: três páginas conferidas visualmente nesta revisão, SHA256 4b61bd83d1fbcfd3b7b5c5523cea5923f7d0f65dc4c004eff58e5db38f763c88. Modelo A: correspondência e divergências aceites em modelo-a-reconciliacao-20261021.md; sem nova validação de proveniência. Incremento em preparação QA: entrada directa em Documentos e Formulários, preenchimento manual dos campos correspondidos de IVA e subconjunto M22, identificação/período, revisão, impressão e ficheiro privado para recuperação mediante confirmação. Escolha técnica: sem persistência no servidor nem ligação automática ao dossier neste incremento; não altera schema, dados, permissões ou cálculos. Ficheiro não publicado. IRPC ajustamentos completos, outros regimes, anexos, assinatura e submissão à AT fora da cobertura; L8 permanece parcial. Ainda sem instalação deste incremento. Próximas provas apenas do delta: fidelidade da recuperação, rejeição de ficheiros incompatíveis sem perda, formatos/valores desconhecidos, acesso público e compilação. + + +## 29/10/2026 — ACT-20261029-01, inventário antes da verificação do incremento + +Continuação sequencial solicitada pela Tempo. Correspondência: ACT-20261029-01 reutiliza D-02 §8, clusters/cadeias e a orientação metodológica documentada em 23/10. Concluído previamente e preservado: gestão de clusters, etapas, ligações, lacunas e relações auditadas; disponibilidade instalada de carteira/âncoras e formulários fiscais conforme recibos de 28/10. Nenhuma perda nova confirmada nesta inspecção de código; não foi feita nova comparação de dados ou alegada prova do servidor. A pasta principal está em fde644af, enquanto o último recibo identifica 332c16eb instalado; reconciliar o delta sobre essa origem antes de instalar, sem substituir o destino pela pasta principal. + +Incremento em preparação: diagnóstico qualitativo das cinco forças na edição do cluster, sector/região editáveis, fonte/período/evidência/justificação/limites/oportunidade por força; indeterminação explícita, ficheiro privado transferível e recuperação com confirmação. Sem alteração de schema, dados, permissões, publicações ou motores fiscais. Persistência no servidor, ligação territorial canónica, dados sectoriais fundamentados e consulta pública permanecem por implementar; ACT-01 não está concluída. Verificação dirigida e instalação ainda pendentes neste registo inicial. Restantes ACT-02 a 16 mantêm o estado anterior e a sequência, sem trabalho SGF nesta passagem. +### ACT-20261029-01 — candidato sequencial preparado, 29/10/2026 + +Primeiro incremento implementado, **parcial e ainda não instalado**: gestão → clusters → editar → Competitividade. As cinco forças conservam fonte, período, evidência, apreciação, limites e oportunidade; sector e região explícitos, sem inferências automáticas. Transferência de rascunhos JSON e recuperação restrita ao mesmo cluster, com confirmação antes de substituir. Quatro testes frontend dirigidos, TypeScript e compilação QA passaram tanto na pasta de trabalho como no candidato baseado em 332c16eb. Guarda C132 local passou; não foram efectuados testes nem escritas na base activa. A edição existente recebe apenas a inclusão do componente; contratos e permissões existentes preservados. + +Origem reconciliada: main remoto e revisão pública conferidos em 332c16eb em 29/10. Candidato em C:/laragon/www/portal-investidor-competitiveness-qa-20261029, ramo codex/competitiveness-sequential-20261029. Consulta do estado remoto não equivale a inventário novo da base. Sessão SSH anterior encerrada; autenticação por chave recusada. Instalação requer autenticação SSH, backup fresco, restauro isolado, guarda de versão, pipeline existente e conferência posterior. Não instalar a pasta principal sobre o destino. A sequência solicitada mantém ACT-02 a 16 por iniciar após a instalação e o saldo de ACT-01; persistência no servidor, cobertura factual e consulta pública continuam pendentes. Sem declaração de conferência visual autenticada. \ No newline at end of file diff --git a/docs/investmoz/v2/auditoria-portal/backlog.md b/docs/investmoz/v2/auditoria-portal/backlog.md index 747d0beb..df7f72fd 100644 --- a/docs/investmoz/v2/auditoria-portal/backlog.md +++ b/docs/investmoz/v2/auditoria-portal/backlog.md @@ -3006,3 +3006,15 @@ AUD-SIM-INT01 through AUD-SIM-INT08 are recorded in the same matrix and manual. Actualização QA de 20/10/2026: os mesmos oito itens foram reconciliados no manual e nas duas tabelas existentes. Data com mensagem junto ao campo; inventário dos parâmetros efectivos (zero regras fiscais QA); controlos matemáticos independentes; aviso de recálculo; quatro relatórios reais do exportador com 6/6/6/5 páginas; consulta fiscal com erro/nova tentativa e traduções comuns verificados. O formulário personalizado já não exige contexto fiscal que não envia ao cálculo. Restam continuidade autenticada entre dois dossiers/nova sessão e histórico, mudança de parâmetros, controlos delimitados de cash-flow/plano, impressão nativa Edge/comparação pela interface e mensagens dinâmicas/falhas dirigidas de gravação/exportação. Estimativa restante **6–10 h**, distinta das 18–30 h iniciais. Sem main, servidor remoto, importação ou activação fiscal. Exemplos PDF são renderizações do DOCX; não certificam impressão do Edge. Ver o saldo detalhado AUD-SIM-INT01–08 no mesmo manual. Continuação delimitada: dois dossiers e nova entrada conferidos no navegador; histórico agora visível no percurso de pequeno negócio, hashes preservados; Abrir cálculo encaminha para a etapa financeira; repetição de gravação após 503 corrigida e conferida; controlos independentes de tesouraria/plano passaram. CSV e Word descarregados pela interface coincidem nos valores comuns e apresentam a receita completa do crédito. Seis páginas do DOCX conferidas; PDF nativo ainda aguarda gravação pelo utilizador. QA tem zero regras com visibilidade completa, mas existem três originais fiscais versionados e análises/referências reutilizáveis; não se concluiu ausência na homologação sem autenticação SSH. Versões sintéticas testadas apenas em memória, sem cadastro/activação. Encontrada e corrigida perda dos pressupostos da tesouraria: snapshot no JSON existente, recuperação por dossier e relatório, sem migration; testes HTTP/frontend passaram, falta conferência visual deste novo delta. Saldo **2–4 h** após acessos/ficheiro, detalhado no mesmo manual; integração do motor fiscal permanece separada e não foi implementada. Sem main ou intervenção remota. + + +## 29/10/2026 — ACT-20261029-01, inventário antes da verificação do incremento + +Continuação sequencial solicitada pela Tempo. Correspondência: ACT-20261029-01 reutiliza D-02 §8, clusters/cadeias e a orientação metodológica documentada em 23/10. Concluído previamente e preservado: gestão de clusters, etapas, ligações, lacunas e relações auditadas; disponibilidade instalada de carteira/âncoras e formulários fiscais conforme recibos de 28/10. Nenhuma perda nova confirmada nesta inspecção de código; não foi feita nova comparação de dados ou alegada prova do servidor. A pasta principal está em fde644af, enquanto o último recibo identifica 332c16eb instalado; reconciliar o delta sobre essa origem antes de instalar, sem substituir o destino pela pasta principal. + +Incremento em preparação: diagnóstico qualitativo das cinco forças na edição do cluster, sector/região editáveis, fonte/período/evidência/justificação/limites/oportunidade por força; indeterminação explícita, ficheiro privado transferível e recuperação com confirmação. Sem alteração de schema, dados, permissões, publicações ou motores fiscais. Persistência no servidor, ligação territorial canónica, dados sectoriais fundamentados e consulta pública permanecem por implementar; ACT-01 não está concluída. Verificação dirigida e instalação ainda pendentes neste registo inicial. Restantes ACT-02 a 16 mantêm o estado anterior e a sequência, sem trabalho SGF nesta passagem. +### ACT-20261029-01 — candidato sequencial preparado, 29/10/2026 + +Primeiro incremento implementado, **parcial e ainda não instalado**: gestão → clusters → editar → Competitividade. As cinco forças conservam fonte, período, evidência, apreciação, limites e oportunidade; sector e região explícitos, sem inferências automáticas. Transferência de rascunhos JSON e recuperação restrita ao mesmo cluster, com confirmação antes de substituir. Quatro testes frontend dirigidos, TypeScript e compilação QA passaram tanto na pasta de trabalho como no candidato baseado em 332c16eb. Guarda C132 local passou; não foram efectuados testes nem escritas na base activa. A edição existente recebe apenas a inclusão do componente; contratos e permissões existentes preservados. + +Origem reconciliada: main remoto e revisão pública conferidos em 332c16eb em 29/10. Candidato em C:/laragon/www/portal-investidor-competitiveness-qa-20261029, ramo codex/competitiveness-sequential-20261029. Consulta do estado remoto não equivale a inventário novo da base. Sessão SSH anterior encerrada; autenticação por chave recusada. Instalação requer autenticação SSH, backup fresco, restauro isolado, guarda de versão, pipeline existente e conferência posterior. Não instalar a pasta principal sobre o destino. A sequência solicitada mantém ACT-02 a 16 por iniciar após a instalação e o saldo de ACT-01; persistência no servidor, cobertura factual e consulta pública continuam pendentes. Sem declaração de conferência visual autenticada. \ No newline at end of file diff --git a/resources/js/lib/cluster-competitiveness.ts b/resources/js/lib/cluster-competitiveness.ts new file mode 100644 index 00000000..6f5b4dd4 --- /dev/null +++ b/resources/js/lib/cluster-competitiveness.ts @@ -0,0 +1,107 @@ +export const forceLabels = { + rivalry: 'Rivalidade entre concorrentes', + entrants: 'Ameaça de novos entrantes', + substitutes: 'Ameaça de produtos ou serviços substitutos', + suppliers: 'Poder negocial dos fornecedores', + buyers: 'Poder negocial dos clientes', +} as const; + +export type Force = keyof typeof forceLabels; +export type Assessment = { + pressure: '' | 'low' | 'medium' | 'high'; + evidence: string; + source: string; + period: string; + rationale: string; + limitations: string; + opportunity: string; +}; +export type CompetitivenessDraft = { + version: 1; + clusterId: string; + sector: string; + region: string; + forces: Record; +}; + +export const forces = Object.keys(forceLabels) as Force[]; +const fields = [ + 'evidence', + 'source', + 'period', + 'rationale', + 'limitations', + 'opportunity', +] as const; + +export function newCompetitivenessDraft( + clusterId: string, + sector: string, +): CompetitivenessDraft { + return { + version: 1, + clusterId, + sector, + region: '', + forces: Object.fromEntries( + forces.map((force) => [ + force, + { + pressure: '', + evidence: '', + source: '', + period: '', + rationale: '', + limitations: '', + opportunity: '', + }, + ]), + ) as Record, + }; +} + +// A pressure is an author's qualitative assessment, never inferred from missing data. +export function assessmentReady(row: Assessment): boolean { + return ( + ['low', 'medium', 'high'].includes(row.pressure) && + fields.every((field) => row[field].trim().length > 0) + ); +} + +export function diagnosticReady(draft: CompetitivenessDraft): boolean { + return ( + Boolean(draft.sector.trim() && draft.region.trim()) && + forces.every((force) => assessmentReady(draft.forces[force])) + ); +} + +export function readCompetitivenessDraft( + raw: string, + clusterId: string, +): CompetitivenessDraft { + if (raw.length > 250_000) + throw Error('O ficheiro excede o limite de 250 KB.'); + const input = JSON.parse(raw); + if (!input || input.version !== 1 || input.clusterId !== clusterId) { + throw Error( + 'O ficheiro não corresponde a este cluster ou tem uma versão incompatível.', + ); + } + const text = (value: unknown, max: number): string => { + if (typeof value !== 'string' || value.length > max) + throw Error('O ficheiro contém campos inválidos.'); + return value; + }; + const draft = newCompetitivenessDraft(clusterId, text(input.sector, 200)); + draft.region = text(input.region, 1000); + for (const force of forces) { + const row = input.forces?.[force]; + if (!row || !['', 'low', 'medium', 'high'].includes(row.pressure)) { + throw Error('O ficheiro não contém as cinco forças válidas.'); + } + draft.forces[force].pressure = row.pressure; + for (const field of fields) + draft.forces[force][field] = text(row[field], 5000); + } + return draft; +} diff --git a/resources/js/pages/structures/competitiveness.tsx b/resources/js/pages/structures/competitiveness.tsx new file mode 100644 index 00000000..821a07e7 --- /dev/null +++ b/resources/js/pages/structures/competitiveness.tsx @@ -0,0 +1,237 @@ +import { useState } from 'react'; +import { + assessmentReady, + diagnosticReady, + forceLabels, + forces, + newCompetitivenessDraft, + readCompetitivenessDraft, +} from '@/lib/cluster-competitiveness'; +import type { + Assessment, + CompetitivenessDraft, + Force, +} from '@/lib/cluster-competitiveness'; +import { button, control } from '../management/shared'; + +const labels = { + evidence: 'Observações e evidências locais', + source: 'Fonte e referência recuperável (documento, página ou endereço)', + period: 'Período dos dados e data da consulta', + rationale: 'Justificação da pressão atribuída', + limitations: 'Limitações, cobertura e pressupostos', + opportunity: 'Oportunidade de investimento e fundamento', +} as const; + +export default function Competitiveness({ + clusterId, + sector, +}: { + clusterId: string; + sector: string; +}) { + const [draft, setDraft] = useState(() => + newCompetitivenessDraft(clusterId, sector), + ); + const [pending, setPending] = useState(null); + const [message, setMessage] = useState(''); + function change(force: Force, field: keyof Assessment, value: string) { + setDraft((previous) => ({ + ...previous, + forces: { + ...previous.forces, + [force]: { ...previous.forces[force], [field]: value }, + }, + })); + } + function download() { + const url = URL.createObjectURL( + new Blob([JSON.stringify(draft, null, 2)], { + type: 'application/json', + }), + ); + const link = document.createElement('a'); + link.href = url; + link.download = `competitividade-${clusterId}.json`; + link.click(); + setTimeout(() => URL.revokeObjectURL(url), 1000); + setMessage( + 'Ficheiro preparado para transferência. Conserve-o para recuperar a análise.', + ); + } + return ( +
+

+ Competitividade — cinco forças de Porter +

+

+ Análise qualitativa por sector e região ligada a este cluster. + Descreva as evidências e justifique cada pressão; não existe + pontuação automática nem média entre forças. +

+

+ Rascunho nesta página: transfira o ficheiro antes de sair. Não é + gravado no servidor nem publicado. A classificação representa a + apreciação do autor. +

+ + {draft.sector !== sector && ( +

+ O sector da análise difere do sector actual do cluster ( + {sector}). Confira o âmbito antes de reutilizar conclusões. +

+ )} + +

+ {diagnosticReady(draft) + ? 'Cinco forças preenchidas para revisão; fontes e conclusões requerem leitura do autor.' + : 'Diagnóstico incompleto: indique o âmbito e complete as evidências das cinco forças.'} +

+ {forces.map((force) => ( +
+ + {forceLabels[force]} ·{' '} + {assessmentReady(draft.forces[force]) + ? 'Preenchida para revisão' + : 'Indeterminada / incompleta'} + +
+ + {Object.entries(labels).map(([field, label]) => ( +