Compare commits

..
8 changed files with 799 additions and 153 deletions
@@ -71,6 +71,7 @@ class PortalServicesController extends Controller
$props = ['section' => $page, 'filters' => $filters, 'countries' => app(CountryCatalogue::class)->all()];
if ($page === 'sectors') {
$props['sectors'] = \App\Services\SectorOrganization::catalog();
$props['sectorDetails'] = $props['sectors']->map(fn (Sector $sector) => app(\App\Services\SectorInvestment::class)->page($sector))->values();
}
if (in_array($page, ['documents', 'legislation'], true)) {
$query = $this->resources();
@@ -1405,14 +1405,20 @@ Retomar na sessão local com SSH autenticado, usando a autorização já regista
Recibo de preparação: `docs/investmoz/unification/sequential-delivery-preparation-20261027.json`. Provas e instrução de passagem: `outputs/sequential-delivery-20261027/`. Os recibos de execução e as duas revisões servidas serão registados apenas depois de execução real na sessão autenticada; este recibo não os substitui.
## 27/10/2026 — correcção delimitada da navegação dos sectores
## 27/10/2026 — L8 e percurso proactivo instalados pela sequência autorizada
Reprodução local autorizada da intenção de `88b39b527859bd420f2e223a7afea89bea38de54`, indisponível no origin/outro ambiente. Base exacta `9fda6d6e12832a3cf25e7314f1f895dd430238c5`; novo ramo `codex/sector-navigation-fix-local-20261027`. Não se afirma identidade binária com o pacote externo. QA concluída; instalação ainda pendente nesta revisão documental.
Entrega1: **L8 `c85e772d`**, anterior `f8d57bdd`. Entrega2, apenas após conferir a primeira: **percurso proactivo `9fda6d6e`**, incluindo `56c0c6a7` e preservando L8. As duas revisões foram conferidas no registo do instalador e no recurso público `/build/revision.json`, com hash do manifest e marcadores compilados dos deltas. `c85e772d` conserva o código aceite de `14360748`; o merge proactivo mantém ambos os fornecedores de linhas do relatório. A referência final servida passa a `9fda6d6e`.
**Causa e alteração:** o índice `/sectores` carregava `sectorDetails` para todos os sectores e apresentava as páginas completas abaixo dos cartões; os cartões passavam a âncoras locais. Removidos esse carregamento e a apresentação agregada. Os cartões usam `sector.url`, preservando as páginas individuais, a especialização Fintech e os caminhos históricos existentes. O índice mantém descrições curtas, contagens e acesso aos sectores, sem repetir guias e oportunidades de todos eles.
A tentativa proactiva em `56c0c6a7` não foi activada: um teste dependia de um ficheiro de outputs existente apenas no QA local. L8 permaneceu em serviço. A correcção `9fda6d6e` torna esse teste portátil com um exemplar mínimo sintético da projecção pública da API, sem perfis, credenciais ou pilotos reais e sem escrita em outputs. O teste dirigido passou; o código da aplicação é byte a byte idêntico a `56c0c6a7`. Não se transportou a base QA nem se saltou a guarda do instalador. A nova tentativa usou o backup/restauro ainda fresco, com capacidade e fonte activa novamente conferidas.
**Preservação:** página individual, `SectorContent`, `SectorEntry`, `SectorInvestment`, `SectorOrganization`, filtros, dados mestres, originais e continuidade para o mesmo dossier não foram alterados. PEC `7fdb6b42` continua no seu ramo QA; não é integrado nesta correcção. L8 instalado e percurso proactivo de `9fda6d6e` preservados. Songo/Gurué privados; sem fixtures QA no activo, publicação, importações, migrations ou alterações de permissões.
A ligação SSH foi restabelecida pelo responsável na janela local, sem credenciais na conversa ou nos recibos. A medição inicial encontrou91,6GiB livres e inodes suficientes; a capacidade foi novamente conferida depois de cada backup. Instalador existente e suas guardas preservados; a versão anterior permaneceu activa até a nova release estar pronta. Os controlos automáticos do instalador foram mantidos, sem nova ronda manual das provas funcionais/económicas aceites.
**Delta demonstrado:** actualizados os dois testes afectados de `SectorCatalogueContentTest` e acrescentado um caso de isolamento/regresso/recarregamento:3 testes/319 asserções, base isolada `proactive_investment_20261027_test`, loopback55441, transacções com rollback; sem `migrate:fresh`. Wayfinder `--with-form`, TypeScript e build QA aprovados. Navegador real:10 cartões, acesso separado a Agronegócio e Turismo, oportunidades próprias, filtro Agronegócio mantido no catálogo, recarregamento da página individual e regresso ao índice sem repetição. Entrada Turismo para o dossier conserva o contexto sectorial na ligação e encaminha o visitante para a autenticação existente; não se repetiu a prova completa de gravação do dossier já aceite. A amostra QA não tem guias editoriais carregados; a preservação dos guias assenta na comparação dos consumidores/fontes, e será conferida no destino sem republicar conteúdo. A captura de imagem do browser devolveu «0 width»; árvore de acessibilidade e testes guardados, sem alegação de captura visual inexistente.
**Backup/restauro fresco em cada etapa:** `/var/backups/investmoz-sequential-20261027/l8/backup` e `/var/backups/investmoz-sequential-20261027/proactive/backup`. Em cada uma, dump consistente e369ficheiros persistentes/configuração verificados; restauro das174tabelas num contentor sem rede, porta55441, com contagens/checksums iguais. Recibos `backup-receipt.json`, `restore-receipt.json`, `schema-permissions-before.json`, `schema-permissions-after.json`, `preservation-receipt.json` e `operational-delta-receipt.json` conservados nesses directórios. Hashes exactos no recibo consolidado.
Requisitos existentes actualizados apenas neste delta: `MASTER-471012ab-0007` e `MASTER-9dee08d0-0017`; não se encerra automaticamente o âmbito mais amplo dessas linhas. Recibo QA: `docs/investmoz/unification/sector-navigation-qa-20261027.json`. Provas locais: `outputs/sector-navigation-fix-20261027/`. Entrega autorizada seguirá o processo Git existente, com capacidade, backup/restauro fresco, comparação de fonte/dados/schema/configuração/permissões e revisão efectivamente servida.
**Preservação demonstrada:**172tabelas de negócio intactas em cada comparação; apenas cache/sessões operacionais reconciliados individualmente por identidade/timeouts e pedidos públicos de entrega. Schema, papéis, grants, permissões e originais persistentes conservados. Fontes instaladas comparadas aos commits:996ficheiros após L8 e1000após o proactivo. Sem migrations na base activa, importação de amostras QA, seeders ou publicação dos pilotos. O instalador manteve base e storage sintéticos isolados; o storage QA fica fora da árvore servida, e a release usa os mesmos `.env` e storage persistentes por ligação.
**Âmbito:** L8 continua parcial, com PEC e créditos de regra própria separados; os resultados continuam simulações. Songo permanece privado e pendente de respostas externas; Gurué continua hipótese agrícola privada. Disponibilização técnica não confirma clientes, procura, sustentabilidade ou publicação comercial. L3, ISPC, câmbio, L6/L7 e SGF TEMPO preservados.
Recibo consolidado: `docs/investmoz/unification/sequential-delivery-installed-20261027.json`. Provas locais: `outputs/l8-proactive-install-20261027/`. Os recibos históricos de preparação/suspensão mantêm-se como histórico e deixam de representar pendência de instalação destas duas candidatas.
Recibo remoto consolidado: `/var/backups/investmoz-sequential-20261027/installed-receipt.json`;SHA256 `62584b5abb50a05303a24379a0563e8dc147e25e682dc3566bb8edf015ba9d0b`. A fixture sintética portátil é apenas input de teste, sem importação na aplicação e sem dados dos pilotos.
@@ -1,88 +0,0 @@
{
"date": "2026-10-27",
"status": "QA passed; not yet installed",
"source_intention": "88b39b527859bd420f2e223a7afea89bea38de54",
"reproduced_locally": true,
"byte_equivalence_to_external_commit": false,
"baseline": "9fda6d6e12832a3cf25e7314f1f895dd430238c5",
"branch": "codex/sector-navigation-fix-local-20261027",
"runtime_delta": [
"app/Http/Controllers/PortalServicesController.php",
"resources/js/pages/portal/services.tsx"
],
"tests": {
"feature_tests": 3,
"assertions": 319,
"database": "proactive_investment_20261027_test",
"port": 55441,
"rollback": true,
"routes": "passed",
"typescript": "passed",
"build": "passed"
},
"browser": {
"index_cards": 10,
"separate_sectors": [
"agronegocio",
"turismo"
],
"opportunities_isolated": true,
"filter": "agronegocio",
"back": true,
"reload": true,
"fintech_url_preserved": true,
"dossier_entry": "sector:3; existing guest authentication, no new dossier created",
"screenshots": "capture unavailable: zero width; AX proof retained",
"editorial_guides": "not loaded in QA sample; consumers unchanged, destination to check"
},
"protected_files": [
{
"file": "resources/js/components/sector-content.tsx",
"sha256_LF": "4ee55554f8a88ba7db1ac0dc74259d4e66603979c3873f9ec897437830b8e0ae",
"unchanged": true
},
{
"file": "resources/js/components/sector-entry.tsx",
"sha256_LF": "96672c709b70f840d105c664a92d49a17acb66cd852d43dadaa8f3fef8b3e845",
"unchanged": true
},
{
"file": "resources/js/components/public-sector-cards.tsx",
"sha256_LF": "858ba6235ef8b46461d416b3264436e4df2c9555b8e6d8c3644dbd8c55463484",
"unchanged": true
},
{
"file": "app/Services/SectorInvestment.php",
"sha256_LF": "0b8fbc6e3d03ede627e95332703e4749a53d86708bdc0f9579e958caf30e4d17",
"unchanged": true
},
{
"file": "app/Services/SectorOrganization.php",
"sha256_LF": "f0e54fe6acfe746c6758387f43610232cb51371b0404bcefaf466d8b704f3ae3",
"unchanged": true
},
{
"file": "app/Http/Controllers/PublicPortalController.php",
"sha256_LF": "cb6abd75533660a52e18148cc5f1ab706d2e08f9c94334510541e6b0e003b461",
"unchanged": true
},
{
"file": "resources/js/lib/project-dossier.ts",
"sha256_LF": "d59f3fa4dcb3fade1f967564c972e30932aa095a4e5dd4ba0be8b4e07492ef0a",
"unchanged": true
},
{
"file": "resources/js/lib/irpc-annual-settlement.ts",
"sha256_LF": "8cca5316259e3ebabfa75be9f64143a8664a447ac30b23b882318edfd70a68f1",
"unchanged": true
},
{
"file": "routes/web.php",
"sha256_LF": "95930c76977d446b74a422acc4b60869ff6bae5d4567c21cc644484c5f09cbda",
"unchanged": true
}
],
"pec_branch_not_integrated": "7fdb6b42",
"active_writes": 0,
"private_pilots_published": false
}
@@ -0,0 +1,719 @@
{
"date": "2026-10-27T10:51:18.002627+00:00",
"status": "both_deliveries_installed_in_requested_order",
"order": [
"l8",
"proactive"
],
"stages": {
"l8": {
"candidate": "c85e772d2fd2be5182eb4d768ddd34d43962bf59",
"previous": "f8d57bddf96b4610604a013fcc5bdd703c7dc6d5",
"installed": {
"status": "installed",
"commit": "c85e772d2fd2be5182eb4d768ddd34d43962bf59",
"release": "/var/www/investmoz-homologacao/releases/c85e772d2fd2be5182eb4d768ddd34d43962bf59-ae7401aa",
"previous_release": "/var/www/investmoz-homologacao/releases/f8d57bddf96b4610604a013fcc5bdd703c7dc6d5-7a31e8ac",
"database_migrations": 0,
"content_imports": 0
},
"served": {
"commit": "c85e772d2fd2be5182eb4d768ddd34d43962bf59",
"branch": "main",
"built_at": "2026-10-27T10:32:26.854949+00:00",
"manifest_sha256": "f27852b26d53b46f6927ee46c13370db17a4bec1623655552fd8fdca3589005a"
},
"backup": {
"stage": "/var/backups/investmoz-sequential-20261027/l8/backup",
"created_at": "2026-10-27T10:25:59Z",
"target": {
"database": "teste_investidor",
"port": 5432
},
"tables": 174,
"files_verified": 369,
"dump_sha256": "11bc7c8f25edc861b18785a9714845384cf29a5da92f58f55ff53f6913e69d42",
"files_sha256": "52ffe40bcf943ce580047b55c34b60d3ba876d81dd4bc3ff4fbe1ec97a9c6f56",
"restore_verified": false
},
"restore": {
"restore_verified": true,
"tables_verified": 174,
"container_network": "none",
"database": "irpc_l8_first_restore_20261027",
"port": 55441,
"source_dump_sha256": "11bc7c8f25edc861b18785a9714845384cf29a5da92f58f55ff53f6913e69d42",
"active_database_writes": 0
},
"capacity_after_backup": {
"at": "2026-10-27T10:28:35.403687+00:00",
"baseline": "f8d57bddf96b4610604a013fcc5bdd703c7dc6d5",
"capacity_after_backup_satisfied": true,
"volumes": [
{
"path": "/var/www/investmoz-homologacao/releases",
"free_bytes": 98153496576,
"free_inodes": 23860325
},
{
"path": "/var/lib/investmoz-deploy",
"free_bytes": 98153496576,
"free_inodes": 23860325
},
{
"path": "/var/backups",
"free_bytes": 98153496576,
"free_inodes": 23860325
},
{
"path": "/tmp",
"free_bytes": 98153496576,
"free_inodes": 23860325
},
{
"path": "/var/lib/docker",
"free_bytes": 98153496576,
"free_inodes": 23860325
}
],
"backup_age_seconds": 156.403651,
"backup_dump_sha256": "11bc7c8f25edc861b18785a9714845384cf29a5da92f58f55ff53f6913e69d42",
"restore_verified": true,
"tables_verified": 174,
"files_verified": 369,
"backup_paths": {
"dump": "/var/backups/investmoz-sequential-20261027/l8/backup/database.dump",
"files": "/var/backups/investmoz-sequential-20261027/l8/backup/files-before.tar.gz"
},
"active_writes": 0
},
"source": {
"phase": "after",
"installed": "c85e772d2fd2be5182eb4d768ddd34d43962bf59",
"files_verified": 996,
"source_matches_Git": true,
"active_writes": 0
},
"schema_permissions": {
"schema_sha256": "5045b3e51d2185377aa3d9ac7fa07a44fad3ec620cdfa83ffe31c8884ee1601d",
"roles_grants_sha256": "1530c33cb016e67bfdc64891ddfce7f185b13b56b2cd87c012871a685e118109",
"filesystem_permissions": {
".env": {
"mode": 416,
"uid": 1001,
"gid": 33
},
"storage": {
"mode": 1533,
"uid": 1001,
"gid": 33
},
"storage/app": {
"mode": 1533,
"uid": 1001,
"gid": 33
},
"bootstrap/cache": {
"mode": 1528,
"uid": 1001,
"gid": 33
}
},
"comparison_identity": "postgres;catalogue read only;no passwords collected",
"unchanged": true
},
"preservation": {
"checked_at": "2026-10-27T10:34:53.148104+00:00",
"installed": "c85e772d2fd2be5182eb4d768ddd34d43962bf59",
"business_tables_unchanged": 172,
"total_tables": 174,
"operational_tables_to_reconcile": {
"cache": {
"before": {
"rows": 106,
"checksum": "6c05e12d73079effd52f3add83104de2"
},
"after": {
"rows": 106,
"checksum": "a9c2d7df6ecc8cabba35d5c6f1d019c5"
}
},
"sessions": {
"before": {
"rows": 11,
"checksum": "b83d84b3894b8fc6ef42b0b8fd937304"
},
"after": {
"rows": 12,
"checksum": "fea9aecf735aa394f40e9a4b6a55e27f"
}
}
},
"persistent_files_unchanged": 369,
"original_configuration_preserved": true,
"synthetic_QA_storage_not_served": true,
"QA_storage_saved_outside_active": "/var/lib/investmoz-deploy/qa-runtime/c85e772d2fd2be5182eb4d768ddd34d43962bf59-ae7401aa",
"fixture_or_content_imports": 0,
"migrations": 0,
"comparison_identity": "postgres;UTC;consistent before snapshot and after read-only transaction",
"active_database_writes": 0
},
"operational_delta": {
"status": "operational_delta_reconciled",
"comparison_identity": "postgres before/after;isolated restored baseline;no RLS visibility inference",
"cache": "queue:restart;requestthrottle;identical home-sector cached value with refreshed expiry(HomeController remember10min);no deletedcache",
"sessions": "anonymous sessions from delivery user-agent or exactUA/time correlated with nginx public request;deleted sessions individually expired beyond configured lifetime,normal database session garbage collection(config/session lottery2/100)",
"delta": {
"cache": {
"before_rows": 106,
"after_rows": 106,
"changes": [
{
"key_sha256": "7f7dd83f47f5bf404d55e6e9f1367948721579970b195680645e356031a070ff",
"kind": "updated",
"fields": [
"value",
"expiration"
],
"key_pattern": "investemoz-cache-illuminate:queue:restart",
"is_queue_restart_key": true,
"expiration_unchanged": false,
"throttle_timer_pair": false,
"same_value_home_cache_expiry_refresh": false
}
]
},
"sessions": {
"before_rows": 11,
"after_rows": 15,
"changes": [
{
"key_sha256": "7a0b0d7c40ef99d4c033c9df695a5cd69d6433f068463fdf9f8baff6835453f6",
"kind": "added",
"fields": [
"id",
"ip_address",
"user_agent",
"payload",
"last_activity"
],
"anonymous_user": true,
"delivery_user_agent": true,
"expired_before_comparison": false,
"session_lifetime_minutes": 120,
"anonymous_public_request_logged": true
},
{
"key_sha256": "9f06bc9fb6ef8d45fe1c1093b4e54b59ee080463861b8051271597b0ec5e45a4",
"kind": "added",
"fields": [
"id",
"ip_address",
"user_agent",
"payload",
"last_activity"
],
"anonymous_user": true,
"delivery_user_agent": true,
"expired_before_comparison": false,
"session_lifetime_minutes": 120,
"anonymous_public_request_logged": true
},
{
"key_sha256": "e66f917f9d71192fe17cf2c7c26046afa6bff0db292d0c232a5b59629be030a3",
"kind": "added",
"fields": [
"id",
"ip_address",
"user_agent",
"payload",
"last_activity"
],
"anonymous_user": true,
"delivery_user_agent": true,
"expired_before_comparison": false,
"session_lifetime_minutes": 120,
"anonymous_public_request_logged": true
},
{
"key_sha256": "a84b009ac5f5358a0452f4a40968a4826e34ced257930b6ecf08a0289aa6684f",
"kind": "added",
"fields": [
"id",
"ip_address",
"user_agent",
"payload",
"last_activity"
],
"anonymous_user": true,
"delivery_user_agent": true,
"expired_before_comparison": false,
"session_lifetime_minutes": 120,
"anonymous_public_request_logged": true
}
]
}
},
"business_tables_to_confirm_separately": 172,
"active_data_writes_by_comparison": 0,
"restore_network": "none"
},
"public_delta": {
"stage": "l8",
"served_revision": {
"commit": "c85e772d2fd2be5182eb4d768ddd34d43962bf59",
"branch": "main",
"built_at": "2026-10-27T10:32:26.854949+00:00",
"manifest_sha256": "f27852b26d53b46f6927ee46c13370db17a4bec1623655552fd8fdca3589005a"
},
"manifest_hash_matches": true,
"compiled_delta_markers": {
"IRPC-annual-settlement/1": [
"assets/project-dossier-BNt4O6Rz.js"
],
"Preparar saldo anual L8": [
"assets/services-BvFvahdE.js"
],
"Calcular e guardar saldo anual L8": [
"assets/services-BvFvahdE.js"
],
"Saldo anual simulado": [
"assets/project-dossier-BNt4O6Rz.js"
],
"PEC e créditos separados": [
"assets/project-dossier-BNt4O6Rz.js"
]
},
"assets": [
{
"file": "assets/simulation-form-zFZ4YNrB.js",
"status": 200,
"sha256": "338068b86cce1c3215ee71b1328a6cb2f754b903ed78ff1886f8554df506d2d6"
},
{
"file": "assets/services-BvFvahdE.js",
"status": 200,
"sha256": "97615f0d113746ee5ac1550a1e59c567dd152ef5ddac68f133bd2cee9e9c16d1"
},
{
"file": "assets/project-dossier-BNt4O6Rz.js",
"status": 200,
"sha256": "8cf6a6678c5c9aed0d7b996147bc2fde35d20f86c73ec7bd894df2ae11aae75c"
},
{
"file": "assets/project-dossier-document-Cj4QX7oF.js",
"status": 200,
"sha256": "302ec6831893f2ef4c09a90273b458c49a6ff281adb01c4594a166a90efe53e2"
}
],
"simulator_status": 200,
"served_component": "portal/services",
"private_operations": 0,
"QA_fixture_imports": 0,
"accepted_QA_proofs_reused": true,
"method": "Read-only served revision/manifest/compiled delta;no synthetic active records or publication"
},
"backup_restore_verified": true
},
"proactive": {
"candidate": "9fda6d6e12832a3cf25e7314f1f895dd430238c5",
"previous": "c85e772d2fd2be5182eb4d768ddd34d43962bf59",
"installed": {
"status": "installed",
"commit": "9fda6d6e12832a3cf25e7314f1f895dd430238c5",
"release": "/var/www/investmoz-homologacao/releases/9fda6d6e12832a3cf25e7314f1f895dd430238c5-88fc859c",
"previous_release": "/var/www/investmoz-homologacao/releases/c85e772d2fd2be5182eb4d768ddd34d43962bf59-ae7401aa",
"database_migrations": 0,
"content_imports": 0
},
"served": {
"commit": "9fda6d6e12832a3cf25e7314f1f895dd430238c5",
"branch": "main",
"built_at": "2026-10-27T10:50:00.471464+00:00",
"manifest_sha256": "40de38cbf74b475f73de825bfd16af6fabe339faad4817e2857fd9ae36ec7cc0"
},
"backup": {
"stage": "/var/backups/investmoz-sequential-20261027/proactive/backup",
"created_at": "2026-10-27T10:36:28Z",
"target": {
"database": "teste_investidor",
"port": 5432
},
"tables": 174,
"files_verified": 369,
"dump_sha256": "45107e48e18ab2b9a78af08374fe3bfa0af216f34433c10fc81f264400e331ce",
"files_sha256": "f7196314f915e75975a9e580a69ddc4dffae64597ae05e9ae59cfbcbb0b47731",
"restore_verified": false
},
"restore": {
"restore_verified": true,
"tables_verified": 174,
"container_network": "none",
"database": "irpc_l8_first_restore_20261027",
"port": 55441,
"source_dump_sha256": "45107e48e18ab2b9a78af08374fe3bfa0af216f34433c10fc81f264400e331ce",
"active_database_writes": 0
},
"capacity_after_backup": {
"at": "2026-10-27T10:46:06.120042+00:00",
"baseline": "c85e772d2fd2be5182eb4d768ddd34d43962bf59",
"capacity_after_backup_satisfied": true,
"volumes": [
{
"path": "/var/www/investmoz-homologacao/releases",
"free_bytes": 95851429888,
"free_inodes": 23794207
},
{
"path": "/var/lib/investmoz-deploy",
"free_bytes": 95851429888,
"free_inodes": 23794207
},
{
"path": "/var/backups",
"free_bytes": 95851429888,
"free_inodes": 23794207
},
{
"path": "/tmp",
"free_bytes": 95851429888,
"free_inodes": 23794207
},
{
"path": "/var/lib/docker",
"free_bytes": 95851429888,
"free_inodes": 23794207
}
],
"backup_age_seconds": 578.120001,
"backup_dump_sha256": "45107e48e18ab2b9a78af08374fe3bfa0af216f34433c10fc81f264400e331ce",
"restore_verified": true,
"tables_verified": 174,
"files_verified": 369,
"backup_paths": {
"dump": "/var/backups/investmoz-sequential-20261027/proactive/backup/database.dump",
"files": "/var/backups/investmoz-sequential-20261027/proactive/backup/files-before.tar.gz"
},
"active_writes": 0
},
"source": {
"phase": "after",
"installed": "9fda6d6e12832a3cf25e7314f1f895dd430238c5",
"files_verified": 1000,
"source_matches_Git": true,
"active_writes": 0
},
"schema_permissions": {
"schema_sha256": "5045b3e51d2185377aa3d9ac7fa07a44fad3ec620cdfa83ffe31c8884ee1601d",
"roles_grants_sha256": "1530c33cb016e67bfdc64891ddfce7f185b13b56b2cd87c012871a685e118109",
"filesystem_permissions": {
".env": {
"mode": 416,
"uid": 1001,
"gid": 33
},
"storage": {
"mode": 1533,
"uid": 1001,
"gid": 33
},
"storage/app": {
"mode": 1533,
"uid": 1001,
"gid": 33
},
"bootstrap/cache": {
"mode": 1528,
"uid": 1001,
"gid": 33
}
},
"comparison_identity": "postgres;catalogue read only;no passwords collected",
"unchanged": true
},
"preservation": {
"checked_at": "2026-10-27T10:50:49.400700+00:00",
"installed": "9fda6d6e12832a3cf25e7314f1f895dd430238c5",
"business_tables_unchanged": 172,
"total_tables": 174,
"operational_tables_to_reconcile": {
"cache": {
"before": {
"rows": 106,
"checksum": "a9c2d7df6ecc8cabba35d5c6f1d019c5"
},
"after": {
"rows": 106,
"checksum": "db78f82003db53db664667b8e6f46088"
}
},
"sessions": {
"before": {
"rows": 15,
"checksum": "4df23659bf65acacc88e1d84d095a243"
},
"after": {
"rows": 20,
"checksum": "64d48443c5027a2fc08615f7528126b5"
}
}
},
"persistent_files_unchanged": 369,
"original_configuration_preserved": true,
"synthetic_QA_storage_not_served": true,
"QA_storage_saved_outside_active": "/var/lib/investmoz-deploy/qa-runtime/9fda6d6e12832a3cf25e7314f1f895dd430238c5-88fc859c",
"fixture_or_content_imports": 0,
"migrations": 0,
"comparison_identity": "postgres;UTC;consistent before snapshot and after read-only transaction",
"active_database_writes": 0
},
"operational_delta": {
"status": "operational_delta_reconciled",
"comparison_identity": "postgres before/after;isolated restored baseline;no RLS visibility inference",
"cache": "queue:restart;requestthrottle;identical home-sector cached value with refreshed expiry(HomeController remember10min);no deletedcache",
"sessions": "anonymous sessions from delivery user-agent or exactUA/time correlated with nginx public request;deleted sessions individually expired beyond configured lifetime,normal database session garbage collection(config/session lottery2/100)",
"delta": {
"cache": {
"before_rows": 106,
"after_rows": 106,
"changes": [
{
"key_sha256": "7f7dd83f47f5bf404d55e6e9f1367948721579970b195680645e356031a070ff",
"kind": "updated",
"fields": [
"value",
"expiration"
],
"key_pattern": "investemoz-cache-illuminate:queue:restart",
"is_queue_restart_key": true,
"expiration_unchanged": false,
"throttle_timer_pair": false,
"same_value_home_cache_expiry_refresh": false
}
]
},
"sessions": {
"before_rows": 15,
"after_rows": 20,
"changes": [
{
"key_sha256": "f78ee9e6f589e1bbda4730d4e71a90c9ad082cc25776c415b6cc815ce4b4d491",
"kind": "added",
"fields": [
"id",
"ip_address",
"user_agent",
"payload",
"last_activity"
],
"anonymous_user": true,
"delivery_user_agent": true,
"expired_before_comparison": false,
"session_lifetime_minutes": 120,
"anonymous_public_request_logged": true
},
{
"key_sha256": "7eb58435f5d60610be81945c0da4331a1dbfda680f7da3f107dbbeb6b60c39df",
"kind": "added",
"fields": [
"id",
"ip_address",
"user_agent",
"payload",
"last_activity"
],
"anonymous_user": true,
"delivery_user_agent": true,
"expired_before_comparison": false,
"session_lifetime_minutes": 120,
"anonymous_public_request_logged": true
},
{
"key_sha256": "1d5026f13ace3894011db54d801f72cd76f9b138d54fe2f7b89f579743282db6",
"kind": "added",
"fields": [
"id",
"ip_address",
"user_agent",
"payload",
"last_activity"
],
"anonymous_user": true,
"delivery_user_agent": true,
"expired_before_comparison": false,
"session_lifetime_minutes": 120,
"anonymous_public_request_logged": true
},
{
"key_sha256": "56820cc8560f13a2c0962a973bd05624712648c2beffd2058dfbae6c9103c7fa",
"kind": "added",
"fields": [
"id",
"ip_address",
"user_agent",
"payload",
"last_activity"
],
"anonymous_user": true,
"delivery_user_agent": true,
"expired_before_comparison": false,
"session_lifetime_minutes": 120,
"anonymous_public_request_logged": true
},
{
"key_sha256": "a91337e37e71b520dd6d621b0ef7bbf68a9a20d099198ecd8edfdc365a9f52f1",
"kind": "added",
"fields": [
"id",
"ip_address",
"user_agent",
"payload",
"last_activity"
],
"anonymous_user": true,
"delivery_user_agent": true,
"expired_before_comparison": false,
"session_lifetime_minutes": 120,
"anonymous_public_request_logged": true
}
]
}
},
"business_tables_to_confirm_separately": 172,
"active_data_writes_by_comparison": 0,
"restore_network": "none"
},
"public_delta": {
"stage": "proactive",
"served_revision": {
"commit": "9fda6d6e12832a3cf25e7314f1f895dd430238c5",
"branch": "main",
"built_at": "2026-10-27T10:50:00.471464+00:00",
"manifest_sha256": "40de38cbf74b475f73de825bfd16af6fabe339faad4817e2857fd9ae36ec7cc0"
},
"manifest_hash_matches": true,
"compiled_delta_markers": {
"IRPC-annual-settlement/1": [
"assets/project-dossier-DFH3NDYe.js"
],
"Preparar saldo anual L8": [
"assets/services-CV6TWKfT.js"
],
"Calcular e guardar saldo anual L8": [
"assets/services-CV6TWKfT.js"
],
"Saldo anual simulado": [
"assets/project-dossier-DFH3NDYe.js"
],
"PEC e créditos separados": [
"assets/project-dossier-DFH3NDYe.js"
],
"Território e sector": [
"assets/proactive-investment-CPkgrUFY.js"
],
"Preparar análise e ideia": [
"assets/proactive-investment-CPkgrUFY.js"
],
"Hipótese analítica": [
"assets/project-dossier-DFH3NDYe.js",
"assets/proactive-investment-CPkgrUFY.js"
],
"Documento existente associado": [
"assets/proactive-investment-CPkgrUFY.js"
],
"Origem privada autorizada deste dossier": [
"assets/proactive-investment-CPkgrUFY.js"
]
},
"assets": [
{
"file": "assets/project-dossier-DFH3NDYe.js",
"status": 200,
"sha256": "62f4e47f6efbb7916663604450ee427401080fad6f9332be2cd0dc017866b85a"
},
{
"file": "assets/project-dossier-document-CgBtS4qw.js",
"status": 200,
"sha256": "fb9aa7e305891719ae49cf8bbcee05b3114c05c8ee1df20238964fddb56721dd"
},
{
"file": "assets/catalogue-DRXXB4Y7.js",
"status": 200,
"sha256": "66a2ae7c819f1ac36ffd37e8456c3cfd2537c820845b20eddb4dd7bfaf4a32a2"
},
{
"file": "assets/services-CV6TWKfT.js",
"status": 200,
"sha256": "656da0adacd9141fcd9b1e82ab9959a62ea381a3815e183c5535801c95d92dff"
},
{
"file": "assets/simulation-form-zFZ4YNrB.js",
"status": 200,
"sha256": "338068b86cce1c3215ee71b1328a6cb2f754b903ed78ff1886f8554df506d2d6"
},
{
"file": "assets/proactive-investment-CPkgrUFY.js",
"status": 200,
"sha256": "288cd1005a05c82241a1ddd8c5bb446e297ead7b7605060cec80d268726522ac"
},
{
"file": "assets/opportunity-draft-Da2FBHoN.js",
"status": 200,
"sha256": "ba50f61a39693ebebbe30ba1937d45c92b80f81ab9a98418ef75662f893cdfb2"
},
{
"file": "assets/detail-BfSgTC5d.js",
"status": 200,
"sha256": "234657a2e5ebad7985d78ea49c5536c31e4aac9a143a02e7682d898e47cee530"
},
{
"file": "assets/show-CJYhqIE4.js",
"status": 200,
"sha256": "6579df14a154c98235330f550e13bee20e74f19517cc1d166ae26322f56c74f9"
}
],
"simulator_status": 200,
"served_component": "portal/services",
"public_catalogue_delta": {
"ideas": {
"status": 200,
"component": "investment/catalogue"
},
"needs": {
"status": 200,
"component": "investment/catalogue"
},
"exists": {
"status": 200,
"component": "investment/catalogue"
}
},
"private_operations": 0,
"QA_fixture_imports": 0,
"accepted_QA_proofs_reused": true,
"method": "Read-only served revision/manifest/compiled delta;no synthetic active records or publication"
},
"backup_restore_verified": true
}
},
"main": "9fda6d6e12832a3cf25e7314f1f895dd430238c5",
"final_served_revision": "9fda6d6e12832a3cf25e7314f1f895dd430238c5",
"private_pilots_preserved_no_publication": true,
"fixtures_not_imported_into_active_database": true,
"QA_runtime_storage_outside_active_release": true,
"active_schema_migrations": 0,
"new_grants": false,
"SGF_TEMPO": "untouched",
"accepted_business_proofs": "reused;post-install checks limited to deltas;existing installer automated checks retained",
"prior_capacity_GiB": 91.60694122314453,
"second_delivery_retry": {
"initial_candidate": "56c0c6a7caa65a4cac54e17f9d455739f8eb6198",
"initial_candidate_activated": false,
"active_L8_preserved": true,
"reason": "One frontend test depended on ignored local outputs/proactive-investment-20261027/recovered-context.json",
"correction": "9fda6d6e12832a3cf25e7314f1f895dd430238c5 changes only the test and a minimal synthetic public API projection;no profile,pilot,credentials or live records",
"application_runtime_identical_to_56c0c6a7": true,
"directed_portability_test": "passed;one test;no skipped test",
"existing_installer_guards_retained": true
}
}
@@ -6926,14 +6926,20 @@ Retomar na sessão local com SSH autenticado, usando a autorização já regista
Recibo de preparação: `docs/investmoz/unification/sequential-delivery-preparation-20261027.json`. Provas e instrução de passagem: `outputs/sequential-delivery-20261027/`. Os recibos de execução e as duas revisões servidas serão registados apenas depois de execução real na sessão autenticada; este recibo não os substitui.
## 27/10/2026 — correcção delimitada da navegação dos sectores
## 27/10/2026 — L8 e percurso proactivo instalados pela sequência autorizada
Reprodução local autorizada da intenção de `88b39b527859bd420f2e223a7afea89bea38de54`, indisponível no origin/outro ambiente. Base exacta `9fda6d6e12832a3cf25e7314f1f895dd430238c5`; novo ramo `codex/sector-navigation-fix-local-20261027`. Não se afirma identidade binária com o pacote externo. QA concluída; instalação ainda pendente nesta revisão documental.
Entrega1: **L8 `c85e772d`**, anterior `f8d57bdd`. Entrega2, apenas após conferir a primeira: **percurso proactivo `9fda6d6e`**, incluindo `56c0c6a7` e preservando L8. As duas revisões foram conferidas no registo do instalador e no recurso público `/build/revision.json`, com hash do manifest e marcadores compilados dos deltas. `c85e772d` conserva o código aceite de `14360748`; o merge proactivo mantém ambos os fornecedores de linhas do relatório. A referência final servida passa a `9fda6d6e`.
**Causa e alteração:** o índice `/sectores` carregava `sectorDetails` para todos os sectores e apresentava as páginas completas abaixo dos cartões; os cartões passavam a âncoras locais. Removidos esse carregamento e a apresentação agregada. Os cartões usam `sector.url`, preservando as páginas individuais, a especialização Fintech e os caminhos históricos existentes. O índice mantém descrições curtas, contagens e acesso aos sectores, sem repetir guias e oportunidades de todos eles.
A tentativa proactiva em `56c0c6a7` não foi activada: um teste dependia de um ficheiro de outputs existente apenas no QA local. L8 permaneceu em serviço. A correcção `9fda6d6e` torna esse teste portátil com um exemplar mínimo sintético da projecção pública da API, sem perfis, credenciais ou pilotos reais e sem escrita em outputs. O teste dirigido passou; o código da aplicação é byte a byte idêntico a `56c0c6a7`. Não se transportou a base QA nem se saltou a guarda do instalador. A nova tentativa usou o backup/restauro ainda fresco, com capacidade e fonte activa novamente conferidas.
**Preservação:** página individual, `SectorContent`, `SectorEntry`, `SectorInvestment`, `SectorOrganization`, filtros, dados mestres, originais e continuidade para o mesmo dossier não foram alterados. PEC `7fdb6b42` continua no seu ramo QA; não é integrado nesta correcção. L8 instalado e percurso proactivo de `9fda6d6e` preservados. Songo/Gurué privados; sem fixtures QA no activo, publicação, importações, migrations ou alterações de permissões.
A ligação SSH foi restabelecida pelo responsável na janela local, sem credenciais na conversa ou nos recibos. A medição inicial encontrou91,6GiB livres e inodes suficientes; a capacidade foi novamente conferida depois de cada backup. Instalador existente e suas guardas preservados; a versão anterior permaneceu activa até a nova release estar pronta. Os controlos automáticos do instalador foram mantidos, sem nova ronda manual das provas funcionais/económicas aceites.
**Delta demonstrado:** actualizados os dois testes afectados de `SectorCatalogueContentTest` e acrescentado um caso de isolamento/regresso/recarregamento:3 testes/319 asserções, base isolada `proactive_investment_20261027_test`, loopback55441, transacções com rollback; sem `migrate:fresh`. Wayfinder `--with-form`, TypeScript e build QA aprovados. Navegador real:10 cartões, acesso separado a Agronegócio e Turismo, oportunidades próprias, filtro Agronegócio mantido no catálogo, recarregamento da página individual e regresso ao índice sem repetição. Entrada Turismo para o dossier conserva o contexto sectorial na ligação e encaminha o visitante para a autenticação existente; não se repetiu a prova completa de gravação do dossier já aceite. A amostra QA não tem guias editoriais carregados; a preservação dos guias assenta na comparação dos consumidores/fontes, e será conferida no destino sem republicar conteúdo. A captura de imagem do browser devolveu «0 width»; árvore de acessibilidade e testes guardados, sem alegação de captura visual inexistente.
**Backup/restauro fresco em cada etapa:** `/var/backups/investmoz-sequential-20261027/l8/backup` e `/var/backups/investmoz-sequential-20261027/proactive/backup`. Em cada uma, dump consistente e369ficheiros persistentes/configuração verificados; restauro das174tabelas num contentor sem rede, porta55441, com contagens/checksums iguais. Recibos `backup-receipt.json`, `restore-receipt.json`, `schema-permissions-before.json`, `schema-permissions-after.json`, `preservation-receipt.json` e `operational-delta-receipt.json` conservados nesses directórios. Hashes exactos no recibo consolidado.
Requisitos existentes actualizados apenas neste delta: `MASTER-471012ab-0007` e `MASTER-9dee08d0-0017`; não se encerra automaticamente o âmbito mais amplo dessas linhas. Recibo QA: `docs/investmoz/unification/sector-navigation-qa-20261027.json`. Provas locais: `outputs/sector-navigation-fix-20261027/`. Entrega autorizada seguirá o processo Git existente, com capacidade, backup/restauro fresco, comparação de fonte/dados/schema/configuração/permissões e revisão efectivamente servida.
**Preservação demonstrada:**172tabelas de negócio intactas em cada comparação; apenas cache/sessões operacionais reconciliados individualmente por identidade/timeouts e pedidos públicos de entrega. Schema, papéis, grants, permissões e originais persistentes conservados. Fontes instaladas comparadas aos commits:996ficheiros após L8 e1000após o proactivo. Sem migrations na base activa, importação de amostras QA, seeders ou publicação dos pilotos. O instalador manteve base e storage sintéticos isolados; o storage QA fica fora da árvore servida, e a release usa os mesmos `.env` e storage persistentes por ligação.
**Âmbito:** L8 continua parcial, com PEC e créditos de regra própria separados; os resultados continuam simulações. Songo permanece privado e pendente de respostas externas; Gurué continua hipótese agrícola privada. Disponibilização técnica não confirma clientes, procura, sustentabilidade ou publicação comercial. L3, ISPC, câmbio, L6/L7 e SGF TEMPO preservados.
Recibo consolidado: `docs/investmoz/unification/sequential-delivery-installed-20261027.json`. Provas locais: `outputs/l8-proactive-install-20261027/`. Os recibos históricos de preparação/suspensão mantêm-se como histórico e deixam de representar pendência de instalação destas duas candidatas.
Recibo remoto consolidado: `/var/backups/investmoz-sequential-20261027/installed-receipt.json`;SHA256 `62584b5abb50a05303a24379a0563e8dc147e25e682dc3566bb8edf015ba9d0b`. A fixture sintética portátil é apenas input de teste, sem importação na aplicação e sem dados dos pilotos.
File diff suppressed because one or more lines are too long
+34 -1
View File
@@ -71,6 +71,7 @@ type Page<T> = {
};
type Props = {
sectorEntry?: SectorDetailData['sectorEntry'];
sectorDetails?: SectorDetailData[];
countries?: Country[];
section: string;
filters?: Record<string, string | string[]>;
@@ -517,9 +518,41 @@ export default function Services(p: Props) {
description: sector.short_description,
opportunitiesCount: sector.opportunities_count,
specializations: sector.specializations,
url: sector.url,
url: p.sectorDetails?.length
? `#sector-${sector.slug}`
: sector.url,
}))}
/>
{p.sectorDetails?.map((detail) => (
<section
id={`sector-${detail.sector.slug}`}
key={detail.sector.id}
aria-labelledby={`sector-heading-${detail.sector.slug}`}
className="scroll-mt-24 space-y-5 border-t border-slate-200 pt-6"
>
<div className="flex flex-wrap items-center justify-between gap-3">
<h2
id={`sector-heading-${detail.sector.slug}`}
className="text-2xl font-bold"
>
{detail.sector.name}
</h2>
<Link
href={`/sectores/${detail.sector.slug}`}
className="inline-flex min-h-11 items-center text-sm font-semibold underline"
>
Abrir página do sector
</Link>
</div>
<SectorContent {...detail} headingLevel={3} />
<a
href="#sectores-indice"
className="inline-flex min-h-11 items-center text-sm font-semibold underline"
>
Voltar à lista de sectores
</a>
</section>
))}
{!p.sectors?.length && (
<p>Não existem sectores publicados.</p>
)}
+12 -43
View File
@@ -47,22 +47,17 @@ class SectorCatalogueContentTest extends TestCase
$before = [DB::table('desk_cases')->count(), DB::table('opportunities')->count(), DB::table('portal_resources')->count()];
$props = $this->get('/sectores')->assertOk()->inertiaProps();
$this->assertArrayNotHasKey('sectorDetails', $props);
$ids = array_column($props['sectors'], 'id');
$this->assertSame($ids, array_values(array_unique($ids)));
foreach ($props['sectors'] as $card) {
$this->assertSame(\App\Services\SectorOrganization::href($card['slug']), $card['url']);
$this->assertStringNotContainsString('#', $card['url']);
$single = $this->get($card['url'])->assertOk()->inertiaProps();
$this->assertSame('sector', $single['section']);
$this->assertSame($card['id'], $single['sector']['id']);
$this->assertArrayNotHasKey('sectorDetails', $single);
$this->assertArrayNotHasKey('sectors', $single);
$this->assertIsArray($single['sectorEntry']['articles']);
$this->assertIsArray($single['sectorEntry']['relations']);
$this->assertIsArray($single['sectorEntry']['locations']);
$this->assertSame(array_column($props['sectors'], 'id'), array_column(array_column($props['sectorDetails'], 'sector'), 'id'));
foreach ($props['sectorDetails'] as $detail) {
$single = $this->get('/sectores/'.$detail['sector']['slug'])->assertOk()->inertiaProps();
$this->assertSame($single['sector'], $detail['sector']);
$this->assertSame($single['sectorEntry'], $detail['sectorEntry']);
$this->assertSame($single['opportunities'], $detail['opportunities']);
$this->assertIsArray($detail['sectorEntry']['articles']);
$this->assertIsArray($detail['sectorEntry']['relations']);
$this->assertIsArray($detail['sectorEntry']['locations']);
}
$entry = $this->get('/sectores/'.$withoutGuide->slug)->assertOk()->inertiaProps()['sectorEntry'];
$entry = collect($props['sectorDetails'])->firstWhere('sector.id', $withoutGuide->id)['sectorEntry'];
$this->assertSame([], $entry['articles']);
$this->assertSame('/minhas-oportunidades/create?sector_id='.$withoutGuide->id, $entry['register_href']);
$this->assertNotNull($entry['start_href']);
@@ -85,11 +80,10 @@ class SectorCatalogueContentTest extends TestCase
}
config(['investor-path.enabled' => false, 'portal.profile_access_enabled' => false]);
$props = $this->get('/sectores')->assertOk()->inertiaProps();
$this->assertArrayNotHasKey('sectorDetails', $props);
$ids = array_column($props['sectors'], 'id');
$ids = array_column(array_column($props['sectorDetails'], 'sector'), 'id');
$this->assertNotContains($unpublished->id, $ids);
$this->assertNotContains($inactive->id, $ids);
$detail = $this->get('/sectores/'.$public->slug)->assertOk()->inertiaProps();
$detail = collect($props['sectorDetails'])->firstWhere('sector.id', $public->id);
$this->assertSame([$slugs['published']], array_column($detail['opportunities'], 'slug'));
$this->assertNull($detail['sectorEntry']['start_href']);
$this->assertNull($detail['sectorEntry']['register_href']);
@@ -98,29 +92,4 @@ class SectorCatalogueContentTest extends TestCase
$this->get('/sectores/'.$unpublished->slug)->assertNotFound();
$this->get('/sectores/'.$inactive->slug)->assertNotFound();
}
public function test_selecting_a_sector_keeps_opportunities_scoped_on_reload_and_return(): void
{
$a = $this->sector();$b = $this->sector();
foreach ([$a, $b] as $sector) {
$slug = 'qa-isolated-'.Str::lower(Str::random(12));
Opportunity::create(['sector_id'=>$sector->id, 'reference'=>$slug, 'title'=>$slug, 'slug'=>$slug,
'summary'=>'Synthetic scoped opportunity', 'status'=>'published', 'published_at'=>now()->subDay()]);
}
$index = $this->get('/sectores')->assertOk()->inertiaProps();
foreach ([$a, $b, $a] as $sector) {
$url = collect($index['sectors'])->firstWhere('id', $sector->id)['url'];
$first = $this->get($url)->assertOk()->inertiaProps();
$reload = $this->get($url)->assertOk()->inertiaProps();
$this->assertSame($first['sector'], $reload['sector']);
$this->assertSame($first['opportunities'], $reload['opportunities']);
$this->assertSame($sector->id, $first['sector']['id']);
$this->assertSame(Opportunity::where('sector_id',$sector->id)->pluck('slug')->all(), array_column($first['opportunities'],'slug'));
$this->assertSame('/investimentos/a-partir-do-setor/'.$sector->slug, $first['sectorEntry']['start_href']);
$this->get($first['sectorEntry']['start_href'])->assertRedirect('/login');
$returned = $this->get('/sectores')->assertOk()->inertiaProps();
$this->assertSame($index['sectors'], $returned['sectors']);
$this->assertArrayNotHasKey('sectorDetails', $returned);
}
}
}