Compare commits

..
Author SHA1 Message Date
nala 3f7082a3e6 docs: record sequential competitiveness installation and preservation 2026-10-29 13:11:31 +02:00
nala 23ecccfaad feat(clusters): prepare evidence-based five forces analysis
tests / ci (push) Canceled after 0s
2026-10-29 13:03:02 +02:00
11 changed files with 630 additions and 289 deletions
@@ -1828,15 +1828,18 @@ Pedido expresso da Tempo: preencher formulário de IVA ou IRPC, em vez de abrir
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.
## 28/10/2026 — formulários fiscais 332c16eb, QA concluída e autenticação pendente
## 29/10/2026 — ACT-20261029-01, inventário antes da verificação do incremento
Documentos e Formulários disponibiliza no candidato entradas directas IVA/IRPC: identificação, período, 27 campos manuais de Modelo A ou 24 campos manuais de M22, revisão, impressão, ficheiro privado e recuperação com confirmação antes de substituir. Sem geração de zeros, arredondamento, apuramento ou submissão à AT. IRPC permanece parcial: outros ajustamentos, regimes e anexos não incluídos; L8 parcial. Persistência no servidor e associação automática ao dossier não incluídas. Cinco testes frontend e um teste Feature/20 asserções passaram na base QA isolada 127.0.0.1:55441/proactive_investment_20261027_test; tipos, compilação, sintaxe PHP e diff conferidos. Não equivalem a validação visual autenticada. Relatório: docs/investmoz/unification/fiscal-form-preparation-qa-20261028.json.
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.
Candidato publicado apenas no ramo Git codex/fiscal-forms-qa-20261028; main e activo não alterados. Instalação anterior 789fffd0 preservada. Preparação de entrega em outputs/fiscal-forms-delivery-20261028, com guarda do destino, backup fresco, restauro isolado e pipeline existentes; execução remota depende de nova autenticação SSH, pois a sessão anterior foi encerrada.
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.
## 28/10/2026 — formulários fiscais instalados, 332c16eb
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.
### 29/10/2026 — ACT-20261029-01, primeiro incremento instalado: 23ecccfa
A entrega Git autorizada instalou 332c16eb no portal de teste, depois de capacidade, guarda da versão anterior 789fffd0, backup fresco e restauro isolado conferidos. Disponível: Documentos e Formulários → Preencher IVA — Modelo A / Preparar IRPC — Modelo 22. Rotas públicas IVA/IRPC conferidas HTTP200 e com imposto pedido; rótulos dos acessos e preenchimento conferidos nos assets instalados. Estas provas não substituem a conferência visual autenticada, que permanece pendente.
Estado corrente de ACT-01: **parcial — primeiro incremento instalado**, substituindo apenas o estado planeado/preparado deste recorte; ACT-02 a 16 não foram implementadas nesta entrega. Instalado por avanço normal de main e pipeline existente em https://teste-investidor.tempo.co.mz/. Entrada: Gestão → Clusters → Editar → Competitividade — cinco forças de Porter. Preparação qualitativa por sector/região, evidências/fontes/períodos/limites e oportunidades, transferência JSON e recuperação confirmada do mesmo cluster. Sem gravação no servidor nem publicação da análise. Persistência, cobertura factual, território canónico e consulta pública continuam pendentes; esta instalação não encerra o critério amplo ACT-01.
Cobertura: identificação/período, 27 campos manuais IVA e 24 campos manuais M22, conferência, impressão e ficheiro privado para recuperar mediante confirmação. Sem cálculo, submissão ou assinatura AT; sem gravação no servidor nem associação automática ao dossier. Outros ajustamentos, regimes e anexos M22 não incluídos; L8 permanece parcial. Motores, permissões e provas fiscais anteriores preservados. As 176 tabelas de domínio e os 369 ficheiros/configuração foram comparados; apenas cache/sessões operacionais podem diferir. Base interna preservada, sem publicação: 361.464 observações, não empresas únicas; Gurué 435 observações, cobertura parcial. Relatório instalado: docs/investmoz/unification/fiscal-form-preparation-git-delivery-20261028.json. O relatório QA anterior mantém-se como histórico, não estado corrente de instalação.
Provas: backup fresco, restauro isolado sem rede das 176 tabelas; pipeline com lifecycle de schema (3.106 asserções), 59 testes Unit/320 asserções e 1.180 testes frontend sem falhas, tipos e compilação; quatro ficheiros do incremento conferidos por SHA-256 no instalado; C132 no destino passou. Comparação após instalação: todas as 176 tabelas de domínio preservadas, diferenças operacionais apenas em cache/sessões; 369 ficheiros/configuração idênticos. Acesso anónimo ao catálogo privado recusado HTTP401. Zero migrações/importações, nenhuma nova divulgação nem mudança de permissões; versão anterior 332c16eb preservada. Conferência visual autenticada permanece pendente. Recibo: `docs/investmoz/unification/competitiveness-sequential-qa-20261029.json`, em docs/investmoz/unification. As provas aceites anteriores foram reutilizadas; não foram reabertas por esta entrega.
@@ -0,0 +1,147 @@
{
"activity": "ACT-20261029-01",
"status": "partial_increment_installed_preservation_verified_visual_pending",
"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"
],
"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"
},
"candidate": "23ecccfaad1053f55bf0591fd242cb5fa3406c38",
"backup": {
"stage": "/var/backups/investmoz-competitiveness-delivery-20261029/backup",
"created_at": "2026-10-29T11:04:46Z",
"target": {
"database": "teste_investidor",
"port": 5432
},
"tables": 176,
"files_verified": 369,
"dump_sha256": "7979fa2b3fe1f69e62aad9fadf60a60d7b068e198cdc291a8fc79d409d67c7ca",
"files_sha256": "8e0022b73cd0074e36f8efef0b7c803a386e24e6e4cf85f567757ff67e311369",
"restore_verified": false
},
"restore": {
"restore_verified": true,
"tables_verified": 176,
"container_network": "none",
"database": "competitiveness_delivery_20261029_test",
"port": 55441,
"source_dump_sha256": "7979fa2b3fe1f69e62aad9fadf60a60d7b068e198cdc291a8fc79d409d67c7ca",
"active_database_writes": 0
},
"preservation": {
"navigation_asset_and_shared_props_verified": true,
"at": "2026-10-29T11:10:44.303271+00:00",
"served_commit": "23ecccfaad1053f55bf0591fd242cb5fa3406c38",
"backup_dump_sha256": "7979fa2b3fe1f69e62aad9fadf60a60d7b068e198cdc291a8fc79d409d67c7ca",
"prior_public_tables_checked": 176,
"prior_domain_rows_preserved": true,
"new_migration_registry_rows": 0,
"operational_delta": {
"cache": {
"before": {
"rows": 119,
"checksum": "5f3c36637fbf1e9dc4700066d4c45530"
},
"after": {
"rows": 119,
"checksum": "b1c9949cfacc34906c406e27e95cbd9b"
}
},
"sessions": {
"before": {
"rows": 37,
"checksum": "b0e409c4c77c58b48252d9847c3c61ef"
},
"after": {
"rows": 38,
"checksum": "f4d94db55488bc0bcf6f727f222473c1"
}
}
},
"persistent_files_and_configuration_identical": 369,
"private_catalogue": {
"observations": 361464,
"distinct_administrative_observations": 214299,
"preserved_repetitions": 147165,
"unmapped_territory": 205608
},
"preservation_identity": "postgres, matching backup reader; no actor impersonation",
"anonymous_query_status": 401,
"public_revision_confirmed": true,
"actual_team_UI_journey": "pending authenticated browser conference;accepted QA is not repeated",
"unique_company_count": null,
"publication": false,
"national_coverage": "partial",
"investment_conclusion": "hypothesis,not proved demand or viability",
"L8": "unchanged,partial"
},
"installed_code": {
"candidate": "23ecccfaad1053f55bf0591fd242cb5fa3406c38",
"source_files_verified": 4,
"diagnostics_C132": {
"result": "passed",
"case": "C132",
"source_explicit_constant_OFF": true,
"effective_configuration_OFF": true,
"configuration_cached": false,
"database_connections_used": 0,
"active_writes": 0
},
"active_writes": 0
},
"pipeline": {
"unit_tests": 59,
"frontend_failures": 0,
"schema_assertions": 3106,
"checks": [
"\u001b[30;42mOK (1 test, 3106 assertions)\u001b[0m",
"✓ built in 34.50s",
"\u001b[30;42mOK (59 tests, 320 assertions)\u001b[0m",
"# tests 1180",
"# pass 1180",
"# fail 0"
],
"frontend_tests": 1180,
"result": {
"status": "installed",
"commit": "23ecccfaad1053f55bf0591fd242cb5fa3406c38",
"release": "/var/www/investmoz-homologacao/releases/23ecccfaad1053f55bf0591fd242cb5fa3406c38-0e0b8a65",
"previous_release": "/var/www/investmoz-homologacao/releases/332c16ebb555e8d6b244fa519bc777d89c1a8b8f-f0dfc218",
"database_migrations": 0,
"content_imports": 0,
"requested_commits": [
"23ecccfaad1053f55bf0591fd242cb5fa3406c38"
],
"finished_at": "2026-10-29T11:10:02.884033+00:00"
},
"unit_assertions": 320
}
}
@@ -1,213 +0,0 @@
{
"status": "installed_delta_verified_authenticated_visual_conference_pending",
"candidate": "332c16ebb555e8d6b244fa519bc777d89c1a8b8f",
"expected_installed": "789fffd098fa222c85e1283de07f23ee6f42bd04",
"target": "https://teste-investidor.tempo.co.mz/",
"coverage": {
"IVA": "27 manually editable Modelo A fields; source correspondence reused",
"IRPC": "24 manually editable M22 fields from Q10/Q11 general regime/Q12; partial preparation",
"flow": [
"identification and period",
"amounts",
"review",
"download private recovery file",
"explicit recovery confirmation",
"print"
],
"entry": "Documentos e Formulários → IVA or IRPC"
},
"limits": [
"No automatic calculations or eligibility validation",
"No AT submission or signature",
"No server persistence or automatic dossier link",
"Remaining M22 adjustments, other regimes and annexes not covered",
"Manual credit entries require parcel conference; existing calculation engines unchanged",
"L8 remains partial",
"Authenticated visual browser validation not demonstrated"
],
"preserved": [
"installed 789fffd0 features",
"accepted Modelo A and M20/M22 proofs",
"L8 accepted increments",
"all originals, data, roles and internal supply base; no active writes"
],
"QA": {
"frontend_tests": 5,
"feature_tests": 1,
"feature_assertions": 20,
"isolated_database": "proactive_investment_20261027_test",
"port": 55441,
"types": "passed",
"build": "passed",
"PHP_syntax": "passed",
"diff_check": "passed",
"source_visual_check": "M22 three existing page renders inspected; accepted IVA mapping reused"
},
"schema_changes": false,
"role_changes": false,
"new_publications": false,
"active_changes": "332c16eb installed through normal main Git pipeline",
"main_ancestor_verified": true,
"delivery_next_steps": [
"Authenticated visual conference of filling, review, download and recovery"
],
"source_code_sha256": {
"resources\\js\\pages\\portal\\fiscal-forms.tsx": "d27d16b174f6a9784b2ad0a2f6e46563d75192ebe4064d700b0df59e953317e0",
"resources\\js\\lib\\fiscal-form-preparation.ts": "776a563b4ebc1ddc6ceb14e2e8b1e6c9542e8cc9c17053811f6381d83bf8b3ec",
"routes\\web.php": "a25b4dccbf64b123117573ea31b83cfcc1ee642db9b0f95ed2f77387696879d9"
},
"backup": {
"stage": "/var/backups/investmoz-fiscal-forms-delivery-20261028/backup",
"created_at": "2026-10-28T20:07:20Z",
"target": {
"database": "teste_investidor",
"port": 5432
},
"tables": 176,
"files_verified": 369,
"dump_sha256": "341662f3c8f6e6c7b42d18aa468f3270d9d6a08bb908b7f52cbb1e132e5bd976",
"files_sha256": "c6b242850118003792b420f1e2706633489dc874bef664b2245dfbd5dbb50430",
"restore_verified": false
},
"restore": {
"restore_verified": true,
"tables_verified": 176,
"container_network": "none",
"database": "fiscal_forms_delivery_20261028_test",
"port": 55441,
"source_dump_sha256": "341662f3c8f6e6c7b42d18aa468f3270d9d6a08bb908b7f52cbb1e132e5bd976",
"active_database_writes": 0
},
"preservation": {
"navigation_asset_and_shared_props_verified": true,
"at": "2026-10-28T20:13:13.258269+00:00",
"served_commit": "332c16ebb555e8d6b244fa519bc777d89c1a8b8f",
"backup_dump_sha256": "341662f3c8f6e6c7b42d18aa468f3270d9d6a08bb908b7f52cbb1e132e5bd976",
"prior_public_tables_checked": 176,
"prior_domain_rows_preserved": true,
"new_migration_registry_rows": 0,
"operational_delta": {
"cache": {
"before": {
"rows": 119,
"checksum": "c98548e1038986975b95a959d469b828"
},
"after": {
"rows": 119,
"checksum": "5375b8b28b8af40c8c3ffc891975d6d5"
}
},
"sessions": {
"before": {
"rows": 25,
"checksum": "bcf0421fabf8473d5862941ec18e96af"
},
"after": {
"rows": 28,
"checksum": "78066be68c90956a3ac31a90bbeba676"
}
}
},
"persistent_files_and_configuration_identical": 369,
"private_catalogue": {
"observations": 361464,
"distinct_administrative_observations": 214299,
"preserved_repetitions": 147165,
"unmapped_territory": 205608
},
"preservation_identity": "postgres, matching backup reader; no actor impersonation",
"anonymous_query_status": 401,
"public_revision_confirmed": true,
"actual_team_UI_journey": "pending authenticated browser conference;accepted QA is not repeated",
"unique_company_count": null,
"publication": false,
"national_coverage": "partial",
"investment_conclusion": "hypothesis,not proved demand or viability",
"L8": "unchanged,partial"
},
"public_delta": {
"at_UTC": "2026-10-28T20:13:02.411009+00:00",
"candidate": "332c16ebb555e8d6b244fa519bc777d89c1a8b8f",
"public_pages": [
{
"tax": "iva",
"HTTP": 200,
"component": "portal/fiscal-forms",
"requested_form": "iva"
},
{
"tax": "irpc",
"HTTP": 200,
"component": "portal/fiscal-forms",
"requested_form": "irpc"
}
],
"assets": {
"fiscal-forms": {
"asset": "assets/fiscal-forms-CQflZEeP.js",
"sha256": "f559122ea47ac15d061ed4b2e008beeb0e9815626afd50315bd465ea6109bde0",
"expected_labels": 4
},
"services": {
"asset": "assets/services-DOFOHuLj.js",
"sha256": "4a9de7c6dd6d1d48adf55e47b6dd8561af2a53163f63eb14b0f8d9d135fd66d7",
"expected_labels": 2
}
},
"authenticated_visual_conference": "pending; connector availability not established",
"AT_submission": false,
"server_draft_persistence": false,
"L8": "partial"
},
"pipeline": {
"reports": [
{
"status": "already_installed",
"commit": "332c16ebb555e8d6b244fa519bc777d89c1a8b8f",
"requested_commits": [
"332c16ebb555e8d6b244fa519bc777d89c1a8b8f"
],
"finished_at": "2026-10-28T20:12:04.008171+00:00"
},
{
"status": "installed",
"commit": "332c16ebb555e8d6b244fa519bc777d89c1a8b8f",
"release": "/var/www/investmoz-homologacao/releases/332c16ebb555e8d6b244fa519bc777d89c1a8b8f-f0dfc218",
"previous_release": "/var/www/investmoz-homologacao/releases/789fffd098fa222c85e1283de07f23ee6f42bd04-29ab1f0d",
"database_migrations": 0,
"content_imports": 0,
"requested_commits": [
"332c16ebb555e8d6b244fa519bc777d89c1a8b8f"
],
"finished_at": "2026-10-28T20:12:02.675406+00:00"
}
],
"build_logs": [
{
"file": "/var/lib/investmoz-deploy/logs/1793218102227589181.log",
"sha256": "670358412945027ec26a2c7e2129a06b0af164aac26990db5d73c9aeb5ba9b6c",
"summaries": [
"\u001b[30;42mOK (1 test, 3106 assertions)\u001b[0m",
"✓ built in 32.91s",
"\u001b[30;42mOK (59 tests, 320 assertions)\u001b[0m",
"# tests 1176",
"# pass 1176",
"# fail 0"
]
}
]
},
"delivery_completed": [
"SSH authentication",
"capacity and baseline guard",
"fresh backup",
"isolated restore",
"normal Git main delivery",
"existing pipeline: 1176 frontend tests passed",
"public route/assets delta",
"176 table preservation comparison and 369 identical files/configuration"
],
"verification_notes": [
"Initial X-Inertia probe without matching asset version received protocol HTTP409. Normal initial page requests returned200; initial public page payload and installed assets verified. Failed probe receipt retained; no application change needed."
]
}
@@ -1,63 +0,0 @@
{
"status": "QA_passed_not_installed_SSH_authentication_required",
"candidate": "332c16ebb555e8d6b244fa519bc777d89c1a8b8f",
"expected_installed": "789fffd098fa222c85e1283de07f23ee6f42bd04",
"target": "https://teste-investidor.tempo.co.mz/",
"coverage": {
"IVA": "27 manually editable Modelo A fields; source correspondence reused",
"IRPC": "24 manually editable M22 fields from Q10/Q11 general regime/Q12; partial preparation",
"flow": [
"identification and period",
"amounts",
"review",
"download private recovery file",
"explicit recovery confirmation",
"print"
],
"entry": "Documentos e Formulários → IVA or IRPC"
},
"limits": [
"No automatic calculations or eligibility validation",
"No AT submission or signature",
"No server persistence or automatic dossier link",
"Remaining M22 adjustments, other regimes and annexes not covered",
"Manual credit entries require parcel conference; existing calculation engines unchanged",
"L8 remains partial",
"Authenticated visual browser validation not demonstrated"
],
"preserved": [
"installed 789fffd0 features",
"accepted Modelo A and M20/M22 proofs",
"L8 accepted increments",
"all originals, data, roles and internal supply base; no active writes"
],
"QA": {
"frontend_tests": 5,
"feature_tests": 1,
"feature_assertions": 20,
"isolated_database": "proactive_investment_20261027_test",
"port": 55441,
"types": "passed",
"build": "passed",
"PHP_syntax": "passed",
"diff_check": "passed",
"source_visual_check": "M22 three existing page renders inspected; accepted IVA mapping reused"
},
"schema_changes": false,
"role_changes": false,
"new_publications": false,
"active_changes": false,
"main_ancestor_verified": true,
"delivery_next_steps": [
"authenticate SSH using existing strict host-key bridge",
"recheck installed baseline, capacity and schema contract",
"fresh backup and isolated restoration",
"normal Git main delivery and existing guarded pipeline",
"installed route/assets verification and full preservation delta"
],
"source_code_sha256": {
"resources\\js\\pages\\portal\\fiscal-forms.tsx": "d27d16b174f6a9784b2ad0a2f6e46563d75192ebe4064d700b0df59e953317e0",
"resources\\js\\lib\\fiscal-form-preparation.ts": "776a563b4ebc1ddc6ceb14e2e8b1e6c9542e8cc9c17053811f6381d83bf8b3ec",
"routes\\web.php": "a25b4dccbf64b123117573ea31b83cfcc1ee642db9b0f95ed2f77387696879d9"
}
}
@@ -7386,15 +7386,18 @@ Pedido expresso da Tempo: preencher formulário de IVA ou IRPC, em vez de abrir
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.
## 28/10/2026 — formulários fiscais 332c16eb, QA concluída e autenticação pendente
## 29/10/2026 — ACT-20261029-01, inventário antes da verificação do incremento
Documentos e Formulários disponibiliza no candidato entradas directas IVA/IRPC: identificação, período, 27 campos manuais de Modelo A ou 24 campos manuais de M22, revisão, impressão, ficheiro privado e recuperação com confirmação antes de substituir. Sem geração de zeros, arredondamento, apuramento ou submissão à AT. IRPC permanece parcial: outros ajustamentos, regimes e anexos não incluídos; L8 parcial. Persistência no servidor e associação automática ao dossier não incluídas. Cinco testes frontend e um teste Feature/20 asserções passaram na base QA isolada 127.0.0.1:55441/proactive_investment_20261027_test; tipos, compilação, sintaxe PHP e diff conferidos. Não equivalem a validação visual autenticada. Relatório: docs/investmoz/unification/fiscal-form-preparation-qa-20261028.json.
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.
Candidato publicado apenas no ramo Git codex/fiscal-forms-qa-20261028; main e activo não alterados. Instalação anterior 789fffd0 preservada. Preparação de entrega em outputs/fiscal-forms-delivery-20261028, com guarda do destino, backup fresco, restauro isolado e pipeline existentes; execução remota depende de nova autenticação SSH, pois a sessão anterior foi encerrada.
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.
## 28/10/2026 — formulários fiscais instalados, 332c16eb
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.
### 29/10/2026 — ACT-20261029-01, primeiro incremento instalado: 23ecccfa
A entrega Git autorizada instalou 332c16eb no portal de teste, depois de capacidade, guarda da versão anterior 789fffd0, backup fresco e restauro isolado conferidos. Disponível: Documentos e Formulários → Preencher IVA — Modelo A / Preparar IRPC — Modelo 22. Rotas públicas IVA/IRPC conferidas HTTP200 e com imposto pedido; rótulos dos acessos e preenchimento conferidos nos assets instalados. Estas provas não substituem a conferência visual autenticada, que permanece pendente.
Estado corrente de ACT-01: **parcial — primeiro incremento instalado**, substituindo apenas o estado planeado/preparado deste recorte; ACT-02 a 16 não foram implementadas nesta entrega. Instalado por avanço normal de main e pipeline existente em https://teste-investidor.tempo.co.mz/. Entrada: Gestão → Clusters → Editar → Competitividade — cinco forças de Porter. Preparação qualitativa por sector/região, evidências/fontes/períodos/limites e oportunidades, transferência JSON e recuperação confirmada do mesmo cluster. Sem gravação no servidor nem publicação da análise. Persistência, cobertura factual, território canónico e consulta pública continuam pendentes; esta instalação não encerra o critério amplo ACT-01.
Cobertura: identificação/período, 27 campos manuais IVA e 24 campos manuais M22, conferência, impressão e ficheiro privado para recuperar mediante confirmação. Sem cálculo, submissão ou assinatura AT; sem gravação no servidor nem associação automática ao dossier. Outros ajustamentos, regimes e anexos M22 não incluídos; L8 permanece parcial. Motores, permissões e provas fiscais anteriores preservados. As 176 tabelas de domínio e os 369 ficheiros/configuração foram comparados; apenas cache/sessões operacionais podem diferir. Base interna preservada, sem publicação: 361.464 observações, não empresas únicas; Gurué 435 observações, cobertura parcial. Relatório instalado: docs/investmoz/unification/fiscal-form-preparation-git-delivery-20261028.json. O relatório QA anterior mantém-se como histórico, não estado corrente de instalação.
Provas: backup fresco, restauro isolado sem rede das 176 tabelas; pipeline com lifecycle de schema (3.106 asserções), 59 testes Unit/320 asserções e 1.180 testes frontend sem falhas, tipos e compilação; quatro ficheiros do incremento conferidos por SHA-256 no instalado; C132 no destino passou. Comparação após instalação: todas as 176 tabelas de domínio preservadas, diferenças operacionais apenas em cache/sessões; 369 ficheiros/configuração idênticos. Acesso anónimo ao catálogo privado recusado HTTP401. Zero migrações/importações, nenhuma nova divulgação nem mudança de permissões; versão anterior 332c16eb preservada. Conferência visual autenticada permanece pendente. Recibo: `docs/investmoz/unification/competitiveness-sequential-qa-20261029.json`, em docs/investmoz/unification. As provas aceites anteriores foram reutilizadas; não foram reabertas por esta entrega.
@@ -3006,3 +3006,20 @@ 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.
### 29/10/2026 — ACT-20261029-01, primeiro incremento instalado: 23ecccfa
Estado corrente de ACT-01: **parcial — primeiro incremento instalado**, substituindo apenas o estado planeado/preparado deste recorte; ACT-02 a 16 não foram implementadas nesta entrega. Instalado por avanço normal de main e pipeline existente em https://teste-investidor.tempo.co.mz/. Entrada: Gestão → Clusters → Editar → Competitividade — cinco forças de Porter. Preparação qualitativa por sector/região, evidências/fontes/períodos/limites e oportunidades, transferência JSON e recuperação confirmada do mesmo cluster. Sem gravação no servidor nem publicação da análise. Persistência, cobertura factual, território canónico e consulta pública continuam pendentes; esta instalação não encerra o critério amplo ACT-01.
Provas: backup fresco, restauro isolado sem rede das 176 tabelas; pipeline com lifecycle de schema (3.106 asserções), 59 testes Unit/320 asserções e 1.180 testes frontend sem falhas, tipos e compilação; quatro ficheiros do incremento conferidos por SHA-256 no instalado; C132 no destino passou. Comparação após instalação: todas as 176 tabelas de domínio preservadas, diferenças operacionais apenas em cache/sessões; 369 ficheiros/configuração idênticos. Acesso anónimo ao catálogo privado recusado HTTP401. Zero migrações/importações, nenhuma nova divulgação nem mudança de permissões; versão anterior 332c16eb preservada. Conferência visual autenticada permanece pendente. Recibo: `docs/investmoz/unification/competitiveness-sequential-qa-20261029.json`, em docs/investmoz/unification. As provas aceites anteriores foram reutilizadas; não foram reabertas por esta entrega.
File diff suppressed because one or more lines are too long
+107
View File
@@ -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<Force, Assessment>;
};
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<Force, Assessment>,
};
}
// 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;
}
@@ -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<CompetitivenessDraft | null>(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 (
<section
className="max-w-3xl space-y-4 rounded border p-4"
aria-label="Diagnóstico de competitividade"
>
<h2 className="text-xl font-semibold">
Competitividade — cinco forças de Porter
</h2>
<p>
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.
</p>
<p>
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.
</p>
<label className="block">
Sector analisado
<input
className={control}
maxLength={200}
value={draft.sector}
onChange={(e) =>
setDraft({ ...draft, sector: e.target.value })
}
/>
</label>
{draft.sector !== sector && (
<p>
O sector da análise difere do sector actual do cluster (
{sector}). Confira o âmbito antes de reutilizar conclusões.
</p>
)}
<label className="block">
Região / província / distrito e abrangência
<input
className={control}
maxLength={1000}
value={draft.region}
onChange={(e) =>
setDraft({ ...draft, region: e.target.value })
}
/>
</label>
<p role="status">
{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.'}
</p>
{forces.map((force) => (
<details key={force} className="rounded border p-3">
<summary className="cursor-pointer font-semibold">
{forceLabels[force]} ·{' '}
{assessmentReady(draft.forces[force])
? 'Preenchida para revisão'
: 'Indeterminada / incompleta'}
</summary>
<div className="space-y-3 pt-3">
<label className="block">
Pressão competitiva
<select
className={control}
value={draft.forces[force].pressure}
onChange={(e) =>
change(force, 'pressure', e.target.value)
}
>
<option value="">Indeterminada</option>
<option value="low">Baixa</option>
<option value="medium">Média</option>
<option value="high">Alta</option>
</select>
</label>
{Object.entries(labels).map(([field, label]) => (
<label key={field} className="block">
{label}
<textarea
className={control}
rows={3}
maxLength={5000}
value={
draft.forces[force][
field as keyof typeof labels
]
}
onChange={(e) =>
change(
force,
field as keyof typeof labels,
e.target.value,
)
}
/>
</label>
))}
</div>
</details>
))}
<button type="button" className={button} onClick={download}>
Transferir análise / rascunho
</button>
<label className="block">
Recuperar ficheiro deste cluster
<input
className={control}
type="file"
accept=".json,application/json"
onChange={async (e) => {
const file = e.target.files?.[0];
e.target.value = '';
if (!file) return;
setPending(null);
try {
if (file.size > 250_000)
throw Error(
'O ficheiro excede o limite de 250 KB.',
);
setPending(
readCompetitivenessDraft(
await file.text(),
clusterId,
),
);
setMessage(
'Ficheiro lido. Confira o âmbito e confirme a substituição dos campos actuais.',
);
} catch (error) {
setMessage(
error instanceof Error
? error.message
: 'Não foi possível ler o ficheiro.',
);
}
}}
/>
</label>
{pending && (
<div className="space-y-2 rounded border p-3">
<p>
Recuperar análise de{' '}
{pending.sector || 'sector não indicado'} em{' '}
{pending.region || 'região não indicada'}? Os campos
actuais serão substituídos. Transfira primeiro o
rascunho actual se o quiser conservar.
</p>
<button
type="button"
className={button}
onClick={() => {
setDraft(pending);
setPending(null);
setMessage('Análise recuperada para edição.');
}}
>
Confirmar recuperação
</button>{' '}
<button
type="button"
className={button}
onClick={() => {
setPending(null);
setMessage(
'Recuperação cancelada; campos actuais preservados.',
);
}}
>
Cancelar
</button>
</div>
)}
{message && <p role="status">{message}</p>}
</section>
);
}
+2
View File
@@ -6,6 +6,7 @@ import { Management, Field, button, control } from '../management/shared';
import { titles, statuses, stages, roles } from './types';
import type { Structure, StructureRecord, Chain } from './types';
import ValueNetwork from './value-network';
import Competitiveness from './competitiveness';
import type { ChainLink, Gap } from './value-network';
function ChainForm({ clusterId, chain }: { clusterId: string; chain?: Chain }) {
@@ -298,6 +299,7 @@ export default function Editor({
</form>
{isCluster && record && (
<>
<Competitiveness key={record.id} clusterId={record.id} sector={record.sector ?? ''} />
<section className="max-w-3xl space-y-4">
<h2 className="text-xl font-semibold">
Projectos associados
@@ -0,0 +1,101 @@
import assert from 'node:assert/strict';
import test from 'node:test';
import {
assessmentReady,
diagnosticReady,
forces,
newCompetitivenessDraft,
readCompetitivenessDraft,
} from '../../resources/js/lib/cluster-competitiveness.ts';
function complete() {
const draft = newCompetitivenessDraft('cluster-a', 'Energia');
draft.region = 'Distrito de demonstração';
for (const force of forces)
Object.assign(draft.forces[force], {
pressure: 'medium',
evidence: 'Observação de demonstração',
source: 'Fonte de demonstração, p. 2',
period: '2025',
rationale: 'Pressão fundamentada na observação',
limitations: 'Amostra parcial',
opportunity: 'Investigar capacidade de armazenamento',
});
return draft;
}
test('ausência de dados permanece indeterminada nas cinco forças independentes', () => {
const draft = newCompetitivenessDraft('cluster-a', 'Energia');
assert.equal(forces.length, 5);
assert.equal(diagnosticReady(draft), false);
draft.forces.buyers.evidence = 'Observação';
assert.equal(draft.forces.suppliers.evidence, '');
assert.ok(forces.every((force) => draft.forces[force].pressure === ''));
});
test('classificação exige todos os fundamentos; âmbito é necessário para o diagnóstico', () => {
const draft = complete();
assert.equal(diagnosticReady(draft), true);
for (const field of [
'evidence',
'source',
'period',
'rationale',
'limitations',
'opportunity',
]) {
const row = { ...draft.forces.buyers, [field]: ' ' };
assert.equal(assessmentReady(row), false, field);
}
draft.region = '';
assert.equal(diagnosticReady(draft), false);
});
test('ficheiro conserva rascunhos, dados e incertezas sem modificar o original', () => {
const draft = complete();
draft.forces.entrants.pressure = '';
const restored = readCompetitivenessDraft(
JSON.stringify(draft),
'cluster-a',
);
assert.deepEqual(restored, draft);
restored.forces.buyers.source = 'Outra fonte';
assert.notEqual(restored.forces.buyers.source, draft.forces.buyers.source);
assert.equal(diagnosticReady(restored), false);
});
test('recuperação rejeita outro cluster, versões, forças ausentes e campos inválidos', () => {
assert.throws(() =>
readCompetitivenessDraft(JSON.stringify(complete()), 'cluster-b'),
);
for (const mutate of [
(d) => {
d.version = 2;
},
(d) => {
delete d.forces.buyers;
},
(d) => {
d.forces.buyers.pressure = 'zero';
},
(d) => {
d.forces.buyers.source = 42;
},
(d) => {
d.forces.buyers.source = 'x'.repeat(5001);
},
(d) => {
d.region = null;
},
]) {
const draft = complete();
mutate(draft);
assert.throws(() =>
readCompetitivenessDraft(JSON.stringify(draft), 'cluster-a'),
);
}
assert.throws(() => readCompetitivenessDraft('null', 'cluster-a'));
assert.throws(() =>
readCompetitivenessDraft('x'.repeat(250001), 'cluster-a'),
);
});