diff --git a/app/Services/InvestmentCatalogue.php b/app/Services/InvestmentCatalogue.php index d6a6e860..3bd96dbb 100644 --- a/app/Services/InvestmentCatalogue.php +++ b/app/Services/InvestmentCatalogue.php @@ -538,11 +538,12 @@ class InvestmentCatalogue } $public = app(ProactiveInvestment::class)->publicData($record); $evidence = array_values(array_filter(StructuredData::collectionItems($public['evidence'] ?? []), - fn ($proof) => in_array($proof['category'] ?? null, ['resource', 'infrastructure'], true) && ($proof['purpose'] ?? null) === 'local')); + fn ($proof) => in_array($proof['category'] ?? null, ['resource', 'infrastructure', 'workforce', 'activity', 'supply', 'demand'], true) && ($proof['purpose'] ?? null) === 'local')); if ($evidence !== []) { $thematic[] = ['id' => $record->id, 'title' => $record->title, 'url' => $properties['url'], 'location' => $properties['location'], 'state' => $public['state'] ?? 'hypothesis', 'limitations' => $public['limitations'] ?? null, - 'evidence' => array_map(fn ($proof) => array_intersect_key($proof, array_flip(['id', 'category', 'statement', 'source', 'locator', 'period', 'scope', 'coverage', 'verified_on'])), $evidence)]; + 'evidence' => array_map(fn ($proof) => [...array_intersect_key($proof, array_flip(['id', 'statement', 'source', 'locator', 'period', 'scope', 'coverage', 'verified_on'])), + 'source_category' => $proof['category'], 'category' => in_array($proof['category'], ['activity', 'supply', 'demand'], true) ? 'economic_potential' : $proof['category']], $evidence)]; } $sites = collect($points->get($record->id) ?? []); if ($sites->isNotEmpty()) { diff --git a/app/Services/ProactiveInvestment.php b/app/Services/ProactiveInvestment.php index 52cb6757..172522d7 100644 --- a/app/Services/ProactiveInvestment.php +++ b/app/Services/ProactiveInvestment.php @@ -33,7 +33,7 @@ final class ProactiveInvestment 'proactive.evidence.*.observation_id' => 'sometimes|nullable|string|size:64', 'proactive.supply_comparisons' => 'sometimes|array|max:40', 'proactive.evidence.*.id' => 'required|uuid|distinct', - 'proactive.evidence.*.category' => 'required|in:activity,resource,infrastructure,supply,demand', + 'proactive.evidence.*.category' => 'required|in:activity,resource,infrastructure,supply,demand,workforce', 'proactive.evidence.*.purpose' => 'required|in:local,methodology', 'proactive.evidence.*.access' => 'required|in:internal,public', 'proactive.evidence.*.document_id' => 'nullable|uuid', diff --git a/docs/investmoz/consolidated-review-matrix-2026-10-03.md b/docs/investmoz/consolidated-review-matrix-2026-10-03.md index 04884677..f1403ad5 100644 --- a/docs/investmoz/consolidated-review-matrix-2026-10-03.md +++ b/docs/investmoz/consolidated-review-matrix-2026-10-03.md @@ -2243,3 +2243,18 @@ Delta aprovado: oito testes de BD/438 asserções (quatro novos e quatro de filt Conferência visual no Edge exclusivamente em `127.0.0.2:18644`, base `main_visual_20261104_test`: duas fixtures claramente sintéticas criadas apenas em QA, uma por categoria; nenhuma coordenada/geometria criada. Hash de todas as oportunidades anteriores idêntico antes/depois. Alternância entre ambas conserva pesquisa provincial e duas fichas gerais; cada camada mostra a sua ficha, fonte/período/âmbito/limites e ausência de ponto. Infra-estruturas recuperada após recarregar. Recibo: `docs/investmoz/unification/thematic-map-local-20261104.json`; inventário, hashes, DOM e capturas em `outputs/thematic-map-20261104/`. Demonstração sintética não constitui cobertura económica factual ou publicação de dados reais. **ACT-02 permanece parcial.** Cobertura factual de recursos/infra-estruturas no destino, georreferenciação própria quando documentada, mão-de-obra, potencial económico quantitativo, restantes fontes temáticas e lacunas cartográficas continuam pendentes. Não se inferem estas camadas de descrições nem do número de oportunidades. Mantêm-se conferência remota e aceitação da equipa; V2-07/V2-10 não encerrados. Sem push, alteração activa, migração, publicação automática ou alteração de permissões/serviços. + + +### 04/11/2026 — ACT-20261029-02: mão-de-obra documentada e contexto económico + +Incremento qualitativo na main local sobre `21ebad73`, reutilizando mapa, evidências, síntese publicada, divulgação expressa e fontes/períodos existentes. Inventário na cópia QA: duas evidências públicas locais de actividade anteriores (fichas sintéticas 350/383), nenhuma de mão-de-obra/oferta/procura; recursos/infra-estruturas da demonstração anterior conservados. As declarações labour_demands/training_capacities têm acesso EducationAccess interno; emprego/indicadores financeiros canónicos não fazem parte da publicProjection. Observações de zonas conservam divulgação/unidades/metodologia próprias. Não foram expostas nem agregadas essas fontes privadas por existir revisão interna. As fontes oficiais aceites e as decisões de divulgação anteriores não foram reabertas. + +Acrescentada categoria explícita Mão-de-obra no editor e validação da evidência existente, sem migração ou reclassificação automática dos textos anteriores. Guardar, recuperar, pré-visualizar e confirmar publicação usam o percurso existente. A camada económica reutiliza apenas evidências públicas locais de actividade/oferta/procura, mantendo `source_category` original, facto, fonte, localizador, período, verificação, âmbito, cobertura e limitações. Agrupá-las para consulta é uma escolha de apresentação, não aprovação de viabilidade nem índice de potencial. + +Ambas as opções entram no selector do mapa único; a escolha é recuperada na sessão. Recursos/infra-estruturas, geometrias públicas, filtros territoriais, fichas sem ponto e contagens anteriores mantêm-se. Nenhuma posição é criada ou atribuída ao recurso/mão-de-obra. Necessidade ou qualificação não equivale a trabalhador disponível; previsão de emprego não é emprego efectivo. Contexto económico não calcula retorno, score ou viabilidade. Períodos/fontes incompatíveis permanecem separados, sem totais ou zero presumido. + +Delta aprovado: sete testes de BD/77 asserções (cinco do mapa, incluindo o novo caso; dois de publicação, um novo e um de acesso/fonte privada já existente), 22 testes frontend, PHPStan sem erros, tipos, Pint, lint e build QA aprovados. Teste do percurso: criar evidência workforce → recuperar → confirmar pela publicação existente → consultar mapa → alterar rascunho privado sem substituir a categoria/fonte públicas. Testes mantêm metodologia/internos fora do mapa, categorias originais, períodos distintos e ausência de quantidades/índices inferidos. Não se repetiu a bateria geral aceite. + +Conferência visual no Edge exclusivamente na QA `127.0.0.2:18644`/`main_visual_20261104_test`: contexto económico reutiliza a evidência de actividade já publicada da ficha 383, sintética e explicitamente identificada. Mão-de-obra usa uma única fixture nova, também sintética; escolha e fonte recuperadas após recarregar. Nenhuma coordenada/geometria criada; hash de todas as oportunidades anteriores idêntico antes/depois. Conferidos 36 artefactos dos três recibos anteriores, todos com SHA256 inalterado. Recibo: `docs/investmoz/unification/workforce-economic-map-local-20261104.json`; inventário, DOM, capturas e hashes em `outputs/workforce-economic-map-20261104/`. + +**ACT-02 permanece parcial.** Cobertura factual laboral/económica, séries, unidades e metodologia quantitativa comparável, georreferenciação própria documentada e cobertura cartográfica continuam pendentes. A demonstração sintética não mede oferta laboral nem potencial regional real. Mantêm-se conferência remota e aceitação da equipa; V2-07/V2-10 não encerrados. Sem push, migração, publicação automática, escrita activa, alteração de permissões ou reinício de serviços. diff --git a/docs/investmoz/unification/traceability-and-manual.md b/docs/investmoz/unification/traceability-and-manual.md index 2fdf80ab..5b8bfcab 100644 --- a/docs/investmoz/unification/traceability-and-manual.md +++ b/docs/investmoz/unification/traceability-and-manual.md @@ -9139,3 +9139,18 @@ Delta aprovado: oito testes de BD/438 asserções (quatro novos e quatro de filt Conferência visual no Edge exclusivamente em `127.0.0.2:18644`, base `main_visual_20261104_test`: duas fixtures claramente sintéticas criadas apenas em QA, uma por categoria; nenhuma coordenada/geometria criada. Hash de todas as oportunidades anteriores idêntico antes/depois. Alternância entre ambas conserva pesquisa provincial e duas fichas gerais; cada camada mostra a sua ficha, fonte/período/âmbito/limites e ausência de ponto. Infra-estruturas recuperada após recarregar. Recibo: `docs/investmoz/unification/thematic-map-local-20261104.json`; inventário, hashes, DOM e capturas em `outputs/thematic-map-20261104/`. Demonstração sintética não constitui cobertura económica factual ou publicação de dados reais. **ACT-02 permanece parcial.** Cobertura factual de recursos/infra-estruturas no destino, georreferenciação própria quando documentada, mão-de-obra, potencial económico quantitativo, restantes fontes temáticas e lacunas cartográficas continuam pendentes. Não se inferem estas camadas de descrições nem do número de oportunidades. Mantêm-se conferência remota e aceitação da equipa; V2-07/V2-10 não encerrados. Sem push, alteração activa, migração, publicação automática ou alteração de permissões/serviços. + + +### 04/11/2026 — ACT-20261029-02: mão-de-obra documentada e contexto económico + +Incremento qualitativo na main local sobre `21ebad73`, reutilizando mapa, evidências, síntese publicada, divulgação expressa e fontes/períodos existentes. Inventário na cópia QA: duas evidências públicas locais de actividade anteriores (fichas sintéticas 350/383), nenhuma de mão-de-obra/oferta/procura; recursos/infra-estruturas da demonstração anterior conservados. As declarações labour_demands/training_capacities têm acesso EducationAccess interno; emprego/indicadores financeiros canónicos não fazem parte da publicProjection. Observações de zonas conservam divulgação/unidades/metodologia próprias. Não foram expostas nem agregadas essas fontes privadas por existir revisão interna. As fontes oficiais aceites e as decisões de divulgação anteriores não foram reabertas. + +Acrescentada categoria explícita Mão-de-obra no editor e validação da evidência existente, sem migração ou reclassificação automática dos textos anteriores. Guardar, recuperar, pré-visualizar e confirmar publicação usam o percurso existente. A camada económica reutiliza apenas evidências públicas locais de actividade/oferta/procura, mantendo `source_category` original, facto, fonte, localizador, período, verificação, âmbito, cobertura e limitações. Agrupá-las para consulta é uma escolha de apresentação, não aprovação de viabilidade nem índice de potencial. + +Ambas as opções entram no selector do mapa único; a escolha é recuperada na sessão. Recursos/infra-estruturas, geometrias públicas, filtros territoriais, fichas sem ponto e contagens anteriores mantêm-se. Nenhuma posição é criada ou atribuída ao recurso/mão-de-obra. Necessidade ou qualificação não equivale a trabalhador disponível; previsão de emprego não é emprego efectivo. Contexto económico não calcula retorno, score ou viabilidade. Períodos/fontes incompatíveis permanecem separados, sem totais ou zero presumido. + +Delta aprovado: sete testes de BD/77 asserções (cinco do mapa, incluindo o novo caso; dois de publicação, um novo e um de acesso/fonte privada já existente), 22 testes frontend, PHPStan sem erros, tipos, Pint, lint e build QA aprovados. Teste do percurso: criar evidência workforce → recuperar → confirmar pela publicação existente → consultar mapa → alterar rascunho privado sem substituir a categoria/fonte públicas. Testes mantêm metodologia/internos fora do mapa, categorias originais, períodos distintos e ausência de quantidades/índices inferidos. Não se repetiu a bateria geral aceite. + +Conferência visual no Edge exclusivamente na QA `127.0.0.2:18644`/`main_visual_20261104_test`: contexto económico reutiliza a evidência de actividade já publicada da ficha 383, sintética e explicitamente identificada. Mão-de-obra usa uma única fixture nova, também sintética; escolha e fonte recuperadas após recarregar. Nenhuma coordenada/geometria criada; hash de todas as oportunidades anteriores idêntico antes/depois. Conferidos 36 artefactos dos três recibos anteriores, todos com SHA256 inalterado. Recibo: `docs/investmoz/unification/workforce-economic-map-local-20261104.json`; inventário, DOM, capturas e hashes em `outputs/workforce-economic-map-20261104/`. + +**ACT-02 permanece parcial.** Cobertura factual laboral/económica, séries, unidades e metodologia quantitativa comparável, georreferenciação própria documentada e cobertura cartográfica continuam pendentes. A demonstração sintética não mede oferta laboral nem potencial regional real. Mantêm-se conferência remota e aceitação da equipa; V2-07/V2-10 não encerrados. Sem push, migração, publicação automática, escrita activa, alteração de permissões ou reinício de serviços. diff --git a/docs/investmoz/unification/workforce-economic-map-local-20261104.json b/docs/investmoz/unification/workforce-economic-map-local-20261104.json new file mode 100644 index 00000000..ef1be3bb --- /dev/null +++ b/docs/investmoz/unification/workforce-economic-map-local-20261104.json @@ -0,0 +1,168 @@ +{ + "date": "2026-11-04", + "base_commit": "21ebad73", + "requirement": "ACT-20261029-02; V2-07/V2-10", + "scope": "Incremento qualitativo de mão-de-obra e contexto do potencial económico; main local", + "source_inventory": { + "database": "main_visual_20261104_test", + "public_local_evidence": { + "resource": 1, + "infrastructure": 1, + "workforce": 0, + "activity": 2, + "supply": 0, + "demand": 0 + }, + "private_sources_not_disclosed": [ + "labour_demands/training_capacities: EducationAccess authorization", + "canonical employment and financial payloads: not part of publicProjection", + "zone observations: separate disclosure/units/methodology; not automatically mixed with opportunity evidence" + ], + "existing_economic_sources": [ + { + "opportunity_id": 383, + "title": "SYNTHETIC QA — ideia de apoio em Cahora Bassa", + "category": "activity", + "source": "SYNTHETIC QA exemplar público da interface", + "period": "2026 — cenário sintético", + "scope": "Cahora Bassa — referência distrital existente" + }, + { + "opportunity_id": 350, + "title": "SYNTHETIC QA — ideia de apoio em Cahora Bassa", + "category": "activity", + "source": "SYNTHETIC QA exemplar público da interface", + "period": "2026 — cenário sintético", + "scope": "Cahora Bassa — referência distrital existente" + } + ] + }, + "implementation": [ + "Categoria workforce no editor/validação existentes; mesmos campos de fonte, período, âmbito e divulgação expressa.", + "Mapeamento de activity/supply/demand já públicos para a camada economic_potential, conservando source_category e cada fonte/período.", + "Apenas evidência publicada, seleccionada e de propósito local; rascunhos privados e metodologia excluídos.", + "Escolha de ambas as novas camadas recuperável na sessão; pontos/áreas limitados aos locais existentes das oportunidades.", + "Sem unidades, quantidades, disponibilidade laboral, índices ou retorno calculados a partir dos textos; sem agregação de períodos/fontes incompatíveis." + ], + "existing_private_sources": [ + "EducationAccess protege labour_demands/training_capacities; revisão interna não é publicação.", + "OpportunityCanonicalContract::publicProjection não divulga emprego/indicadores financeiros privados.", + "Observações de zonas conservam divulgação, unidades e metodologia próprias; não foram automaticamente misturadas." + ], + "contract": { + "route": "GET /api/v1/investment/map", + "field": "thematic_records.evidence", + "new_categories": [ + "workforce", + "economic_potential" + ], + "source_category": "categoria original explícita; recursos/infra-estruturas mantêm categoria anterior", + "version_compatibility": "Campos/fichas/históricos anteriores conservados; adição de categoria no rascunho existente, sem migração" + }, + "checks": { + "database_tests": 7, + "database_assertions": 77, + "map_tests": 5, + "publication_tests": 2, + "frontend_tests": 22, + "phpstan_errors": 0, + "types": "passed", + "pint": "passed", + "lint": "passed", + "qa_build": "passed; avisos de fonte opcional e dimensão de chunks" + }, + "visual": { + "url_base": "http://127.0.0.2:18644", + "database": "main_visual_20261104_test", + "economic_existing_opportunity_id": 383, + "economic_source_reused": "Evidência de actividade já pública, explicitamente sintética no snapshot QA; original preservado.", + "new_workforce_fixture_ids": [ + 534 + ], + "workforce_recovered_after_reload": true, + "new_coordinates": false, + "new_geometry": false, + "existing_opportunities_sha256": "90aaee1f9e47bb7793e18746fb1f6935bfb0a21329deca9c569707fadd2c04c6" + }, + "preservation": { + "prior_artifact_hashes_verified_unchanged": 36, + "accepted_full_database_tests_preserved_not_reexecuted": 1325, + "previous_pending_items_retained": true + }, + "pending": [ + "Cobertura factual laboral e económica real, séries comparáveis, unidades e metodologia quantitativa permanecem pendentes.", + "A camada económica é contexto documental, sem classificação de viabilidade ou score regional.", + "Procura de trabalho, formação, empregos previstos e trabalhadores disponíveis não são intercambiáveis.", + "Conferência remota, aceitação da equipa e cobertura cartográfica permanecem pendentes; ACT-02/V2-07/V2-10 não encerrados." + ], + "safety": { + "push": false, + "active_database_changed": false, + "migration": false, + "permissions_changed": false, + "automatic_publication": false, + "services_restarted": false + }, + "proofs": { + "outputs/workforce-economic-map-20261104/source-inventory-before-fixtures.json": { + "sha256": "cd8a8943bf86778e041cc3f2ced51467d44e6e83147c8a8215024a27431d2964", + "bytes": 1313 + }, + "outputs/workforce-economic-map-20261104/fixture-receipt.json": { + "sha256": "7d680fed74a70d4367f1a7d8e0af60a0b7c5e5f89bc3ff4a17be5e119d294dc0", + "bytes": 405 + }, + "outputs/workforce-economic-map-20261104/existing-economic-context.jpg": { + "sha256": "6f81b29cbefad611b035fe26324d4b876d5dd5a9499a6fbd8ee75e8b4416aec5", + "bytes": 157445 + }, + "outputs/workforce-economic-map-20261104/existing-economic-context.dom.txt": { + "sha256": "1eb2687e9a3e73f4b3ed9808a9328930c54ca08dd6270e7d2e7bff1cfbef7227", + "bytes": 20535 + }, + "outputs/workforce-economic-map-20261104/workforce-reopened.jpg": { + "sha256": "b4c919ca4bfc344cd4feb5adcbe4a483d241d549d75450ee62a8b3854ccc6f34", + "bytes": 161166 + }, + "outputs/workforce-economic-map-20261104/workforce-reopened.dom.txt": { + "sha256": "01c6081409ea36d88e01d1c8b9af558eb497e68b596fe8be1b45cdec437c7515", + "bytes": 13566 + }, + "outputs/main-db-qa-20261104/workforce-economic-map.log": { + "sha256": "c6fb7cddb76a2589f3c41dc3d144b0387573417ddd9afcff96f331b801f53c0f", + "bytes": 94 + }, + "outputs/main-db-qa-20261104/workforce-economic-map.junit.xml": { + "sha256": "5824abb4aaa07c37e9420802d82a8ed08c4cd1babc76c337043016eb098f37ec", + "bytes": 2374 + }, + "outputs/main-db-qa-20261104/workforce-publication.log": { + "sha256": "3b0cb219fb68177a1a026ac7c076fe0da81f40c20aa6a8415a54866a08895c9d", + "bytes": 95 + }, + "outputs/main-db-qa-20261104/workforce-publication.junit.xml": { + "sha256": "7ad274166f6520990241a82530c51ac662540907bcc831c83889190176a80eaf", + "bytes": 958 + }, + "outputs/main-db-qa-20261104/workforce-economic-frontend.log": { + "sha256": "02dc96a1acd6887ac3fdc282562e8e5579e05bcf29283330effdf5689619ee09", + "bytes": 2811 + }, + "outputs/main-db-qa-20261104/workforce-economic-phpstan.log": { + "sha256": "1997495ca8f04c2ec09798d55607d1a2f85e3338b772f258666465bf658032d2", + "bytes": 49 + }, + "outputs/main-db-qa-20261104/workforce-economic-types.log": { + "sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "bytes": 0 + }, + "outputs/main-db-qa-20261104/workforce-economic-lint.log": { + "sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "bytes": 0 + }, + "outputs/main-db-qa-20261104/workforce-economic-build.log": { + "sha256": "ec0f15282f2dfe3c3c8dbe349773a88adbd87efba7e26332cd55c34b8f21bf25", + "bytes": 31606 + } + } +} diff --git a/docs/investmoz/v2/auditoria-portal/backlog.md b/docs/investmoz/v2/auditoria-portal/backlog.md index 05d8b0c9..eda0d353 100644 --- a/docs/investmoz/v2/auditoria-portal/backlog.md +++ b/docs/investmoz/v2/auditoria-portal/backlog.md @@ -3447,3 +3447,18 @@ Delta aprovado: oito testes de BD/438 asserções (quatro novos e quatro de filt Conferência visual no Edge exclusivamente em `127.0.0.2:18644`, base `main_visual_20261104_test`: duas fixtures claramente sintéticas criadas apenas em QA, uma por categoria; nenhuma coordenada/geometria criada. Hash de todas as oportunidades anteriores idêntico antes/depois. Alternância entre ambas conserva pesquisa provincial e duas fichas gerais; cada camada mostra a sua ficha, fonte/período/âmbito/limites e ausência de ponto. Infra-estruturas recuperada após recarregar. Recibo: `docs/investmoz/unification/thematic-map-local-20261104.json`; inventário, hashes, DOM e capturas em `outputs/thematic-map-20261104/`. Demonstração sintética não constitui cobertura económica factual ou publicação de dados reais. **ACT-02 permanece parcial.** Cobertura factual de recursos/infra-estruturas no destino, georreferenciação própria quando documentada, mão-de-obra, potencial económico quantitativo, restantes fontes temáticas e lacunas cartográficas continuam pendentes. Não se inferem estas camadas de descrições nem do número de oportunidades. Mantêm-se conferência remota e aceitação da equipa; V2-07/V2-10 não encerrados. Sem push, alteração activa, migração, publicação automática ou alteração de permissões/serviços. + + +### 04/11/2026 — ACT-20261029-02: mão-de-obra documentada e contexto económico + +Incremento qualitativo na main local sobre `21ebad73`, reutilizando mapa, evidências, síntese publicada, divulgação expressa e fontes/períodos existentes. Inventário na cópia QA: duas evidências públicas locais de actividade anteriores (fichas sintéticas 350/383), nenhuma de mão-de-obra/oferta/procura; recursos/infra-estruturas da demonstração anterior conservados. As declarações labour_demands/training_capacities têm acesso EducationAccess interno; emprego/indicadores financeiros canónicos não fazem parte da publicProjection. Observações de zonas conservam divulgação/unidades/metodologia próprias. Não foram expostas nem agregadas essas fontes privadas por existir revisão interna. As fontes oficiais aceites e as decisões de divulgação anteriores não foram reabertas. + +Acrescentada categoria explícita Mão-de-obra no editor e validação da evidência existente, sem migração ou reclassificação automática dos textos anteriores. Guardar, recuperar, pré-visualizar e confirmar publicação usam o percurso existente. A camada económica reutiliza apenas evidências públicas locais de actividade/oferta/procura, mantendo `source_category` original, facto, fonte, localizador, período, verificação, âmbito, cobertura e limitações. Agrupá-las para consulta é uma escolha de apresentação, não aprovação de viabilidade nem índice de potencial. + +Ambas as opções entram no selector do mapa único; a escolha é recuperada na sessão. Recursos/infra-estruturas, geometrias públicas, filtros territoriais, fichas sem ponto e contagens anteriores mantêm-se. Nenhuma posição é criada ou atribuída ao recurso/mão-de-obra. Necessidade ou qualificação não equivale a trabalhador disponível; previsão de emprego não é emprego efectivo. Contexto económico não calcula retorno, score ou viabilidade. Períodos/fontes incompatíveis permanecem separados, sem totais ou zero presumido. + +Delta aprovado: sete testes de BD/77 asserções (cinco do mapa, incluindo o novo caso; dois de publicação, um novo e um de acesso/fonte privada já existente), 22 testes frontend, PHPStan sem erros, tipos, Pint, lint e build QA aprovados. Teste do percurso: criar evidência workforce → recuperar → confirmar pela publicação existente → consultar mapa → alterar rascunho privado sem substituir a categoria/fonte públicas. Testes mantêm metodologia/internos fora do mapa, categorias originais, períodos distintos e ausência de quantidades/índices inferidos. Não se repetiu a bateria geral aceite. + +Conferência visual no Edge exclusivamente na QA `127.0.0.2:18644`/`main_visual_20261104_test`: contexto económico reutiliza a evidência de actividade já publicada da ficha 383, sintética e explicitamente identificada. Mão-de-obra usa uma única fixture nova, também sintética; escolha e fonte recuperadas após recarregar. Nenhuma coordenada/geometria criada; hash de todas as oportunidades anteriores idêntico antes/depois. Conferidos 36 artefactos dos três recibos anteriores, todos com SHA256 inalterado. Recibo: `docs/investmoz/unification/workforce-economic-map-local-20261104.json`; inventário, DOM, capturas e hashes em `outputs/workforce-economic-map-20261104/`. + +**ACT-02 permanece parcial.** Cobertura factual laboral/económica, séries, unidades e metodologia quantitativa comparável, georreferenciação própria documentada e cobertura cartográfica continuam pendentes. A demonstração sintética não mede oferta laboral nem potencial regional real. Mantêm-se conferência remota e aceitação da equipa; V2-07/V2-10 não encerrados. Sem push, migração, publicação automática, escrita activa, alteração de permissões ou reinício de serviços. diff --git a/resources/js/components/proactive-investment.tsx b/resources/js/components/proactive-investment.tsx index 5bfdb46a..80f65e68 100644 --- a/resources/js/components/proactive-investment.tsx +++ b/resources/js/components/proactive-investment.tsx @@ -399,6 +399,8 @@ export function ProactiveInvestmentEditor({ infrastructure: 'Infra-estrutura', supply: 'Oferta', demand: 'Procura', + workforce: + 'Mão-de-obra — evidência documentada', }, (v) => change({ category: v }), )} diff --git a/resources/js/components/thematic-investment-map.tsx b/resources/js/components/thematic-investment-map.tsx index 568b85bd..de1d5819 100644 --- a/resources/js/components/thematic-investment-map.tsx +++ b/resources/js/components/thematic-investment-map.tsx @@ -14,7 +14,12 @@ export type ThematicMapRecord = { limitations: string | null; evidence: { id: string; - category: 'resource' | 'infrastructure'; + category: + | 'resource' + | 'infrastructure' + | 'workforce' + | 'economic_potential'; + source_category?: string; statement?: string | null; source?: string | null; locator?: string | null; @@ -27,6 +32,16 @@ export type ThematicMapRecord = { const labels = { resource: 'Recursos documentados', infrastructure: 'Infra-estruturas documentadas', + workforce: 'Mão-de-obra documentada', + economic_potential: 'Potencial económico — contexto documentado', +}; +const sourceLabels: Record = { + activity: 'Actividade', + supply: 'Oferta', + demand: 'Procura', + workforce: 'Mão-de-obra', + resource: 'Recurso', + infrastructure: 'Infra-estrutura', }; const value = (text?: string | null) => text?.trim() || 'Não indicado'; @@ -49,8 +64,7 @@ export default function ThematicInvestmentMap({ const saved = window.sessionStorage.getItem( 'investmoz-map-theme-v1', ); - if (saved === 'resource' || saved === 'infrastructure') - setTheme(saved); + if (saved && Object.hasOwn(labels, saved)) setTheme(saved); } catch { // The server and first client render use the same default layer. } @@ -129,9 +143,12 @@ export default function ThematicInvestmentMap({ pesquisa geral mantém os seus filtros e resultados.

- Mão-de-obra e potencial económico: sem camada quantitativa - nesta consulta; não são inferidos de descrições ou do número - de oportunidades. + Mão-de-obra: a evidência pode descrever necessidades ou + qualificações; não mede trabalhadores disponíveis. Potencial + económico: contexto de actividade, oferta e procura, sem + índice, previsão de retorno ou viabilidade calculados. + Fontes e períodos não são agregados como se fossem + comparáveis.

{truncated && (

@@ -152,13 +169,14 @@ export default function ThematicInvestmentMap({ ).length }{' '} sem ponto público utilizável. Ausência de evidência - não significa ausência do recurso. + não significa ausência de recursos ou actividade + económica.

{selected.length === 0 && (

- Sem evidência pública local de recursos ou - infra-estruturas para esta camada e estes - filtros. Não foram criadas posições no mapa. + Sem evidência pública local para esta camada e + estes filtros. Não foram criadas posições no + mapa.

)} {selected.length > 0 && ( @@ -207,6 +225,15 @@ export default function ThematicInvestmentMap({

{labels[proof.category]}

+ {proof.source_category && ( +

+ Categoria de origem:{' '} + {sourceLabels[ + proof + .source_category + ] ?? 'Não indicada'} +

+ )}

{value(proof.statement)}

diff --git a/tests/Feature/InvestmentThematicMapTest.php b/tests/Feature/InvestmentThematicMapTest.php index 735bd54b..3d9a195a 100644 --- a/tests/Feature/InvestmentThematicMapTest.php +++ b/tests/Feature/InvestmentThematicMapTest.php @@ -55,15 +55,34 @@ class InvestmentThematicMapTest extends TestCase $record = $this->record($prefix, [$this->proof(), $this->proof('infrastructure'), $this->proof('resource', 'methodology'), $this->proof('demand')]); $before = $record->fresh()->getRawOriginal('workflow_context'); $response = $this->getJson($this->url($prefix))->assertOk()->assertJsonPath('matched_count', 1) - ->assertJsonPath('thematic_records.0.id', $record->id)->assertJsonCount(2, 'thematic_records.0.evidence') + ->assertJsonPath('thematic_records.0.id', $record->id)->assertJsonCount(3, 'thematic_records.0.evidence') ->assertJsonPath('thematic_records.0.evidence.0.source', 'Synthetic public source') ->assertJsonPath('thematic_records.0.evidence.0.period', '2026') ->assertJsonPath('thematic_records.0.evidence.0.scope', 'Synthetic district') + ->assertJsonPath('thematic_records.0.evidence.2.category', 'economic_potential') + ->assertJsonPath('thematic_records.0.evidence.2.source_category', 'demand') ->assertJsonPath('thematic_records.0.state', 'hypothesis')->assertJsonCount(0, 'features')->assertJsonCount(1, 'unlocated'); $this->assertStringNotContainsString('PRIVATE-DRAFT-MUST-NOT-LEAK', $response->getContent()); $this->assertSame($before, $record->fresh()->getRawOriginal('workflow_context')); } + public function test_workforce_is_explicit_and_economic_context_preserves_each_source_without_inferred_totals(): void + { + $prefix = 'THEME-'.Str::uuid(); + $this->record($prefix, [$this->proof('workforce'), $this->proof('activity'), $this->proof('supply'), $this->proof('demand'), $this->proof('workforce', 'methodology')]); + $r = $this->getJson($this->url($prefix))->assertOk()->assertJsonCount(4, 'thematic_records.0.evidence') + ->assertJsonPath('thematic_records.0.evidence.0.category', 'workforce') + ->assertJsonPath('thematic_records.0.evidence.0.source_category', 'workforce') + ->assertJsonPath('thematic_records.0.state', 'hypothesis')->assertJsonCount(0, 'features')->json('thematic_records.0'); + foreach (['activity', 'supply', 'demand'] as $i => $source) { + $this->assertSame('economic_potential', $r['evidence'][$i + 1]['category']); + $this->assertSame($source, $r['evidence'][$i + 1]['source_category']); + $this->assertSame('2026', $r['evidence'][$i + 1]['period']); + } + $this->assertArrayNotHasKey('available_workers', $r); + $this->assertArrayNotHasKey('potential_score', $r); + } + public function test_unpublished_future_restricted_and_unselected_private_evidence_are_not_disclosed(): void { $prefix = 'THEME-'.Str::uuid(); diff --git a/tests/Feature/ProactiveInvestmentTest.php b/tests/Feature/ProactiveInvestmentTest.php index ea9afd88..a6521d70 100644 --- a/tests/Feature/ProactiveInvestmentTest.php +++ b/tests/Feature/ProactiveInvestmentTest.php @@ -62,6 +62,26 @@ final class ProactiveInvestmentTest extends TestCase return $input; } + public function test_workforce_evidence_saves_recovers_and_requires_existing_explicit_publication(): void + { + $input = F::input(); + $input['proactive']['evidence'][0]['category'] = 'workforce'; + $id = $this->postJson('/minhas-oportunidades/drafts', $input)->assertCreated()->json('id'); + $o = Opportunity::findOrFail($id); + $analysis = app(OpportunityManagement::class)->values($o)['proactive']; + $this->assertSame('workforce', $analysis['evidence'][0]['category']); + $this->assertNull(app(ProactiveInvestment::class)->publicData($o)); + $this->publish($o); + $this->getJson('/api/v1/investment/map?opportunity_id='.$id)->assertOk() + ->assertJsonPath('thematic_records.0.evidence.0.category', 'workforce') + ->assertJsonPath('thematic_records.0.evidence.0.source', $analysis['evidence'][0]['source']) + ->assertJsonCount(1, 'thematic_records.0.evidence')->assertJsonCount(0, 'features'); + $analysis['evidence'][0]['category'] = 'demand'; + $this->putJson('/minhas-oportunidades/'.$id.'/draft', ['etag' => app(OpportunityManagement::class)->etag($o->fresh()), 'proactive' => $analysis, + 'reason' => 'SYNTHETIC QA later private changes must not replace the published workforce source'])->assertOk(); + $this->getJson('/api/v1/investment/map?opportunity_id='.$id)->assertOk()->assertJsonPath('thematic_records.0.evidence.0.category', 'workforce'); + } + public function test_legacy_records_without_analysis_add_no_publication_lookup(): void { $o = new Opportunity; diff --git a/tests/frontend/thematic-investment-map.test.mjs b/tests/frontend/thematic-investment-map.test.mjs index 481b5fd0..d58ebcf1 100644 --- a/tests/frontend/thematic-investment-map.test.mjs +++ b/tests/frontend/thematic-investment-map.test.mjs @@ -137,7 +137,7 @@ test('default preserves every opportunity and one map, with sourced thematic evi 'Partial coverage', '2026-10-01', 'não representam a posição exacta', - 'não são inferidos', + 'sem índice', ]) assert.ok(html.includes(text), text); assert.match(html, /2 fichas com evidência temática pública/); @@ -216,7 +216,7 @@ test('changing query data never retains previous evidence; loading and incomplet ); assert.match(html, /Sem evidência pública local/); assert.match(html, /A resposta está limitada/); - assert.match(html, /não significa ausência do recurso/); + assert.match(html, /não significa ausência de recursos/); }); test('theme selection survives reload in the tab and unavailable or invalid storage does not break the map', () => { @@ -276,3 +276,63 @@ test('theme selection survives reload in the tab and unavailable or invalid stor else globalThis.window = previous; } }); + +test('workforce and economic context preserve source categories and incompatible periods without inferred totals or borrowed points', () => { + const Component = setup(); + const extra = [ + { + ...records[0], + id: 4, + title: 'Workforce evidence', + evidence: [{ ...proof('workforce'), source_category: 'workforce' }], + }, + { + ...records[1], + id: 5, + title: 'Economic context', + evidence: [ + { + ...proof('economic_potential'), + id: 'supply', + source_category: 'supply', + period: '2023', + }, + { + ...proof('economic_potential'), + id: 'demand', + source_category: 'demand', + period: '2026', + }, + ], + }, + ]; + const data = { ...props, records: [...records, ...extra] }; + flat(Component(data)) + .find((node) => node.type === 'select') + .props.onChange({ target: { value: 'workforce' } }); + let tree = Component(data); + assert.deepEqual( + flat(tree).find((node) => node.type === Map).props.points, + [], + ); + let html = renderToStaticMarkup(tree); + assert.match(html, /Workforce evidence/); + assert.match(html, /Categoria de origem: Mão-de-obra/); + assert.match(html, /não mede trabalhadores disponíveis/); + flat(tree) + .find((node) => node.type === 'select') + .props.onChange({ target: { value: 'economic_potential' } }); + tree = Component(data); + html = renderToStaticMarkup(tree); + assert.match(html, /Economic context/); + assert.match(html, /Categoria de origem: Oferta/); + assert.match(html, /Categoria de origem: Procura/); + assert.match(html, /2023/); + assert.match(html, /2026/); + assert.match(html, /sem índice, previsão de retorno ou viabilidade/); + assert.doesNotMatch(html, /Workforce evidence/); + assert.deepEqual( + flat(tree).find((node) => node.type === Map).props.points, + [], + ); +});